base::RefCounted should provide a way for subclasses to access its internal SequenceChecker |
||
Issue descriptionSay you have a base::RefCounted subclass that is internally non-thread-safe (i.e., beyond the reference count itself not being thread safe, the guts of your object are also not thread safe). Such as gfx::Image (see Issue 600229 ). So you add a SequenceChecker and call CheckCalledOnValidSequence() in all methods that read and write the internals. This catches bad cases like this: void CodeOnUIThread() { base::scoped_refptr<MyClass> my_obj(new MyClass(...)); my_obj.DoSomething(); PostTaskToFILEThread(base::BindOnce(&CodeOnFILEThread, my_obj)); my_obj.DoSomethingElse(); // Race with CodeOnFILEThread! } void CodeOnFILEThread(base::scoped_refptr<MyClass> my_obj) { my_obj.DoSomethingOrOther(); // Race with CodeOnUIThread! } But it also catches perfectly safe cases where the object is properly handed off to the other thread, like this: void CodeOnUIThread() { base::scoped_refptr<MyClass> my_obj(new MyClass(...)); my_obj.DoSomething(); PostTaskToFILEThread(base::BindOnce(&CodeOnFILEThread, std::move(my_obj))); } void CodeOnFILEThread(base::scoped_refptr<MyClass> my_obj) { my_obj.DoSomethingOrOther(); // Sole reference holder at this time. } To avoid false positives in the latter case, you have to introduce a method on your object to call SequenceChecker::DetachFromSequence, and call it before a hand-off. It turns out there's a mechanical rule to determine when to detach from sequence: whenever the reference count drops to 1, detach, and if the reference count is 1, do not attach. Because when the reference count is 1, only one thread can hold a reference to the object. If you follow this rule, you won't catch RAW pointers to the object being passed across threads, but that is obviously bad behaviour; we really just want to catch scoped_refptrs that share an object across threads. It turns out that base::RefCounted itself already follows this exact rule to automatically trap when the reference count changes. However, the above example DOES NOT change the reference count on the FILE thread so it won't automatically be caught. What we need is a way to expose RefCounted's internal sequence checker to subclasses so that they can manually perform checks in their own methods, and take advantage of the fact that RefCounted automatically detaches when the refcount drops to 1. NOTE: I tried a more automated approach, DCHECKing the sequence checker whenever get(), operator*() or operator->() is called on a scoped_refptr, but it caused all sorts of trouble including scoped_refptrs to various custom classes that don't inherit from RefCounted, and use of get() from contexts that can't see the full class definition. I think this approach is overkill, so I prefer to just provide a method for manual sequence checking by interested subclasses (and intend to use this in gfx::Image). Thus I propose a method: class RefCountedBase { protected: bool AccessIsThreadSafe() const { return ref_count_ <= 1 || CalledOnValidSequence(); } }; To be used thusly: class MyClass { public: void DoSomething() { DCHECK(AccessIsThreadSafe()); // Do this in each method. ... } };
,
Jul 24 2017
The following revision refers to this bug: https://chromium.googlesource.com/chromium/src.git/+/ba1643624e7d103cdc9f9dc86d94210e5387435a commit ba1643624e7d103cdc9f9dc86d94210e5387435a Author: Matt Giuca <mgiuca@chromium.org> Date: Mon Jul 24 07:56:10 2017 Expose RefCounted sequence checker to subclass and use in gfx::Image. This adds a Debug-only method base::RefCounted::IsOnValidSequence (protected) for subclasses to DCHECK whenever they want, to ensure they are being accessed in a thread-safe manner. This has the advantage over creating their own SequenceChecker of avoiding false positives when the object has been safely handed off to another thread (by std::moving a single reference object). gfx::Image's internal storage class now DCHECKs this on all method calls, to catch illegal use of images shared by multiple threads (e.g., https://crbug.com/596348 ). Bug: 600229 , 746792 Change-Id: I1de5e59d403b3b5abe1abd5ebc3a9d09db23949c Reviewed-on: https://chromium-review.googlesource.com/578750 Commit-Queue: Matt Giuca <mgiuca@chromium.org> Reviewed-by: danakj <danakj@chromium.org> Reviewed-by: Trent Apted <tapted@chromium.org> Cr-Commit-Position: refs/heads/master@{#488929} [modify] https://crrev.com/ba1643624e7d103cdc9f9dc86d94210e5387435a/base/memory/ref_counted.h [modify] https://crrev.com/ba1643624e7d103cdc9f9dc86d94210e5387435a/chrome/browser/web_applications/update_shortcut_worker_win.cc [modify] https://crrev.com/ba1643624e7d103cdc9f9dc86d94210e5387435a/ui/gfx/image/image.cc [modify] https://crrev.com/ba1643624e7d103cdc9f9dc86d94210e5387435a/ui/gfx/image/image.h [modify] https://crrev.com/ba1643624e7d103cdc9f9dc86d94210e5387435a/ui/gfx/image/image_family.cc [modify] https://crrev.com/ba1643624e7d103cdc9f9dc86d94210e5387435a/ui/gfx/image/image_family.h
,
Jul 25 2017
|
||
►
Sign in to add a comment |
||
Comment 1 by mgiuca@chromium.org
, Jul 20 2017