Re: wait and spurious wakeups
On 27 Nov, 21:14, Eric Sosman <esos...@ieee-dot-org.invalid> wrote:
special attention to the "Other reasons for verifying the invariant"
Here it is:
Practical reasons exist for checking the invariant after a return from
a wait other than spurious wakeups. For example, a waked-up thread may
not be scheduled immediately after the wake up, but be at the mercy of
the system scheduler. A scheduler may preempt a process abruptly or
schedule other threads. It may be the case that in the mean time, an
external entity (another process, hardware) has invalidated the
invariant assumption. Wrapping the wait with a loop avoids such cases.
it may not apply to your very simple situation as things
You're right, it doesn't.
but what if the next version of this third-party API
grows a few new features? You may as well insure against the
future, when the insurance is as cheap as writing `while' instead
I still don't understand. [...]
The situation you describe is unusually simple. So simple
that there seems no technical reason to use threads at all (you
mention that the multi-thread framework was imposed by external
forces). In your simple situation, the "wrong" way will work
just fine -- but will your situation remain this simple forever?
Useful programs have a way of evolving, of extending, of
finding themselves employed in new circumstances. Ponder a
few of the kinds of things that might happen:
- Somebody decides that the single "supplier" thread makes
poor use of his multi-core CPU, and decides to divide the
work across eight suppliers in hopes of better throughput.
Your condition for proceeding changes from "The supplier
is no longer active" to "The count of active suppliers
has gone to zero." As each supplier finishes it will call
notify() just in case it was the last, and all but the
last awakening will be a false alarm.
- An additional "consumer" is added, maybe to do auditing
or logging or something. This consumer is also interested
in the list of items, but wants to be awakened every time
an item is added. Your original "accumulator" and the new
consumer both wait() on the list, and the supplier now
calls notifyAll() for every new item and for the "poison
pill" at the end, and all but one of these is a false alarm
as far as your accumulator thread is concerned.
- The same external forces that dictated threading to begin
with may dictate weird changes that will require some
threads to call notify() before it's really time for your
accumulator to proceed. Your accumulator must be prepared
to be awakened prematurely and to go back to sleep.
Here's the nub: The caller of notify() doesn't know what the
caller of wait() is waiting for. That's the waiter's business,
after all, expressed in the waiter's code. All the notifier
needs to know is that the change it has just made to some object
*may* be *part* of something another thread waits for, so it
calls notify() to give the waiter a chance to make its own decision.
Some of those awakenings may be "genuine" in the sense that notify()
was truly called, but "spurious" in the sense that the condition
being waited for does not yet hold. It's a kind of encapsulation,
if you like, that limits the amount of shared knowledge that must
be spread around to the many parts of your program.
Summary: Always use
while (! readyToGo())
.... because it will always be safe, no matter what happens. In
absurdly simple situations you could change `if' to `while', but
why tempt Fate? In any case it is *never* correct to write
thing.wait(); // unguarded wait is R-O-N-G!
.... and the Google phrase for this error is "lost wakeup."