Monitor.Pulse allows thread race

  • Thread starter Thread starter Guest
  • Start date Start date
G

Guest

Here is a quote from MSDN regarding the behavior of Monitor.Pulse:

“The thread that currently owns the lock on the specified object invokes
this method to signal the next thread in line for the lock. Upon receiving
the pulse, the waiting thread is moved to the ready queue. When the thread
that invoked Pulse releases the lock, the next thread in the ready queue
(which is not necessarily the thread that was pulsed) acquires the lock.â€

Evidence shows that when thread A pulses an object for which thread B is
waiting within a locked region of that object, and then thread A leaves the
locked region of that object, thread A can repossess the object's locked
region before thread B demonstrates ownership of that region. This seems to
contradict MSDN notes above.

Given:
Thread B performs: lock(x) { ... Monitor.Wait(x) ... }

Thread A performs: while(1) { lock(x) { ... Monitor.Pulse(x) ... } }

Thread B will be starved out waiting for the mutex on x and there is no
sequencing guarantee that thread B will finish executing before thread A
reaquires the mutex. Is this a bug or intended behavior?
 
[...]
Given:
Thread B performs: lock(x) { ... Monitor.Wait(x) ... }

Thread A performs: while(1) { lock(x) { ... Monitor.Pulse(x) ... } }

Thread B will be starved out waiting for the mutex on x and there is no
sequencing guarantee that thread B will finish executing before thread A
reaquires the mutex. Is this a bug or intended behavior?

I don't know enough about the Monitor class to answer the specific
question. However, assuming it works in conjunction with the usual
thread synchronization and scheduling mechanisms, then I would expect
thread A to continue running until it explicitly blocks or exhausts its
timeslice. So thread B won't get to acquire the lock until that point.

If thread A still has the lock when its timeslice runs out, thread B
still won't be able to acquire the lock.

The docs might not be very clear on that, but it does seem to me to be
a reasonable expectation based on how the rest of Windows works.

Assuming the other threads are all of the same priority as thread A,
then calling Sleep(0) in thread A after releasing the lock should allow
other threads to get their chance at it. If those other threads are of
lower priority, then calling Sleep(1) would be necessary.

I can't say for sure what Microsoft would say, but assuming the intent
is for Monitor to be reasonably lightweight, I would call this
"intended behavior". The alternative is for Monitor to have to do
extra work to decide what happens when a lock is released or acquired
and/or always yield to other threads when releasing a lock, either of
which could impair performance when not needed.

Pete
 
We rather guess that's what happens now, but until recently it appeared to
behave like thread B wasn't in the queue for the lock at the time of the
pulse(), so A got the lock as intended. This matched our interpretation of
the docs.
 
Back
Top