This is a bit of a hack. First, let's look at the code without that:
mExitRequested.store(true);
mLooperCondition.notify_all();
There's a possible race condition here. Some other code might have noticed that mExitRequested was false and started waiting for mLooperCondition right after we called notify_all.
The race would be:
- Other thread checks
mExitRequested, it's false.
- We set
mExitRequested to true.
- We call
mLooperCondition.notify_all.
- Other thread waits for
mLooperCondition.
- Oops. Waiting for notify that already happened.
But in order to wait for a condition variable, you must hold the associated mutex. So that can only happen if some other thread held the mLooperLock mutex. In fact, step 4 would really be: "Other thread releases mLooperLock and waits for mLooperCondition.
So, for this race to happen, it must happen precisely like this:
- Other thread acquires
mLooperLock.
- Other thread checks
mExitRequested, it's false.
- We set
mExitRequested to true.
- We call
mLooperCondition.notify_all.
- Other thread waits for
mLooperCondition, releasing mLooperLock.
- Oops. Waiting for notify that already happened.
So, if we change the code to:
mExitRequested.store(true);
{ std::lock_guard<Mutex> lock(mLooperLock); }
mLooperCondition.notify_all();
That ensures that no other thread could check mExitRequested and see false and then wait for mLooperCondition. Because the other thread would have to hold the mLooperLock lock through the whole process, which can't happen since we acquired it in the middle of that process.
Trying it again:
- Other thread acquires
mLooperLock.
- Other thread checks
mExitRequested, it's false.
- We set
mExitRequested to true.
- By acquiring and releasing
nLooperLock, we do not make any forward progress until the other thread releases mLooperLock.
- We call
mLooperCondition.notify_all.
Now, either the other thread blocks on the condition or it doesn't. If it doesn't, there's no problem. If it does, there's still no problem because the unlocking of mLooperLock is the condition variable's atomic "unlock and wait" operation, guaranteeing that it sees our notify.