"if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {"

96 views
Skip to first unread message

Pavel Feldman

unread,
Dec 15, 2014, 2:39:02 AM12/15/14
to chromium-dev, Kinuko Yasuda
I was recently reviewing the https://codereview.chromium.org/761923004/ and noticed that the following pattern was used extensively in the code:

void DoSomething() {
    if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
        BrowserThread::PostTask(BrowserThread::UI,
            FROM_HERE,
            base::Bind(DoSomething);
        return;
    }
    ...
}

I suggested getting rid of them, but kinuko@ referred to the many of those we already have in the code and suggested to call chromium-dev for helphttps://code.google.com/p/chromium/codesearch#search/&q=%22if%20(!BrowserThread::CurrentlyOn(BrowserThread::UI))%20%7B%22&sq=package:chromium&type=cs.

So here I am, I think this kind of a pattern should be avoided in the general case:

Thread-safe APIs are probably Ok
- Complex threaded call site structures accompanied with comments are probably Ok
- But "I don't want to think what thread I am on (even though it clearly should be a specific thread), I'll just re-post on UI to make things look simple" is definitely not Ok: https://code.google.com/p/chromium/codesearch#chromium/src/content/browser/service_worker/embedded_worker_instance.cc&sq=package:chromium&type=cs&l=60&rcl=1418589580

Is my understanding correct and we strongly discourage the use of the pattern for the general case and prefer the DCHECKs instead?

Regards
Pavel

Ryan Sleevi

unread,
Dec 15, 2014, 2:55:00 AM12/15/14
to Pavel Feldman, Kinuko Yasuda, Chromium-dev

> --
> --
> Chromium Developers mailing list: chromi...@chromium.org
> View archives, change email options, or unsubscribe:
> http://groups.google.com/a/chromium.org/group/chromium-dev

Right, in general, you should design your classes with explicit and well specified threading guarantees, and it should be documented what those are.

As willchan@ has ranted many times, the best design for a class is to have its external interfaces live on a single thread. If there are multiple threads involve, you should try to hide that detail from others, such as through the use of an inner class that is managed exclusively by the parent.

The biggest reason for that, beyond the simplicity of modeling the thread interactions, is because lifetimes should be well defined and explicit whenever possible, and an object that lives on multiple threads by nature introduces significant complexity and ordering requirements.

A DCHECK - or, better (in most cases), a ThreadWatcher member - helps you programmatically enforce the threading restrictions of your class. Assuming your code is well tested and with good coverage, it should make it clear when someone tries to use your object while developing something that they're holding it wrong.

For free functions, it is more complicated. Many times, the incantation you mention is due to the objects they are interacting with having underspecified guarantees, and thus the hop out of fear, uncertainty, or an abundance of caution. For classes / events that you're interacting with that have well defined semantics, they should do exactly as you say - DCHECK the postconditions of the source event / preconditions of the method and then carry on.

If you find a class you're using doesn't have well defined semantics, or that you're unsure, or that you find you're getting events in a way that surprises you, those are all signs of bugs (documentation or implementation) and design smells in the class you're using, and the Googley/Chromey thing would be to fix/refactor/document that code, and then put strong assertions like DCHECKs in your code.

Kinuko Yasuda

unread,
Dec 15, 2014, 4:14:48 AM12/15/14
to rsl...@chromium.org, Pavel Feldman, Chromium-dev
On Mon, Dec 15, 2014 at 4:54 PM, Ryan Sleevi <rsl...@chromium.org> wrote:
On Dec 14, 2014 11:38 PM, "Pavel Feldman" <pfel...@chromium.org> wrote:
>
> I was recently reviewing the https://codereview.chromium.org/761923004/ and noticed that the following pattern was used extensively in the code:
>
> void DoSomething() {
>     if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
>         BrowserThread::PostTask(BrowserThread::UI,
>             FROM_HERE,
>             base::Bind(DoSomething);
>         return;
>     }
>     ...
> }

As far as I know this pattern is often used either when (except for the case where the callsite class is intended to be thread-safe):

- The callsite class is intended to live on a specific thread A
- It wants to notify class B which lives on thread B (and it's fine if it's already dead when it tries to do so)
- The small static function DoSomething() is used to glue these loosely connected two classes while hiding threading details from the call site class

So both class A and class B have clear thread semantics, while DoSomething() just glues them (and the function itself doesn't really have any thread restrictions).  I don't really feel this is the case 'the classes don't have well defined semantics', though I admit that this could probably have better comments and DCHECKs.

We could easily add a better documentation/comment and an assertion like this:

// This must be called on thread A and then forwarded on thread B.
void DoSomething() {
  if (!CurrentlyOn(B)) {
     DCHECK(CurrentlyOn(A));
     PostTask(B, FROM_HERE, base::Bind(&DoSomething));
     return;
  }
  ...
}

Does this look unconfusing enough?

Alternatively we could 1) split this into two functions, one that is always called on thread A and the other that is called on thread B, or 2) always embed 'PostTask' in the call site and allow the function to run only on thread B.  My personal feeling is that 1) makes the code unnecessarily long & complex, while 2) is simpler but loses some benefits of hiding threading details from the call site class.  I'm open to other suggestions.

Ryan Sleevi

unread,
Dec 15, 2014, 4:24:02 AM12/15/14
to Kinuko Yasuda, Pavel Feldman, Chromium-dev

So, while readability is certainly subjective, I tend to find it easier to clearly call out in the style of 1.

DoSomethingOnIOThread(...) {
  DCHECK(CurrentlyOn(IO));
  PostTask(UI, FROM_HERE, base::Bind(DoSomethingOnUIThread)));
}

DoSomethingOnUIThread(...) {
  DCHECK(CurrentlyOn(UI));
  ...
}

Not only do I find this more readable, but it actually ensures that your tests will test the right thing. Depending on which test harness you're using, you may find CurrentlyOn(A) == CurrentlyOn(B) == true when testing, but false when in real code.

The above two-method pattern makes it explicit that there should always be a PostTask yield in between, which can help shake out the subtle lifetime bugs inevitably introduced in multithreaded code.

The only time I recommend the if (!CurrentlyOn(..)) pattern is when you explicitly want to leverage this fact. For example, on Linux, our NSS TLS sockets are multithreaded, due to library limitations, whereas on Windows/Mac, they were single threaded. The if pattern helped to make this code easier to flow for single and multithreaded code, but at the cost of complexity.

To ensure you're always fully testing the right behaviors, I recommend the two-function approach. It is the least likely to have subtle surprises.

Kinuko Yasuda

unread,
Jan 7, 2015, 5:34:20 AM1/7/15
to rsl...@chromium.org, Pavel Feldman, Chromium-dev
To close the loop: I'm going to remove this pattern when the code is not meant to be truly multi-threaded (I'll leave it otherwise), in order to make the intended threading model more explicit.  CL is here: https://codereview.chromium.org/796393005/

One more open question is whether we want to note this in our coding style or somewhere on the wiki to broadly discourage this pattern.  Maybe we can add a note in 'Subtle threading bugs and patterns to avoid them' page?
Reply all
Reply to author
Forward
0 new messages