www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Proposal: adding condition variable to object monitors

reply Jeremie Pelletier <jeremiep gmail.com> writes:
Content-Type: text/plain

I would like to propose the addition of condition variables to the language, or
at least to the standard library. They complement the current monitor's mutex
very well, are only allocated when needed and can even be part of the base
Object class.

I've included the basic implementation i currently use on the language runtime
I am working on in my spare time (which is a fork of phobos from about a year
ago). It supports vista and posix so far, although I didn't test the posix one
yet.

The included file also contains a working unittest with sample usage.

Here is what I added to my Object declaration for even more convenience:
---
abstract class Object {
	// [...]

	synchronized void Wait(uint interval = -1) {
		(cast(Monitor*)this.__monitor).Wait(interval);
	}
	synchronized void Notify() {
		(cast(Monitor*)this.__monitor).Notify();
	}
	synchronized void NotifyAll() {
		(cast(Monitor*)this.__monitor).NotifyAll();
	}
}

private import dlib.Monitor;
---
Mar 14 2009
next sibling parent reply Sean Kelly <sean invisibleduck.org> writes:
They're already in druntime for D2, though they haven't been distributed 
yet (dunno why).  And they can act like they're built in:

class C
{
     Mutex     m;
     Condition c;

     this()
     {
         // make m this object's monitor
         m = new Mutex( this );
         c = new Condition( m );
     }

     synchronized void foo()
     {
         // m is locked
         c.notify();
     }
}
Mar 15 2009
next sibling parent reply Jeremie Pelletier <jeremiep gmail.com> writes:
Sean Kelly Wrote:

 They're already in druntime for D2, though they haven't been distributed 
 yet (dunno why).  And they can act like they're built in:
 
 class C
 {
      Mutex     m;
      Condition c;
 
      this()
      {
          // make m this object's monitor
          m = new Mutex( this );
          c = new Condition( m );
      }
 
      synchronized void foo()
      {
          // m is locked
          c.notify();
      }
 }

 Oh, on Windows, condition variables were added in Vista, so that code 
 won't work on XP or earlier.

Hmm, I wasn't aware of that, it's been a while since I last checked the druntime project to tell the truth (my runtime has been running the full set of D features for quite some time now). But my proposal was to make both mutexes and conditions completely transparent, only exposing wait, notify and notifyall through Object, as they are implemented directly in the object's hidden monitor. The way druntime does it from your example is explicit: both the mutex and condition have to be manually declared and set, which may be because automatic object monitors are still allocated through alloc/free. Moreover, if only a handful of instanciated objects uses the condition, there's wasted memory for the condition. I heavily modified my runtime to allow most of it to completely remove it's usage of the C alloc and free routines (i think only memory pool structs still use these), so I implemented my condition variable directly into the monitor struct, the monitor is already created lazily for the object and so is the condition for the monitor. And I am aware conditions were added in vista, the file which runs a check at startup for vista. I just haven't coded a fallback for xp and down yet ;)
Mar 15 2009
parent reply Sean Kelly <sean invisibleduck.org> writes:
Jeremie Pelletier wrote:
 Sean Kelly Wrote:
 
 They're already in druntime for D2, though they haven't been distributed 
 yet (dunno why).  And they can act like they're built in:

 class C
 {
      Mutex     m;
      Condition c;

      this()
      {
          // make m this object's monitor
          m = new Mutex( this );
          c = new Condition( m );
      }

      synchronized void foo()
      {
          // m is locked
          c.notify();
      }
 }

 Oh, on Windows, condition variables were added in Vista, so that code 
 won't work on XP or earlier.

Hmm, I wasn't aware of that, it's been a while since I last checked the druntime project to tell the truth (my runtime has been running the full set of D features for quite some time now). But my proposal was to make both mutexes and conditions completely transparent, only exposing wait, notify and notifyall through Object, as they are implemented directly in the object's hidden monitor.

I considered this as well, but it imposes limitations that aren't present with the current approach. It's possible to have more than one condition associated with a particular mutex, for example, and to plug in a shared mutex for interprocess synchronization using 'synchronized'.
 The way druntime does it from your example is explicit: both the mutex and
condition have to be manually declared and set, which may be because automatic
object monitors are still allocated through alloc/free. Moreover, if only a
handful of instanciated objects uses the condition, there's wasted memory for
the condition.

They could always be added to a project-specific base class. The additional memory allocations are still an issue I suppose, but even normal monitors are allocated on the heap, even if it is via malloc.
 I heavily modified my runtime to allow most of it to completely remove it's
usage of the C alloc and free routines (i think only memory pool structs still
use these), so I implemented my condition variable directly into the monitor
struct, the monitor is already created lazily for the object and so is the
condition for the monitor.

So the monitor struct is still dynamically allocated, correct?
 And I am aware conditions were added in vista, the file which runs a check at
startup for vista. I just haven't coded a fallback for xp and down yet ;)

Just wanted to make sure you were aware of that limitation :-) Creating a correct condvar implementation is non-trivial, so it's worth being aware of.
Mar 15 2009
next sibling parent Jeremie Pelletier <jeremiep gmail.com> writes:
Sean Kelly Wrote:

 I considered this as well, but it imposes limitations that aren't 
 present with the current approach.  It's possible to have more than one 
 condition associated with a particular mutex, for example, and to plug 
 in a shared mutex for interprocess synchronization using 'synchronized'.

That's a good point, I haven't come across such cases yet. I could be solved using a ChainedMonitor wrapper, but that would still require explicit initialization.
 They could always be added to a project-specific base class.  The 
 additional memory allocations are still an issue I suppose, but even 
 normal monitors are allocated on the heap, even if it is via malloc.

That's also true, but this still requires explicitly setting the base class, Object is set as the base class implicitly. The issue with the alloc/free memory allocations is tracking the monitors so they get freed, garbage collection is automatic. I haven't seen benchmarks comparing alloc/free to D's new, but I bet D wins.
 So the monitor struct is still dynamically allocated, correct?

Yes, lazily allocated on the garbage collected heap, the only monitor using alloc is the one needed to bootstrap the memory manager. I used to maintain a freelist of monitors but I dropped the code in favor of centralizing memory to the GC which already maintains freelists. I added pool caching to freelist entries to speed it up a lot, all allocations have at least 16 bytes anyways so there's room to cache data. The overhead of setting a pool pointer on a freelist entry is much smaller than querying the pool after reallocating that entry. I've also centralized all the D ABI which uses memory routines into the manager itself to greatly reduce the overhead. I haven't looked at the state of the GC as of now, but if you're interested in seeing my changes I could email you the file, although it's really a different beast almost entirely.
Mar 15 2009
prev sibling parent Sean Kelly <sean invisibleduck.org> writes:
Graham St Jack wrote:
 class C
 {
      Mutex     m;
      Condition c;

      this()
      {
          // make m this object's monitor
          m = new Mutex( this );
          c = new Condition( m );
      }

      synchronized void foo()
      {
          // m is locked
          c.notify();
      }
 }



I like this approach, and agree that it is very useful to be able to have more than one condition share the same mutex. I don't mind the handraulic creation of the mutex and condition when you are using a condition, because of the added flexibility and the fact that it doesn't come up much. Being able to use the Object's monitor for the mutex is really good too. Please release it soon!

It was meant to be in the last D2 release and something happened with the script that assembles the zipfile. I'll make double sure it's in this release.
Mar 20 2009
prev sibling parent Graham St Jack <graham.stjack internode.on.net> writes:
 class C
 {
      Mutex     m;
      Condition c;

      this()
      {
          // make m this object's monitor
          m = new Mutex( this );
          c = new Condition( m );
      }

      synchronized void foo()
      {
          // m is locked
          c.notify();
      }
 }



I like this approach, and agree that it is very useful to be able to have more than one condition share the same mutex. I don't mind the handraulic creation of the mutex and condition when you are using a condition, because of the added flexibility and the fact that it doesn't come up much. Being able to use the Object's monitor for the mutex is really good too. Please release it soon!
Mar 19 2009
prev sibling parent Sean Kelly <sean invisibleduck.org> writes:
Oh, on Windows, condition variables were added in Vista, so that code 
won't work on XP or earlier.
Mar 15 2009