Post by Ersek, LaszloDear List,
wrt. the discussion started under [0], I respectfully ask for help
interpreting the following statement from the pthread_cond_broadcast()
----v----
if predictable scheduling behavior is required, then that mutex shall be
locked by the thread calling pthread_cond_broadcast() or
pthread_cond_signal()
----^----
What does "predictable scheduling behavior" mean? What scheduling
characteristics may not hold if the mutex is not locked at the time of
signalling or broadcasting the condition variable?
Thank you very much,
Laszlo Ersek
[0] http://groups.google.com/group/comp.programming.threads/browse_thread/thread/c045203eae78d6ff/2e6b78fd2d43ce68#2e6b78fd2d43ce68
[1] http://www.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_broadcast.html
Basically, it was there due to some concerns between the real time and
threads "gangs" in the original 1003.4 working group that developed
POSIX threads.
The problem (from the realtime side) with condition variables is that if
you can signal/broadcast without holding the mutex, and any thread
currently running can acquire an unlocked mutex and check a predicate
without reference to the condition variable, then you can have an
indirect priority inversion.
That is, high priority consumer thread A blocks on a condition variable
because there are no entries on the queue; while lower priority consumer
thread B continues processing some request. Producer thread C now adds
an entry to the queue, unlocks the mutex, and signals the associated
condition variable to unblock the highest priority waiter. But before
the thread can be unblocked, scheduled, and contend for the mutex,
thread B completes its work, acquires the mutex (which has no waiters),
and removes the next work item. Higher priority thread A, when it is
scheduled, either finds the mutex still locked by thread B and needs to
reblock, or finds the mutex unlocked and "its" queue item gone. (On top
of this, it may have preempted thread B to find this out, so that no
work is getting done while we do the scheduling dance.) Even on a strict
realtime scheduling uniprocessor this is perfectly reasonable; there
might be a timeslice (e.g., producer and consumer B are SCHED_RR of
equal priority) between the unlock and the signal or broadcast; the
higher priority thread remains blocked and can't be considered by the
scheduler. (Many realtime designers would consider this bad design; and
in many ways it certainly is; but take it as just a simplified
conceptual sketch. ;-) )
Now, in "high performance thread" terms, this whole concept is generally
bad design; if the threads were symmetric and priority was associated
with the work (that is, higher priority work was simply at the head of
the queue to be handled by whatever consumer gets to it next), it
wouldn't matter that "the wrong thread" had the work item. (There is no
"wrong thread", only "wrong queued work"; and that can easily be managed
at the application level.) Still, we can imagine cases where this might
not meet the intent of the application designer. (Almost everyone would
agree "thread per client" is a bad model; yet it's amazingly pervasive
and generates an enormous amount of confusion and most of the poorly
performing code purporting to demonstrate that threads don't work.)
The quoted statement simply indicates that if the producer continues to
hold the mutex while waking consumer thread A, it will be able to run
and block on the mutex in priority order before consumer thread B can
acquire the mutex. (With wait morphing, the condition variable operation
immediately transfers the highest priority waiter to the mutex with
minimal overhead; but on any strict realtime implementation the act of
unblocking a high priority thread will immediately preempt a lower
priority thread and allow it to block on the mutex immediately.) Now,
consumer thread B may not contend for the mutex before the unlock, in
which case the "right thread" goes next, or it may contend anywhere
during this sequence and be sorted into the proper priority order along
with consumer thread A; in either case, the higher priority thread will
have the first chance at the queue.
That's what "predictable scheduling behavior" means in this context. In
more pragmatic terms, I'm pretty sure this means that someone on the
realtime side of the virtual aisle thought it was a bad idea to
recommend signaling "outside" the mutex, and the working group
compromised by adding that statement, which was sufficient to ward off
(or possibly to remove) a formal objection to the draft standard. This
is how a lot of the text in the standard grew.