Boost logo

Boost :

From: Sean Kelly (sean_at_[hidden])
Date: 2004-01-26 00:38:21


Beman Dawes wrote:

> At 04:05 PM 1/24/2004, Sean Kelly wrote:
> >The condition object is needlessly complex for instances where a thread
> >must wait on a signal but isn't immediately concerned with shared data.
> >Basically, I'd like a simplified version of condition that supports wait
> >and timed_wait without the predicate and without the need to pass a
> mutex.
> >It might be used like this:
> >
> >mutex sync;
> >queue<T> mq;
> >event ev;
> >
> >void add_message( T const& msg ) {
> > lock l( sync );
> > mq.add( msg );
> > ev.set();
> >}
> >
> >void process_msgs() {
> > // synchronized access to mq
> > // acts on messages
> >}
> >
> >
> >void loop() {
> > for(;;) {
> > ev.wait();
> > ev.reset();
> > process_msgs();
> > }
> >}
>
>
> How is this different, and safer, than the traditional event variable?
>
> (See www.boost.org/libs/thread/doc/rationale.html#Events)

I hadn't read that bit before, and I think it's kind of vague so I'm
going to guess at what it refers to. Events in Windows can be
initialized one of two ways... either to auto-reset or to require a
manual reset. Conditions under pthreads can be used either way as well,
though the process isn't as automatic either way. The equivalent to
auto-reset under pthreads is kind of like waiting on the condition
variable without checking it first--only threads waiting on a condition
will recieve the signal. The equivalent to manual reset is the standard
method that pthread condition variables are used: lock a mutex, check
the variable and if it's not set as signalled then wait on it.

I'm guessing that the described common use of events under Windows is to
use them in auto-reset mode. This creates a race condition because the
programmer is relying on the assumption that the signalled thread will
be waiting on the event before the signalling thread set it. Unless
there's something I really don't know about Windows events however,
these two code segments are functionally identical:

// windows implementation

HANDLE ev( CreateEvent( NULL, TRUE, FALSE, NULL ) );

void thread1_wait()
{
     WaitForSingleObject( ev, INFINITE );
     ResetEvent( ev );
     // do stuff
}

void thread2_signal()
{
     SetEvent( ev );
}

// pthread implementation

pthread_mutex_t mut;
pthread_cond_t cond;
bool signal( false );
timespec timeout;

void thread1_wait()
{
     pthread_mutex_lock( &mut );
     while( !signal )
         pthread_cond_timedwait( &cond, &mut, &timeout );
     signal = false;
     // do stuff
}

void thread2_signal()
{
     pthread_mutex_lock( &mut );
     if( !signal )
         signal = true;
     pthread_cond_signal( &cond );
}

> In your code above, if queue<> doesn't itself protect against races,
> what happens if process_msgs() and add_message() modify mq at the same
> time. OTOH, if queue<> is multithread safe, why do you need ev at all?

To signal that there's data in the queue ready for processing. While I
grant that my example might lend itself quite well to the existing
boost::condition method, imagine that there are 5 different message
queues which all use different mutexes but all signal the same event to
notify that they're ready for processing. Or more simply, why use a
mutex in Windows when the event functionality doesn't require one?
Also, please notice that access to queue<> above is guarded with a
mutex. Atomicity is guranteed.

My goal is really mostly improved efficiency for the Windows side. I
don't want to have to use a mutex if I don't need one. Not even to
mention the semaphores I saw in the Windows version of the code. For
such a simple case as my above example, this seems like overkill.

If for some reason I'm mistaken about how events work in Windows, please
let me know. I've been using them in applications this way for years
and have never had a single problem.

Sean


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk