Re: Events vs. Condition Variables

143 views
Skip to first unread message

Joe Seigh

unread,
Nov 21, 2006, 10:30:29 PM11/21/06
to
Tony wrote:
> What's all the hullabuloo dissing events in favor of condition variables?
> Is it just certain uses of events (Win32 events that is) that is seen as bad
> practice? And can't they be used just like condition variables anyway?
> Anyone have a link to a discussion of such? And is this question OK in
> here or better in a general programming group?
>

The basic problem is events aren't sharable by more than one waiting
thread. Only one thread can reset the event. This makes it impossible
for other waiting threads to properly coordinate without a race condition.


Followup set to comp.programming.threads.

--
Joe Seigh

When you get lemons, you make lemonade.
When you get hardware, you make software.

David Schwartz

unread,
Nov 22, 2006, 12:31:52 AM11/22/06
to

Joe Seigh wrote:

> Tony wrote:

> > What's all the hullabuloo dissing events in favor of condition variables?
> > Is it just certain uses of events (Win32 events that is) that is seen as bad
> > practice? And can't they be used just like condition variables anyway?
> > Anyone have a link to a discussion of such? And is this question OK in
> > here or better in a general programming group?

> The basic problem is events aren't sharable by more than one waiting
> thread.

Huh?

> Only one thread can reset the event. This makes it impossible
> for other waiting threads to properly coordinate without a race condition.

Why?

IMO, the basic problem with events are two-fold:

1) They are hopelessly broken in the Windows kernel. Threads can miss a
wakeup because the kernel only considers a thread blocked on an event
if it's blocked in the kernel. User-space has no way to control when a
thread is blocked in the kernel.

2) You can't atomically release a lock and block on an event (like
pthread_cond_wait does). This makes it very hard to get code using
events to be right.

As just one consequence of these problems, see this post:
http://blogs.msdn.com/oldnewthing/archive/2005/01/05/346888.aspx

DS

Joe Seigh

unread,
Nov 22, 2006, 5:35:11 AM11/22/06
to
David Schwartz wrote:
> Joe Seigh wrote:
>
>
>>Tony wrote:
>
>
>>>What's all the hullabuloo dissing events in favor of condition variables?
>>>Is it just certain uses of events (Win32 events that is) that is seen as bad
>>>practice? And can't they be used just like condition variables anyway?
>>>Anyone have a link to a discussion of such? And is this question OK in
>>>here or better in a general programming group?
>
>
>>The basic problem is events aren't sharable by more than one waiting
>>thread.
>
>
> Huh?
>
>
>>Only one thread can reset the event. This makes it impossible
>>for other waiting threads to properly coordinate without a race condition.
>
>
> Why?

I meant if you try to use them "just like condition variables". The
event vs. eventcount distinction.

>
> IMO, the basic problem with events are two-fold:
>
> 1) They are hopelessly broken in the Windows kernel. Threads can miss a
> wakeup because the kernel only considers a thread blocked on an event
> if it's blocked in the kernel. User-space has no way to control when a
> thread is blocked in the kernel.
>
> 2) You can't atomically release a lock and block on an event (like
> pthread_cond_wait does). This makes it very hard to get code using
> events to be right.
>
> As just one consequence of these problems, see this post:
> http://blogs.msdn.com/oldnewthing/archive/2005/01/05/346888.aspx
>

PulseEvent is broken so just don't use it. You don't need it anyway.
Events are perfectly usable. I've used them for things like a priority
queue, etc... I don't think there's anything you can't do with an Event
that you can do with a condition variable, just not the same way.
They're different, not the same as, condition variables to answer
the OP's question.

And it's not that hard to get Event based code right once you use
the right synchronization patterns. And easy to get wrong if you
use the wrong synchronization pattern. So just don't use condvar
based patterns with Events. But please feel free to ignore this
and use the difficulty of implementing a condvar design pattern with
an Event as an example of why Events are problematic.

David Schwartz

unread,
Nov 22, 2006, 11:54:50 AM11/22/06
to

Joe Seigh wrote:

> PulseEvent is broken so just don't use it. You don't need it anyway.

It's not just PulseEvent that's broken. The same thing would happen if
you unblocked the event and then blocked it again yourself. The
brokenness in PulseEvent means that the thread that picks up the event
must re-block it if you don't want too many threads to wake. This
significantly complicates using events.

> Events are perfectly usable. I've used them for things like a priority
> queue, etc... I don't think there's anything you can't do with an Event
> that you can do with a condition variable, just not the same way.
> They're different, not the same as, condition variables to answer
> the OP's question.

It's very, very hard to ensure that you don't miss wakeups. Most of the
patterns I've seen using events live with the risk that a wakeup will
be missed and always set a fairly short timeout in the event wait to
ensure that a lost wakeup doesn't stall indefinitely.

> And it's not that hard to get Event based code right once you use
> the right synchronization patterns. And easy to get wrong if you
> use the wrong synchronization pattern. So just don't use condvar
> based patterns with Events. But please feel free to ignore this
> and use the difficulty of implementing a condvar design pattern with
> an Event as an example of why Events are problematic.

You can't have it both ways. You can't say that it's not hard to get
event based code right and at the same time say it's difficult to
implement a condvar design pattern. Condvar design patterns mirror a
huge number of real-world synchronization needs.

Let's take something ridiculously simple -- a queue. You add objects,
you pop objects, you block on the queue. Using events, how hard is it
to make sure that a thread that blocks on the queue never misses a
wakeup without a thundering herd if many threads are waiting?

DS

Anthony Williams

unread,
Nov 22, 2006, 12:27:56 PM11/22/06
to
"David Schwartz" <dav...@webmaster.com> writes:

> Joe Seigh wrote:
>
>> PulseEvent is broken so just don't use it. You don't need it anyway.
>
> It's not just PulseEvent that's broken. The same thing would happen if
> you unblocked the event and then blocked it again yourself. The
> brokenness in PulseEvent means that the thread that picks up the event
> must re-block it if you don't want too many threads to wake. This
> significantly complicates using events.

Auto-reset events do what PulseEvent tries to do, but actually work --- when
you signal the event, it stays signalled until a thread successfully waits on
it, and then it becomes reset. Only one thread will wake if more than one is
waiting, as the first to wake will reset the event.

If you only want a limited number of threads to wake, and that number is not
1, then a raw Event is the wrong synchronization object.

>> Events are perfectly usable. I've used them for things like a priority
>> queue, etc... I don't think there's anything you can't do with an Event
>> that you can do with a condition variable, just not the same way.
>> They're different, not the same as, condition variables to answer
>> the OP's question.
>
> It's very, very hard to ensure that you don't miss wakeups. Most of the
> patterns I've seen using events live with the risk that a wakeup will
> be missed and always set a fairly short timeout in the event wait to
> ensure that a lost wakeup doesn't stall indefinitely.

I don't see lost wake-ups except with PulseEvent, and poorly managed
Manual-Reset events.

>> And it's not that hard to get Event based code right once you use
>> the right synchronization patterns. And easy to get wrong if you
>> use the wrong synchronization pattern. So just don't use condvar
>> based patterns with Events. But please feel free to ignore this
>> and use the difficulty of implementing a condvar design pattern with
>> an Event as an example of why Events are problematic.
>
> You can't have it both ways. You can't say that it's not hard to get
> event based code right and at the same time say it's difficult to
> implement a condvar design pattern. Condvar design patterns mirror a
> huge number of real-world synchronization needs.

The really hard part about implementing condition variables is ensuring that a
broadcast wakes all threads waiting at the time of the broadcast, and not any
that start waiting after the broadcast. You cannot do this with just an Event
object.

> Let's take something ridiculously simple -- a queue. You add objects,
> you pop objects, you block on the queue. Using events, how hard is it
> to make sure that a thread that blocks on the queue never misses a
> wakeup without a thundering herd if many threads are waiting?

This is the wrong scenario for an Event. An Auto-Reset Event will cause
"missed wake-ups" if there are multiple waiting threads, and more than one
entry gets pushed on the queue before any waiting thread wakes --- only one
thread will be woken. A Manual-reset Event will cause a thundering herd when
one item is added.

Events are good for two things: waking exactly one thread (however many times
the event gets signalled before the thread gets woken), or waking a thundering
herd. For anything else, you need a different synchronization object, or maybe
a combination.

Anthony
--
Anthony Williams
Software Developer
Just Software Solutions Ltd
http://www.justsoftwaresolutions.co.uk

Markus Elfring

unread,
Nov 22, 2006, 2:03:04 PM11/22/06
to
> As just one consequence of these problems, see this post:
> http://blogs.msdn.com/oldnewthing/archive/2005/01/05/346888.aspx

Are more software developers looking for an update on the topics "PulseEvent is
fundamentally flawed" and "Strategies for Implementing POSIX Condition Variables
on Win32"?
http://www.cs.wustl.edu/~schmidt/win32-cv-1.html

How do the evolving Windows Vista concurrency APIs fit into the picture for
synchronisation primitives?
http://msdn.microsoft.com/library/en-us/dllproc/base/using_condition_variables.asp

Regards,
Markus

Marcin 'Qrczak' Kowalczyk

unread,
Nov 22, 2006, 5:12:40 PM11/22/06
to
Anthony Williams <anthon...@yahoo.com> writes:

> The really hard part about implementing condition variables is
> ensuring that a broadcast wakes all threads waiting at the time of
> the broadcast, and not any that start waiting after the broadcast.
> You cannot do this with just an Event object.

It is always correct to wake up unneeded threads waiting on a
condition variable, although it might be inefficient.

--
__("< Marcin Kowalczyk
\__/ qrc...@knm.org.pl
^^ http://qrnik.knm.org.pl/~qrczak/

Anthony Williams

unread,
Nov 23, 2006, 4:05:56 AM11/23/06
to
Marcin 'Qrczak' Kowalczyk <qrc...@knm.org.pl> writes:

> Anthony Williams <anthon...@yahoo.com> writes:
>
>> The really hard part about implementing condition variables is
>> ensuring that a broadcast wakes all threads waiting at the time of
>> the broadcast, and not any that start waiting after the broadcast.
>> You cannot do this with just an Event object.
>
> It is always correct to wake up unneeded threads waiting on a
> condition variable, although it might be inefficient.

Yes, but you can't just leave the Event signalled forever, you have to Reset
it sometime. Ensuring that this happens only when all the threads waiting at
the time of the broadcast have been woken is not trivial. I know how to do it,
it's just not trivial.

Joe Seigh

unread,
Nov 24, 2006, 8:49:09 PM11/24/06
to
David Schwartz wrote:
> Joe Seigh wrote:
>
>
>>PulseEvent is broken so just don't use it. You don't need it anyway.
>
>
> It's not just PulseEvent that's broken. The same thing would happen if
> you unblocked the event and then blocked it again yourself. The
> brokenness in PulseEvent means that the thread that picks up the event
> must re-block it if you don't want too many threads to wake. This
> significantly complicates using events.
>
>
>>Events are perfectly usable. I've used them for things like a priority
>>queue, etc... I don't think there's anything you can't do with an Event
>>that you can do with a condition variable, just not the same way.
>>They're different, not the same as, condition variables to answer
>>the OP's question.
>
>
> It's very, very hard to ensure that you don't miss wakeups. Most of the
> patterns I've seen using events live with the risk that a wakeup will
> be missed and always set a fairly short timeout in the event wait to
> ensure that a lost wakeup doesn't stall indefinitely.

So you're familiar with Event anti-patterns. Good for you.


>
>
>>And it's not that hard to get Event based code right once you use
>>the right synchronization patterns. And easy to get wrong if you
>>use the wrong synchronization pattern. So just don't use condvar
>>based patterns with Events. But please feel free to ignore this
>>and use the difficulty of implementing a condvar design pattern with
>>an Event as an example of why Events are problematic.
>
>
> You can't have it both ways. You can't say that it's not hard to get
> event based code right and at the same time say it's difficult to
> implement a condvar design pattern. Condvar design patterns mirror a
> huge number of real-world synchronization needs.

You're just saying that. Condvar design patterns are more natural
for you because that's the only thing you know.

>
> Let's take something ridiculously simple -- a queue. You add objects,
> you pop objects, you block on the queue. Using events, how hard is it
> to make sure that a thread that blocks on the queue never misses a
> wakeup without a thundering herd if many threads are waiting?
>

Not very hard. The way to use Events is to associate it with
a a specific condition. In this case, the queue being empty.
Any code that toggles that condition, sets or resets the Event
appropiately while holding a lock.

To dequeue
EnterCriticalSection(&cs);
while (queue.isEmpty()) {
LeaveCriticalSection(&cs);
WaitForSingleObject(hEvent, INFINITE);
EnterCriticalSection(&cs);
}
item = queue.pop();
if (queue.isEmpty())
ResetEvent(hEvent);
LeaveCriticalSection(&cs);
return item;

To enqueue
EnterCriticalSection(&cs);
bool wasEmpty = queue.isEmpty();
queue.push(item);
if (wasEmpty)
SetEvent(hEvent);
LeaveCriticalSection(&cs);

or something like that.

Though in the case of a queue a semphore might be more appropiate than
an event object especially if you though thundering herd was a problem.

Using pthread_cond_signal with a condvar to avoid thundering herd is
probably a little bit dangerous since the dequeueing threads have to
remember to wake up another waiting thread. And it easy to have the
signal go to a wrong thread. This problem has been discussed before
on c.p.t. A semaphore would also probably be better than a condvar
in this case.

You might argue using events forces cleaner designs since you have to
associate events with specific conditions. Condvars allow you to be
non specific and not make it clear what condition or conditions you
intend to signal.

Joe Seigh

unread,
Nov 24, 2006, 9:56:19 PM11/24/06
to
Joe Seigh wrote:

> Using pthread_cond_signal with a condvar to avoid thundering herd is
> probably a little bit dangerous since the dequeueing threads have to
> remember to wake up another waiting thread. And it easy to have the
> signal go to a wrong thread. This problem has been discussed before
> on c.p.t. A semaphore would also probably be better than a condvar
> in this case.
>

Never mind that. I'm think of one of the condvar anti-patterns.

Reply all
Reply to author
Forward
0 new messages