Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1597 lines
47 KiB

  1. /*++
  2. Copyright (c) 1998-2000 Microsoft Corporation
  3. Module Name :
  4. Locks.h
  5. Abstract:
  6. A collection of locks for multithreaded access to data structures
  7. Author:
  8. George V. Reilly (GeorgeRe) 06-Jan-1998
  9. Environment:
  10. Win32 - User Mode
  11. Project:
  12. Internet Information Server RunTime Library
  13. Revision History:
  14. --*/
  15. #ifndef __LOCKS_H__
  16. #define __LOCKS_H__
  17. //--------------------------------------------------------------------
  18. // File: locks.h
  19. //
  20. // A collection of different implementations of read/write locks that all
  21. // share the same interface. This allows different locks to be plugged
  22. // into C++ templates as parameters.
  23. //
  24. // The implementations are:
  25. // CSmallSpinLock lightweight critical section
  26. // CSpinLock variant of CSmallSpinLock
  27. // CFakeLock do-nothing class; useful as a template parameter
  28. // CCritSec Win32 CRITICAL_SECTION
  29. // Multi-Reader/Single-Writer locks:
  30. // CReaderWriterLock MRSW lock from Neel Jain
  31. // CReaderWriterLock2 smaller implementation of CReaderWriterLock
  32. // CReaderWriterLock3 CReaderWriterLock2 with recursive WriteLock
  33. //
  34. // CAutoReadLock<Lock> and CAutoWriteLock<Lock> can used as
  35. // exception-safe wrappers.
  36. //
  37. // TODO:
  38. // * Add per-class lock-contention statistics
  39. // * Add a timeout feature to Try{Read,Write}Lock
  40. // * Add some way of tracking all the owners of a multi-reader lock
  41. //--------------------------------------------------------------------
  42. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  43. // The __forceinline keyword is new to VC6
  44. // # define LOCK_FORCEINLINE __forceinline
  45. # define LOCK_FORCEINLINE
  46. #else
  47. # define LOCK_FORCEINLINE inline
  48. #endif
  49. #ifndef LOCKS_KERNEL_MODE
  50. # define LOCKS_ENTER_CRIT_REGION() ((void) 0)
  51. # define LOCKS_LEAVE_CRIT_REGION() ((void) 0)
  52. #else
  53. # define LOCKS_ENTER_CRIT_REGION() KeEnterCriticalRegion()
  54. # define LOCKS_LEAVE_CRIT_REGION() KeLeaveCriticalRegion()
  55. #endif
  56. #ifndef __IRTLDBG_H__
  57. # include <irtldbg.h>
  58. #endif
  59. enum LOCK_LOCKTYPE {
  60. LOCK_FAKELOCK = 1,
  61. LOCK_SMALLSPINLOCK,
  62. LOCK_SPINLOCK,
  63. LOCK_CRITSEC,
  64. LOCK_READERWRITERLOCK,
  65. LOCK_READERWRITERLOCK2,
  66. LOCK_READERWRITERLOCK3,
  67. LOCK_KSPINLOCK,
  68. LOCK_FASTMUTEX,
  69. LOCK_ERESOURCE,
  70. };
  71. // Forward declarations
  72. class IRTL_DLLEXP CSmallSpinLock;
  73. class IRTL_DLLEXP CSpinLock;
  74. class IRTL_DLLEXP CFakeLock;
  75. class IRTL_DLLEXP CCritSec;
  76. class IRTL_DLLEXP CReaderWriterLock;
  77. class IRTL_DLLEXP CReaderWriterLock2;
  78. class IRTL_DLLEXP CReaderWriterLock3;
  79. //--------------------------------------------------------------------
  80. // Spin count values.
  81. enum LOCK_SPINS {
  82. LOCK_MAXIMUM_SPINS = 10000, // maximum allowable spin count
  83. LOCK_DEFAULT_SPINS = 4000, // default spin count
  84. LOCK_MINIMUM_SPINS = 100, // minimum allowable spin count
  85. LOCK_USE_DEFAULT_SPINS = 0xFFFF, // use class default spin count
  86. LOCK_DONT_SPIN = 0, // don't spin at all
  87. };
  88. #ifndef LOCKS_KERNEL_MODE
  89. // Boilerplate code for the per-class default spincount and spinfactor
  90. #define LOCK_DEFAULT_SPIN_IMPLEMENTATION() \
  91. protected: \
  92. /* per-class variables */ \
  93. static WORD sm_wDefaultSpinCount; /* global default spin count */ \
  94. static double sm_dblDfltSpinAdjFctr; /* global spin adjustment factor*/\
  95. \
  96. public: \
  97. /* Set the default spin count for all locks */ \
  98. static void SetDefaultSpinCount(WORD wSpins) \
  99. { \
  100. IRTLASSERT((wSpins == LOCK_DONT_SPIN) \
  101. || (wSpins == LOCK_USE_DEFAULT_SPINS) \
  102. || (LOCK_MINIMUM_SPINS <= wSpins \
  103. && wSpins <= LOCK_MAXIMUM_SPINS)); \
  104. \
  105. if ((LOCK_MINIMUM_SPINS <= wSpins && wSpins <= LOCK_MAXIMUM_SPINS)\
  106. || (wSpins == LOCK_DONT_SPIN)) \
  107. sm_wDefaultSpinCount = wSpins; \
  108. else if (wSpins == LOCK_USE_DEFAULT_SPINS) \
  109. sm_wDefaultSpinCount = LOCK_DEFAULT_SPINS; \
  110. } \
  111. \
  112. /* Return the default spin count for all locks */ \
  113. static WORD GetDefaultSpinCount() \
  114. { \
  115. return sm_wDefaultSpinCount; \
  116. } \
  117. \
  118. /* Set the adjustment factor for the spincount, used in each iteration */\
  119. /* of countdown-and-sleep by the backoff algorithm. */ \
  120. static void SetDefaultSpinAdjustmentFactor(double dblAdjFactor) \
  121. { \
  122. IRTLASSERT(0.1 <= dblAdjFactor && dblAdjFactor <= 10.0); \
  123. if (0.1 <= dblAdjFactor && dblAdjFactor <= 10.0) \
  124. sm_dblDfltSpinAdjFctr = dblAdjFactor; \
  125. } \
  126. \
  127. /* Return the default spin count for all locks */ \
  128. static double GetDefaultSpinAdjustmentFactor() \
  129. { \
  130. return sm_dblDfltSpinAdjFctr; \
  131. } \
  132. #endif // !LOCKS_KERNEL_MODE
  133. //--------------------------------------------------------------------
  134. // Various Lock Traits
  135. // Is the lock a simple mutex or a multi-reader/single-writer lock?
  136. enum LOCK_RW_MUTEX {
  137. LOCK_MUTEX = 1, // mutexes allow only one thread to hold the lock
  138. LOCK_MRSW, // multi-reader, single-writer
  139. };
  140. // Can the lock be recursively acquired?
  141. enum LOCK_RECURSION {
  142. LOCK_RECURSIVE = 1, // Write and Read locks can be recursively acquired
  143. LOCK_READ_RECURSIVE, // Read locks can be reacquired, but not Write
  144. LOCK_NON_RECURSIVE, // Will deadlock if attempt to acquire recursively
  145. };
  146. // Does the lock Sleep in a loop or block on a kernel synch object handle?
  147. // May (or may not) spin first before sleeping/blocking.
  148. enum LOCK_WAIT_TYPE {
  149. LOCK_WAIT_SLEEP = 1, // Calls Sleep() in a loop
  150. LOCK_WAIT_HANDLE, // Blocks on a kernel mutex, semaphore, or event
  151. LOCK_WAIT_SPIN, // Spins until lock acquired. Never sleeps.
  152. };
  153. // When the lock is taken, how are the waiters dequeued?
  154. enum LOCK_QUEUE_TYPE {
  155. LOCK_QUEUE_FIFO = 1, // First in, first out. Fair.
  156. LOCK_QUEUE_LIFO, // Unfair but CPU cache friendly
  157. LOCK_QUEUE_KERNEL, // Determined by vagaries of scheduler
  158. };
  159. // Can the lock's spincount be set on a per-lock basis, or is it only
  160. // possible to modify the default spincount for all the locks in this class?
  161. enum LOCK_PERLOCK_SPIN {
  162. LOCK_NO_SPIN = 1, // The locks do not spin at all
  163. LOCK_CLASS_SPIN, // Can set class-wide spincount, not individual
  164. LOCK_INDIVIDUAL_SPIN, // Can set a spincount on an individual lock
  165. };
  166. //--------------------------------------------------------------------
  167. // CLockBase: bundle the above attributes
  168. template < LOCK_LOCKTYPE locktype,
  169. LOCK_RW_MUTEX mutextype,
  170. LOCK_RECURSION recursiontype,
  171. LOCK_WAIT_TYPE waittype,
  172. LOCK_QUEUE_TYPE queuetype,
  173. LOCK_PERLOCK_SPIN spintype
  174. >
  175. class CLockBase
  176. {
  177. public:
  178. static LOCK_LOCKTYPE LockType() {return locktype;}
  179. static LOCK_RW_MUTEX MutexType() {return mutextype;}
  180. static LOCK_RECURSION Recursion() {return recursiontype;}
  181. static LOCK_WAIT_TYPE WaitType() {return waittype;}
  182. static LOCK_QUEUE_TYPE QueueType() {return queuetype;}
  183. static LOCK_PERLOCK_SPIN PerLockSpin() {return spintype;}
  184. };
  185. // Lock instrumentation causes all sorts of interesting statistics about
  186. // lock contention, etc., to be gathered, but makes locks considerably fatter
  187. // and somewhat slower. Turned off by default.
  188. // #define LOCK_INSTRUMENTATION 1
  189. #ifdef LOCK_INSTRUMENTATION
  190. //--------------------------------------------------------------------
  191. // CLockStatistics: statistics for an individual lock
  192. class IRTL_DLLEXP CLockStatistics
  193. {
  194. public:
  195. enum {
  196. L_NAMELEN = 8,
  197. };
  198. double m_nContentions; // #times this lock was already locked
  199. double m_nSleeps; // Total #Sleep()s needed
  200. double m_nContentionSpins; // Total iterations this lock spun
  201. double m_nAverageSpins; // Average spins each contention needed
  202. double m_nReadLocks; // Number of times lock acquired for reading
  203. double m_nWriteLocks; // Number of times lock acquired for writing
  204. TCHAR m_tszName[L_NAMELEN];// Name of this lock
  205. CLockStatistics()
  206. : m_nContentions(0),
  207. m_nSleeps(0),
  208. m_nContentionSpins(0),
  209. m_nAverageSpins(0),
  210. m_nReadLocks(0),
  211. m_nWriteLocks(0)
  212. {
  213. m_tszName[0] = _TEXT('\0');
  214. }
  215. };
  216. //--------------------------------------------------------------------
  217. // CGlobalLockStatistics: statistics for all the known locks
  218. class IRTL_DLLEXP CGlobalLockStatistics
  219. {
  220. public:
  221. LONG m_cTotalLocks; // Total number of locks created
  222. LONG m_cContendedLocks; // Total number of contended locks
  223. LONG m_nSleeps; // Total #Sleep()s needed by all locks
  224. LONGLONG m_cTotalSpins; // Total iterations all locks spun
  225. double m_nAverageSpins; // Average spins needed for each contended lock
  226. LONG m_nReadLocks; // Total ReadLocks
  227. LONG m_nWriteLocks; // Total WriteLocks
  228. CGlobalLockStatistics()
  229. : m_cTotalLocks(0),
  230. m_cContendedLocks(0),
  231. m_nSleeps(0),
  232. m_cTotalSpins(0),
  233. m_nAverageSpins(0),
  234. m_nReadLocks(0),
  235. m_nWriteLocks(0)
  236. {}
  237. };
  238. # define LOCK_INSTRUMENTATION_DECL() \
  239. private: \
  240. volatile LONG m_nContentionSpins; /* #iterations this lock spun */ \
  241. volatile WORD m_nContentions; /* #times lock was already locked */\
  242. volatile WORD m_nSleeps; /* #Sleep()s needed */ \
  243. volatile WORD m_nReadLocks; /* #ReadLocks */ \
  244. volatile WORD m_nWriteLocks; /* #WriteLocks */ \
  245. TCHAR m_tszName[CLockStatistics::L_NAMELEN]; /* Name of lock */\
  246. \
  247. static LONG sm_cTotalLocks; /* Total number of locks created */ \
  248. static LONG sm_cContendedLocks; /* Total number of contended locks */\
  249. static LONG sm_nSleeps; /* Total #Sleep()s by all locks */ \
  250. static LONGLONG sm_cTotalSpins; /* Total iterations all locks spun */\
  251. static LONG sm_nReadLocks; /* Total ReadLocks */ \
  252. static LONG sm_nWriteLocks; /* Total WriteLocks */ \
  253. \
  254. public: \
  255. const TCHAR* Name() const {return m_tszName;} \
  256. \
  257. CLockStatistics Statistics() const; \
  258. static CGlobalLockStatistics GlobalStatistics(); \
  259. static void ResetGlobalStatistics(); \
  260. private: \
  261. // Add this to constructors
  262. # define LOCK_INSTRUMENTATION_INIT(ptszName) \
  263. m_nContentionSpins = 0; \
  264. m_nContentions = 0; \
  265. m_nSleeps = 0; \
  266. m_nReadLocks = 0; \
  267. m_nWriteLocks = 0; \
  268. ++sm_cTotalLocks; \
  269. if (ptszName == NULL) \
  270. m_tszName[0] = _TEXT('\0'); \
  271. else \
  272. _tcsncpy(m_tszName, ptszName, sizeof(m_tszName)/sizeof(TCHAR))
  273. // Note: we are not using Interlocked operations for the shared
  274. // statistical counters. We'll lose perfect accuracy, but we'll
  275. // gain by reduced bus synchronization traffic.
  276. # define LOCK_READLOCK_INSTRUMENTATION() \
  277. { ++m_nReadLocks; \
  278. ++sm_nReadLocks; }
  279. # define LOCK_WRITELOCK_INSTRUMENTATION() \
  280. { ++m_nWriteLocks; \
  281. ++sm_nWriteLocks; }
  282. #else // !LOCK_INSTRUMENTATION
  283. # define LOCK_INSTRUMENTATION_DECL()
  284. # define LOCK_READLOCK_INSTRUMENTATION() ((void) 0)
  285. # define LOCK_WRITELOCK_INSTRUMENTATION() ((void) 0)
  286. #endif // !LOCK_INSTRUMENTATION
  287. //--------------------------------------------------------------------
  288. // CAutoReadLock<Lock> and CAutoWriteLock<Lock> provide exception-safe
  289. // acquisition and release of the other locks defined below
  290. template <class _Lock>
  291. class IRTL_DLLEXP CAutoReadLock
  292. {
  293. private:
  294. bool m_fLocked;
  295. _Lock& m_Lock;
  296. public:
  297. CAutoReadLock(
  298. _Lock& rLock,
  299. bool fLockNow = true)
  300. : m_fLocked(false), m_Lock(rLock)
  301. {
  302. if (fLockNow)
  303. Lock();
  304. }
  305. ~CAutoReadLock()
  306. {
  307. Unlock();
  308. }
  309. void Lock()
  310. {
  311. // disallow recursive acquisition of the lock through this wrapper
  312. if (!m_fLocked)
  313. {
  314. m_fLocked = true;
  315. m_Lock.ReadLock();
  316. }
  317. }
  318. void Unlock()
  319. {
  320. if (m_fLocked)
  321. {
  322. m_Lock.ReadUnlock();
  323. m_fLocked = false;
  324. }
  325. }
  326. };
  327. template <class _Lock>
  328. class IRTL_DLLEXP CAutoWriteLock
  329. {
  330. private:
  331. bool m_fLocked;
  332. _Lock& m_Lock;
  333. public:
  334. CAutoWriteLock(
  335. _Lock& rLock,
  336. bool fLockNow = true)
  337. : m_fLocked(false), m_Lock(rLock)
  338. {
  339. if (fLockNow)
  340. Lock();
  341. }
  342. ~CAutoWriteLock()
  343. {
  344. Unlock();
  345. }
  346. void Lock()
  347. {
  348. // disallow recursive acquisition of the lock through this wrapper
  349. if (!m_fLocked)
  350. {
  351. m_fLocked = true;
  352. m_Lock.WriteLock();
  353. }
  354. }
  355. void Unlock()
  356. {
  357. if (m_fLocked)
  358. {
  359. m_fLocked = false;
  360. m_Lock.WriteUnlock();
  361. }
  362. }
  363. };
  364. //--------------------------------------------------------------------
  365. // A dummy class, primarily useful as a template parameter
  366. class IRTL_DLLEXP CFakeLock :
  367. public CLockBase<LOCK_FAKELOCK, LOCK_MUTEX,
  368. LOCK_RECURSIVE, LOCK_WAIT_SLEEP, LOCK_QUEUE_FIFO,
  369. LOCK_NO_SPIN
  370. >
  371. {
  372. private:
  373. LOCK_INSTRUMENTATION_DECL();
  374. public:
  375. CFakeLock() {}
  376. #ifdef LOCK_INSTRUMENTATION
  377. CFakeLock(const char*) {}
  378. #endif // LOCK_INSTRUMENTATION
  379. ~CFakeLock() {}
  380. void WriteLock() {}
  381. void ReadLock() {}
  382. bool ReadOrWriteLock() {return true;}
  383. bool TryWriteLock() {return true;}
  384. bool TryReadLock() {return true;}
  385. void WriteUnlock() {}
  386. void ReadUnlock() {}
  387. void ReadOrWriteUnlock(bool) {}
  388. bool IsWriteLocked() const {return true;}
  389. bool IsReadLocked() const {return IsWriteLocked();}
  390. bool IsWriteUnlocked() const {return true;}
  391. bool IsReadUnlocked() const {return true;}
  392. void ConvertSharedToExclusive() {}
  393. void ConvertExclusiveToShared() {}
  394. bool SetSpinCount(WORD dwSpins) {return false;}
  395. WORD GetSpinCount() const {return LOCK_DONT_SPIN;}
  396. #ifndef LOCKS_KERNEL_MODE
  397. LOCK_DEFAULT_SPIN_IMPLEMENTATION();
  398. #endif // !LOCKS_KERNEL_MODE
  399. static const TCHAR* ClassName() {return _TEXT("CFakeLock");}
  400. }; // CFakeLock
  401. //--------------------------------------------------------------------
  402. // A spinlock is a sort of lightweight critical section. Its main
  403. // advantage over a true Win32 CRITICAL_SECTION is that it occupies 4 bytes
  404. // instead of 24 (+ another 32 bytes for the RTL_CRITICAL_SECTION_DEBUG data),
  405. // which is important when we have many thousands of locks
  406. // and we're trying to be L1 cache-conscious. A CRITICAL_SECTION also
  407. // contains a HANDLE to a semaphore, although this is not initialized until
  408. // the first time that the CRITICAL_SECTION blocks.
  409. //
  410. // On a multiprocessor machine, a spinlock tries to acquire the lock. If
  411. // it fails, it sits in a tight loop, testing the lock and decrementing a
  412. // counter. If the counter reaches zero, it does a Sleep(0), yielding the
  413. // processor to another thread. When control returns to the thread, the
  414. // lock is probably free. If not, the loop starts again and it is
  415. // terminated only when the lock is acquired. The theory is that it is
  416. // less costly to spin in a busy loop for a short time rather than
  417. // immediately yielding the processor, forcing an expensive context switch
  418. // that requires the old thread's state (registers, etc) be saved, the new
  419. // thread's state be reloaded, and the L1 and L2 caches be left full of
  420. // stale data.
  421. //
  422. // You can tune the spin count (global only: per-lock spin counts are
  423. // disabled) and the backoff algorithm (the factor by which the spin
  424. // count is multiplied after each Sleep).
  425. //
  426. // On a 1P machine, the loop is pointless---this thread has control,
  427. // hence no other thread can possibly release the lock while this thread
  428. // is looping---so the processor is yielded immediately.
  429. //
  430. // The kernel uses spinlocks internally and spinlocks were also added to
  431. // CRITICAL_SECTIONs in NT 4.0 sp3. In the CRITICAL_SECTION implementation,
  432. // however, the counter counts down only once and waits on a semaphore
  433. // thereafter (i.e., the same blocking behavior that it exhibits without
  434. // the spinlock).
  435. //
  436. // A disadvantage of a user-level spinlock such as this is that if the
  437. // thread that owns the spinlock blocks for any reason (or is preempted by
  438. // the scheduler), all the other threads will continue to spin on the
  439. // spinlock, wasting CPU, until the owning thread completes its wait and
  440. // releases the lock. (The kernel spinlocks, however, are smart enough to
  441. // switch to another runnable thread instead of wasting time spinning.)
  442. // The backoff algorithm decreases the spin count on each iteration in an
  443. // attempt to minimize this effect. The best policy---and this is true for
  444. // all locks---is to hold the lock for as short as time as possible.
  445. //
  446. // Note: unlike a CRITICAL_SECTION, a CSmallSpinLock cannot be recursively
  447. // acquired; i.e., if you acquire a spinlock and then attempt to acquire it
  448. // again *on the same thread* (perhaps from a different function), the
  449. // thread will hang forever. Use CSpinLock instead, which is safe though a
  450. // little slower than a CSmallSpinLock. If you own all the code
  451. // that is bracketed by Lock() and Unlock() (e.g., no callbacks or passing
  452. // back of locked data structures to callers) and know for certain that it
  453. // will not attempt to reacquire the lock, you can use CSmallSpinLock.
  454. //
  455. // See also http://muralik/work/performance/spinlocks.htm and John Vert's
  456. // MSDN article, "Writing Scalable Applications for Windows NT".
  457. //
  458. // The original implementation is due to PALarson.
  459. class IRTL_DLLEXP CSmallSpinLock :
  460. public CLockBase<LOCK_SMALLSPINLOCK, LOCK_MUTEX,
  461. LOCK_NON_RECURSIVE, LOCK_WAIT_SLEEP, LOCK_QUEUE_KERNEL,
  462. LOCK_CLASS_SPIN
  463. >
  464. {
  465. private:
  466. volatile LONG m_lTid; // The lock state variable
  467. enum {
  468. SL_UNOWNED = 0,
  469. #ifdef LOCK_SMALL_SPIN_NO_THREAD_ID
  470. SL_LOCKED = 1,
  471. #endif // LOCK_SMALL_SPIN_NO_THREAD_ID
  472. };
  473. LOCK_INSTRUMENTATION_DECL();
  474. LOCK_FORCEINLINE static LONG _CurrentThreadId()
  475. {
  476. #ifdef LOCK_SMALL_SPIN_NO_THREAD_ID
  477. DWORD dwTid = SL_LOCKED;
  478. #else // !LOCK_SMALL_SPIN_NO_THREAD_ID
  479. #ifdef LOCKS_KERNEL_MODE
  480. DWORD dwTid = (DWORD) HandleToULong(::PsGetCurrentThreadId());
  481. #else // !LOCKS_KERNEL_MODE
  482. DWORD dwTid = ::GetCurrentThreadId();
  483. #endif // !LOCKS_KERNEL_MODE
  484. #endif // !LOCK_SMALL_SPIN_NO_THREAD_ID
  485. return (LONG) (dwTid);
  486. }
  487. private:
  488. // Does all the spinning (and instrumentation) if the lock is contended.
  489. void _LockSpin();
  490. // Attempt to acquire the lock
  491. bool _TryLock();
  492. // Release the lock
  493. void _Unlock();
  494. public:
  495. #ifndef LOCK_INSTRUMENTATION
  496. CSmallSpinLock()
  497. : m_lTid(SL_UNOWNED)
  498. {}
  499. #else // LOCK_INSTRUMENTATION
  500. CSmallSpinLock(
  501. const TCHAR* ptszName)
  502. : m_lTid(SL_UNOWNED)
  503. {
  504. LOCK_INSTRUMENTATION_INIT(ptszName);
  505. }
  506. #endif // LOCK_INSTRUMENTATION
  507. #ifdef IRTLDEBUG
  508. ~CSmallSpinLock()
  509. {
  510. IRTLASSERT(m_lTid == SL_UNOWNED);
  511. }
  512. #endif // IRTLDEBUG
  513. // Acquire an exclusive lock for writing.
  514. // Blocks (if needed) until acquired.
  515. inline void WriteLock()
  516. {
  517. LOCKS_ENTER_CRIT_REGION();
  518. LOCK_WRITELOCK_INSTRUMENTATION();
  519. // Optimize for the common case by helping the processor's branch
  520. // prediction algorithm.
  521. if (_TryLock())
  522. return;
  523. _LockSpin();
  524. }
  525. // Acquire a (possibly shared) lock for reading.
  526. // Blocks (if needed) until acquired.
  527. inline void ReadLock()
  528. {
  529. LOCKS_ENTER_CRIT_REGION();
  530. LOCK_READLOCK_INSTRUMENTATION();
  531. if (_TryLock())
  532. return;
  533. _LockSpin();
  534. }
  535. // Try to acquire an exclusive lock for writing. Returns true
  536. // if successful. Non-blocking.
  537. inline bool TryWriteLock()
  538. {
  539. LOCKS_ENTER_CRIT_REGION();
  540. bool fAcquired = _TryLock();
  541. if (fAcquired)
  542. LOCK_WRITELOCK_INSTRUMENTATION();
  543. else
  544. LOCKS_LEAVE_CRIT_REGION();
  545. return fAcquired;
  546. }
  547. // Try to acquire a (possibly shared) lock for reading. Returns true
  548. // if successful. Non-blocking.
  549. inline bool TryReadLock()
  550. {
  551. LOCKS_ENTER_CRIT_REGION();
  552. bool fAcquired = _TryLock();
  553. if (fAcquired)
  554. LOCK_READLOCK_INSTRUMENTATION();
  555. else
  556. LOCKS_LEAVE_CRIT_REGION();
  557. return fAcquired;
  558. }
  559. // Unlock the lock after a successful call to {,Try}WriteLock().
  560. // Assumes caller owned the lock.
  561. inline void WriteUnlock()
  562. {
  563. _Unlock();
  564. LOCKS_LEAVE_CRIT_REGION();
  565. }
  566. // Unlock the lock after a successful call to {,Try}ReadLock().
  567. // Assumes caller owned the lock.
  568. inline void ReadUnlock()
  569. {
  570. _Unlock();
  571. LOCKS_LEAVE_CRIT_REGION();
  572. }
  573. // Is the lock already locked for writing by this thread?
  574. bool IsWriteLocked() const
  575. {
  576. return (m_lTid == _CurrentThreadId());
  577. }
  578. // Is the lock already locked for reading?
  579. bool IsReadLocked() const
  580. {
  581. return IsWriteLocked();
  582. }
  583. // Is the lock unlocked for writing?
  584. bool IsWriteUnlocked() const
  585. {
  586. return (m_lTid == SL_UNOWNED);
  587. }
  588. // Is the lock unlocked for reading?
  589. bool IsReadUnlocked() const
  590. {
  591. return IsWriteUnlocked();
  592. }
  593. // Convert a reader lock to a writer lock
  594. void ConvertSharedToExclusive()
  595. {
  596. // no-op
  597. }
  598. // Convert a writer lock to a reader lock
  599. void ConvertExclusiveToShared()
  600. {
  601. // no-op
  602. }
  603. #ifdef LOCK_DEFAULT_SPIN_IMPLEMENTATION
  604. // Set the spin count for this lock.
  605. // Returns true if successfully set the per-lock spincount, false otherwise
  606. bool SetSpinCount(WORD wSpins)
  607. {
  608. IRTLASSERT((wSpins == LOCK_DONT_SPIN)
  609. || (wSpins == LOCK_USE_DEFAULT_SPINS)
  610. || (LOCK_MINIMUM_SPINS <= wSpins
  611. && wSpins <= LOCK_MAXIMUM_SPINS));
  612. return false;
  613. }
  614. // Return the spin count for this lock.
  615. WORD GetSpinCount() const
  616. {
  617. return sm_wDefaultSpinCount;
  618. }
  619. LOCK_DEFAULT_SPIN_IMPLEMENTATION();
  620. #endif // LOCK_DEFAULT_SPIN_IMPLEMENTATION
  621. static const TCHAR* ClassName() {return _TEXT("CSmallSpinLock");}
  622. }; // CSmallSpinLock
  623. //--------------------------------------------------------------------
  624. // CSpinLock is a spinlock that doesn't deadlock if recursively acquired.
  625. // This version occupies only 4 bytes. Uses 28 bits for the thread id.
  626. class IRTL_DLLEXP CSpinLock :
  627. public CLockBase<LOCK_SPINLOCK, LOCK_MUTEX,
  628. LOCK_RECURSIVE, LOCK_WAIT_SLEEP, LOCK_QUEUE_KERNEL,
  629. LOCK_CLASS_SPIN
  630. >
  631. {
  632. private:
  633. // a union for convenience
  634. volatile LONG m_lTid;
  635. enum {
  636. SL_THREAD_SHIFT = 0,
  637. SL_THREAD_BITS = 28,
  638. SL_OWNER_SHIFT = SL_THREAD_BITS,
  639. SL_OWNER_BITS = 4,
  640. SL_THREAD_MASK = ((1 << SL_THREAD_BITS) - 1) << SL_THREAD_SHIFT,
  641. SL_OWNER_INCR = 1 << SL_THREAD_BITS,
  642. SL_OWNER_MASK = ((1 << SL_OWNER_BITS) - 1) << SL_OWNER_SHIFT,
  643. SL_UNOWNED = 0,
  644. };
  645. LOCK_INSTRUMENTATION_DECL();
  646. private:
  647. // Get the current thread ID. Assumes that it can fit into 28 bits,
  648. // which is fairly safe as NT recycles thread IDs and failing to fit into
  649. // 28 bits would mean that more than 268,435,456 threads were currently
  650. // active. This is improbable in the extreme as NT runs out of
  651. // resources if there are more than a few thousands threads in
  652. // existence and the overhead of context swapping becomes unbearable.
  653. LOCK_FORCEINLINE static DWORD _GetCurrentThreadId()
  654. {
  655. #ifdef LOCKS_KERNEL_MODE
  656. return (DWORD) HandleToULong(::PsGetCurrentThreadId());
  657. #else // !LOCKS_KERNEL_MODE
  658. return ::GetCurrentThreadId();
  659. #endif // !LOCKS_KERNEL_MODE
  660. }
  661. LOCK_FORCEINLINE static LONG _CurrentThreadId()
  662. {
  663. DWORD dwTid = _GetCurrentThreadId();
  664. // Thread ID 0 is used by the System Idle Process (Process ID 0).
  665. // We use a thread-id of zero to indicate that the lock is unowned.
  666. // NT uses +ve thread ids, Win9x uses -ve ids
  667. IRTLASSERT(dwTid != SL_UNOWNED
  668. && ((dwTid <= SL_THREAD_MASK) || (dwTid > ~SL_THREAD_MASK)));
  669. return (LONG) (dwTid & SL_THREAD_MASK);
  670. }
  671. // Attempt to acquire the lock without blocking
  672. bool _TryLock();
  673. // Acquire the lock, recursively if need be
  674. void _Lock();
  675. // Release the lock
  676. void _Unlock();
  677. // Return true if the lock is owned by this thread
  678. bool _IsLocked() const
  679. {
  680. const LONG lTid = m_lTid;
  681. if (lTid == SL_UNOWNED)
  682. return false;
  683. bool fLocked = ((lTid ^ _GetCurrentThreadId()) << SL_OWNER_BITS) == 0;
  684. IRTLASSERT(!fLocked
  685. || ((lTid & SL_OWNER_MASK) > 0
  686. && (lTid & SL_THREAD_MASK) == _CurrentThreadId()));
  687. return fLocked;
  688. }
  689. // Does all the spinning (and instrumentation) if the lock is contended.
  690. void _LockSpin();
  691. public:
  692. #ifndef LOCK_INSTRUMENTATION
  693. CSpinLock()
  694. : m_lTid(SL_UNOWNED)
  695. {}
  696. #else // LOCK_INSTRUMENTATION
  697. CSpinLock(
  698. const TCHAR* ptszName)
  699. : m_lTid(SL_UNOWNED)
  700. {
  701. LOCK_INSTRUMENTATION_INIT(ptszName);
  702. }
  703. #endif // LOCK_INSTRUMENTATION
  704. #ifdef IRTLDEBUG
  705. ~CSpinLock()
  706. {
  707. IRTLASSERT(m_lTid == SL_UNOWNED);
  708. }
  709. #endif // IRTLDEBUG
  710. // Acquire an exclusive lock for writing. Blocks until acquired.
  711. inline void WriteLock()
  712. {
  713. LOCKS_ENTER_CRIT_REGION();
  714. LOCK_WRITELOCK_INSTRUMENTATION();
  715. // Is the lock unowned?
  716. if (_TryLock())
  717. return; // got the lock
  718. _Lock();
  719. }
  720. // Acquire a (possibly shared) lock for reading. Blocks until acquired.
  721. inline void ReadLock()
  722. {
  723. LOCKS_ENTER_CRIT_REGION();
  724. LOCK_READLOCK_INSTRUMENTATION();
  725. // Is the lock unowned?
  726. if (_TryLock())
  727. return; // got the lock
  728. _Lock();
  729. }
  730. // See the description under CReaderWriterLock3::ReadOrWriteLock
  731. inline bool ReadOrWriteLock()
  732. {
  733. ReadLock();
  734. return true;
  735. }
  736. // Try to acquire an exclusive lock for writing. Returns true
  737. // if successful. Non-blocking.
  738. inline bool TryWriteLock()
  739. {
  740. LOCKS_ENTER_CRIT_REGION();
  741. bool fAcquired = _TryLock();
  742. if (fAcquired)
  743. LOCK_WRITELOCK_INSTRUMENTATION();
  744. else
  745. LOCKS_LEAVE_CRIT_REGION();
  746. return fAcquired;
  747. }
  748. // Try to acquire a (possibly shared) lock for reading. Returns true
  749. // if successful. Non-blocking.
  750. inline bool TryReadLock()
  751. {
  752. LOCKS_ENTER_CRIT_REGION();
  753. bool fAcquired = _TryLock();
  754. if (fAcquired)
  755. LOCK_READLOCK_INSTRUMENTATION();
  756. else
  757. LOCKS_LEAVE_CRIT_REGION();
  758. return fAcquired;
  759. }
  760. // Unlock the lock after a successful call to {,Try}WriteLock().
  761. inline void WriteUnlock()
  762. {
  763. _Unlock();
  764. LOCKS_LEAVE_CRIT_REGION();
  765. }
  766. // Unlock the lock after a successful call to {,Try}ReadLock().
  767. inline void ReadUnlock()
  768. {
  769. _Unlock();
  770. LOCKS_LEAVE_CRIT_REGION();
  771. }
  772. // Unlock the lock after a call to ReadOrWriteLock().
  773. inline void ReadOrWriteUnlock(bool)
  774. {
  775. ReadUnlock();
  776. }
  777. // Is the lock already locked for writing?
  778. bool IsWriteLocked() const
  779. {
  780. return _IsLocked();
  781. }
  782. // Is the lock already locked for reading?
  783. bool IsReadLocked() const
  784. {
  785. return _IsLocked();
  786. }
  787. // Is the lock unlocked for writing?
  788. bool IsWriteUnlocked() const
  789. {
  790. return !IsWriteLocked();
  791. }
  792. // Is the lock unlocked for reading?
  793. bool IsReadUnlocked() const
  794. {
  795. return !IsReadLocked();
  796. }
  797. // Convert a reader lock to a writer lock
  798. void ConvertSharedToExclusive()
  799. {
  800. // no-op
  801. }
  802. // Convert a writer lock to a reader lock
  803. void ConvertExclusiveToShared()
  804. {
  805. // no-op
  806. }
  807. #ifdef LOCK_DEFAULT_SPIN_IMPLEMENTATION
  808. // Set the spin count for this lock.
  809. bool SetSpinCount(WORD dwSpins) {return false;}
  810. // Return the spin count for this lock.
  811. WORD GetSpinCount() const
  812. {
  813. return sm_wDefaultSpinCount;
  814. }
  815. LOCK_DEFAULT_SPIN_IMPLEMENTATION();
  816. #endif // LOCK_DEFAULT_SPIN_IMPLEMENTATION
  817. static const TCHAR* ClassName() {return _TEXT("CSpinLock");}
  818. }; // CSpinLock
  819. #ifndef LOCKS_KERNEL_MODE
  820. //--------------------------------------------------------------------
  821. // A Win32 CRITICAL_SECTION
  822. class IRTL_DLLEXP CCritSec :
  823. public CLockBase<LOCK_CRITSEC, LOCK_MUTEX,
  824. LOCK_RECURSIVE, LOCK_WAIT_HANDLE, LOCK_QUEUE_KERNEL,
  825. LOCK_INDIVIDUAL_SPIN
  826. >
  827. {
  828. private:
  829. CRITICAL_SECTION m_cs;
  830. LOCK_INSTRUMENTATION_DECL();
  831. public:
  832. CCritSec()
  833. {
  834. InitializeCriticalSection(&m_cs);
  835. SetSpinCount(sm_wDefaultSpinCount);
  836. }
  837. #ifdef LOCK_INSTRUMENTATION
  838. CCritSec(const char*)
  839. {
  840. InitializeCriticalSection(&m_cs);
  841. SetSpinCount(sm_wDefaultSpinCount);
  842. }
  843. #endif // LOCK_INSTRUMENTATION
  844. ~CCritSec() { DeleteCriticalSection(&m_cs); }
  845. void WriteLock() { EnterCriticalSection(&m_cs); }
  846. void ReadLock() { WriteLock(); }
  847. bool ReadOrWriteLock() { ReadLock(); return true; }
  848. bool TryWriteLock();
  849. bool TryReadLock() { return TryWriteLock(); }
  850. void WriteUnlock() { LeaveCriticalSection(&m_cs); }
  851. void ReadUnlock() { WriteUnlock(); }
  852. void ReadOrWriteUnlock(bool) { ReadUnlock(); }
  853. bool IsWriteLocked() const {return true;} // TODO: fix this
  854. bool IsReadLocked() const {return IsWriteLocked();}
  855. bool IsWriteUnlocked() const {return true;} // TODO: fix this
  856. bool IsReadUnlocked() const {return true;} // TODO: fix this
  857. // Convert a reader lock to a writer lock
  858. void ConvertSharedToExclusive()
  859. {
  860. // no-op
  861. }
  862. // Convert a writer lock to a reader lock
  863. void ConvertExclusiveToShared()
  864. {
  865. // no-op
  866. }
  867. // Wrapper for ::SetCriticalSectionSpinCount which was introduced
  868. // in NT 4.0 sp3 and hence is not available on all platforms
  869. static DWORD SetSpinCount(LPCRITICAL_SECTION pcs,
  870. DWORD dwSpinCount=LOCK_DEFAULT_SPINS);
  871. #ifdef LOCK_DEFAULT_SPIN_IMPLEMENTATION
  872. bool SetSpinCount(WORD wSpins)
  873. {SetSpinCount(&m_cs, wSpins); return true;}
  874. WORD GetSpinCount() const { return sm_wDefaultSpinCount; } // TODO
  875. LOCK_DEFAULT_SPIN_IMPLEMENTATION();
  876. #endif // LOCK_DEFAULT_SPIN_IMPLEMENTATION
  877. static const TCHAR* ClassName() {return _TEXT("CCritSec");}
  878. }; // CCritSec
  879. #endif // !LOCKS_KERNEL_MODE
  880. //--------------------------------------------------------------------
  881. // CReaderWriterlock is a multi-reader, single-writer spinlock due to NJain,
  882. // which in turn is derived from an exclusive spinlock by DmitryR.
  883. // Gives priority to writers. Cannot be acquired recursively.
  884. // No error checking. Use CReaderWriterLock3.
  885. class IRTL_DLLEXP CReaderWriterLock :
  886. public CLockBase<LOCK_READERWRITERLOCK, LOCK_MRSW,
  887. LOCK_READ_RECURSIVE, LOCK_WAIT_SLEEP, LOCK_QUEUE_KERNEL,
  888. LOCK_CLASS_SPIN
  889. >
  890. {
  891. private:
  892. volatile LONG m_nState; // > 0 => that many readers
  893. volatile LONG m_cWaiting; // number of would-be writers
  894. LOCK_INSTRUMENTATION_DECL();
  895. private:
  896. enum {
  897. SL_FREE = 0,
  898. SL_EXCLUSIVE = -1,
  899. };
  900. void _LockSpin(bool fWrite);
  901. void _WriteLockSpin() { _LockSpin(true); }
  902. void _ReadLockSpin() { _LockSpin(false); }
  903. bool _CmpExch(LONG lNew, LONG lCurrent);
  904. bool _TryWriteLock();
  905. bool _TryReadLock();
  906. public:
  907. CReaderWriterLock()
  908. : m_nState(SL_FREE),
  909. m_cWaiting(0)
  910. {
  911. }
  912. #ifdef LOCK_INSTRUMENTATION
  913. CReaderWriterLock(
  914. const TCHAR* ptszName)
  915. : m_nState(SL_FREE),
  916. m_cWaiting(0)
  917. {
  918. LOCK_INSTRUMENTATION_INIT(ptszName);
  919. }
  920. #endif // LOCK_INSTRUMENTATION
  921. #ifdef IRTLDEBUG
  922. ~CReaderWriterLock()
  923. {
  924. IRTLASSERT(m_nState == SL_FREE && m_cWaiting == 0);
  925. }
  926. #endif // IRTLDEBUG
  927. void WriteLock();
  928. void ReadLock();
  929. bool TryWriteLock();
  930. bool TryReadLock();
  931. void WriteUnlock();
  932. void ReadUnlock();
  933. bool IsWriteLocked() const {return m_nState == SL_EXCLUSIVE;}
  934. bool IsReadLocked() const {return m_nState > SL_FREE;}
  935. bool IsWriteUnlocked() const {return m_nState != SL_EXCLUSIVE;}
  936. bool IsReadUnlocked() const {return m_nState <= SL_FREE;}
  937. void ConvertSharedToExclusive();
  938. void ConvertExclusiveToShared();
  939. #ifdef LOCK_DEFAULT_SPIN_IMPLEMENTATION
  940. bool SetSpinCount(WORD wSpins) {return false;}
  941. WORD GetSpinCount() const {return sm_wDefaultSpinCount;}
  942. LOCK_DEFAULT_SPIN_IMPLEMENTATION();
  943. #endif // LOCK_DEFAULT_SPIN_IMPLEMENTATION
  944. static const TCHAR* ClassName() {return _TEXT("CReaderWriterLock");}
  945. }; // CReaderWriterLock
  946. //--------------------------------------------------------------------
  947. // CReaderWriterlock2 is a multi-reader, single-writer spinlock due to NJain,
  948. // which in turn is derived from an exclusive spinlock by DmitryR.
  949. // Gives priority to writers. Cannot be acquired recursively.
  950. // No error checking. The difference between this and CReaderWriterLock is
  951. // that all the state is packed into a single LONG, instead of two LONGs.
  952. class IRTL_DLLEXP CReaderWriterLock2 :
  953. public CLockBase<LOCK_READERWRITERLOCK2, LOCK_MRSW,
  954. LOCK_READ_RECURSIVE, LOCK_WAIT_SLEEP, LOCK_QUEUE_KERNEL,
  955. LOCK_CLASS_SPIN
  956. >
  957. {
  958. private:
  959. volatile LONG m_lRW;
  960. // LoWord is state. ==0 => free; >0 => readers; ==0xFFFF => 1 writer.
  961. // HiWord is count of writers, W.
  962. // If LoWord==0xFFFF => W-1 waiters, 1 writer;
  963. // otherwise W waiters.
  964. enum {
  965. SL_FREE = 0x00000000,
  966. SL_STATE_MASK = 0x0000FFFF,
  967. SL_STATE_SHIFT = 0,
  968. SL_WAITING_MASK = 0xFFFF0000, // waiting writers
  969. SL_WAITING_SHIFT = 16,
  970. SL_READER_INCR = 0x00000001,
  971. SL_READER_MASK = 0x00007FFF,
  972. SL_EXCLUSIVE = 0x0000FFFF, // one writer
  973. SL_WRITER_INCR = 0x00010000,
  974. SL_ONE_WRITER = SL_EXCLUSIVE | SL_WRITER_INCR,
  975. SL_ONE_READER = (SL_FREE + 1),
  976. SL_WRITERS_MASK = ~SL_READER_MASK,
  977. };
  978. LOCK_INSTRUMENTATION_DECL();
  979. private:
  980. void _LockSpin(bool fWrite);
  981. void _WriteLockSpin();
  982. void _ReadLockSpin() { _LockSpin(false); }
  983. bool _CmpExch(LONG lNew, LONG lCurrent);
  984. bool _TryWriteLock(LONG nIncr);
  985. bool _TryReadLock();
  986. public:
  987. CReaderWriterLock2()
  988. : m_lRW(SL_FREE)
  989. {}
  990. #ifdef LOCK_INSTRUMENTATION
  991. CReaderWriterLock2(
  992. const TCHAR* ptszName)
  993. : m_lRW(SL_FREE)
  994. {
  995. LOCK_INSTRUMENTATION_INIT(ptszName);
  996. }
  997. #endif // LOCK_INSTRUMENTATION
  998. #ifdef IRTLDEBUG
  999. ~CReaderWriterLock2()
  1000. {
  1001. IRTLASSERT(m_lRW == SL_FREE);
  1002. }
  1003. #endif // IRTLDEBUG
  1004. inline void WriteLock()
  1005. {
  1006. LOCKS_ENTER_CRIT_REGION();
  1007. LOCK_WRITELOCK_INSTRUMENTATION();
  1008. // Optimize for the common case
  1009. if (_TryWriteLock(SL_WRITER_INCR))
  1010. return;
  1011. _WriteLockSpin();
  1012. }
  1013. inline void ReadLock()
  1014. {
  1015. LOCKS_ENTER_CRIT_REGION();
  1016. LOCK_READLOCK_INSTRUMENTATION();
  1017. // Optimize for the common case
  1018. if (_TryReadLock())
  1019. return;
  1020. _ReadLockSpin();
  1021. }
  1022. inline bool TryWriteLock()
  1023. {
  1024. LOCKS_ENTER_CRIT_REGION();
  1025. if (_TryWriteLock(SL_WRITER_INCR))
  1026. {
  1027. LOCK_WRITELOCK_INSTRUMENTATION();
  1028. return true;
  1029. }
  1030. else
  1031. {
  1032. LOCKS_LEAVE_CRIT_REGION();
  1033. return false;
  1034. }
  1035. }
  1036. inline bool TryReadLock()
  1037. {
  1038. LOCKS_ENTER_CRIT_REGION();
  1039. if (_TryReadLock())
  1040. {
  1041. LOCK_READLOCK_INSTRUMENTATION();
  1042. return true;
  1043. }
  1044. else
  1045. {
  1046. LOCKS_LEAVE_CRIT_REGION();
  1047. return false;
  1048. }
  1049. }
  1050. void WriteUnlock();
  1051. void ReadUnlock();
  1052. bool IsWriteLocked() const
  1053. {return (m_lRW & SL_STATE_MASK) == SL_EXCLUSIVE;}
  1054. bool IsReadLocked() const
  1055. {return (m_lRW & SL_READER_MASK) >= SL_READER_INCR ;}
  1056. bool IsWriteUnlocked() const
  1057. {return !IsWriteLocked();}
  1058. bool IsReadUnlocked() const
  1059. {return !IsReadLocked();}
  1060. void ConvertSharedToExclusive();
  1061. void ConvertExclusiveToShared();
  1062. #ifdef LOCK_DEFAULT_SPIN_IMPLEMENTATION
  1063. bool SetSpinCount(WORD wSpins) {return false;}
  1064. WORD GetSpinCount() const {return sm_wDefaultSpinCount;}
  1065. LOCK_DEFAULT_SPIN_IMPLEMENTATION();
  1066. #endif // LOCK_DEFAULT_SPIN_IMPLEMENTATION
  1067. static const TCHAR* ClassName() {return _TEXT("CReaderWriterLock2");}
  1068. }; // CReaderWriterLock2
  1069. //--------------------------------------------------------------------
  1070. // CReaderWriterLock3 is a multi-reader, single-writer spinlock due
  1071. // to NJain, which in turn is derived from an exclusive spinlock by DmitryR.
  1072. // Gives priority to writers. Cannot be acquired recursively.
  1073. // No error checking. Much like CReaderWriterLock2, except that the WriteLock
  1074. // can be acquired recursively.
  1075. class IRTL_DLLEXP CReaderWriterLock3 :
  1076. public CLockBase<LOCK_READERWRITERLOCK3, LOCK_MRSW,
  1077. LOCK_RECURSIVE, LOCK_WAIT_SLEEP, LOCK_QUEUE_KERNEL,
  1078. LOCK_CLASS_SPIN
  1079. >
  1080. {
  1081. private:
  1082. volatile LONG m_lRW; // Reader-Writer state
  1083. volatile LONG m_lTid; // Owning Thread ID + recursion count
  1084. // m_lRW:
  1085. // LoWord is state. ==0 => free; >0 => readers; ==0xFFFF => 1 writer
  1086. // HiWord is count of writers. If LoWord==0xFFFF => N-1 waiters, 1 writer;
  1087. // otherwise N waiters.
  1088. // m_lTid:
  1089. // If readers, then 0; if a write lock, then thread id + recursion count
  1090. enum {
  1091. // m_lRW
  1092. SL_FREE = 0x00000000,
  1093. SL_STATE_MASK = 0x0000FFFF,
  1094. SL_STATE_SHIFT = 0,
  1095. SL_WAITING_MASK = 0xFFFF0000, // waiting writers
  1096. SL_WAITING_SHIFT = 16,
  1097. SL_READER_INCR = 0x00000001,
  1098. SL_READER_MASK = 0x00007FFF,
  1099. SL_EXCLUSIVE = 0x0000FFFF, // one writer
  1100. SL_WRITER_INCR = 0x00010000,
  1101. SL_ONE_WRITER = SL_EXCLUSIVE | SL_WRITER_INCR,
  1102. SL_ONE_READER = (SL_FREE + 1),
  1103. SL_WRITERS_MASK = ~SL_READER_MASK,
  1104. // m_lTid
  1105. SL_UNOWNED = 0,
  1106. SL_THREAD_SHIFT = 0,
  1107. SL_THREAD_BITS = 28,
  1108. SL_OWNER_SHIFT = SL_THREAD_BITS,
  1109. SL_OWNER_BITS = 4,
  1110. SL_THREAD_MASK = ((1 << SL_THREAD_BITS) - 1) << SL_THREAD_SHIFT,
  1111. SL_OWNER_INCR = 1 << SL_THREAD_BITS,
  1112. SL_OWNER_MASK = ((1 << SL_OWNER_BITS) - 1) << SL_OWNER_SHIFT,
  1113. };
  1114. LOCK_INSTRUMENTATION_DECL();
  1115. private:
  1116. enum SPIN_TYPE {
  1117. SPIN_WRITE = 1,
  1118. SPIN_READ,
  1119. SPIN_READ_RECURSIVE,
  1120. };
  1121. void _LockSpin(SPIN_TYPE st);
  1122. void _WriteLockSpin();
  1123. void _ReadLockSpin(SPIN_TYPE st) { _LockSpin(st); }
  1124. bool _CmpExch(LONG lNew, LONG lCurrent);
  1125. LONG _SetTid(LONG lNewTid);
  1126. bool _TryWriteLock(LONG nIncr);
  1127. bool _TryReadLock();
  1128. bool _TryReadLockRecursive();
  1129. // Get the current thread ID. Assumes that it can fit into 28 bits,
  1130. // which is fairly safe as NT recycles thread IDs and failing to fit into
  1131. // 28 bits would mean that more than 268,435,456 threads were currently
  1132. // active. This is improbable in the extreme as NT runs out of
  1133. // resources if there are more than a few thousands threads in
  1134. // existence and the overhead of context swapping becomes unbearable.
  1135. LOCK_FORCEINLINE static DWORD _GetCurrentThreadId()
  1136. {
  1137. #ifdef LOCKS_KERNEL_MODE
  1138. return (DWORD) HandleToULong(::PsGetCurrentThreadId());
  1139. #else // !LOCKS_KERNEL_MODE
  1140. return ::GetCurrentThreadId();
  1141. #endif // !LOCKS_KERNEL_MODE
  1142. }
  1143. LOCK_FORCEINLINE static LONG _CurrentThreadId()
  1144. {
  1145. DWORD dwTid = _GetCurrentThreadId();
  1146. // Thread ID 0 is used by the System Idle Process (Process ID 0).
  1147. // We use a thread-id of zero to indicate that the lock is unowned.
  1148. // NT uses +ve thread ids, Win9x uses -ve ids
  1149. IRTLASSERT(dwTid != SL_UNOWNED
  1150. && ((dwTid <= SL_THREAD_MASK) || (dwTid > ~SL_THREAD_MASK)));
  1151. return (LONG) (dwTid & SL_THREAD_MASK);
  1152. }
  1153. public:
  1154. CReaderWriterLock3()
  1155. : m_lRW(SL_FREE),
  1156. m_lTid(SL_UNOWNED)
  1157. {}
  1158. #ifdef LOCK_INSTRUMENTATION
  1159. CReaderWriterLock3(
  1160. const TCHAR* ptszName)
  1161. : m_lRW(SL_FREE),
  1162. m_lTid(SL_UNOWNED)
  1163. {
  1164. LOCK_INSTRUMENTATION_INIT(ptszName);
  1165. }
  1166. #endif // LOCK_INSTRUMENTATION
  1167. #ifdef IRTLDEBUG
  1168. ~CReaderWriterLock3()
  1169. {
  1170. IRTLASSERT(m_lRW == SL_FREE && m_lTid == SL_UNOWNED);
  1171. }
  1172. #endif // IRTLDEBUG
  1173. inline void
  1174. WriteLock()
  1175. {
  1176. LOCKS_ENTER_CRIT_REGION();
  1177. LOCK_WRITELOCK_INSTRUMENTATION();
  1178. // Optimize for the common case
  1179. if (_TryWriteLock(SL_WRITER_INCR))
  1180. return;
  1181. _WriteLockSpin();
  1182. }
  1183. inline void
  1184. ReadLock()
  1185. {
  1186. LOCKS_ENTER_CRIT_REGION();
  1187. LOCK_READLOCK_INSTRUMENTATION();
  1188. // Optimize for the common case
  1189. if (_TryReadLock())
  1190. return;
  1191. _ReadLockSpin(SPIN_READ);
  1192. }
  1193. // If already locked, recursively acquires another lock of the same
  1194. // kind (read or write). Otherwise, just acquires a read lock.
  1195. // Needed for cases like this.
  1196. // pTable->WriteLock();
  1197. // if (!pTable->FindKey(&SomeKey))
  1198. // InsertRecord(&Whatever);
  1199. // pTable->WriteUnlock();
  1200. // where FindKey looks like
  1201. // Table::FindKey(pKey) {
  1202. // ReadOrWriteLock();
  1203. // // find pKey if present in table
  1204. // ReadOrWriteUnlock();
  1205. // }
  1206. // and InsertRecord looks like
  1207. // Table::InsertRecord(pRecord) {
  1208. // WriteLock();
  1209. // // insert pRecord into table
  1210. // WriteUnlock();
  1211. // }
  1212. // If FindKey called ReadLock while the thread already had done a
  1213. // WriteLock, the thread would deadlock.
  1214. bool ReadOrWriteLock();
  1215. inline bool
  1216. TryWriteLock()
  1217. {
  1218. LOCKS_ENTER_CRIT_REGION();
  1219. if (_TryWriteLock(SL_WRITER_INCR))
  1220. {
  1221. LOCK_WRITELOCK_INSTRUMENTATION();
  1222. return true;
  1223. }
  1224. else
  1225. {
  1226. LOCKS_LEAVE_CRIT_REGION();
  1227. return false;
  1228. }
  1229. }
  1230. inline bool
  1231. TryReadLock()
  1232. {
  1233. LOCKS_ENTER_CRIT_REGION();
  1234. if (_TryReadLock())
  1235. {
  1236. LOCK_READLOCK_INSTRUMENTATION();
  1237. return true;
  1238. }
  1239. else
  1240. {
  1241. LOCKS_LEAVE_CRIT_REGION();
  1242. return false;
  1243. }
  1244. }
  1245. void WriteUnlock();
  1246. void ReadUnlock();
  1247. void ReadOrWriteUnlock(bool fIsReadLocked);
  1248. // Does current thread hold a write lock?
  1249. bool
  1250. IsWriteLocked() const
  1251. {
  1252. const LONG lTid = m_lTid;
  1253. if (lTid == SL_UNOWNED)
  1254. return false;
  1255. bool fLocked = ((lTid ^ _GetCurrentThreadId()) << SL_OWNER_BITS) == 0;
  1256. IRTLASSERT(!fLocked
  1257. || ((m_lRW & SL_STATE_MASK) == SL_EXCLUSIVE
  1258. && (lTid & SL_THREAD_MASK) == _CurrentThreadId()
  1259. && (lTid & SL_OWNER_MASK) > 0));
  1260. return fLocked;
  1261. }
  1262. bool
  1263. IsReadLocked() const
  1264. { return (m_lRW & SL_READER_MASK) >= SL_READER_INCR; }
  1265. bool
  1266. IsWriteUnlocked() const
  1267. { return !IsWriteLocked(); }
  1268. bool
  1269. IsReadUnlocked() const
  1270. { return !IsReadLocked(); }
  1271. // Note: if there's more than one reader, then there's a window where
  1272. // another thread can acquire and release a writelock before this routine
  1273. // returns.
  1274. void
  1275. ConvertSharedToExclusive();
  1276. // There is no such window when converting from a writelock to a readlock
  1277. void
  1278. ConvertExclusiveToShared();
  1279. #ifdef LOCK_DEFAULT_SPIN_IMPLEMENTATION
  1280. bool
  1281. SetSpinCount(WORD wSpins)
  1282. {return false;}
  1283. WORD
  1284. GetSpinCount() const
  1285. {return sm_wDefaultSpinCount;}
  1286. LOCK_DEFAULT_SPIN_IMPLEMENTATION();
  1287. #endif // LOCK_DEFAULT_SPIN_IMPLEMENTATION
  1288. static const TCHAR*
  1289. ClassName()
  1290. {return _TEXT("CReaderWriterLock3");}
  1291. }; // CReaderWriterLock3
  1292. #endif // __LOCKS_H__