Bug
NotificationQueue::wakeUpAll() only signals threads that are already blocked in waitDequeueNotification(). If a consumer thread has not yet entered the wait when wakeUpAll() is called, the signal is lost and the thread blocks indefinitely on the next waitDequeueNotification() call. This causes a deadlock during shutdown when the pattern recommended in the class documentation is followed:
- set a termination flag for every worker thread
- call the wakeUpAll() method
- join each worker thread
Root cause
wakeUpAll() iterates _waitQueue and signals each WaitInfo::nfAvailable event, then clears the queue. If a thread calls waitDequeueNotification() after wakeUpAll() returns, it creates a new WaitInfo, adds itself to the (now empty) _waitQueue, and blocks on nfAvailable.wait() with no one to wake it:
Thread A (shutdown) Thread B (worker)
───────────────────── ─────────────────────
while (!_stopped) { // _stopped is false
_stopped = true;
wakeUpAll():
lock(_mutex)
_waitQueue is empty → no-op
unlock(_mutex)
waitDequeueNotification():
lock(_mutex)
queue empty, create WaitInfo
push to _waitQueue
unlock(_mutex)
nfAvailable.wait() // blocks forever
_mainThread.join() // deadlock
Bug
NotificationQueue::wakeUpAll()only signals threads that are already blocked inwaitDequeueNotification(). If a consumer thread has not yet entered the wait whenwakeUpAll()is called, the signal is lost and the thread blocks indefinitely on the nextwaitDequeueNotification()call. This causes a deadlock during shutdown when the pattern recommended in the class documentation is followed:Root cause
wakeUpAll()iterates_waitQueueand signals eachWaitInfo::nfAvailableevent, then clears the queue. If a thread callswaitDequeueNotification()afterwakeUpAll()returns, it creates a newWaitInfo, adds itself to the (now empty)_waitQueue, and blocks onnfAvailable.wait()with no one to wake it: