views:

182

answers:

2

I have the following code which replicates the windows manual and auto reset events.

class event
{
public:
    event( bool signalled = false, bool ar = true ) :
        _auto( ar ),
        _signalled( signalled )
    {
        pthread_mutex_init( &_mutex, NULL );
        pthread_cond_init( &_cond, NULL );
    }

    ~event()
    {
        pthread_cond_destroy( &_cond );
        pthread_mutex_destroy( &_mutex );
    }

    void set()
    {
        pthread_mutex_lock( &_mutex );

        // only set and signal if we are unset
        if ( _signalled == false )
        {
            _signalled = true;

            pthread_cond_signal( &_cond );
        }

        pthread_mutex_unlock( &_mutex );
    }

    void wait()
    {
        pthread_mutex_lock( &_mutex );

        while ( _signalled == false )
        {
            pthread_cond_wait( &_cond, &_mutex );
        }

        // if we're an autoreset event, auto reset
        if ( _auto )
        {
            _signalled = false;
        }

        pthread_mutex_unlock( &_mutex );
    }

    void reset()
    {
        pthread_mutex_lock( &_mutex );

        _signalled = false;

        pthread_mutex_unlock( &_mutex );
    }

private:
    pthread_mutex_t _mutex;
    pthread_cond_t _cond;
    bool _signalled;
    bool _auto;
};

My question surrounds the "optimisation" I've put in place in the set() method where I only call pthread_cond_signal() if the event was unsignalled. Is this a valid optimisation or have I introduced some subtle flaw by doing so.

+1  A: 

There's definitely a difference in behavior due to that "optimization" if multiple threads are waiting for the same event. Consider this sequence of events (manual reset mode):

thread 1 - wait
thread 2 - wait
thread 3 - wait
thread 4 - set
thread 4 - set
thread 4 - set
thread 4 - reset

With your code, pthread_cond_signal will only be called once (unblocking one of threads 1-3); without the optimization it would be called 3 times (unblocking all 3 of them).

I don't know if that's a "flaw" or not, because I don't know the precise semantics of the Windows API you're emulating.

David Gelhar
That is in the case of the non-auto-reset events, where you probably only want 1 action based on multiple set before reset. so the set() is actually better with the "optimisation". Typically, condition variables should signal a change in condition.
stefaanv
@David. I agree with your appraisal and thank you for your comments.The auto-reset nature of the event is such that calling set on an already signalled object is a no-op. So I think the code I have is ok, but am open to arguments.
ScaryAardvark
If calling set on an already set manual-reset event is a noop, then this is not an optimization , it is needed for correct behavior. Otherwise, calling set before the event is reset again would unblock additional threads.
nos
A: 

I'd qualify _signalled with volatile, to prevent any clever compiler tricks regarding that variable.

Anonym
I understand volatile to mean that any variable marked as such could potentially be changed by code not available to the compiler and as such the compiler needs to take care in optimisations. However, all code paths are available to the compiler in the code where _signalled is used so I would have thought that any potential optimisations would be valid. I could of course not have a firm grasp of the volatile concept.
ScaryAardvark
Not needed. Your variables are guarded by locks, volatile serves no purpose here, the compiler is not allowed to cache the variable across the pthread_ calls.
nos