Re: Monitor.Wait(object, timeout) confusion

From: Jon Skeet [C# MVP] (skeet_at_pobox.com)
Date: 10/21/04


Date: Thu, 21 Oct 2004 15:36:57 +0100

Axel Müller <axel@mueller-hartefeld.de> wrote:
> the documentation is definitely NOT WRONG !

Well, while the detailed explanation is correct, the summary is very
misleading.

> > If I understand it (and my vocabulary is probably a little off):
> >
> > // releases the lock on the object and waits until it is singalled or
> > until the time specified elapses. It is then moved to the ready queue,
> > and blocks until it can reaquire the lock.
> > public static bool Wait(object, int timeout);
>
> Obviousley you did not unstand the whole sychronization process :

Actually, I think he understands it pretty well...

> First you must Monitor.Enter the lock-object before you can
> call Monitor.Wait .
> If you call Monitor.Wait, the lock-object will be released and
> another thread from the lock queue for the lock-object or
> from a Monitor.Enter call will get the lock-object.

So far we're agreed.

> The Thread that called Monitor.Wait is placed at the end in a
> dedicated queue associated with the lock, called 'wait queue'.
> All the pending threads in the 'wait queue' are blocked.
> If a timeout happened to the waiting thread it will be removed
> from that wait queue and the Monitor.Wait method will return.

Not immediately it won't - it won't return until it has regained the
lock.

> In the case that another thread ( owner of the lock object )
> calls Monitor.Pulse :
> The first waiting thread in the wait queue will removed from
> this queue and will be added to the end of the lock queue,
> REGARDLESS if it has an INFINITE timeout-Value or
> not !!

And that's what the summary fails to mention. It doesn't talk about
pulsing at all - it just says that the method releases the lock and
blocks until the lock has been reacquired.

> If this thread in the lock queue later gets the lock object,
> the Monitor.Wait will return ( regardless of an infinite
> timeout-value for his Monitor.Wait-Method !)

Sure.
 
> > Furthermore, under the "remarks", I believe the text is just
> > completely wrong. For instance:
>
> NO ! IT IS COMPLETELY RIGHT !
>
> > "Once the specified time has elapsed, this method returns a value that
> > indicates whether or not the lock has been reacquired by the caller."
> >
> > This is not true, the method could very well block indefinately!!!
>
> NO, this is true !!!

Again, I disagree. The method has to block until it reacquires the
lock, whether or not it's been pulsed. The docs actually later say
this:

<quote>
However, if millisecondsTimeout elapses before another thread invokes
this object's Pulse or PulseAll method, the original thread is moved to
the ready queue in order to regain the lock.
</quote>

Putting the thread back to the ready queue is very different from
returning just a value to indicate whether or not the lock has been
reacquired. The lock will *always* have been reacquired (leaving aside
some interesting issues to do with Thread.Abort or Interrupt).

The first part of the documentation implies that if you call
Monitor.Wait (foo, 1000) the call will return after a second, pretty
much. As the OP wrote, it could actually block indefinitely, as
demonstrated below:

using System;
using System.Threading;

class Test
{
    static object foo = new object();
    
    static void Main()
    {
        new Thread (new ThreadStart(AcquireAndWait)).Start();
        Thread.Sleep (200);
        
        lock (foo)
        {
            while (true)
            {
                Console.WriteLine ("First thread sleeping");
                Thread.Sleep (5000);
            }
        }
    }
    
    static void AcquireAndWait()
    {
        lock (foo)
        {
            Console.WriteLine ("Second thread waiting");
            Monitor.Wait (foo, 1000);
            Console.WriteLine ("Second thread finished waiting");
        }
    }
}

Please run the program above and tell us whether or not it blocks
indefinitely for you, contrary to the documentation which states:

<quote>
This method behaves identically to Wait(Object), except that it does
not block indefinitely unless Infinite is specified for the
millisecondsTimeout parameter.
</quote>

-- 
Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too


Relevant Pages

  • Re: Seeing lock order reversal
    ... 21 vm page queue free mutex -- ... 18 UMA zone -- ... 18 sleep mtxpool -- ... 15 process lock -- ...
    (freebsd-current)
  • Re: location of bioq lock
    ... queue is owned by the driver, and the locking scheme remains the same. ... from a different scheduler than the default, it can be easily plugged in. ... process you have to lock each queue before playing with it. ...
    (freebsd-current)
  • Re: thread-safety
    ... but that would depend on the implementation of your Queue class. ... lock { ... It's odd for you to be passing a WaitHandle of any sort to the Monitor class; if you've got a WaitHandle, you would normally just wait on _that_, rather than using the WaitHandle simply as a synchronization object for Monitor. ... It would be a problem to call Monitor.Pulseif no other thread is waiting at a call to Monitor.Wait, because then the waiting thread would miss the Pulse. ...
    (microsoft.public.dotnet.languages.csharp)
  • Re: thread communication
    ... I changed this into a simple class which has a SyncRoot which I can Wait and Pulse, and then some code to process the queue. ... One thing I noticed in your code though, in your desire to move the actual processing outside of the lock, you have introduced an inefficiency in the synchronization. ... IFileProcessor processor = Queue.Dequeue; ... Especially with the above alternative, you could easily maintain a local Queue to which all of the public Queue elements are copied, and then just loop while unlocked until the local Queue is empty, adding the "ready" items back into the local Queue as you find them. ...
    (microsoft.public.dotnet.languages.csharp)
  • Re: [GIT PULL] block/splice bits for 2.6.29
    ... Fix small typo in bio.h's documentation ... This patch is wrong, in fact Vergard has noted this in patch log. ... prefer just unifying the lock ordering. ...
    (Linux-Kernel)