Thread overview
Using of core.sync.condition.Condition
Oct 21, 2011
Alexander
Oct 24, 2011
Sean Kelly
Oct 26, 2011
Alexander
October 21, 2011
Hi,

I've the code (see below) which produces an exception (SyncException "Unable to wait for condition")
unless "synchronized" is used when waiting on condition (Fedora Linux, 32 bit, DMD 2.055).

Do I do something wrong? Using "synchronized" when accessing anything that is synchronization object
by itself is a little bit counter-intuitive, IMHO.

---snip---
import std.conv;
import std.stdio;
import core.thread;
import core.sync.mutex;
import core.sync.condition;

__gshared Mutex         mutex;
__gshared Condition     cond;

void logs(string text)
{
        synchronized {
                writeln(text);
        }
}

void worker()
{
        logs("Worker started");
        while (true) {
                //synchronized (mutex)
                {
                        try {
                                cond.wait();
                        } catch (Exception ex) {
                                logs("Oops: %s" ~ to!string(ex));
                                return;
                        }
                }
                logs("Got notify");
        }
}

void main()
{
        mutex = new Mutex();
        cond = new Condition(mutex);
        (new Thread(&worker)).start();
        (new Thread(&worker)).start();
        (new Thread(&worker)).start();
        (new Thread(&worker)).start();
        Thread.sleep(dur!("msecs")(250));
        logs("Sending notify");
        cond.notifyAll();
        thread_joinAll();
}
---snip---

-- 
/Alexander
October 24, 2011
On Fri, 21 Oct 2011 14:32:15 -0400, Alexander <aldem+dmars@nk7.net> wrote:

> Hi,
>
> I've the code (see below) which produces an exception (SyncException "Unable to wait for condition")
> unless "synchronized" is used when waiting on condition (Fedora Linux, 32 bit, DMD 2.055).
>
> Do I do something wrong? Using "synchronized" when accessing anything that is synchronization object
> by itself is a little bit counter-intuitive, IMHO.
>
> ---snip---
> import std.conv;
> import std.stdio;
> import core.thread;
> import core.sync.mutex;
> import core.sync.condition;
>
> __gshared Mutex         mutex;
> __gshared Condition     cond;
>
> void logs(string text)
> {
>          synchronized {
>                  writeln(text);
>          }
> }
>
> void worker()
> {
>          logs("Worker started");
>          while (true) {
>                  //synchronized (mutex)
>                  {
>                          try {
>                                  cond.wait();
>                          } catch (Exception ex) {
>                                  logs("Oops: %s" ~ to!string(ex));
>                                  return;
>                          }
>                  }
>                  logs("Got notify");
>          }
> }
>
> void main()
> {
>          mutex = new Mutex();
>          cond = new Condition(mutex);
>          (new Thread(&worker)).start();
>          (new Thread(&worker)).start();
>          (new Thread(&worker)).start();
>          (new Thread(&worker)).start();
>          Thread.sleep(dur!("msecs")(250));
>          logs("Sending notify");
>          cond.notifyAll();
>          thread_joinAll();
> }
> ---snip---

When waiting on a condition, you must have its associative mutex locked, or Bad Things could happen.  You should also have the mutex locked when signaling the condition, but I don't think that's an absolute requirement.

The typical producer-consumer process works like this:

__gshared bool data;

void thread1()
{
   while(1)
   {
      synchronized(mutex)
      {
         if(!data)
         {
             data = true; // produce!
             condition.notify();
         }
      }
   }
}

void thread2()
{
   synchronized(mutex)
   {
      while(!data)
          condition.wait();
      data = false; // consume!
   }
}

The key here is, waiting on a condition atomically unlocks the mutex.  If waiting on a condition was allowed without locking the mutex, potentially thread2 could miss thread1's signal, and you encounter a deadlock!

-Steve
October 24, 2011
On Oct 24, 2011, at 7:12 AM, Steven Schveighoffer wrote:
> 
> When waiting on a condition, you must have its associative mutex locked, or Bad Things could happen.  You should also have the mutex locked when signaling the condition, but I don't think that's an absolute requirement.
> 
> The key here is, waiting on a condition atomically unlocks the mutex.  If waiting on a condition was allowed without locking the mutex, potentially thread2 could miss thread1's signal, and you encounter a deadlock!

Bartosz just posted a tutorial on Mutexes and Conditions.  How timely:

http://www.corensic.com/Learn/Resources/ConcurrencyTutorialPartSeven.aspx
October 26, 2011
On 24.10.2011 16:12, Steven Schveighoffer wrote:

> The key here is, waiting on a condition atomically unlocks the mutex. If waiting on a condition was allowed without locking the mutex, potentially thread2 could miss thread1's signal, and you encounter a deadlock!

  OK, thanks. For ages I didn't touch pthreads stuff, so I forgot this semantics completely :)

-- 
/Alexander