New issue
Advanced search Search tips
Note: Color blocks (like or ) mean that a user may not be available. Tooltip shows the reason.

Issue 746792 link

Starred by 1 user

Issue metadata

Status: Fixed
Owner:
Long OOO (go/where-is-mgiuca)
Closed: Jul 2017
Components:
EstimatedDays: ----
NextAction: ----
OS: All
Pri: 3
Type: Feature

Blocking:
issue 600229



Sign in to add a comment

base::RefCounted should provide a way for subclasses to access its internal SequenceChecker

Project Member Reported by mgiuca@chromium.org, Jul 20 2017

Issue description

Say 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.
        ...
      }
    };
 

Comment 1 by mgiuca@chromium.org, Jul 20 2017

Blocking: 600229
Project Member

Comment 2 by bugdroid1@chromium.org, 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

Comment 3 by mgiuca@chromium.org, Jul 25 2017

Status: Fixed (was: Started)

Sign in to add a comment