June 22nd, 2006

An auto-reset event is just a stupid semaphore

When you create an event with the CreateEvent function, you get to specify whether you want an auto-reset event or a manual-reset event.

Manual-reset events are easy to understand: If the event is clear, then a wait on the event is not satisfied. If the event is set, then a wait on the event succeeds. Doesn’t matter how many people are waiting for the event; they all behave the same way, and the state of the event is unaffected by how many people are waiting for it.

Auto-reset events are more confusing. Probably the easiest way to think about them is as if they were semaphores with a maximum token count of one. If the event is clear, then a wait on the event is not satisfied. If the event is set, then one waiter succeeds and the event is reset; the other waiters keep waiting. (And from our discussion of PulseEvent, you already know that it is indeterminate which waiter will be released if there is more than one.)

The gotcha with auto-reset events is the case where you set an event that is already set. Since an event has only two states (set and reset), setting an event that is already set has no effect. If you are using an event to control a resource producer/consumer model, then the “setting an event that is already set” case will result in you appearing to “lose” a token. Consider the following intended pattern:

Producer Consumer
  Wait
Produce work  
SetEvent  
  Wake up and reset event
  Do work
Produce work  
  Wait
SetEvent  
  Wake up and reset event
  Do work

But what if the timing doesn’t quite come out? What if the consumer thread is a little slow to do the work (or the producer thread is a little fast in generating it):

Producer Consumer
  Wait
Produce work  
SetEvent  
  Wake up and reset event
Produce work  
SetEvent  
  Do work
Produce work  
SetEvent (has no effect)  
  Wait satisfied immediately
  Reset event
  Do work
  Wait

Notice that the producer produced three work items, but the consumer performed only two of them. The third SetEvent had no effect since the event was already set. (You have the same problem if you try to increase a semaphore’s token count past its maximum.) If you want the number of wakes to match the number of sets, then you need to use a semaphore with a maximum token count as high as the maximum number of outstanding work items you will support.

Moral of the story: Know your tools, know their limits, and use the right tool for the right job.

Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

0 comments

Discussion are closed.