Advertisement
Guest User

even faster condvar

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