4.3 No-starve mutex 79
4.3 No-starve mutex
In the previous section, we addressed what I’ll call categorical starvation,
in which one category of threads (readers) allows another category (writers) to
starve. At a more basic level, we have to address the issue of thread starvation,
which is the possibility that one thread might wait indefinitely while others
proceed.
For most concurrent applications, starvation is unacceptable, so we enforce
the requirement of bounded waiting, which means that the time a thread
waits on a semaphore (or anywhere else, for that matter) has to be provably
finite.
In part, starvation is the responsibility of the scheduler. Whenever multiple
threads are ready to run, the scheduler decides which one or, on a parallel
processor, which set of threads gets to run. If a thread is never scheduled, then
it will starve, no matter what we do with semaphores.
So in order to say anything about starvation, we have to start with some
assumptions about the scheduler. If we are willing to make a strong assumption,
we can assume that the scheduler uses one of the many algorithms that can
be proven to enforce bounded waiting. If we don’t know what algorithm the
scheduler uses, then we can get by with a weaker assumption:
Property 1: if there is only one thread that is ready to run, the
scheduler has to let it run.
If we can assume Property 1, then we can build a system that is provably
free of starvation. For example, if there are a finite number of threads, then any
program that contains a barrier cannot starve, since eventually all threads but
one will be waiting at the barrier, at which point the last thread has to run.
In general, though, it is non-trivial to write programs that are free from
starvation unless with make the stronger assumption:
Property 2: if a thread is ready to run, then the time it waits until
it runs is bounded.
In our discussion so far, we have been assuming Property 2 implicitly, and
we will continue to. On the other hand, you should know that many existing
systems use schedulers that do not guarantee this property strictly.
Even with Property 2, starvation rears its ugly head again when we introduce
semaphores. In the definition of a semaphore, we said that when one thread
executes signal, one of the waiting threads gets woken up. But we never said
which one. Again, in order to say anything about starvation, we have to make
assumptions about the behavior of semaphores.
The weakest assumption that makes it possible to avoid starvation is:
Property 3: if there are threads waiting on a semaphore when a
thread executes signal, then one of the waiting threads has to be
woken.