ncbimtx.cpp
上传用户:yhdzpy8989
上传日期:2007-06-13
资源大小:13604k
文件大小:30k
源码类别:

生物技术

开发平台:

C/C++

  1. /*
  2.  * ===========================================================================
  3.  * PRODUCTION $Log: ncbimtx.cpp,v $
  4.  * PRODUCTION Revision 1000.2  2004/06/03 19:28:17  gouriano
  5.  * PRODUCTION PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.17
  6.  * PRODUCTION
  7.  * ===========================================================================
  8.  */
  9. /*  $Id: ncbimtx.cpp,v 1000.2 2004/06/03 19:28:17 gouriano Exp $
  10.  * ===========================================================================
  11.  *
  12.  *                            PUBLIC DOMAIN NOTICE
  13.  *               National Center for Biotechnology Information
  14.  *
  15.  *  This software/database is a "United States Government Work" under the
  16.  *  terms of the United States Copyright Act.  It was written as part of
  17.  *  the author's official duties as a United States Government employee and
  18.  *  thus cannot be copyrighted.  This software/database is freely available
  19.  *  to the public for use. The National Library of Medicine and the U.S.
  20.  *  Government have not placed any restriction on its use or reproduction.
  21.  *
  22.  *  Although all reasonable efforts have been taken to ensure the accuracy
  23.  *  and reliability of the software and data, the NLM and the U.S.
  24.  *  Government do not and cannot warrant the performance or results that
  25.  *  may be obtained by using this software or data. The NLM and the U.S.
  26.  *  Government disclaim all warranties, express or implied, including
  27.  *  warranties of performance, merchantability or fitness for any particular
  28.  *  purpose.
  29.  *
  30.  *  Please cite the author in any work or product based on this material.
  31.  *
  32.  * ===========================================================================
  33.  *
  34.  * Author:  Denis Vakatov, Aleksey Grichenko
  35.  *
  36.  * File Description:
  37.  *   Multi-threading -- fast mutexes
  38.  *
  39.  *   MUTEX:
  40.  *      CInternalMutex   -- platform-dependent mutex functionality
  41.  *
  42.  */
  43. #include <ncbi_pch.hpp>
  44. #include <corelib/ncbimtx.hpp>
  45. #include <corelib/ncbi_limits.h>
  46. #include "ncbidbg_p.hpp"
  47. #include <stdio.h>
  48. #include <algorithm>
  49. #ifdef NCBI_POSIX_THREADS
  50. #  include <sys/time.h> // for gettimeofday()
  51. #endif
  52. #define STACK_THRESHOLD (1024)
  53. BEGIN_NCBI_SCOPE
  54. /////////////////////////////////////////////////////////////////////////////
  55. //  CInternalMutex::
  56. //
  57. void SSystemFastMutex::InitializeHandle(void)
  58. {
  59.     // Create platform-dependent mutex handle
  60. #if defined(NCBI_WIN32_THREADS)
  61.     xncbi_Validate((m_Handle = CreateMutex(NULL, FALSE, NULL)) != NULL,
  62.                    "Mutex creation failed");
  63. #elif defined(NCBI_POSIX_THREADS)
  64.     xncbi_Validate(pthread_mutex_init(&m_Handle, 0) == 0,
  65.                    "Mutex creation failed");
  66. #endif
  67. }
  68. void SSystemFastMutex::DestroyHandle(void)
  69. {
  70.     // Destroy system mutex handle
  71. #if defined(NCBI_WIN32_THREADS)
  72.     xncbi_Verify(CloseHandle(m_Handle) != 0);
  73. #elif defined(NCBI_POSIX_THREADS)
  74.     xncbi_Verify(pthread_mutex_destroy(&m_Handle) == 0);
  75. #endif
  76. }
  77. void SSystemFastMutex::InitializeStatic(void)
  78. {
  79. #if !defined(NCBI_NO_THREADS)
  80.     switch ( m_Magic ) {
  81.     case eMutexUninitialized: // ok
  82.         break;
  83.     case eMutexInitialized:
  84.         xncbi_Validate(0, "Double initialization of mutex");
  85.         break;
  86.     default:
  87.         xncbi_Validate(0, "SSystemFastMutex::m_Magic contains invalid value");
  88.         break;
  89.     }
  90.     InitializeHandle();
  91. #endif
  92.     m_Magic = eMutexInitialized;
  93. }
  94. void SSystemFastMutex::InitializeDynamic(void)
  95. {
  96. #if !defined(NCBI_NO_THREADS)
  97.     InitializeHandle();
  98. #endif
  99.     m_Magic = eMutexInitialized;
  100. }
  101. void SSystemFastMutex::Destroy(void)
  102. {
  103. #if !defined(NCBI_NO_THREADS)
  104.     xncbi_Validate(IsInitialized(), "Destruction of uninitialized mutex");
  105. #endif
  106.     m_Magic = eMutexUninitialized;
  107.     DestroyHandle();
  108. }
  109. void SSystemFastMutex::ThrowUninitialized(void)
  110. {
  111.     NCBI_THROW(CMutexException, eUninitialized, "Mutex uninitialized");
  112. }
  113. void SSystemFastMutex::ThrowLockFailed(void)
  114. {
  115.     NCBI_THROW(CMutexException, eLock, "Mutex lock failed");
  116. }
  117. void SSystemFastMutex::ThrowUnlockFailed(void)
  118. {
  119.     NCBI_THROW(CMutexException, eUnlock, "Mutex unlock failed");
  120. }
  121. void SSystemFastMutex::ThrowTryLockFailed(void)
  122. {
  123.     NCBI_THROW(CMutexException, eTryLock,
  124.                "Mutex check (TryLock) failed");
  125. }
  126. void SSystemMutex::Destroy(void)
  127. {
  128.     xncbi_Validate(m_Count == 0, "Destruction of locked mutex");
  129.     m_Mutex.Destroy();
  130. }
  131. #if !defined(NCBI_NO_THREADS)
  132. void SSystemMutex::Lock(void)
  133. {
  134.     m_Mutex.CheckInitialized();
  135.     CThreadSystemID owner = CThreadSystemID::GetCurrent();
  136.     if ( m_Count > 0 && m_Owner.Is(owner) ) {
  137.         // Don't lock twice, just increase the counter
  138.         m_Count++;
  139.         return;
  140.     }
  141.     // Lock the mutex and remember the owner
  142.     m_Mutex.Lock();
  143.     assert(m_Count == 0);
  144.     m_Owner.Set(owner);
  145.     m_Count = 1;
  146. }
  147. bool SSystemMutex::TryLock(void)
  148. {
  149.     m_Mutex.CheckInitialized();
  150.     CThreadSystemID owner = CThreadSystemID::GetCurrent();
  151.     if ( m_Count > 0 && m_Owner.Is(owner) ) {
  152.         // Don't lock twice, just increase the counter
  153.         m_Count++;
  154.         return true;
  155.     }
  156.     // If TryLock is successful, remember the owner
  157.     if ( m_Mutex.TryLock() ) {
  158.         assert(m_Count == 0);
  159.         m_Owner.Set(owner);
  160.         m_Count = 1;
  161.         return true;
  162.     }
  163.     // Cannot lock right now
  164.     return false;
  165. }
  166. void SSystemMutex::Unlock(void)
  167. {
  168.     m_Mutex.CheckInitialized();
  169.     // No unlocks by threads other than owner.
  170.     // This includes no unlocks of unlocked mutex.
  171.     CThreadSystemID owner = CThreadSystemID::GetCurrent();
  172.     if ( m_Count == 0 || m_Owner.IsNot(owner) ) {
  173.         ThrowNotOwned();
  174.     }
  175.     // No real unlocks if counter > 1, just decrease it
  176.     if (--m_Count > 0) {
  177.         return;
  178.     }
  179.     assert(m_Count == 0);
  180.     // This was the last lock - clear the owner and unlock the mutex
  181.     m_Mutex.Unlock();
  182. }
  183. #endif
  184. void SSystemMutex::ThrowNotOwned(void)
  185. {
  186.     NCBI_THROW(CMutexException, eOwner,
  187.                "Mutex is not owned by current thread");
  188. }
  189. #if defined(NEED_AUTO_INITIALIZE_MUTEX)
  190. const char* kInitMutexName = "NCBI_CAutoInitializeStaticMutex";
  191. void CAutoInitializeStaticFastMutex::Initialize(void)
  192. {
  193.     if ( m_Mutex.IsInitialized() ) {
  194.         return;
  195.     }
  196.     TSystemMutex init_mutex = CreateMutex(NULL, FALSE, kInitMutexName);
  197.     assert(init_mutex);
  198.     assert(WaitForSingleObject(init_mutex, INFINITE) == WAIT_OBJECT_0);
  199.     if ( !m_Mutex.IsInitialized() ) {
  200.         m_Mutex.InitializeStatic();
  201.     }
  202.     assert(ReleaseMutex(init_mutex));
  203.     assert(m_Mutex.IsInitialized());
  204.     CloseHandle(init_mutex);
  205. }
  206. void CAutoInitializeStaticMutex::Initialize(void)
  207. {
  208.     if ( m_Mutex.IsInitialized() ) {
  209.         return;
  210.     }
  211.     TSystemMutex init_mutex = CreateMutex(NULL, FALSE, kInitMutexName);
  212.     assert(init_mutex);
  213.     assert(WaitForSingleObject(init_mutex, INFINITE) == WAIT_OBJECT_0);
  214.     if ( !m_Mutex.IsInitialized() ) {
  215.         m_Mutex.InitializeStatic();
  216.     }
  217.     assert(ReleaseMutex(init_mutex));
  218.     assert(m_Mutex.IsInitialized());
  219.     CloseHandle(init_mutex);
  220. }
  221. #endif
  222. const char* CMutexException::GetErrCodeString(void) const
  223. {
  224.     switch (GetErrCode()) {
  225.     case eLock:    return "eLock";
  226.     case eUnlock:  return "eUnlock";
  227.     case eTryLock: return "eTryLock";
  228.     case eOwner:   return "eOwner";
  229.     case eUninitialized:  return "eUninitialized";
  230.     default:       return CException::GetErrCodeString();
  231.     }
  232. }
  233. /////////////////////////////////////////////////////////////////////////////
  234. //  CInternalRWLock::
  235. //
  236. #if defined(NCBI_WIN32_THREADS)
  237. class CWindowsHandle
  238. {
  239. public:
  240.     CWindowsHandle(HANDLE h = NULL) : m_Handle(h) {}
  241.     CWindowsHandle(HANDLE h, const char* errorMessage) : m_Handle(h)
  242.     {
  243.         xncbi_Validate(h != NULL, errorMessage);
  244.     }
  245.     ~CWindowsHandle(void) { Close(); }
  246.     void Close(void)
  247.     {
  248.         if ( m_Handle != NULL ) {
  249.             CloseHandle(m_Handle);
  250.             m_Handle = NULL;
  251.         }
  252.     }
  253.     void Set(HANDLE h)
  254.     {
  255.         Close();
  256.         m_Handle = h;
  257.     }
  258.     void Set(HANDLE h, const char* errorMessage)
  259.     {
  260.         xncbi_Validate(h != NULL, errorMessage);
  261.         Set(h);        
  262.     }
  263.     operator HANDLE(void) const { return m_Handle; }
  264. protected:
  265.     HANDLE m_Handle;
  266. private:
  267.     CWindowsHandle(const CWindowsHandle& h);
  268.     CWindowsHandle& operator=(const CWindowsHandle& h);
  269. };
  270. class CWindowsSemaphore : public CWindowsHandle
  271. {
  272. public:
  273.     CWindowsSemaphore(LONG initialCount = 0, LONG maximumCount = INFINITE)
  274.         : CWindowsHandle(CreateSemaphore(NULL,
  275.                                          initialCount, maximumCount,
  276.                                          NULL),
  277.                          "CreateSemaphore() failed")
  278.     {
  279.     }
  280.     LONG Release(LONG add = 1)
  281.     {
  282.         LONG prev_sema;
  283.         xncbi_Validate(ReleaseSemaphore(*this, add, &prev_sema),
  284.                        "CWindowsSemaphore::Release() failed");
  285.         return prev_sema;
  286.     }
  287. };
  288. #endif
  289. #if defined(NCBI_POSIX_THREADS)
  290. class CPthreadCond
  291. {
  292. public:
  293.     CPthreadCond(void)
  294.         : m_Initialized(pthread_cond_init(&m_Handle, 0) != 0)
  295.     {
  296.     }
  297.     ~CPthreadCond(void)
  298.     {
  299.         if ( m_Initialized ) {
  300.             pthread_cond_destroy(&m_Handle);
  301.         }
  302.     }
  303.     operator pthread_cond_t*(void) { return &m_Handle; }
  304.     operator pthread_cond_t&(void) { return m_Handle; }
  305. protected:
  306.     pthread_cond_t  m_Handle;
  307.     bool            m_Initialized;
  308. };
  309. #endif
  310. class CInternalRWLock
  311. {
  312. public:
  313.     CInternalRWLock(void);
  314.     // Platform-dependent RW-lock data
  315. #if defined(NCBI_WIN32_THREADS)
  316.     CWindowsSemaphore   m_Rsema;
  317.     CWindowsSemaphore   m_Wsema;
  318.     CFastMutex          m_Mutex;
  319. #elif defined(NCBI_POSIX_THREADS)
  320.     CPthreadCond        m_Rcond;
  321.     CPthreadCond        m_Wcond;
  322.     CFastMutex          m_Mutex;
  323. #endif
  324. };
  325. inline
  326. CInternalRWLock::CInternalRWLock(void)
  327. #if defined(NCBI_WIN32_THREADS)
  328.     : m_Rsema(1, 1), m_Wsema(1, 1)
  329. #endif
  330. {
  331. }
  332. /////////////////////////////////////////////////////////////////////////////
  333. //  CRWLock::
  334. //
  335. CRWLock::CRWLock(void)
  336.     : m_RW(new CInternalRWLock),
  337.       m_Count(0)
  338. {
  339. #if defined(_DEBUG)
  340.     m_Readers.reserve(16);
  341. #endif
  342. }
  343. CRWLock::~CRWLock(void)
  344. {
  345. }
  346. void CRWLock::ReadLock(void)
  347. {
  348. #if defined(NCBI_NO_THREADS)
  349.     return;
  350. #else
  351.     // Lock mutex now, unlock before exit.
  352.     // (in fact, it will be unlocked by the waiting function for a while)
  353.     CFastMutexGuard guard(m_RW->m_Mutex);
  354.     CThreadSystemID self_id = CThreadSystemID::GetCurrent();
  355.     if ( m_Count < 0 ) {
  356.         if ( m_Owner.Is(self_id) ) {
  357.             // if W-locked by the same thread - update W-counter
  358.             m_Count--;
  359.         }
  360.         else {
  361.             // W-locked by another thread
  362. #if defined(NCBI_WIN32_THREADS)
  363.             HANDLE obj[2];
  364.             DWORD  wait_res;
  365.             obj[0] = m_RW->m_Mutex.GetHandle();
  366.             obj[1] = m_RW->m_Rsema;
  367.             xncbi_Validate(ReleaseMutex(m_RW->m_Mutex.GetHandle()),
  368.                            "CRWLock::ReadLock() - release mutex error");
  369.             wait_res = WaitForMultipleObjects(2, obj, TRUE, INFINITE);
  370.             xncbi_Validate(wait_res >= WAIT_OBJECT_0  &&
  371.                            wait_res < WAIT_OBJECT_0 + 2,
  372.                            "CRWLock::ReadLock() - R-lock waiting error");
  373.             // Success, check the semaphore
  374.             xncbi_Validate(m_RW->m_Rsema.Release() == 0,
  375.                            "CRWLock::ReadLock() - invalid R-semaphore state");
  376.             if (m_Count == 0) {
  377.                 xncbi_Validate(WaitForSingleObject(m_RW->m_Wsema,
  378.                                                    0) == WAIT_OBJECT_0,
  379.                                "CRWLock::ReadLock() - "
  380.                                "failed to lock W-semaphore");
  381.             }
  382. #elif defined(NCBI_POSIX_THREADS)
  383.             while (m_Count < 0) {
  384.                 xncbi_Validate(pthread_cond_wait(m_RW->m_Rcond,
  385.                                                  m_RW->m_Mutex.GetHandle())
  386.                                == 0,
  387.                                "CRWLock::ReadLock() - R-lock waiting error");
  388.             }
  389. #else
  390.             // Can not be already W-locked by another thread without MT
  391.             xncbi_Validate(0,
  392.                            "CRWLock::ReadLock() - "
  393.                            "weird R-lock error in non-MT mode");
  394. #endif
  395.             xncbi_Validate(m_Count >= 0,
  396.                            "CRWLock::ReadLock() - invalid readers counter");
  397.             m_Count++;
  398.         }
  399.     }
  400.     else {
  401. #if defined(NCBI_WIN32_THREADS)
  402.         if (m_Count == 0) {
  403.             // Unlocked
  404.             // Lock against writers
  405.             xncbi_Validate(WaitForSingleObject(m_RW->m_Wsema, 0)
  406.                            == WAIT_OBJECT_0,
  407.                            "CRWLock::ReadLock() - "
  408.                            "can not lock W-semaphore");
  409.         }
  410. #endif
  411.         m_Count++;
  412.     }
  413. #if defined(_DEBUG)
  414.     // Remember new reader
  415.     if (m_Count > 0) {
  416.         m_Readers.push_back(self_id);
  417.     }
  418. #endif
  419. #endif
  420. }
  421. bool CRWLock::TryReadLock(void)
  422. {
  423. #if defined(NCBI_NO_THREADS)
  424.     return true;
  425. #else
  426.     CFastMutexGuard guard(m_RW->m_Mutex);
  427.     CThreadSystemID self_id = CThreadSystemID::GetCurrent();
  428.     if (m_Count < 0) {
  429.         if ( m_Owner.IsNot(self_id) ) {
  430.             // W-locked by another thread
  431.             return false;
  432.         }
  433.         else {
  434.             // W-locked, try to set R after W if in the same thread
  435.             m_Count--;
  436.             return true;
  437.         }
  438.     }
  439.     // Unlocked - do R-lock
  440. #if defined(NCBI_WIN32_THREADS)
  441.     if (m_Count == 0) {
  442.         // Lock W-semaphore in MSWIN
  443.         xncbi_Validate(WaitForSingleObject(m_RW->m_Wsema, 0)
  444.                        == WAIT_OBJECT_0,
  445.                        "CRWLock::TryReadLock() - "
  446.                        "can not lock W-semaphore");
  447.     }
  448. #endif
  449.     m_Count++;
  450. #if defined(_DEBUG)
  451.     m_Readers.push_back(self_id);
  452. #endif
  453.     return true;
  454. #endif
  455. }
  456. void CRWLock::WriteLock(void)
  457. {
  458. #if defined(NCBI_NO_THREADS)
  459.     return;
  460. #else
  461.     CFastMutexGuard guard(m_RW->m_Mutex);
  462.     CThreadSystemID self_id = CThreadSystemID::GetCurrent();
  463.     if ( m_Count < 0 && m_Owner.Is(self_id) ) {
  464.         // W-locked by the same thread
  465.         m_Count--;
  466.     }
  467.     else {
  468.         // Unlocked or RW-locked by another thread
  469.         // Look in readers - must not be there
  470.         xncbi_Validate(find(m_Readers.begin(), m_Readers.end(), self_id)
  471.                        == m_Readers.end(),
  472.                        "CRWLock::WriteLock() - "
  473.                        "attempt to set W-after-R lock");
  474. #if defined(NCBI_WIN32_THREADS)
  475.         HANDLE obj[3];
  476.         obj[0] = m_RW->m_Rsema;
  477.         obj[1] = m_RW->m_Wsema;
  478.         obj[2] = m_RW->m_Mutex.GetHandle();
  479.         DWORD wait_res;
  480.         if (m_Count == 0) {
  481.             // Unlocked - lock both semaphores
  482.             wait_res = WaitForMultipleObjects(2, obj, TRUE, 0);
  483.             xncbi_Validate(wait_res >= WAIT_OBJECT_0  &&
  484.                            wait_res < WAIT_OBJECT_0+2,
  485.                            "CRWLock::WriteLock() - "
  486.                            "error locking R&W-semaphores");
  487.         }
  488.         else {
  489.             // Locked by another thread - wait for unlock
  490.             xncbi_Validate(ReleaseMutex(m_RW->m_Mutex.GetHandle()),
  491.                            "CRWLock::ReadLock() - release mutex error");
  492.             wait_res = WaitForMultipleObjects(3, obj, TRUE, INFINITE);
  493.             xncbi_Validate(wait_res >= WAIT_OBJECT_0  &&
  494.                            wait_res < WAIT_OBJECT_0+3,
  495.                            "CRWLock::WriteLock() - "
  496.                            "error locking R&W-semaphores");
  497.         }
  498. #elif defined(NCBI_POSIX_THREADS)
  499.         while (m_Count != 0) {
  500.             xncbi_Validate(pthread_cond_wait(m_RW->m_Wcond,
  501.                                              m_RW->m_Mutex.GetHandle()) == 0,
  502.                            "CRWLock::WriteLock() - "
  503.                            "error locking R&W-conditionals");
  504.         }
  505. #endif
  506.         xncbi_Validate(m_Count >= 0,
  507.                        "CRWLock::WriteLock() - invalid readers counter");
  508.         m_Count = -1;
  509.         m_Owner.Set(self_id);
  510.     }
  511.     // No readers allowed
  512.     _ASSERT(m_Readers.empty());
  513. #endif
  514. }
  515. bool CRWLock::TryWriteLock(void)
  516. {
  517. #if defined(NCBI_NO_THREADS)
  518.     return true;
  519. #else
  520.     CFastMutexGuard guard(m_RW->m_Mutex);
  521.     CThreadSystemID self_id = CThreadSystemID::GetCurrent();
  522.     if ( m_Count < 0 ) {
  523.         // W-locked
  524.         if ( m_Owner.IsNot(self_id) ) {
  525.             // W-locked by another thread
  526.             return false;
  527.         }
  528.         // W-locked by same thread
  529.         m_Count--;
  530.     }
  531.     else if ( m_Count > 0 ) {
  532.         // R-locked
  533.         return false;
  534.     }
  535.     else {
  536.         // Unlocked - do W-lock
  537. #if defined(NCBI_WIN32_THREADS)
  538.         // In MSWIN lock semaphores
  539.         HANDLE obj[2];
  540.         obj[0] = m_RW->m_Rsema;
  541.         obj[1] = m_RW->m_Wsema;
  542.         DWORD wait_res;
  543.         wait_res = WaitForMultipleObjects(2, obj, TRUE, 0);
  544.         xncbi_Validate(wait_res >= WAIT_OBJECT_0  &&
  545.                        wait_res < WAIT_OBJECT_0 + 2,
  546.                        "CRWLock::TryWriteLock() - "
  547.                        "error locking R&W-semaphores");
  548. #endif
  549.         m_Count = -1;
  550.         m_Owner.Set(self_id);
  551.     }
  552.     // No readers allowed
  553.     _ASSERT(m_Readers.empty());
  554.     return true;
  555. #endif
  556. }
  557. void CRWLock::Unlock(void)
  558. {
  559. #if defined(NCBI_NO_THREADS)
  560.     return;
  561. #else
  562.     CFastMutexGuard guard(m_RW->m_Mutex);
  563.     CThreadSystemID self_id = CThreadSystemID::GetCurrent();
  564.     if (m_Count < 0) {
  565.         // Check it is R-locked or W-locked by the same thread
  566.         xncbi_Validate(m_Owner.Is(self_id),
  567.                        "CRWLock::Unlock() - "
  568.                        "RWLock is locked by another thread");
  569.         if ( ++m_Count == 0 ) {
  570.             // Unlock the last W-lock
  571. #if defined(NCBI_WIN32_THREADS)
  572.             xncbi_Validate(m_RW->m_Rsema.Release() == 0,
  573.                            "CRWLock::Unlock() - invalid R-semaphore state");
  574.             xncbi_Validate(m_RW->m_Wsema.Release() == 0,
  575.                            "CRWLock::Unlock() - invalid R-semaphore state");
  576. #elif defined(NCBI_POSIX_THREADS)
  577.             xncbi_Validate(pthread_cond_broadcast(m_RW->m_Rcond) == 0,
  578.                            "CRWLock::Unlock() - error signalling unlock");
  579.             xncbi_Validate(pthread_cond_signal(m_RW->m_Wcond) == 0,
  580.                            "CRWLock::Unlock() - error signalling unlock");
  581. #endif
  582.         }
  583. #if defined(_DEBUG)
  584.         // Check if the unlocking thread is in the owners list
  585.         _ASSERT(find(m_Readers.begin(), m_Readers.end(), self_id)
  586.                == m_Readers.end());
  587. #endif
  588.     }
  589.     else {
  590.         xncbi_Validate(m_Count != 0,
  591.                        "CRWLock::Unlock() - RWLock is not locked");
  592.         if ( --m_Count == 0 ) {
  593.             // Unlock the last R-lock
  594. #if defined(NCBI_WIN32_THREADS)
  595.             xncbi_Validate(m_RW->m_Wsema.Release() == 0,
  596.                            "CRWLock::Unlock() - invalid W-semaphore state");
  597. #elif defined(NCBI_POSIX_THREADS)
  598.             xncbi_Validate(pthread_cond_signal(m_RW->m_Wcond) == 0,
  599.                            "CRWLock::Unlock() - error signaling unlock");
  600. #endif
  601.         }
  602. #if defined(_DEBUG)
  603.         // Check if the unlocking thread is in the owners list
  604.         vector<CThreadSystemID>::iterator found =
  605.             find(m_Readers.begin(), m_Readers.end(), self_id);
  606.         _ASSERT(found != m_Readers.end());
  607.         m_Readers.erase(found);
  608.         if ( m_Count == 0 ) {
  609.             _ASSERT(m_Readers.empty());
  610.         }
  611. #endif
  612.     }
  613. #endif
  614. }
  615. /////////////////////////////////////////////////////////////////////////////
  616. //  SEMAPHORE
  617. //
  618. // Platform-specific representation (or emulation) of semaphore
  619. struct SSemaphore
  620. {
  621. #if defined(NCBI_POSIX_THREADS)
  622.     unsigned int     max_count;
  623.     unsigned int     count; 
  624.     unsigned int     wait_count;  // # of threads currently waiting on the sema
  625.     pthread_mutex_t  mutex;
  626.     pthread_cond_t   cond;
  627. #elif defined(NCBI_WIN32_THREADS)
  628.     HANDLE           sem;
  629. #else
  630.     unsigned int     max_count;
  631.     unsigned int     count;
  632. #endif
  633. };
  634. CSemaphore::CSemaphore(unsigned int init_count, unsigned int max_count)
  635. {
  636.     xncbi_Validate(max_count != 0,
  637.                    "CSemaphore::CSemaphore() - max_count passed zero");
  638.     xncbi_Validate(init_count <= max_count,
  639.                    "CSemaphore::CSemaphore() - init_count "
  640.                    "greater than max_count");
  641.     m_Sem = new SSemaphore;
  642.     auto_ptr<SSemaphore> auto_sem(m_Sem);
  643. #if defined(NCBI_POSIX_THREADS)
  644.     m_Sem->max_count = max_count;
  645.     m_Sem->count     = init_count;
  646.     m_Sem->wait_count = 0;
  647.     xncbi_Validate(pthread_mutex_init(&m_Sem->mutex, 0) == 0,
  648.                    "CSemaphore::CSemaphore() - pthread_mutex_init() failed");
  649.     xncbi_Validate(pthread_cond_init(&m_Sem->cond, 0) == 0,
  650.                    "CSemaphore::CSemaphore() - pthread_cond_init() failed");
  651. #elif defined(NCBI_WIN32_THREADS)
  652.     m_Sem->sem = CreateSemaphore(NULL, init_count, max_count, NULL);
  653.     xncbi_Validate(m_Sem->sem != NULL,
  654.                    "CSemaphore::CSemaphore() - CreateSemaphore() failed");
  655. #else
  656.     m_Sem->max_count = max_count;
  657.     m_Sem->count     = init_count;
  658. #endif
  659.     auto_sem.release();
  660. }
  661. CSemaphore::~CSemaphore(void)
  662. {
  663. #if defined(NCBI_POSIX_THREADS)
  664.     _ASSERT(m_Sem->wait_count == 0);
  665.     xncbi_Verify(pthread_mutex_destroy(&m_Sem->mutex) == 0);
  666.     xncbi_Verify(pthread_cond_destroy(&m_Sem->cond)  == 0);
  667. #elif defined(NCBI_WIN32_THREADS)
  668.     xncbi_Verify( CloseHandle(m_Sem->sem) );
  669. #endif
  670.     delete m_Sem;
  671. }
  672. void CSemaphore::Wait(void)
  673. {
  674. #if defined(NCBI_POSIX_THREADS)
  675.     xncbi_Validate(pthread_mutex_lock(&m_Sem->mutex) == 0,
  676.                    "CSemaphore::Wait() - pthread_mutex_lock() failed");
  677.     if (m_Sem->count != 0) {
  678.         m_Sem->count--;
  679.     }
  680.     else {
  681.         m_Sem->wait_count++;
  682.         do {
  683.             if (pthread_cond_wait(&m_Sem->cond, &m_Sem->mutex) != 0) {
  684.                 xncbi_Validate(pthread_mutex_unlock(&m_Sem->mutex) == 0,
  685.                                "CSemaphore::Wait() - "
  686.                                "pthread_cond_wait() and "
  687.                                "pthread_mutex_unlock() failed");
  688.                 xncbi_Validate(0,
  689.                                "CSemaphore::Wait() - "
  690.                                "pthread_cond_wait() failed");
  691.             }
  692.         } while (m_Sem->count == 0);
  693.         m_Sem->wait_count--;
  694.         m_Sem->count--;
  695.     }
  696.     xncbi_Validate(pthread_mutex_unlock(&m_Sem->mutex) == 0,
  697.                    "CSemaphore::Wait() - pthread_mutex_unlock() failed");
  698. #elif defined(NCBI_WIN32_THREADS)
  699.     xncbi_Validate(WaitForSingleObject(m_Sem->sem, INFINITE) == WAIT_OBJECT_0,
  700.                    "CSemaphore::Wait() - WaitForSingleObject() failed");
  701. #else
  702.     xncbi_Validate(m_Sem->count != 0,
  703.                    "CSemaphore::Wait() - "
  704.                    "wait with zero count in one-thread mode(?!)");
  705.     m_Sem->count--;
  706. #endif
  707. }
  708. #if defined(NCBI_NO_THREADS)
  709. # define NCBI_THREADS_ARG(arg) 
  710. #else
  711. # define NCBI_THREADS_ARG(arg) arg
  712. #endif
  713. bool CSemaphore::TryWait(unsigned int NCBI_THREADS_ARG(timeout_sec),
  714.                          unsigned int NCBI_THREADS_ARG(timeout_nsec))
  715. {
  716. #if defined(NCBI_POSIX_THREADS)
  717.     xncbi_Validate(pthread_mutex_lock(&m_Sem->mutex) == 0,
  718.                    "CSemaphore::TryWait() - pthread_mutex_lock() failed");
  719.     bool retval = false;
  720.     if (m_Sem->count != 0) {
  721.         m_Sem->count--;
  722.         retval = true;
  723.     }
  724.     else if (timeout_sec > 0  ||  timeout_nsec > 0) {
  725. # ifdef NCBI_OS_SOLARIS
  726.         // arbitrary limit of 100Ms (~3.1 years) -- supposedly only for
  727.         // native threads, but apparently also for POSIX threads :-/
  728.         if (timeout_sec >= 100 * 1000 * 1000) {
  729.             timeout_sec  = 100 * 1000 * 1000;
  730.             timeout_nsec = 0;
  731.         }
  732. # endif
  733.         static const unsigned int kBillion = 1000 * 1000 * 1000;
  734.         struct timeval  now;
  735.         struct timespec timeout = { 0, 0 };
  736.         gettimeofday(&now, 0);
  737.         // timeout_sec added below to avoid overflow
  738.         timeout.tv_sec  = now.tv_sec;
  739.         timeout.tv_nsec = now.tv_usec * 1000 + timeout_nsec;
  740.         if ((unsigned int)timeout.tv_nsec >= kBillion) {
  741.             timeout.tv_sec  += timeout.tv_nsec / kBillion;
  742.             timeout.tv_nsec %= kBillion;
  743.         }
  744.         if (timeout_sec > (unsigned int)(kMax_Int - timeout.tv_sec)) {
  745.             // Max out rather than overflowing
  746.             timeout.tv_sec  = kMax_Int;
  747.             timeout.tv_nsec = kBillion - 1;
  748.         } else {
  749.             timeout.tv_sec += timeout_sec;
  750.         }
  751.         
  752.         m_Sem->wait_count++;
  753.         do {
  754.             int status = pthread_cond_timedwait(&m_Sem->cond, &m_Sem->mutex,
  755.                                                 &timeout);
  756.             if (status == ETIMEDOUT) {
  757.                 break;
  758.             } else if (status != 0  &&  status != EINTR) {
  759.                 // EINVAL, presumably?
  760.                 xncbi_Validate(pthread_mutex_unlock(&m_Sem->mutex) == 0,
  761.                                "CSemaphore::TryWait() - "
  762.                                "pthread_cond_timedwait() and "
  763.                                "pthread_mutex_unlock() failed");
  764.                 xncbi_Validate(0, "CSemaphore::TryWait() - "
  765.                                "pthread_cond_timedwait() failed");
  766.             }
  767.         } while (m_Sem->count == 0);
  768.         m_Sem->wait_count--;
  769.         m_Sem->count--;
  770.         retval = true;
  771.     }
  772.     xncbi_Validate(pthread_mutex_unlock(&m_Sem->mutex) == 0,
  773.                    "CSemaphore::TryWait() - pthread_mutex_unlock() failed");
  774.     return retval;
  775. #elif defined(NCBI_WIN32_THREADS)
  776.     DWORD timeout_msec; // DWORD == unsigned long
  777.     if (timeout_sec >= kMax_ULong / 1000) {
  778.         timeout_msec = kMax_ULong;
  779.     } else {
  780.         timeout_msec = timeout_sec * 1000 + timeout_nsec / (1000 * 1000);
  781.     }
  782.     DWORD res = WaitForSingleObject(m_Sem->sem, timeout_msec);
  783.     xncbi_Validate(res == WAIT_OBJECT_0  ||  res == WAIT_TIMEOUT,
  784.                    "CSemaphore::TryWait() - WaitForSingleObject() failed");
  785.     return (res == WAIT_OBJECT_0);
  786. #else
  787.     if (m_Sem->count == 0)
  788.         return false;
  789.     m_Sem->count--;
  790.     return true;
  791. #endif
  792. }
  793. void CSemaphore::Post(unsigned int count)
  794. {
  795.     if (count == 0)
  796.         return;
  797. #if defined (NCBI_POSIX_THREADS)
  798.     xncbi_Validate(pthread_mutex_lock(&m_Sem->mutex) == 0,
  799.                    "CSemaphore::Post() - pthread_mutex_lock() failed");
  800.     if (m_Sem->count > kMax_UInt - count  ||
  801.         m_Sem->count + count > m_Sem->max_count) {
  802.         xncbi_Validate(pthread_mutex_unlock(&m_Sem->mutex) == 0,
  803.                        "CSemaphore::Post() - "
  804.                        "attempt to exceed max_count and "
  805.                        "pthread_mutex_unlock() failed");
  806.         xncbi_Validate(m_Sem->count <= kMax_UInt - count,
  807.                        "CSemaphore::Post() - "
  808.                        "would result in counter > MAX_UINT");
  809.         xncbi_Validate(m_Sem->count + count <= m_Sem->max_count,
  810.                        "CSemaphore::Post() - attempt to exceed max_count");
  811.         _TROUBLE;
  812.     }
  813.     // Signal some (or all) of the threads waiting on this semaphore
  814.     int err_code = 0;
  815.     if (m_Sem->count + count >= m_Sem->wait_count) {
  816.         err_code = pthread_cond_broadcast(&m_Sem->cond);
  817.     } else {
  818.         // Do not use broadcast here to avoid waking up more threads
  819.         // than really needed...
  820.         for (unsigned int n_sig = 0;  n_sig < count;  n_sig++) {
  821.             err_code = pthread_cond_signal(&m_Sem->cond);
  822.             if (err_code != 0) {
  823.                 err_code = pthread_cond_broadcast(&m_Sem->cond);
  824.                 break;
  825.             }
  826.         }
  827.     }
  828.     // Success
  829.     if (err_code == 0) {
  830.         m_Sem->count += count;
  831.         xncbi_Validate(pthread_mutex_unlock(&m_Sem->mutex) == 0,
  832.                        "CSemaphore::Post() - pthread_mutex_unlock() failed");
  833.         return;
  834.     }
  835.     // Error
  836.     xncbi_Validate(pthread_mutex_unlock(&m_Sem->mutex) == 0,
  837.                    "CSemaphore::Post() - "
  838.                    "pthread_cond_signal/broadcast() and "
  839.                    "pthread_mutex_unlock() failed");
  840.     xncbi_Validate(0,
  841.                    "CSemaphore::Post() - "
  842.                    "pthread_cond_signal/broadcast() failed");
  843. #elif defined(NCBI_WIN32_THREADS)
  844.     xncbi_Validate(ReleaseSemaphore(m_Sem->sem, count, NULL),
  845.                    "CSemaphore::Post() - ReleaseSemaphore() failed");
  846. #else
  847.     xncbi_Validate(m_Sem->count + count <= m_Sem->max_count,
  848.                    "CSemaphore::Post() - attempt to exceed max_count");
  849.     m_Sem->count += count;
  850. #endif
  851. }
  852. END_NCBI_SCOPE
  853. /*
  854.  * ===========================================================================
  855.  * $Log: ncbimtx.cpp,v $
  856.  * Revision 1000.2  2004/06/03 19:28:17  gouriano
  857.  * PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.17
  858.  *
  859.  * Revision 1.17  2004/06/02 16:27:06  ucko
  860.  * SSystemFastMutex::InitializeDynamic: drop gratuituous and potentially
  861.  * error-prone check for double initialization after discussion with
  862.  * Eugene Vasilchenko.
  863.  *
  864.  * Revision 1.16  2004/05/14 13:59:27  gorelenk
  865.  * Added include of ncbi_pch.hpp
  866.  *
  867.  * Revision 1.15  2003/09/29 20:45:54  ivanov
  868.  * CAutoInitializeStatic[Fast]Mutex:  changed method of initialization for static mutexes on MS Windows (by Eugene Vasilchenko)
  869.  *
  870.  * Revision 1.14  2003/09/17 17:56:47  vasilche
  871.  * Fixed volatile methods of CThreadSystemID.
  872.  *
  873.  * Revision 1.13  2003/09/17 15:20:46  vasilche
  874.  * Moved atomic counter swap functions to separate file.
  875.  * Added CRef<>::AtomicResetFrom(), CRef<>::AtomicReleaseTo() methods.
  876.  *
  877.  * Revision 1.12  2003/09/02 19:03:59  vasilche
  878.  * Removed debug abort().
  879.  *
  880.  * Revision 1.11  2003/09/02 16:08:49  vasilche
  881.  * Fixed race condition with optimization on some compilers - added 'volatile'.
  882.  * Moved mutex Lock/Unlock methods out of inline section - they are quite complex.
  883.  *
  884.  * Revision 1.10  2003/05/06 16:12:24  vasilche
  885.  * Added check for mutexes located in stack.
  886.  *
  887.  * Revision 1.9  2002/09/24 18:29:53  vasilche
  888.  * Removed TAB symbols. Removed unused arg warning in single thread mode.
  889.  *
  890.  * Revision 1.8  2002/09/23 13:47:23  vasilche
  891.  * Made static mutex structures POD-types on Win32
  892.  *
  893.  * Revision 1.7  2002/09/20 18:46:24  vasilche
  894.  * Fixed volatile incompatibility on Win32
  895.  *
  896.  * Revision 1.6  2002/09/19 20:24:08  vasilche
  897.  * Replace missing std::count() by std::find()
  898.  *
  899.  * Revision 1.5  2002/09/19 20:05:42  vasilche
  900.  * Safe initialization of static mutexes
  901.  *
  902.  * Revision 1.4  2002/07/11 14:18:27  gouriano
  903.  * exceptions replaced by CNcbiException-type ones
  904.  *
  905.  * Revision 1.3  2002/04/11 21:08:02  ivanov
  906.  * CVS log moved to end of the file
  907.  *
  908.  * Revision 1.2  2001/12/13 19:45:36  gouriano
  909.  * added xxValidateAction functions
  910.  *
  911.  * Revision 1.1  2001/03/26 20:31:13  vakatov
  912.  * Initial revision (moved code from "ncbithr.cpp")
  913.  *
  914.  * ===========================================================================
  915.  */