Guest User

final condition-variable for Win32

a guest
May 21st, 2016
232
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #include <windows.h>
  2. #include <cassert>
  3. #include <exception>
  4.  
  5. #if !defined(NDEBUG)
  6.     #define ONDEBUG(expr) (expr)
  7. #else
  8.     #define ONDEBUG(expr) ((void)0)
  9. #endif
  10.  
  11. class Exception : public std::exception
  12. {
  13. };
  14.  
  15. class ResourceException : Exception
  16. {
  17. };
  18.  
  19. template <typename TYPE>
  20. inline
  21. TYPE AutoThrowResourceException( TYPE t )
  22. {
  23.     if( !t )
  24.         throw ResourceException();
  25.  
  26.     return t;
  27. }
  28.  
  29. class XHANDLE
  30. {
  31. public:
  32.          XHANDLE( HANDLE h = NULL );
  33.          XHANDLE();
  34.          ~XHANDLE();
  35.     void CloseHandle();
  36.  
  37. public:
  38.     HANDLE h;
  39. };
  40.  
  41. inline
  42. XHANDLE::XHANDLE( HANDLE h )
  43. {
  44.     this->h = h;
  45. }
  46.  
  47. inline
  48. XHANDLE::~XHANDLE()
  49. {
  50.     BOOL fClosed;
  51.  
  52.     if( h && h != INVALID_HANDLE_VALUE )
  53.         fClosed = ::CloseHandle( h ),
  54.         assert(fClosed);
  55. }
  56.  
  57. inline
  58. void XHANDLE::CloseHandle()
  59. {
  60.     ::CloseHandle( h );
  61.     h = NULL;
  62. }
  63.  
  64.  
  65. class CondVar
  66. {
  67. public:
  68.          CondVar();
  69.          ~CondVar();
  70.     void Enter();
  71.     void Wait();
  72.     void Release();
  73.     void ReleaseAll();
  74.     void Leave();
  75.  
  76. private:
  77.     LONGLONG volatile m_llOwnersAndWaiters;
  78.     DWORD    volatile m_dwRecursionCount;
  79.     DWORD    volatile m_dwOwningThreadId;
  80.     XHANDLE           m_xhEvtEnter,
  81.                       m_xhSemRelease;
  82.  
  83. private:
  84.     static
  85.     DWORD Owners( LONGLONG llOwnersAndWaiters )
  86.     {
  87.         return (DWORD)llOwnersAndWaiters;
  88.     }
  89.  
  90.     static
  91.     DWORD Waiters( LONGLONG llOwnersAndWaiters )
  92.     {
  93.         return (DWORD)((DWORDLONG)llOwnersAndWaiters >> 32);
  94.     }
  95. };
  96.  
  97. CondVar::CondVar() :
  98.     m_xhEvtEnter(   AutoThrowResourceException( ::CreateEvent( NULL, FALSE, FALSE, NULL ) ) ),
  99.     m_xhSemRelease( AutoThrowResourceException( ::CreateSemaphore( NULL, 0, 0x7FFFFFFF, NULL ) ) )
  100. {
  101.     m_llOwnersAndWaiters = 0;
  102.     m_dwRecursionCount   = 0;
  103.     m_dwOwningThreadId   = 0;
  104. }
  105.  
  106. CondVar::~CondVar()
  107. {
  108. }
  109.  
  110. void CondVar::Enter()
  111. {
  112.     if( m_dwOwningThreadId == GetCurrentThreadId() )
  113.     {
  114.         m_dwRecursionCount++;
  115.         return;
  116.     }
  117.  
  118.     LONGLONG llOwnersAndWaiters = ::InterlockedIncrement64( &m_llOwnersAndWaiters  );
  119.  
  120.     assert(Owners( llOwnersAndWaiters ) > Waiters( llOwnersAndWaiters ));
  121.  
  122.     if( (Owners( llOwnersAndWaiters ) - Waiters( llOwnersAndWaiters )) > 1 )
  123.         for( ; ::WaitForSingleObject( m_xhEvtEnter.h, INFINITE ) != WAIT_OBJECT_0; assert(false) );
  124.  
  125.     ONDEBUG(llOwnersAndWaiters = m_llOwnersAndWaiters);
  126.     assert(Owners( llOwnersAndWaiters ) > Waiters( llOwnersAndWaiters ) && m_dwOwningThreadId == 0);
  127.  
  128.     m_dwOwningThreadId = GetCurrentThreadId();
  129. }
  130.  
  131. void CondVar::Wait()
  132. {
  133.     LONGLONG llOwnersAndWaiters;
  134.     DWORD    dwSavedRecusionCount;
  135.  
  136.     ONDEBUG(llOwnersAndWaiters = m_llOwnersAndWaiters);
  137.     assert(Owners( llOwnersAndWaiters ) > Waiters( llOwnersAndWaiters ) && m_dwOwningThreadId == GetCurrentThreadId());
  138.  
  139.     llOwnersAndWaiters   = ::InterlockedAdd64( &m_llOwnersAndWaiters, 0x100000000 );
  140.     m_dwOwningThreadId   = 0;
  141.     dwSavedRecusionCount = m_dwRecursionCount;
  142.     m_dwRecursionCount   = 0;
  143.  
  144.     if( Owners( llOwnersAndWaiters ) > Waiters( llOwnersAndWaiters ) )
  145.         for( ; !::SetEvent( m_xhEvtEnter.h ); assert(false) );
  146.  
  147.     HANDLE ahWait[2] = { m_xhEvtEnter.h, m_xhSemRelease.h };
  148.  
  149.     for( ; ::WaitForMultipleObjects( 2, ahWait, TRUE, INFINITE ) != WAIT_OBJECT_0; assert(false) );
  150.  
  151.     ONDEBUG(llOwnersAndWaiters = m_llOwnersAndWaiters);
  152.     assert(Owners( llOwnersAndWaiters ) > Waiters( llOwnersAndWaiters ) && m_dwOwningThreadId == 0);
  153.  
  154.     m_dwOwningThreadId = GetCurrentThreadId();
  155.     m_dwRecursionCount = dwSavedRecusionCount;
  156. }
  157.  
  158. void CondVar::Release()
  159. {
  160.     LONGLONG llOwnersAndWaiters,
  161.              llOwnersAndWaitersPrevOrChanged;
  162.  
  163.     for( llOwnersAndWaiters = m_llOwnersAndWaiters; Waiters( llOwnersAndWaiters ); llOwnersAndWaiters = llOwnersAndWaitersPrevOrChanged )
  164.     {
  165.         assert(Owners( llOwnersAndWaiters ) > Waiters( llOwnersAndWaiters ) && m_dwOwningThreadId == GetCurrentThreadId());
  166.  
  167.         if( (llOwnersAndWaitersPrevOrChanged = ::InterlockedCompareExchange64( &m_llOwnersAndWaiters, llOwnersAndWaiters - 0x100000000, llOwnersAndWaiters )) == llOwnersAndWaiters )
  168.         {
  169.             for (; !::ReleaseSemaphore( m_xhSemRelease.h, 1, NULL ); assert( false ));
  170.             break;
  171.         }
  172.     }
  173. }
  174.  
  175. void CondVar::ReleaseAll()
  176. {
  177.     LONGLONG llOwnersAndWaiters,
  178.              llOwnersAndWaitersPrevOrChanged;
  179.  
  180.     for( llOwnersAndWaiters = m_llOwnersAndWaiters; Waiters( llOwnersAndWaiters ); llOwnersAndWaiters = llOwnersAndWaitersPrevOrChanged )
  181.     {
  182.         assert(Owners( llOwnersAndWaiters ) > Waiters( llOwnersAndWaiters ) && m_dwOwningThreadId == GetCurrentThreadId());
  183.  
  184.         if( (llOwnersAndWaitersPrevOrChanged = ::InterlockedCompareExchange64( &m_llOwnersAndWaiters, llOwnersAndWaiters & 0x0FFFFFFFF, llOwnersAndWaiters )) == llOwnersAndWaiters )
  185.         {
  186.             for (; !::ReleaseSemaphore( m_xhSemRelease.h, (LONG)Waiters( llOwnersAndWaiters ), NULL ); assert( false ));
  187.             break;
  188.         }
  189.     }
  190. }
  191.  
  192. void CondVar::Leave()
  193. {
  194.     LONGLONG llOwnersAndWaiters;
  195.     LONG     lRecursionCount;
  196.  
  197.     ONDEBUG(llOwnersAndWaiters = m_llOwnersAndWaiters);
  198.     assert(Owners( llOwnersAndWaiters ) > Waiters( llOwnersAndWaiters ) && m_dwOwningThreadId == GetCurrentThreadId());
  199.  
  200.     if( (lRecursionCount = m_dwRecursionCount) != 0 )
  201.     {
  202.         m_dwRecursionCount = lRecursionCount - 1;
  203.         return;
  204.     }
  205.    
  206.     m_dwOwningThreadId = 0;
  207.  
  208.     llOwnersAndWaiters = ::InterlockedDecrement64( &m_llOwnersAndWaiters );
  209.  
  210.     if( Owners( llOwnersAndWaiters ) > Waiters( llOwnersAndWaiters ) )
  211.         for( ; !::SetEvent( m_xhEvtEnter.h ); assert(false) );
  212. }
  213.  
  214. /* ---------------------------- unit-test ---------------------------- */
  215.  
  216. #include <cstdio>
  217. #include <cstdlib>
  218. #include <deque>
  219. #include <map>
  220.  
  221. DWORD WINAPI ThreadFunc( LPVOID lpvThreadParam );
  222.  
  223. using namespace std;
  224.  
  225. struct ThreadResult
  226. {
  227.     DWORD dwThreadId;
  228.     DWORD dwCounter;
  229. };
  230.  
  231. CondVar             cv;
  232. deque<ThreadResult> dqtr;
  233. bool volatile       fStop = false;
  234.  
  235. int main()
  236. {
  237.     int const              NTHREADS = 16;
  238.     HANDLE                 ahThreads[NTHREADS];
  239.     int                    i;
  240.     std::map<DWORD, DWORD> mTRs;
  241.  
  242.     for( i = 0; i < NTHREADS; i++ )
  243.         ahThreads[i] = CreateThread( NULL, 0, ThreadFunc, NULL, 0, NULL );
  244.  
  245.     for( i = 0; i < 10000; i++ )
  246.     {
  247.         ThreadResult tr;
  248.  
  249.         ::cv.Enter();
  250.  
  251.         if( ::dqtr.empty() )
  252.             ::cv.Wait();
  253.  
  254.         tr = ::dqtr.front();
  255.         ::dqtr.pop_front();
  256.         ::cv.Leave();
  257.  
  258.         printf( "Thread: %08X - Number: %d\n", (unsigned)tr.dwThreadId, (unsigned)tr.dwCounter );
  259.  
  260.         if( mTRs.find( tr.dwThreadId ) == mTRs.end() )
  261.             mTRs[tr.dwThreadId] = tr.dwCounter;
  262.         else
  263.             assert((mTRs[tr.dwThreadId] + 1) == tr.dwCounter),
  264.             mTRs[tr.dwThreadId] = tr.dwCounter;
  265.     }
  266.  
  267.     for( ::fStop = true, i = 0; i < NTHREADS; i++ )
  268.         WaitForSingleObject( ahThreads[i], INFINITE );
  269.  
  270.     return 0;
  271. }
  272.  
  273. DWORD WINAPI ThreadFunc( LPVOID lpvThreadParam )
  274. {
  275.     ThreadResult tr;
  276.  
  277.     tr.dwThreadId = GetCurrentThreadId();
  278.     tr.dwCounter  = 0;
  279.  
  280.     for( ; !::fStop; tr.dwCounter++ )
  281.     {
  282.         ::cv.Enter();
  283.         ::dqtr.push_back( tr );
  284.         ::cv.Release();
  285.         ::cv.Leave();
  286.     }
  287.  
  288.     return 0;
  289. }
Advertisement
Add Comment
Please, Sign In to add comment