Condition variables and monitors for Delphi

A condition variable is a threading abstraction which can help in implementing robust multi-threaded code. Condition variables are core features of Java (Object.wait, .notify, .notifyAll) and .NET (Monitor.Wait, .Pulse and .PulseAll) but we needed an implementation for Delphi. I couldn’t find one so we wrote my own. If you are interested in (using) the code, we made it publicly available on the following page:

Win32 Condition Variables and Monitors for Delphi

Comments are welcome.

This entry was posted in DelphiFeeds.com, Programming. Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

14 Comments

  1. JustSomeDane
    Posted August 20, 2007 at 14:38 | Permalink

    Coincidentally (I presume), Per Brinch Hansen, the inventor of the monitor concept, died less than a week before your release.

  2. Ivan Levashew
    Posted September 22, 2008 at 07:17 | Permalink

    Thanks, guys. After two years of multithread development with c+pthreads and then Ada I have found Delphi’s parallel features to be, less to say, toys. I can hardly use events as freely as I was using condvars. It’s a pity that I can’t do current development in Ada: our systems are heavily tied on ADO.

  3. Ivan Levashew
    Posted September 23, 2008 at 04:36 | Permalink

    I’ve noticed one mistake pinning out of ThreadDemo.dpr source code:

    finally
    FLock.Leave;
    end;
    FEmpty.PulseAll;

    and the same in the Put():

    finally
    FLock.Leave;
    end;
    FFull.PulseAll;

    Don’t do like this! From what I know about pthreads, it is a bounded error. One must signal on condvar only when mutex is owned.
    E. g. “FEmpty.PulseAll;” must be placed before finally. I just hope there are no bounded errors in the Threads.pas itself.

  4. Posted September 23, 2008 at 08:04 | Permalink

    Hello Ivan,

    If I recall correctly, both variants are correct (putting the PulseAll in or after the try/finally block) for the Threads.pas implementation. PulseAll doesn’t use the external lock for synchronization, so it shouldn’t matter which variant you choose. Threads.pas is basically a port of pthreads for win32 which doesn’t require this either (also see ftp://sourceware.org/pub/pthreads-win32/sources/pthreads-w32-2-8-0-release/pthread_cond_signal.c), so it should be safe.

  5. Ivan Levashew
    Posted September 24, 2008 at 12:43 | Permalink

    Producer might Pulse condvar when consumer has owned mutex but is not waiting on condvar yet. Consumer will have to wait the whole timeout or infinitelly.

    Timeout in ThreadsDemo is 250 so it won’t cause serious problems in this particular case.

  6. Ivan Levashew
    Posted September 26, 2008 at 12:21 | Permalink

    I’ve found a discussion on this topic:

    http://thread.gmane.org/gmane.os.ecos.general/13018

  7. Posted September 26, 2008 at 12:29 | Permalink

    Thanks the link Ivan, I will look into it. As far as I understand it, the PulseAll is not required to be inside the lock (according to the pthreads implementation).

  8. Posted September 27, 2008 at 22:09 | Permalink

    To your comment above, the producer signals the condition variable only when the related predicate/condition is already true. It actually shouldn’t matter at all (performance differences aside as mentioned by your discussion) if the signal is issued within the lock or after – except if the internal implementation requires holding the lock.

    While the producer adds an item to the queue, the whole section is locked and the client cannot enter the same lock. Once the item has been added, the mutex is unlocked and the consumer can enter the critical section. Since the condition is true, it shouldn’t matter if the producer is preempted right before signaling the condition variable because the consumer does not wait anyway.

    Being able to signal the condition variable after releasing the lock seems to be an implementation specific feature since in Java and .NET, for example, you are required to hold the lock while signaling the condition variable. The new condition variable API which ships with Windows Vista, on the other hand, supports this feature as well (See SleepConditionVariableCS and WakeConditionVariable):

    You can wake other threads using WakeConditionVariable or WakeAllConditionVariable either inside or outside the lock associated with the condition variable. It is usually better to release the lock before waking other threads to reduce the number of context switches.

    As a side note, I just learned that the new Delphi 2009 finally got support for condition variables with a new TMonitor record. Also, if you are developing against Windows Vista, it’s probably a better idea to directly use their new condition variable API.

  9. René Grob
    Posted February 25, 2009 at 09:53 | Permalink

    I looked at the source code. I have the impression that a lot of non-atomic operations are used to simulate an atomic operation. There are a lot of possibilities of race conditions. I consider the code to be NOT THREADSAFE. There is an API function “SignalAndWait” which is atomic, but unfortunately it can only be used with a Mutex not with a CriticalSection.

  10. Tobias Gurock
    Posted February 25, 2009 at 10:28 | Permalink

    Hello René,

    thanks for your comment. Can you give any code lines which contain a race condition? Would be much appreciated. This code is basically a port of the pthreads win32 condition variables implementation.

  11. René Grob
    Posted February 25, 2009 at 22:18 | Permalink

    Well, may be my comment above was a bit exaggerated. My point of view is that everything what is not clearly threadsafe is not threadsafe. I have some doubts concerning the function “WaitConditionVariable”. It just seems to be a too complicated implementation. In the function “InternalPulseConditionVariable” the variable ACond.WaitersBlocked is read and written without the guarding lock of “ACond.BlockSemaphore”. I don’t see a consistent synchronization mechanism to access “ACond.WaitersBlocked”. Besides TMonitor.Wait(..) should check that it has the ownership of the critical section or enforce it by explicitly reentering it.

  12. Tobias Gurock
    Posted February 26, 2009 at 08:25 | Permalink

    Yes, the implementation is complex, maybe too complex. If I recall correctly there was a discussion about this particular case on the pthreads list/forum. There’s a link in Threads.pas to some implementation notes from the pthreads developers. Good suggestion with TMonitor.Wait. In .NET/Java, Wait throws an exception if the current thread does not hold the lock, I think that would be a good feature to add to Threads.pas as well.

  13. René
    Posted February 26, 2009 at 21:35 | Permalink

    The thing is even if the mutual exclusion would be guaranteed, there is no guarantee that the memory write-cache is synchronized between physical processors, cores or hyperthreads unless you are using the SAME synchronization mechanism every time the same variable is accessed. (See also http://msdn.microsoft.com/en-us/magazine/cc163715.aspx)

  14. Posted February 27, 2009 at 08:34 | Permalink

    I’m aware of instructions reordering and cache related problems. However, I still think the pthreads guys got the code right. For example, they are aware of possible race conditions:

    “In the line where you state, “else if ( nWaitersBlocked > nWaitersGone ) { // HARMLESS RACE CONDITION!” there is no race condition for nWaitersGonebecause nWaitersGone is never modified without holding mtxUnblockLock. You are correct that there is a harmless race on nWaitersBlocked, which can increase and make the condition become true just after we check it. If this happens, we interpret it as the wait starting after the signal.”

    I agree that the code is pretty complex and difficult to verify but the equivalent C implementation has shipped with pthreads for several years now.

Post a Comment

Your email is never published nor shared.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Subscribe without commenting