7/08/2011

07-08-11 - Event Count and Condition Variable

If you have either event_count or condition_variable, it's pretty straightforward to get the other from the one you have.

eventcount from condition_variable :

# by Chris M Thomasson
#
# class eventcount {  
# public:  
#   typedef unsigned long key_type;  
#   
#   
# private:  
#   mutable rl::atomic<key_type> m_count;  
#   rl::mutex m_mutex;  
#   rl::condition_variable m_cond;  
#   
#   
#   void prv_signal(key_type key) {  
#     if (key & 1) {  
#       m_mutex.lock($);  
#       while (! m_count($).compare_exchange_weak(key, (key + 2) & ~1,   
#         rl::memory_order_seq_cst));  
#       m_mutex.unlock($);  
#       m_cond.notify_all($);  
#     }  
#   }  
#   
#   
# public:  
#   eventcount() {  
#     m_count($).store(0, rl::memory_order_relaxed);  
#   }  
#   
#   
# public:  
#   key_type get() const {   // aka prepare_wait
#     return m_count($).fetch_or(1, rl::memory_order_acquire);  
#   }  
#   
#   
#   void signal() {  // aka notify_one
#     prv_signal(m_count($).fetch_add(0, rl::memory_order_seq_cst));  
#   }  
#   
#   
#   void signal_relaxed() {  
#     prv_signal(m_count($).load(rl::memory_order_relaxed));  
#   }  
#   
#   
#   void wait(key_type cmp) {  
#     m_mutex.lock($);  
#     if ((m_count($).load(rl::memory_order_seq_cst) & ~1) == (cmp & ~1)) {  
#       m_cond.wait(m_mutex, $);  
#     }  
#     m_mutex.unlock($);  
#   }  
# };  
#
and condition variable from event count :
by Dmitry V'jukov :

   1. class condition_variable  
   2. {  
   3.     eventcount ec_;  
   4.   
   5. public:  
   6.     void wait(mutex& mtx)  
   7.     {  
   8.         int count = ec_.prepare_wait();  
   9.         mtx.unlock();  
  10.         ec_.wait(count);  
  11.         mtx.lock();  
  12.     }  
  13.   
  14.     void signal()  
  15.     {  
  16.         ec_.notify_one();  
  17.     }  
  18.   
  19.     void broadcast()  
  20.     {  
  21.         ec_.notify_all();  
  22.     }  
  23. };   
(note this is a simplified condition variable without all the POSIX compliance crud).

In C++0x you have condition_variable at the stdlib level, so that is probably the best approach for the future. Unfortunately that future is still far away. On Pthreads you also have condition_variable (though a rather more complex one). Unfortunately, on Win32 (pre-Vista) you don't have condition_varaiable at all, so you have to build one of these from OS primitives.

(BTW there are various sources for good condition_var implementations for Win32, such as boost::thread and Win32 pthreads by Alex Terekhov).

ADDENDUM : really eventcount is the more primitive of the two; it's sort of a mistake that C++0x has provided "condition_var" as a primitive. They are not trying to provide a full set of OS-level thread control types (eg. they don't provide semaphore, event, what have you) - they are trying to provide the minimal basic set, and they chose condition_var. They should have done mutex and eventcount, as you can build everything from that.

(actually there's something perhaps even more primitive that eventcount which is "waitset" which can be easily used to build any of the basic blocking thread control devices).

5 comments:

Branimir Karadžić said...

>>"Unfortunately, on Win32 you don't have condition_varaiable at all"

Check this out:
http://msdn.microsoft.com/en-us/library/ms683469%28v=vs.85%29.aspx

It maps 1:1 to pthread_cond_*...

InitializeConditionVariable == pthread_cond_init
WakeConditionVariable == pthread_cond_signal
WakeAllConditionVariable == pthread_cond_broadcast
etc.

cbloom said...

Yeah, I know. It's nice, but it's Vista+ which means as a software writer you just can't use it.

cbloom said...

BTW when I say "Win32" I mean "pre-Vista" because if you're Vista+ you should be running 64 bit and then I refer to it as "Win64".

It's way too much pain in the ass to deal with all the possible permutations of clients, so I simplify it to :

"win32" :
Win95+
32 bit
no SSE gaurantee

"Win64" :
Vista+
64 bit
has at least SSE2
has cmpx128 (*)

(*) = if you are in the weird gap of 64-bit chips that don't have cmpx128 I hate you and you get treated as win32

Branimir Karadžić said...

Ah ok, I still call everything WIN32. :)

cbloom said...

A related problem is

SignalObjectAndWait

which would be a lovely function to use for implementing condition_var (it basically is the heart of condition var - it signals one handle and waits on another handle in one op), but it's Win2k+ only so it's slightly better than Vista+ but still not actually usable in code that has to work on any Windows.

I don't really understand who is writing code that knows they can use Vista+ only APIs. I guess people who release many versions for various hardware/OS combos.

old rants