Two Workers Never Wake

Three worker threads run this loop; a fourth thread shuts the pool down.

std::mutex m;
std::condition_variable cv;
std::queue<Job> q;
bool done = false;

// each of 3 workers
for (;;) {
    std::unique_lock<std::mutex> lk(m);
    cv.wait(lk, []{ return !q.empty() || done; });
    if (done && q.empty()) return;
    Job j = std::move(q.front()); q.pop();
    lk.unlock();
    run(j);
}

// shutdown thread, queue already empty
{ std::lock_guard<std::mutex> lk(m); done = true; }
cv.notify_one();

The queue is empty and all three workers are waiting. What happens at shutdown?

A

One returns and two block, because notify_one must be called while holding m; moving it inside the lock_guard scope wakes all three.

B

One worker returns and the other two block forever, because notify_one unblocks a single waiter and nothing ever signals again.

C

All three return: the two that were not notified wake spuriously soon after, re-evaluate the predicate, see done, and exit.

D

None return: the predicate form of wait swallows the notification because done was already set before the call to notify_one.