Subj : Re: Win32 condition variables redux To : comp.programming.threads From : Oliver S. Date : Thu Aug 04 2005 07:49 am > So, I would settle for a redux version of condition variables... I wrote a condvar-implementation for Win32 some time ago. It does not have full POSICS-semantics and misses a timed-wait-function and is ra- ther like the monitors in Java. And - it works optimally without any additional kernel-support: #include #include #if !defined(NDEBUG) #define ONDEBUG(expr) (expr) #else #define ONDEBUG(expr) ((void)0) #endif #include #include class CCondVar { public: CCondVar(); virtual ~CCondVar(); virtual void Enter(); virtual void Wait(); virtual void Release(); virtual void ReleaseAll(); virtual void Leave(); private: static LONG __fastcall CAS( LONG volatile *plDestination, LONG dwExchange, LONG lComparand ); private: LONG volatile m_lMutex; DWORD volatile m_dwWaitingForSignal; #if !defined(NDEBUG) DWORD volatile m_dwOwningThreadId; #endif HANDLE m_hEvtEnter, m_hSemRelease; }; CCondVar::CCondVar() { ONDEBUG(m_dwOwningThreadId = 0); m_lMutex = 0; m_dwWaitingForSignal = 0; m_hEvtEnter = CreateEvent( NULL, FALSE, FALSE, NULL ); m_hSemRelease = CreateSemaphore( NULL, 0, 0x7FFFFFFF, NULL ); } CCondVar::~CCondVar() { CloseHandle( m_hEvtEnter ); CloseHandle( m_hSemRelease ); } void CCondVar::Enter() { LONG lMutex, lRef; for( lMutex = m_lMutex; ; lMutex = lRef ) { if( lMutex >= 0 ) if( (lRef = CAS( &m_lMutex, (LONG)(lMutex | 0x80000000u), lMutex )) == lMutex ) break; else continue; else { if( (lRef = CAS( &m_lMutex, lMutex + 1, lMutex )) != lMutex ) continue; WaitForSingleObject( m_hEvtEnter, INFINITE ); assert(m_lMutex < 0); break; } } ONDEBUG(m_dwOwningThreadId = GetCurrentThreadId()); } void CCondVar::Wait() { DWORD dwWaitingForSignal = m_dwWaitingForSignal; assert(m_lMutex < 0 && m_dwOwningThreadId == GetCurrentThreadId()); ONDEBUG(m_dwOwningThreadId = 0); m_dwWaitingForSignal = dwWaitingForSignal + 1; LONG lMutex, lRef; for( lMutex = m_lMutex; ; lMutex = lRef ) { DWORD dwWaitingToOwn = lMutex & 0x7FFFFFFFu; assert(dwWaitingToOwn >= dwWaitingForSignal); if( dwWaitingToOwn == dwWaitingForSignal ) if( (lRef = CAS( &m_lMutex, (LONG)(dwWaitingToOwn + 1), lMutex )) == lMutex ) break; else continue; else // dwWaitingToOwn > dwWaitingForSignal { SetEvent( m_hEvtEnter ); break; } } HANDLE ahwait[2] = { m_hEvtEnter, m_hSemRelease }; WaitForMultipleObjects( 2, ahwait, TRUE, INFINITE ); ONDEBUG(m_dwOwningThreadId = GetCurrentThreadId()); assert(m_lMutex < 0); } void CCondVar::Release() { LONG dwWaitingForSignal; assert(m_lMutex < 0 && m_dwOwningThreadId == GetCurrentThreadId()); if( (dwWaitingForSignal = m_dwWaitingForSignal) != 0 ) { m_dwWaitingForSignal = dwWaitingForSignal - 1; ReleaseSemaphore( m_hSemRelease, 1, NULL ); } } void CCondVar::ReleaseAll() { DWORD dwWaitingForSignal; assert(m_lMutex < 0 && m_dwOwningThreadId == GetCurrentThreadId()); if( (dwWaitingForSignal = m_dwWaitingForSignal) != 0 ) { m_dwWaitingForSignal = 0; ReleaseSemaphore( m_hSemRelease, (LONG)dwWaitingForSignal, NULL ); } } void CCondVar::Leave() { LONG lMutex, lRef; assert(m_lMutex < 0 && m_dwOwningThreadId == GetCurrentThreadId()); ONDEBUG(m_dwOwningThreadId = 0); for( lMutex = m_lMutex; ; lMutex = lRef ) { DWORD dwWaitingToOwn = lMutex & 0x7FFFFFFFu, dwWaitingForSignal = m_dwWaitingForSignal; assert(dwWaitingToOwn >= dwWaitingForSignal); if( dwWaitingToOwn == dwWaitingForSignal ) if( (lRef = CAS( &m_lMutex, lMutex & 0x7FFFFFFF, lMutex )) == lMutex ) break; else continue; else // dwWaitingToOwn > dwWaitingForSignal { if( (lRef = CAS( &m_lMutex, lMutex - 1, lMutex )) != lMutex ) continue; SetEvent( m_hEvtEnter ); break; } } } __declspec(naked) LONG __fastcall CCondVar::CAS( LONG volatile *plDestination, LONG dwExchange, LONG lComparand ) { __asm { mov eax, [esp + 4] lock cmpxchg [ecx], edx ret 4 } } .