#ifndef __PipeConditionVar_h_ #define __PipeConditionVar_h_ /************************************************************************ * This implements a condition variable, not too much different from the * pthreads condition variable. The primary difference is that this * version allows you to wait on a condition variable using a poll() * statement. This way you can wait on multiple condition variables * and/or file handles at the same time. * * Another important difference is that you don't need a seperate mutex * to use this conditon variable. There is a mutex, but this is built * into the implementation of this library, and the library user does * not have to know about it. That makes things a little simpler. * * The condition variable can be in one of four states: * * Error: This could happen on creation. This is very unlikely. There is * no way out of the error state; this is a permanent error. * * Clear: This is the default state. If you wait on a condition variable * in this state, you will wait until another thread signals you using the * condition variable. You can return to this state at any time by using * the clear() method. * * Signaled: This tells anyone listing on the condition variable to wake up * now. This state lasts until either someone calls clear() to return to the * clear state, or someone waits for the signal, taking us to the need-reset * state. This is different from the pthreads signal variable. It doesn't * matter if someone signals you before or after you start waiting. Either * way you will wake up. There is no race condition here. * * Need-Reset: The condition variable automatically moves from the signaled * state to the need-reset state after exactly one listener has woken up. * (This is a limitation of the implementation, and is required to work with * a select() statement. I assume it's also required to work with poll()) * * Typical Use: * * A thread is listing for data from external sources via one or more sockets. * The thread is also listening for data from other threads in the process * via a queue or other thread-safe data structure, and possibly a time-out * value. * * The thread sets up a poll() statement to listen to relevant activity on * each socket. It then adds one extra handle: it waits for getWaitHandle() * to be readable. It sleeps until the poll() is finished. * * The thread answers any socket handles which poll() says are ready. Then * the thread calls clear() on the condition variable. Finally the thread * reads from the queue until it is empty. The thread returns to * the top of it's loop. * * Another thread must provide the data. This is done in the opposite order. * The data provider sends one or more items into the queue. Then it calls * set() on the condition variable. Order is very important. Call clear() * before reading from the queue, but call set() after writing to the queue. * * Any number of threads can call set(). Only one thread (at a time) should * try to wait on the condition variable. The same thread that does the * waiting should also call clear(). Extra calls to set() or clear() do * not hurt anything. */ #include #include #include "MiscSupport.h" #include "IPollSet.h" class PipeConditionVar : NoCopy, NoAssign { private: pthread_mutex_t _mutex; int _readHandle, _writeHandle; volatile bool _set; bool _error; IPollSet _pollSet; public: void set(); void clear(); bool isSet() const { return _set; } int getWaitHandle() const { return _readHandle; } bool getError() { return _error; } PipeConditionVar(); ~PipeConditionVar(); // This have been added for convenience. They simply call poll() with // only this one handle, and the given timeout. The timout may be NULL // to say wait forever. We interpret the timeval just like select(). It is // the number of seconds and microseconds to wait starting from now, not // a specific time. void wait(long seconds, long microseconds=0) const; void wait(timeval const *maxPause) const; void waitForever() const { // Notice the typecast. If you tried to use NULL without a typecast, // C++ would convert that into (long)0 rather than (timeval const *)NULL, // and it would wait for 0 seconds rather than forever. wait((timeval const *)NULL); } }; #endif