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.

1940 lines
56 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 a timeout feature to Try{Read,Write}Lock
  39. // * Add some way of tracking all the owners of a multi-reader lock
  40. //--------------------------------------------------------------------
  41. #ifndef __IRTLDBG_H__
  42. # include <irtldbg.h>
  43. #endif
  44. #ifdef __LOCKS_NAMESPACE__
  45. namespace Locks {
  46. #endif // __LOCKS_NAMESPACE__
  47. enum LOCK_LOCKTYPE {
  48. LOCK_SMALLSPINLOCK = 1,
  49. LOCK_SPINLOCK,
  50. LOCK_FAKELOCK,
  51. LOCK_CRITSEC,
  52. LOCK_READERWRITERLOCK,
  53. LOCK_READERWRITERLOCK2,
  54. LOCK_READERWRITERLOCK3,
  55. };
  56. // Forward declarations
  57. class IRTL_DLLEXP CSmallSpinLock;
  58. class IRTL_DLLEXP CSpinLock;
  59. class IRTL_DLLEXP CFakeLock;
  60. class IRTL_DLLEXP CCritSec;
  61. class IRTL_DLLEXP CReaderWriterLock;
  62. class IRTL_DLLEXP CReaderWriterLock2;
  63. class IRTL_DLLEXP CReaderWriterLock3;
  64. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  65. // __forceinline keyword new to VC6
  66. # define LOCK_FORCEINLINE __forceinline
  67. #else
  68. # define LOCK_FORCEINLINE inline
  69. #endif
  70. #ifdef _M_IX86
  71. // The compiler will warn that the assembly language versions of the
  72. // Lock_Atomic* functions don't return a value. Actually, they do: in EAX.
  73. # pragma warning(disable: 4035)
  74. #endif
  75. // Workarounds for certain useful interlocked operations that are not
  76. // available on Windows 95. Note: the CMPXCHG and XADD instructions were
  77. // introduced in the 80486. If you still need to run on a 386 (unlikely in
  78. // 2000), you'll need to use something else.
  79. LOCK_FORCEINLINE
  80. LONG
  81. Lock_AtomicIncrement(
  82. IN OUT PLONG plAddend)
  83. {
  84. #ifdef _M_IX86
  85. __asm
  86. {
  87. mov ecx, plAddend
  88. mov eax, 1
  89. lock xadd [ecx], eax
  90. inc eax // correct result
  91. }
  92. #else
  93. return InterlockedIncrement(plAddend);
  94. #endif
  95. }
  96. LOCK_FORCEINLINE
  97. LONG
  98. Lock_AtomicDecrement(
  99. IN OUT PLONG plAddend)
  100. {
  101. #ifdef _M_IX86
  102. __asm
  103. {
  104. mov ecx, plAddend
  105. mov eax, -1
  106. lock xadd [ecx], eax
  107. dec eax // correct result
  108. }
  109. #else
  110. return InterlockedDecrement(plAddend);
  111. #endif
  112. }
  113. LOCK_FORCEINLINE
  114. LONG
  115. Lock_AtomicExchange(
  116. IN OUT PLONG plAddr,
  117. IN LONG lNew)
  118. {
  119. #ifdef _M_IX86
  120. __asm
  121. {
  122. mov ecx, plAddr
  123. mov edx, lNew
  124. mov eax, [ecx]
  125. LAEloop:
  126. lock cmpxchg [ecx], edx
  127. jnz LAEloop
  128. }
  129. #else
  130. return InterlockedExchange(plAddr, lNew);
  131. #endif
  132. }
  133. LOCK_FORCEINLINE
  134. LONG
  135. Lock_AtomicCompareExchange(
  136. IN OUT PLONG plAddr,
  137. IN LONG lNew,
  138. IN LONG lCurrent)
  139. {
  140. #ifdef _M_IX86
  141. __asm
  142. {
  143. mov ecx, plAddr
  144. mov edx, lNew
  145. mov eax, lCurrent
  146. lock cmpxchg [ecx], edx
  147. }
  148. #else
  149. return InterlockedCompareExchange(plAddr, lNew, lCurrent);
  150. #endif
  151. }
  152. LOCK_FORCEINLINE
  153. LONG
  154. Lock_AtomicExchangeAdd(
  155. IN OUT LPLONG plAddr,
  156. IN LONG lValue)
  157. {
  158. #ifdef _M_IX86
  159. __asm
  160. {
  161. mov ecx, plAddr
  162. mov eax, lValue
  163. lock xadd [ecx], eax
  164. }
  165. #else
  166. return InterlockedExchangeAdd(plAddr, lValue);
  167. #endif
  168. }
  169. #ifdef _M_IX86
  170. # pragma warning(default: 4035)
  171. // Makes tight loops a little more cache friendly and reduces power
  172. // consumption. Needed on Willamette processors.
  173. # define Lock_Yield() _asm { rep nop }
  174. #else
  175. # define Lock_Yield() ((void) 0)
  176. #endif
  177. //--------------------------------------------------------------------
  178. // Spin count values.
  179. enum LOCK_SPINS {
  180. LOCK_MAXIMUM_SPINS = 10000, // maximum allowable spin count
  181. LOCK_DEFAULT_SPINS = 4000, // default spin count
  182. LOCK_MINIMUM_SPINS = 100, // minimum allowable spin count
  183. LOCK_USE_DEFAULT_SPINS = 0xFFFF, // use class default spin count
  184. LOCK_DONT_SPIN = 0, // don't spin at all
  185. };
  186. // Boilerplate code for the per-class default spincount and spinfactor
  187. #define LOCK_DEFAULT_SPIN_IMPLEMENTATION() \
  188. protected: \
  189. /* per-class variables */ \
  190. static WORD sm_wDefaultSpinCount; /* global default spin count */ \
  191. static double sm_dblDfltSpinAdjFctr; /* global spin adjustment factor*/\
  192. \
  193. public: \
  194. /* Set the default spin count for all locks */ \
  195. static void SetDefaultSpinCount(WORD wSpins) \
  196. { \
  197. IRTLASSERT((wSpins == LOCK_DONT_SPIN) \
  198. || (wSpins == LOCK_USE_DEFAULT_SPINS) \
  199. || (LOCK_MINIMUM_SPINS <= wSpins \
  200. && wSpins <= LOCK_MAXIMUM_SPINS)); \
  201. \
  202. if ((LOCK_MINIMUM_SPINS <= wSpins && wSpins <= LOCK_MAXIMUM_SPINS)\
  203. || (wSpins == LOCK_DONT_SPIN)) \
  204. sm_wDefaultSpinCount = wSpins; \
  205. else if (wSpins == LOCK_USE_DEFAULT_SPINS) \
  206. sm_wDefaultSpinCount = LOCK_DEFAULT_SPINS; \
  207. } \
  208. \
  209. /* Return the default spin count for all locks */ \
  210. static WORD GetDefaultSpinCount() \
  211. { \
  212. return sm_wDefaultSpinCount; \
  213. } \
  214. \
  215. /* Set the adjustment factor for the spincount, used in each iteration */\
  216. /* of countdown-and-sleep by the backoff algorithm. */ \
  217. static void SetDefaultSpinAdjustmentFactor(double dblAdjFactor) \
  218. { \
  219. IRTLASSERT(0.1 <= dblAdjFactor && dblAdjFactor <= 10.0); \
  220. if (0.1 <= dblAdjFactor && dblAdjFactor <= 10.0) \
  221. sm_dblDfltSpinAdjFctr = dblAdjFactor; \
  222. } \
  223. \
  224. /* Return the default spin count for all locks */ \
  225. static double GetDefaultSpinAdjustmentFactor() \
  226. { \
  227. return sm_dblDfltSpinAdjFctr; \
  228. } \
  229. //--------------------------------------------------------------------
  230. // Various Lock Traits
  231. // Is the lock a simple mutex or a multi-reader/single-writer lock?
  232. enum LOCK_RW_MUTEX {
  233. LOCK_MUTEX = 1, // mutexes allow only one thread to hold the lock
  234. LOCK_MRSW, // multi-reader, single-writer
  235. };
  236. // Can the lock be recursively acquired?
  237. enum LOCK_RECURSION {
  238. LOCK_RECURSIVE = 1, // Write and Read locks can be recursively acquired
  239. LOCK_READ_RECURSIVE, // Read locks can be reacquired, but not Write
  240. LOCK_NON_RECURSIVE, // Will deadlock if attempt to acquire recursively
  241. };
  242. // Does the lock Sleep in a loop or block on a kernel synch object handle?
  243. // May (or may not) spin first before sleeping/blocking.
  244. enum LOCK_WAIT_TYPE {
  245. LOCK_WAIT_SLEEP = 1, // Calls Sleep() in a loop
  246. LOCK_WAIT_HANDLE, // Blocks on a kernel mutex, semaphore, or event
  247. };
  248. // When the lock is taken, how are the waiters dequeued?
  249. enum LOCK_QUEUE_TYPE {
  250. LOCK_QUEUE_FIFO = 1, // First in, first out. Fair.
  251. LOCK_QUEUE_LIFO, // Unfair but CPU cache friendly
  252. LOCK_QUEUE_KERNEL, // Determined by vagaries of scheduler
  253. };
  254. // Can the lock's spincount be set on a per-lock basis, or is it only
  255. // possible to modify the default spincount for all the locks in this class?
  256. enum LOCK_PERLOCK_SPIN {
  257. LOCK_NO_SPIN = 1, // The locks do not spin at all
  258. LOCK_CLASS_SPIN, // Can set class-wide spincount, not individual
  259. LOCK_INDIVIDUAL_SPIN, // Can set a spincount on an individual lock
  260. };
  261. //--------------------------------------------------------------------
  262. // CLockBase: bundle the above attributes
  263. template < LOCK_LOCKTYPE locktype,
  264. LOCK_RW_MUTEX mutextype,
  265. LOCK_RECURSION recursiontype,
  266. LOCK_WAIT_TYPE waittype,
  267. LOCK_QUEUE_TYPE queuetype,
  268. LOCK_PERLOCK_SPIN spintype
  269. >
  270. class CLockBase
  271. {
  272. public:
  273. static LOCK_LOCKTYPE LockType() {return locktype;}
  274. static LOCK_RW_MUTEX MutexType() {return mutextype;}
  275. static LOCK_RECURSION Recursion() {return recursiontype;}
  276. static LOCK_WAIT_TYPE WaitType() {return waittype;}
  277. static LOCK_QUEUE_TYPE QueueType() {return queuetype;}
  278. static LOCK_PERLOCK_SPIN PerLockSpin() {return spintype;}
  279. };
  280. // Lock instrumentation causes all sorts of interesting statistics about
  281. // lock contention, etc., to be gathered, but makes locks considerably fatter
  282. // and somewhat slower. Turned off by default.
  283. // #define LOCK_INSTRUMENTATION 1
  284. #ifdef LOCK_INSTRUMENTATION
  285. // We generally don't want to instrument CSmallSpinLock in addition
  286. // to CSpinLock1, as it makes a CSpinLock1 huge.
  287. // #define LOCK_SMALL_SPIN_INSTRUMENTATION 1
  288. //--------------------------------------------------------------------
  289. // CLockStatistics: statistics for an individual lock
  290. class IRTL_DLLEXP CLockStatistics
  291. {
  292. public:
  293. enum {
  294. L_NAMELEN = 8,
  295. };
  296. double m_nContentions; // #times this lock was already locked
  297. double m_nSleeps; // Total #Sleep()s needed
  298. double m_nContentionSpins; // Total iterations this lock spun
  299. double m_nAverageSpins; // Average spins each contention needed
  300. double m_nReadLocks; // Number of times lock acquired for reading
  301. double m_nWriteLocks; // Number of times lock acquired for writing
  302. char m_szName[L_NAMELEN];// Name of this lock
  303. CLockStatistics()
  304. : m_nContentions(0),
  305. m_nSleeps(0),
  306. m_nContentionSpins(0),
  307. m_nAverageSpins(0),
  308. m_nReadLocks(0),
  309. m_nWriteLocks(0)
  310. {
  311. m_szName[0] = '\0';
  312. }
  313. };
  314. //--------------------------------------------------------------------
  315. // CGlobalLockStatistics: statistics for all the known locks
  316. class IRTL_DLLEXP CGlobalLockStatistics
  317. {
  318. public:
  319. LONG m_cTotalLocks; // Total number of locks created
  320. LONG m_cContendedLocks; // Total number of contended locks
  321. LONG m_nSleeps; // Total #Sleep()s needed by all locks
  322. LONGLONG m_cTotalSpins; // Total iterations all locks spun
  323. double m_nAverageSpins; // Average spins needed for each contended lock
  324. LONG m_nReadLocks; // Total ReadLocks
  325. LONG m_nWriteLocks; // Total WriteLocks
  326. CGlobalLockStatistics()
  327. : m_cTotalLocks(0),
  328. m_cContendedLocks(0),
  329. m_nSleeps(0),
  330. m_cTotalSpins(0),
  331. m_nAverageSpins(0),
  332. m_nReadLocks(0),
  333. m_nWriteLocks(0)
  334. {}
  335. };
  336. # define LOCK_INSTRUMENTATION_DECL() \
  337. private: \
  338. volatile LONG m_nContentionSpins; /* #iterations this lock spun */ \
  339. volatile WORD m_nContentions; /* #times lock was already locked */\
  340. volatile WORD m_nSleeps; /* #Sleep()s needed */ \
  341. volatile WORD m_nReadLocks; /* #ReadLocks */ \
  342. volatile WORD m_nWriteLocks; /* #WriteLocks */ \
  343. char m_szName[CLockStatistics::L_NAMELEN]; /* Name of lock */\
  344. \
  345. static LONG sm_cTotalLocks; /* Total number of locks created */ \
  346. static LONG sm_cContendedLocks; /* Total number of contended locks */\
  347. static LONG sm_nSleeps; /* Total #Sleep()s by all locks */ \
  348. static LONGLONG sm_cTotalSpins; /* Total iterations all locks spun */\
  349. static LONG sm_nReadLocks; /* Total ReadLocks */ \
  350. static LONG sm_nWriteLocks; /* Total WriteLocks */ \
  351. \
  352. public: \
  353. const char* Name() const {return m_szName;} \
  354. \
  355. CLockStatistics Statistics() const; \
  356. static CGlobalLockStatistics GlobalStatistics(); \
  357. static void ResetGlobalStatistics(); \
  358. private: \
  359. // Add this to constructors
  360. # define LOCK_INSTRUMENTATION_INIT(pszName) \
  361. m_nContentionSpins = 0; \
  362. m_nContentions = 0; \
  363. m_nSleeps = 0; \
  364. m_nReadLocks = 0; \
  365. m_nWriteLocks = 0; \
  366. ++sm_cTotalLocks; \
  367. if (pszName == NULL) \
  368. m_szName[0] = '\0'; \
  369. else \
  370. strncpy(m_szName, pszName, sizeof(m_szName))
  371. // Note: we are not using Interlocked operations for the shared
  372. // statistical counters. We'll lose perfect accuracy, but we'll
  373. // gain by reduced bus synchronization traffic.
  374. # define LOCK_READLOCK_INSTRUMENTATION() \
  375. { ++m_nReadLocks; \
  376. ++sm_nReadLocks; }
  377. # define LOCK_WRITELOCK_INSTRUMENTATION() \
  378. { ++m_nWriteLocks; \
  379. ++sm_nWriteLocks; }
  380. #else // !LOCK_INSTRUMENTATION
  381. # define LOCK_INSTRUMENTATION_DECL()
  382. # define LOCK_READLOCK_INSTRUMENTATION() ((void) 0)
  383. # define LOCK_WRITELOCK_INSTRUMENTATION() ((void) 0)
  384. #endif // !LOCK_INSTRUMENTATION
  385. //--------------------------------------------------------------------
  386. // CAutoReadLock<Lock> and CAutoWriteLock<Lock> provide exception-safe
  387. // acquisition and release of the other locks defined below
  388. template <class _Lock>
  389. class IRTL_DLLEXP CAutoReadLock
  390. {
  391. private:
  392. bool m_fLocked;
  393. _Lock& m_Lock;
  394. public:
  395. CAutoReadLock(
  396. _Lock& rLock,
  397. bool fLockNow = true)
  398. : m_fLocked(false), m_Lock(rLock)
  399. {
  400. if (fLockNow)
  401. Lock();
  402. }
  403. ~CAutoReadLock()
  404. {
  405. Unlock();
  406. }
  407. void Lock()
  408. {
  409. // disallow recursive acquisition of the lock through this wrapper
  410. if (!m_fLocked)
  411. {
  412. m_fLocked = true;
  413. m_Lock.ReadLock();
  414. }
  415. }
  416. void Unlock()
  417. {
  418. if (m_fLocked)
  419. {
  420. m_Lock.ReadUnlock();
  421. m_fLocked = false;
  422. }
  423. }
  424. };
  425. template <class _Lock>
  426. class IRTL_DLLEXP CAutoWriteLock
  427. {
  428. private:
  429. bool m_fLocked;
  430. _Lock& m_Lock;
  431. public:
  432. CAutoWriteLock(
  433. _Lock& rLock,
  434. bool fLockNow = true)
  435. : m_fLocked(false), m_Lock(rLock)
  436. {
  437. if (fLockNow)
  438. Lock();
  439. }
  440. ~CAutoWriteLock()
  441. {
  442. Unlock();
  443. }
  444. void Lock()
  445. {
  446. // disallow recursive acquisition of the lock through this wrapper
  447. if (!m_fLocked)
  448. {
  449. m_fLocked = true;
  450. m_Lock.WriteLock();
  451. }
  452. }
  453. void Unlock()
  454. {
  455. if (m_fLocked)
  456. {
  457. m_fLocked = false;
  458. m_Lock.WriteUnlock();
  459. }
  460. }
  461. };
  462. //--------------------------------------------------------------------
  463. // A spinlock is a sort of lightweight critical section. Its main
  464. // advantage over a true Win32 CRITICAL_SECTION is that it occupies 4 bytes
  465. // instead of 24 (+ another 32 bytes for the RTL_CRITICAL_SECTION_DEBUG data),
  466. // which is important when we have many thousands of locks
  467. // and we're trying to be L1 cache-conscious. A CRITICAL_SECTION also
  468. // contains a HANDLE to a semaphore, although this is not initialized until
  469. // the first time that the CRITICAL_SECTION blocks.
  470. //
  471. // On a multiprocessor machine, a spinlock tries to acquire the lock. If
  472. // it fails, it sits in a tight loop, testing the lock and decrementing a
  473. // counter. If the counter reaches zero, it does a Sleep(0), yielding the
  474. // processor to another thread. When control returns to the thread, the
  475. // lock is probably free. If not, the loop starts again and it is
  476. // terminated only when the lock is acquired. The theory is that it is
  477. // less costly to spin in a busy loop for a short time rather than
  478. // immediately yielding the processor, forcing an expensive context switch
  479. // that requires the old thread's state (registers, etc) be saved, the new
  480. // thread's state be reloaded, and the L1 and L2 caches be left full of
  481. // stale data.
  482. //
  483. // You can tune the spin count (global only: per-lock spin counts are
  484. // disabled) and the backoff algorithm (the factor by which the spin
  485. // count is multiplied after each Sleep).
  486. //
  487. // On a 1P machine, the loop is pointless---this thread has control,
  488. // hence no other thread can possibly release the lock while this thread
  489. // is looping---so the processor is yielded immediately.
  490. //
  491. // The kernel uses spinlocks internally and spinlocks were also added to
  492. // CRITICAL_SECTIONs in NT 4.0 sp3. In the CRITICAL_SECTION implementation,
  493. // however, the counter counts down only once and waits on a semaphore
  494. // thereafter (i.e., the same blocking behavior that it exhibits without
  495. // the spinlock).
  496. //
  497. // A disadvantage of a user-level spinlock such as this is that if the
  498. // thread that owns the spinlock blocks for any reason (or is preempted by
  499. // the scheduler), all the other threads will continue to spin on the
  500. // spinlock, wasting CPU, until the owning thread completes its wait and
  501. // releases the lock. (The kernel spinlocks, however, are smart enough to
  502. // switch to another runnable thread instead of wasting time spinning.)
  503. // The backoff algorithm decreases the spin count on each iteration in an
  504. // attempt to minimize this effect. The best policy---and this is true for
  505. // all locks---is to hold the lock for as short as time as possible.
  506. //
  507. // Note: unlike a CRITICAL_SECTION, a CSmallSpinLock cannot be recursively
  508. // acquired; i.e., if you acquire a spinlock and then attempt to acquire it
  509. // again *on the same thread* (perhaps from a different function), the
  510. // thread will hang forever. Use CSpinLock instead, which is safe though a
  511. // little slower than a CSmallSpinLock. If you own all the code
  512. // that is bracketed by Lock() and Unlock() (e.g., no callbacks or passing
  513. // back of locked data structures to callers) and know for certain that it
  514. // will not attempt to reacquire the lock, you can use CSmallSpinLock.
  515. //
  516. // See also http://muralik/work/performance/spinlocks.htm and John Vert's
  517. // MSDN article, "Writing Scalable Applications for Windows NT".
  518. //
  519. // The original implementation is due to PALarson.
  520. class IRTL_DLLEXP CSmallSpinLock :
  521. public CLockBase<LOCK_SMALLSPINLOCK, LOCK_MUTEX,
  522. LOCK_NON_RECURSIVE, LOCK_WAIT_SLEEP, LOCK_QUEUE_KERNEL,
  523. LOCK_CLASS_SPIN
  524. >
  525. {
  526. private:
  527. volatile LONG m_lTid; // The lock state variable
  528. #ifdef LOCK_SMALL_SPIN_INSTRUMENTATION
  529. LOCK_INSTRUMENTATION_DECL();
  530. #endif // LOCK_SMALL_SPIN_INSTRUMENTATION
  531. LOCK_FORCEINLINE static LONG _CurrentThreadId()
  532. {
  533. DWORD dwTid = ::GetCurrentThreadId();
  534. return (LONG) (dwTid);
  535. }
  536. private:
  537. // Does all the spinning (and instrumentation) if the lock is contended.
  538. void _LockSpin();
  539. LOCK_FORCEINLINE bool _TryLock()
  540. {
  541. if (m_lTid == 0)
  542. {
  543. LONG l = _CurrentThreadId();
  544. return (Lock_AtomicCompareExchange(const_cast<LONG*>(&m_lTid), l,0)
  545. == 0);
  546. }
  547. else
  548. return false;
  549. }
  550. public:
  551. #ifndef LOCK_SMALL_SPIN_INSTRUMENTATION
  552. CSmallSpinLock()
  553. : m_lTid(0)
  554. {}
  555. #else // LOCK_SMALL_SPIN_INSTRUMENTATION
  556. CSmallSpinLock(
  557. const char* pszName)
  558. : m_lTid(0)
  559. {
  560. LOCK_INSTRUMENTATION_INIT(pszName);
  561. }
  562. #endif // LOCK_SMALL_SPIN_INSTRUMENTATION
  563. #ifdef _DEBUG
  564. ~CSmallSpinLock()
  565. {
  566. IRTLASSERT(m_lTid == 0);
  567. }
  568. #endif // _DEBUG
  569. // Acquire an exclusive lock for writing. Blocks until acquired.
  570. inline void WriteLock()
  571. {
  572. #ifdef LOCK_SMALL_SPIN_INSTRUMENTATION
  573. LOCK_WRITELOCK_INSTRUMENTATION();
  574. #endif // LOCK_SMALL_SPIN_INSTRUMENTATION
  575. // Optimize for the common case by helping the processor's branch
  576. // prediction algorithm.
  577. if (_TryLock())
  578. return;
  579. _LockSpin();
  580. }
  581. // Acquire a (possibly shared) lock for reading. Blocks until acquired.
  582. inline void ReadLock()
  583. {
  584. #ifdef LOCK_SMALL_SPIN_INSTRUMENTATION
  585. LOCK_READLOCK_INSTRUMENTATION();
  586. #endif // LOCK_SMALL_SPIN_INSTRUMENTATION
  587. if (_TryLock())
  588. return;
  589. _LockSpin();
  590. }
  591. // Try to acquire an exclusive lock for writing. Returns true
  592. // if successful. Non-blocking.
  593. inline bool TryWriteLock()
  594. {
  595. bool fAcquired = _TryLock();
  596. #ifdef LOCK_SMALL_SPIN_INSTRUMENTATION
  597. if (fAcquired)
  598. LOCK_WRITELOCK_INSTRUMENTATION();
  599. #endif // LOCK_SMALL_SPIN_INSTRUMENTATION
  600. return fAcquired;
  601. }
  602. // Try to acquire a (possibly shared) lock for reading. Returns true
  603. // if successful. Non-blocking.
  604. inline bool TryReadLock()
  605. {
  606. bool fAcquired = _TryLock();
  607. #ifdef LOCK_SMALL_SPIN_INSTRUMENTATION
  608. if (fAcquired)
  609. LOCK_READLOCK_INSTRUMENTATION();
  610. #endif // LOCK_SMALL_SPIN_INSTRUMENTATION
  611. return fAcquired;
  612. }
  613. // Unlock the lock after a successful call to {,Try}WriteLock().
  614. // Assumes caller owned the lock.
  615. inline void WriteUnlock()
  616. {
  617. Lock_AtomicExchange(const_cast<LONG*>(&m_lTid), 0);
  618. }
  619. // Unlock the lock after a successful call to {,Try}ReadLock().
  620. // Assumes caller owned the lock.
  621. inline void ReadUnlock()
  622. {
  623. WriteUnlock();
  624. }
  625. // Is the lock already locked for writing by this thread?
  626. bool IsWriteLocked() const
  627. {
  628. return (m_lTid == _CurrentThreadId());
  629. }
  630. // Is the lock already locked for reading?
  631. bool IsReadLocked() const
  632. {
  633. return IsWriteLocked();
  634. }
  635. // Is the lock unlocked for writing?
  636. bool IsWriteUnlocked() const
  637. {
  638. return (m_lTid == 0);
  639. }
  640. // Is the lock unlocked for reading?
  641. bool IsReadUnlocked() const
  642. {
  643. return IsWriteUnlocked();
  644. }
  645. // Convert a reader lock to a writer lock
  646. void ConvertSharedToExclusive()
  647. {
  648. // no-op
  649. }
  650. // Convert a writer lock to a reader lock
  651. void ConvertExclusiveToShared()
  652. {
  653. // no-op
  654. }
  655. // Set the spin count for this lock.
  656. // Returns true if successfully set the per-lock spincount, false otherwise
  657. bool SetSpinCount(WORD wSpins)
  658. {
  659. IRTLASSERT((wSpins == LOCK_DONT_SPIN)
  660. || (wSpins == LOCK_USE_DEFAULT_SPINS)
  661. || (LOCK_MINIMUM_SPINS <= wSpins
  662. && wSpins <= LOCK_MAXIMUM_SPINS));
  663. return false;
  664. }
  665. // Return the spin count for this lock.
  666. WORD GetSpinCount() const
  667. {
  668. return sm_wDefaultSpinCount;
  669. }
  670. LOCK_DEFAULT_SPIN_IMPLEMENTATION();
  671. static const char* ClassName() {return "CSmallSpinLock";}
  672. }; // CSmallSpinLock
  673. //--------------------------------------------------------------------
  674. // CSpinLock is a spinlock that doesn't deadlock if recursively acquired.
  675. // This version occupies only 4 bytes. Uses 28 bits for the thread id.
  676. class IRTL_DLLEXP CSpinLock :
  677. public CLockBase<LOCK_SPINLOCK, LOCK_MUTEX,
  678. LOCK_RECURSIVE, LOCK_WAIT_SLEEP, LOCK_QUEUE_KERNEL,
  679. LOCK_CLASS_SPIN
  680. >
  681. {
  682. private:
  683. // a union for convenience
  684. volatile LONG m_lTid;
  685. enum {
  686. THREAD_SHIFT = 0,
  687. THREAD_BITS = 28,
  688. OWNER_SHIFT = THREAD_BITS,
  689. OWNER_BITS = 4,
  690. THREAD_MASK = ((1 << THREAD_BITS) - 1) << THREAD_SHIFT,
  691. OWNER_INCR = 1 << THREAD_BITS,
  692. OWNER_MASK = ((1 << OWNER_BITS) - 1) << OWNER_SHIFT,
  693. };
  694. LOCK_INSTRUMENTATION_DECL();
  695. private:
  696. // Get the current thread ID. Assumes that it can fit into 28 bits,
  697. // which is fairly safe as NT recycles thread IDs and failing to fit into
  698. // 28 bits would mean that more than 268,435,456 threads were currently
  699. // active. This is improbable in the extreme as NT runs out of
  700. // resources if there are more than a few thousands threads in
  701. // existence and the overhead of context swapping becomes unbearable.
  702. LOCK_FORCEINLINE static LONG _CurrentThreadId()
  703. {
  704. DWORD dwTid = ::GetCurrentThreadId();
  705. // Thread ID 0 is used by the System Process (Process ID 0).
  706. // We use a thread-id of zero to indicate that the lock is unowned.
  707. // NT uses +ve thread ids, Win9x uses -ve ids
  708. IRTLASSERT(dwTid != 0
  709. && ((dwTid <= THREAD_MASK) || (dwTid > ~THREAD_MASK)));
  710. return (LONG) (dwTid & THREAD_MASK);
  711. }
  712. // Attempt to acquire the lock without blocking
  713. LOCK_FORCEINLINE bool _TryLock()
  714. {
  715. if (m_lTid == 0)
  716. {
  717. LONG l = _CurrentThreadId() | OWNER_INCR;
  718. return (Lock_AtomicCompareExchange(const_cast<LONG*>(&m_lTid), l,0)
  719. == 0);
  720. }
  721. else
  722. return false;
  723. }
  724. // Acquire the lock, recursively if need be
  725. void _Lock()
  726. {
  727. // Do we own the lock already? Just bump the count.
  728. if ((m_lTid & THREAD_MASK) == _CurrentThreadId())
  729. {
  730. // owner count isn't maxed out?
  731. IRTLASSERT((m_lTid & OWNER_MASK) != OWNER_MASK);
  732. Lock_AtomicExchangeAdd(const_cast<LONG*>(&m_lTid), OWNER_INCR);
  733. }
  734. // Some other thread owns the lock. We'll have to spin :-(.
  735. else
  736. _LockSpin();
  737. IRTLASSERT((m_lTid & OWNER_MASK) > 0
  738. && (m_lTid & THREAD_MASK) == _CurrentThreadId());
  739. }
  740. // Release the lock
  741. LOCK_FORCEINLINE void _Unlock()
  742. {
  743. IRTLASSERT((m_lTid & OWNER_MASK) > 0
  744. && (m_lTid & THREAD_MASK) == _CurrentThreadId());
  745. LONG l = m_lTid - OWNER_INCR;
  746. // Last owner? Release completely, if so
  747. if ((l & OWNER_MASK) == 0)
  748. l = 0;
  749. Lock_AtomicExchange(const_cast<LONG*>(&m_lTid), l);
  750. }
  751. // Return true if the lock is owned by this thread
  752. bool _IsLocked() const
  753. {
  754. bool fLocked = ((m_lTid & THREAD_MASK) == _CurrentThreadId());
  755. IRTLASSERT(!fLocked || ((m_lTid & OWNER_MASK) > 0
  756. && (m_lTid & THREAD_MASK)==_CurrentThreadId()));
  757. return fLocked;
  758. }
  759. // Does all the spinning (and instrumentation) if the lock is contended.
  760. void _LockSpin();
  761. public:
  762. #ifndef LOCK_INSTRUMENTATION
  763. CSpinLock()
  764. : m_lTid(0)
  765. {}
  766. #else // LOCK_INSTRUMENTATION
  767. CSpinLock(
  768. const char* pszName)
  769. : m_lTid(0)
  770. {
  771. LOCK_INSTRUMENTATION_INIT(pszName);
  772. }
  773. #endif // LOCK_INSTRUMENTATION
  774. #ifdef _DEBUG
  775. ~CSpinLock()
  776. {
  777. IRTLASSERT(m_lTid == 0);
  778. }
  779. #endif // _DEBUG
  780. // Acquire an exclusive lock for writing. Blocks until acquired.
  781. inline void WriteLock()
  782. {
  783. LOCK_WRITELOCK_INSTRUMENTATION();
  784. // Is the lock unowned?
  785. if (_TryLock())
  786. return; // got the lock
  787. _Lock();
  788. }
  789. // Acquire a (possibly shared) lock for reading. Blocks until acquired.
  790. inline void ReadLock()
  791. {
  792. LOCK_READLOCK_INSTRUMENTATION();
  793. // Is the lock unowned?
  794. if (_TryLock())
  795. return; // got the lock
  796. _Lock();
  797. }
  798. // See the description under CReaderWriterLock3::ReadOrWriteLock
  799. inline bool ReadOrWriteLock()
  800. {
  801. ReadLock();
  802. return true;
  803. }
  804. // Try to acquire an exclusive lock for writing. Returns true
  805. // if successful. Non-blocking.
  806. inline bool TryWriteLock()
  807. {
  808. bool fAcquired = _TryLock();
  809. if (fAcquired)
  810. LOCK_WRITELOCK_INSTRUMENTATION();
  811. return fAcquired;
  812. }
  813. // Try to acquire a (possibly shared) lock for reading. Returns true
  814. // if successful. Non-blocking.
  815. inline bool TryReadLock()
  816. {
  817. bool fAcquired = _TryLock();
  818. if (fAcquired)
  819. LOCK_READLOCK_INSTRUMENTATION();
  820. return fAcquired;
  821. }
  822. // Unlock the lock after a successful call to {,Try}WriteLock().
  823. inline void WriteUnlock()
  824. {
  825. _Unlock();
  826. }
  827. // Unlock the lock after a successful call to {,Try}ReadLock().
  828. inline void ReadUnlock()
  829. {
  830. _Unlock();
  831. }
  832. // Unlock the lock after a call to ReadOrWriteLock().
  833. inline void ReadOrWriteUnlock(bool)
  834. {
  835. ReadUnlock();
  836. }
  837. // Is the lock already locked for writing?
  838. bool IsWriteLocked() const
  839. {
  840. return _IsLocked();
  841. }
  842. // Is the lock already locked for reading?
  843. bool IsReadLocked() const
  844. {
  845. return _IsLocked();
  846. }
  847. // Is the lock unlocked for writing?
  848. bool IsWriteUnlocked() const
  849. {
  850. return !IsWriteLocked();
  851. }
  852. // Is the lock unlocked for reading?
  853. bool IsReadUnlocked() const
  854. {
  855. return !IsReadLocked();
  856. }
  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. // Set the spin count for this lock.
  868. bool SetSpinCount(WORD dwSpins) {return false;}
  869. // Return the spin count for this lock.
  870. WORD GetSpinCount() const
  871. {
  872. return sm_wDefaultSpinCount;
  873. }
  874. LOCK_DEFAULT_SPIN_IMPLEMENTATION();
  875. static const char* ClassName() {return "CSpinLock";}
  876. }; // CSpinLock
  877. //--------------------------------------------------------------------
  878. // A dummy class, primarily useful as a template parameter
  879. class IRTL_DLLEXP CFakeLock :
  880. public CLockBase<LOCK_FAKELOCK, LOCK_MUTEX,
  881. LOCK_RECURSIVE, LOCK_WAIT_SLEEP, LOCK_QUEUE_FIFO,
  882. LOCK_NO_SPIN
  883. >
  884. {
  885. private:
  886. LOCK_INSTRUMENTATION_DECL();
  887. public:
  888. CFakeLock() {}
  889. #ifdef LOCK_INSTRUMENTATION
  890. CFakeLock(const char*) {}
  891. #endif // LOCK_INSTRUMENTATION
  892. ~CFakeLock() {}
  893. void WriteLock() {}
  894. void ReadLock() {}
  895. bool ReadOrWriteLock() {return true;}
  896. bool TryWriteLock() {return true;}
  897. bool TryReadLock() {return true;}
  898. void WriteUnlock() {}
  899. void ReadUnlock() {}
  900. void ReadOrWriteUnlock(bool) {}
  901. bool IsWriteLocked() const {return true;}
  902. bool IsReadLocked() const {return IsWriteLocked();}
  903. bool IsWriteUnlocked() const {return true;}
  904. bool IsReadUnlocked() const {return true;}
  905. void ConvertSharedToExclusive() {}
  906. void ConvertExclusiveToShared() {}
  907. bool SetSpinCount(WORD dwSpins) {return false;}
  908. WORD GetSpinCount() const {return LOCK_DONT_SPIN;}
  909. LOCK_DEFAULT_SPIN_IMPLEMENTATION();
  910. static const char* ClassName() {return "CFakeLock";}
  911. }; // CFakeLock
  912. //--------------------------------------------------------------------
  913. // A Win32 CRITICAL_SECTION
  914. class IRTL_DLLEXP CCritSec :
  915. public CLockBase<LOCK_CRITSEC, LOCK_MUTEX,
  916. LOCK_RECURSIVE, LOCK_WAIT_HANDLE, LOCK_QUEUE_KERNEL,
  917. LOCK_INDIVIDUAL_SPIN
  918. >
  919. {
  920. private:
  921. CRITICAL_SECTION m_cs;
  922. LOCK_INSTRUMENTATION_DECL();
  923. public:
  924. CCritSec()
  925. {
  926. InitializeCriticalSection(&m_cs);
  927. SetSpinCount(sm_wDefaultSpinCount);
  928. }
  929. #ifdef LOCK_INSTRUMENTATION
  930. CCritSec(const char*)
  931. {
  932. InitializeCriticalSection(&m_cs);
  933. SetSpinCount(sm_wDefaultSpinCount);
  934. }
  935. #endif // LOCK_INSTRUMENTATION
  936. ~CCritSec() { DeleteCriticalSection(&m_cs); }
  937. void WriteLock() { EnterCriticalSection(&m_cs); }
  938. void ReadLock() { WriteLock(); }
  939. bool ReadOrWriteLock() { ReadLock(); return true; }
  940. bool TryWriteLock();
  941. bool TryReadLock() { return TryWriteLock(); }
  942. void WriteUnlock() { LeaveCriticalSection(&m_cs); }
  943. void ReadUnlock() { WriteUnlock(); }
  944. void ReadOrWriteUnlock(bool) { ReadUnlock(); }
  945. bool IsWriteLocked() const {return true;} // TODO: fix this
  946. bool IsReadLocked() const {return IsWriteLocked();}
  947. bool IsWriteUnlocked() const {return true;} // TODO: fix this
  948. bool IsReadUnlocked() const {return true;} // TODO: fix this
  949. // Convert a reader lock to a writer lock
  950. void ConvertSharedToExclusive()
  951. {
  952. // no-op
  953. }
  954. // Convert a writer lock to a reader lock
  955. void ConvertExclusiveToShared()
  956. {
  957. // no-op
  958. }
  959. // Wrapper for ::SetCriticalSectionSpinCount which was introduced
  960. // in NT 4.0 sp3 and hence is not available on all platforms
  961. static DWORD SetSpinCount(LPCRITICAL_SECTION pcs,
  962. DWORD dwSpinCount=LOCK_DEFAULT_SPINS);
  963. bool SetSpinCount(WORD wSpins)
  964. {SetSpinCount(&m_cs, wSpins); return true;}
  965. WORD GetSpinCount() const { return sm_wDefaultSpinCount; } // TODO
  966. LOCK_DEFAULT_SPIN_IMPLEMENTATION();
  967. static const char* ClassName() {return "CCritSec";}
  968. }; // CCritSec
  969. //--------------------------------------------------------------------
  970. // CReaderWriterlock is a multi-reader, single-writer spinlock due to NJain,
  971. // which in turn is derived from an exclusive spinlock by DmitryR.
  972. // Gives priority to writers. Cannot be acquired recursively.
  973. // No error checking. Use CReaderWriterLock3.
  974. class IRTL_DLLEXP CReaderWriterLock :
  975. public CLockBase<LOCK_READERWRITERLOCK, LOCK_MRSW,
  976. LOCK_READ_RECURSIVE, LOCK_WAIT_SLEEP, LOCK_QUEUE_KERNEL,
  977. LOCK_CLASS_SPIN
  978. >
  979. {
  980. private:
  981. volatile LONG m_nState; // > 0 => that many readers
  982. volatile LONG m_cWaiting; // number of would-be writers
  983. LOCK_INSTRUMENTATION_DECL();
  984. private:
  985. enum {
  986. SL_FREE = 0,
  987. SL_EXCLUSIVE = -1,
  988. };
  989. void _LockSpin(bool fWrite);
  990. void _WriteLockSpin() { _LockSpin(true); }
  991. void _ReadLockSpin() { _LockSpin(false); }
  992. // _CmpExch is equivalent to
  993. // LONG lTemp = m_lRW;
  994. // if (lTemp == lCurrent) m_lRW = lNew;
  995. // return lCurrent == lTemp;
  996. // except it's one atomic instruction. Using this gives us the basis of
  997. // a protocol because the update only succeeds when we knew exactly what
  998. // used to be in m_lRW. If some other thread slips in and modifies m_lRW
  999. // before we do, the update will fail. In other words, it's transactional.
  1000. LOCK_FORCEINLINE bool _CmpExch(LONG lNew, LONG lCurrent)
  1001. {
  1002. return lCurrent == Lock_AtomicCompareExchange(
  1003. const_cast<LONG*>(&m_nState), lNew, lCurrent);
  1004. }
  1005. LOCK_FORCEINLINE bool _TryWriteLock()
  1006. {
  1007. return (m_nState == SL_FREE && _CmpExch(SL_EXCLUSIVE, SL_FREE));
  1008. }
  1009. LOCK_FORCEINLINE bool _TryReadLock()
  1010. {
  1011. LONG nCurrState = m_nState;
  1012. // Give writers priority
  1013. return (nCurrState != SL_EXCLUSIVE && m_cWaiting == 0
  1014. && _CmpExch(nCurrState + 1, nCurrState));
  1015. }
  1016. public:
  1017. CReaderWriterLock()
  1018. : m_nState(SL_FREE),
  1019. m_cWaiting(0)
  1020. {
  1021. }
  1022. #ifdef LOCK_INSTRUMENTATION
  1023. CReaderWriterLock(
  1024. const char* pszName)
  1025. : m_nState(SL_FREE),
  1026. m_cWaiting(0)
  1027. {
  1028. LOCK_INSTRUMENTATION_INIT(pszName);
  1029. }
  1030. #endif // LOCK_INSTRUMENTATION
  1031. #ifdef _DEBUG
  1032. ~CReaderWriterLock()
  1033. {
  1034. IRTLASSERT(m_nState == SL_FREE && m_cWaiting == 0);
  1035. }
  1036. #endif // _DEBUG
  1037. inline void WriteLock()
  1038. {
  1039. LOCK_WRITELOCK_INSTRUMENTATION();
  1040. // Add ourselves to the queue of waiting writers
  1041. Lock_AtomicIncrement(const_cast<LONG*>(&m_cWaiting));
  1042. if (_TryWriteLock())
  1043. return;
  1044. _WriteLockSpin();
  1045. }
  1046. inline void ReadLock()
  1047. {
  1048. LOCK_READLOCK_INSTRUMENTATION();
  1049. if (_TryReadLock())
  1050. return;
  1051. _ReadLockSpin();
  1052. }
  1053. inline bool TryWriteLock()
  1054. {
  1055. // Add ourselves to the queue of waiting writers
  1056. Lock_AtomicIncrement(const_cast<LONG*>(&m_cWaiting));
  1057. if (_TryWriteLock())
  1058. {
  1059. LOCK_WRITELOCK_INSTRUMENTATION();
  1060. return true;
  1061. }
  1062. Lock_AtomicDecrement(const_cast<LONG*>(&m_cWaiting));
  1063. return false;
  1064. }
  1065. inline bool TryReadLock()
  1066. {
  1067. if (_TryReadLock())
  1068. {
  1069. LOCK_READLOCK_INSTRUMENTATION();
  1070. return true;
  1071. }
  1072. return false;
  1073. }
  1074. inline void WriteUnlock()
  1075. {
  1076. Lock_AtomicExchange(const_cast<LONG*>(&m_nState), SL_FREE);
  1077. Lock_AtomicDecrement(const_cast<LONG*>(&m_cWaiting));
  1078. }
  1079. inline void ReadUnlock()
  1080. {
  1081. Lock_AtomicDecrement(const_cast<LONG*>(&m_nState));
  1082. }
  1083. bool IsWriteLocked() const {return m_nState == SL_EXCLUSIVE;}
  1084. bool IsReadLocked() const {return m_nState > SL_FREE;}
  1085. bool IsWriteUnlocked() const {return m_nState != SL_EXCLUSIVE;}
  1086. bool IsReadUnlocked() const {return m_nState <= SL_FREE;}
  1087. void ConvertSharedToExclusive()
  1088. {
  1089. IRTLASSERT(IsReadLocked());
  1090. Lock_AtomicIncrement(const_cast<LONG*>(&m_cWaiting));
  1091. // single reader?
  1092. if (m_nState == SL_FREE + 1 && _CmpExch(SL_EXCLUSIVE, SL_FREE + 1))
  1093. return;
  1094. // release the reader lock and spin
  1095. Lock_AtomicDecrement(const_cast<LONG*>(&m_nState));
  1096. _WriteLockSpin();
  1097. IRTLASSERT(IsWriteLocked());
  1098. }
  1099. void ConvertExclusiveToShared()
  1100. {
  1101. IRTLASSERT(IsWriteLocked());
  1102. Lock_AtomicExchange(const_cast<LONG*>(&m_nState), SL_FREE + 1);
  1103. Lock_AtomicDecrement(const_cast<LONG*>(&m_cWaiting));
  1104. IRTLASSERT(IsReadLocked());
  1105. }
  1106. bool SetSpinCount(WORD wSpins) {return false;}
  1107. WORD GetSpinCount() const {return sm_wDefaultSpinCount;}
  1108. LOCK_DEFAULT_SPIN_IMPLEMENTATION();
  1109. static const char* ClassName() {return "CReaderWriterLock";}
  1110. }; // CReaderWriterLock
  1111. //--------------------------------------------------------------------
  1112. // CReaderWriterlock2 is a multi-reader, single-writer spinlock due to NJain,
  1113. // which in turn is derived from an exclusive spinlock by DmitryR.
  1114. // Gives priority to writers. Cannot be acquired recursively.
  1115. // No error checking. The difference between this and CReaderWriterLock is
  1116. // that all the state is packed into a single LONG, instead of two LONGs.
  1117. class IRTL_DLLEXP CReaderWriterLock2 :
  1118. public CLockBase<LOCK_READERWRITERLOCK2, LOCK_MRSW,
  1119. LOCK_READ_RECURSIVE, LOCK_WAIT_SLEEP, LOCK_QUEUE_KERNEL,
  1120. LOCK_CLASS_SPIN
  1121. >
  1122. {
  1123. private:
  1124. volatile LONG m_lRW;
  1125. // LoWord is state. ==0 => free; >0 => readers; ==0xFFFF => 1 writer.
  1126. // HiWord is count of writers, W.
  1127. // If LoWord==0xFFFF => W-1 waiters, 1 writer;
  1128. // otherwise W waiters.
  1129. enum {
  1130. SL_FREE = 0x00000000,
  1131. SL_STATE_MASK = 0x0000FFFF,
  1132. SL_STATE_SHIFT = 0,
  1133. SL_WAITING_MASK = 0xFFFF0000, // waiting writers
  1134. SL_WAITING_SHIFT = 16,
  1135. SL_READER_INCR = 0x00000001,
  1136. SL_READER_MASK = 0x00007FFF,
  1137. SL_EXCLUSIVE = 0x0000FFFF, // one writer
  1138. SL_WRITER_INCR = 0x00010000,
  1139. SL_ONE_WRITER = SL_EXCLUSIVE | SL_WRITER_INCR,
  1140. SL_ONE_READER = (SL_FREE + 1),
  1141. SL_WRITERS_MASK = ~SL_READER_MASK,
  1142. };
  1143. LOCK_INSTRUMENTATION_DECL();
  1144. private:
  1145. void _LockSpin(bool fWrite);
  1146. void _WriteLockSpin();
  1147. void _ReadLockSpin() { _LockSpin(false); }
  1148. // _CmpExch is equivalent to
  1149. // LONG lTemp = m_lRW;
  1150. // if (lTemp == lCurrent) m_lRW = lNew;
  1151. // return lCurrent == lTemp;
  1152. // except it's one atomic instruction. Using this gives us the basis of
  1153. // a protocol because the update only succeeds when we knew exactly what
  1154. // used to be in m_lRW. If some other thread slips in and modifies m_lRW
  1155. // before we do, the update will fail. In other words, it's transactional.
  1156. LOCK_FORCEINLINE bool _CmpExch(LONG lNew, LONG lCurrent)
  1157. {
  1158. return lCurrent ==Lock_AtomicCompareExchange(const_cast<LONG*>(&m_lRW),
  1159. lNew, lCurrent);
  1160. }
  1161. LOCK_FORCEINLINE bool _TryWriteLock(
  1162. LONG nIncr)
  1163. {
  1164. LONG l = m_lRW;
  1165. // Grab exclusive access to the lock if it's free. Works even
  1166. // if there are other writers queued up.
  1167. return ((l & SL_STATE_MASK) == SL_FREE
  1168. && _CmpExch((l + nIncr) | SL_EXCLUSIVE, l));
  1169. }
  1170. LOCK_FORCEINLINE bool _TryReadLock()
  1171. {
  1172. LONG l = m_lRW;
  1173. // Give writers priority
  1174. return ((l & SL_WRITERS_MASK) == 0
  1175. && _CmpExch(l + SL_READER_INCR, l));
  1176. }
  1177. public:
  1178. CReaderWriterLock2()
  1179. : m_lRW(SL_FREE)
  1180. {}
  1181. #ifdef LOCK_INSTRUMENTATION
  1182. CReaderWriterLock2(
  1183. const char* pszName)
  1184. : m_lRW(SL_FREE)
  1185. {
  1186. LOCK_INSTRUMENTATION_INIT(pszName);
  1187. }
  1188. #endif // LOCK_INSTRUMENTATION
  1189. #ifdef _DEBUG
  1190. ~CReaderWriterLock2()
  1191. {
  1192. IRTLASSERT(m_lRW == SL_FREE);
  1193. }
  1194. #endif // _DEBUG
  1195. inline void WriteLock()
  1196. {
  1197. LOCK_WRITELOCK_INSTRUMENTATION();
  1198. // Optimize for the common case
  1199. if (_TryWriteLock(SL_WRITER_INCR))
  1200. return;
  1201. _WriteLockSpin();
  1202. }
  1203. inline void ReadLock()
  1204. {
  1205. LOCK_READLOCK_INSTRUMENTATION();
  1206. // Optimize for the common case
  1207. if (_TryReadLock())
  1208. return;
  1209. _ReadLockSpin();
  1210. }
  1211. inline bool TryWriteLock()
  1212. {
  1213. if (_TryWriteLock(SL_WRITER_INCR))
  1214. {
  1215. LOCK_WRITELOCK_INSTRUMENTATION();
  1216. return true;
  1217. }
  1218. return false;
  1219. }
  1220. inline bool TryReadLock()
  1221. {
  1222. if (_TryReadLock())
  1223. {
  1224. LOCK_READLOCK_INSTRUMENTATION();
  1225. return true;
  1226. }
  1227. return false;
  1228. }
  1229. inline void WriteUnlock()
  1230. {
  1231. IRTLASSERT(IsWriteLocked());
  1232. for (LONG l = m_lRW;
  1233. // decrement waiter count, clear loword to SL_FREE
  1234. !_CmpExch((l - SL_WRITER_INCR) & ~SL_STATE_MASK, l);
  1235. l = m_lRW)
  1236. {
  1237. IRTLASSERT(IsWriteLocked());
  1238. Lock_Yield();
  1239. }
  1240. }
  1241. inline void ReadUnlock()
  1242. {
  1243. IRTLASSERT(IsReadLocked());
  1244. for (LONG l = m_lRW; !_CmpExch(l - SL_READER_INCR, l); l = m_lRW)
  1245. {
  1246. IRTLASSERT(IsReadLocked());
  1247. Lock_Yield();
  1248. }
  1249. }
  1250. bool IsWriteLocked() const
  1251. {return (m_lRW & SL_STATE_MASK) == SL_EXCLUSIVE;}
  1252. bool IsReadLocked() const
  1253. {return (m_lRW & SL_READER_MASK) >= SL_READER_INCR ;}
  1254. bool IsWriteUnlocked() const
  1255. {return !IsWriteLocked();}
  1256. bool IsReadUnlocked() const
  1257. {return !IsReadLocked();}
  1258. void ConvertSharedToExclusive()
  1259. {
  1260. IRTLASSERT(IsReadLocked());
  1261. // single reader?
  1262. if (m_lRW != SL_ONE_READER || !_CmpExch(SL_ONE_WRITER,SL_ONE_READER))
  1263. {
  1264. // no, multiple readers
  1265. ReadUnlock();
  1266. _WriteLockSpin();
  1267. }
  1268. IRTLASSERT(IsWriteLocked());
  1269. }
  1270. void ConvertExclusiveToShared()
  1271. {
  1272. IRTLASSERT(IsWriteLocked());
  1273. for (LONG l = m_lRW;
  1274. !_CmpExch(((l-SL_WRITER_INCR) & SL_WAITING_MASK) | SL_READER_INCR,
  1275. l);
  1276. l = m_lRW)
  1277. {
  1278. IRTLASSERT(IsWriteLocked());
  1279. Lock_Yield();
  1280. }
  1281. IRTLASSERT(IsReadLocked());
  1282. }
  1283. bool SetSpinCount(WORD wSpins) {return false;}
  1284. WORD GetSpinCount() const {return sm_wDefaultSpinCount;}
  1285. LOCK_DEFAULT_SPIN_IMPLEMENTATION();
  1286. static const char* ClassName() {return "CReaderWriterLock2";}
  1287. }; // CReaderWriterLock2
  1288. //--------------------------------------------------------------------
  1289. // CReaderWriterLock3 is a multi-reader, single-writer spinlock due
  1290. // to NJain, which in turn is derived from an exclusive spinlock by DmitryR.
  1291. // Gives priority to writers. Cannot be acquired recursively.
  1292. // No error checking. Much like CReaderWriterLock2, except that the WriteLock
  1293. // can be acquired recursively.
  1294. class IRTL_DLLEXP CReaderWriterLock3 :
  1295. public CLockBase<LOCK_READERWRITERLOCK3, LOCK_MRSW,
  1296. LOCK_RECURSIVE, LOCK_WAIT_SLEEP, LOCK_QUEUE_KERNEL,
  1297. LOCK_CLASS_SPIN
  1298. >
  1299. {
  1300. private:
  1301. volatile LONG m_lRW; // Reader-Writer state
  1302. volatile LONG m_lTid; // Owning Thread ID + recursion count
  1303. // m_lRW:
  1304. // LoWord is state. =0 => free; >0 => readers; ==0xFFFF => 1 writer
  1305. // HiWord is count of writers. If LoWord==0xFFFF => N-1 waiters, 1 writer;
  1306. // otherwise N waiters.
  1307. // m_lTid:
  1308. // If readers, then 0; if a write lock, then thread id + recursion count
  1309. enum {
  1310. // m_lRW
  1311. SL_FREE = 0x00000000,
  1312. SL_STATE_MASK = 0x0000FFFF,
  1313. SL_STATE_SHIFT = 0,
  1314. SL_WAITING_MASK = 0xFFFF0000, // waiting writers
  1315. SL_WAITING_SHIFT = 16,
  1316. SL_READER_INCR = 0x00000001,
  1317. SL_READER_MASK = 0x00007FFF,
  1318. SL_EXCLUSIVE = 0x0000FFFF, // one writer
  1319. SL_WRITER_INCR = 0x00010000,
  1320. SL_ONE_WRITER = SL_EXCLUSIVE | SL_WRITER_INCR,
  1321. SL_ONE_READER = (SL_FREE + 1),
  1322. SL_WRITERS_MASK = ~SL_READER_MASK,
  1323. // m_lTid
  1324. SL_THREAD_SHIFT = 0,
  1325. SL_THREAD_BITS = 28,
  1326. SL_OWNER_SHIFT = SL_THREAD_BITS,
  1327. SL_OWNER_BITS = 4,
  1328. SL_THREAD_MASK = ((1 << SL_THREAD_BITS) - 1) << SL_THREAD_SHIFT,
  1329. SL_OWNER_INCR = 1 << SL_THREAD_BITS,
  1330. SL_OWNER_MASK = ((1 << SL_OWNER_BITS) - 1) << SL_OWNER_SHIFT,
  1331. };
  1332. LOCK_INSTRUMENTATION_DECL();
  1333. private:
  1334. void _LockSpin(bool fWrite);
  1335. void _WriteLockSpin();
  1336. void _ReadLockSpin() { _LockSpin(false); }
  1337. // _CmpExch is equivalent to
  1338. // LONG lTemp = m_lRW;
  1339. // if (lTemp == lCurrent) m_lRW = lNew;
  1340. // return lCurrent == lTemp;
  1341. // except it's one atomic instruction. Using this gives us the basis of
  1342. // a protocol because the update only succeeds when we knew exactly what
  1343. // used to be in m_lRW. If some other thread slips in and modifies m_lRW
  1344. // before we do, the update will fail. In other words, it's transactional.
  1345. LOCK_FORCEINLINE bool _CmpExch(LONG lNew, LONG lCurrent)
  1346. {
  1347. return lCurrent==Lock_AtomicCompareExchange(const_cast<LONG*>(&m_lRW),
  1348. lNew, lCurrent);
  1349. }
  1350. // Get the current thread ID. Assumes that it can fit into 28 bits,
  1351. // which is fairly safe as NT recycles thread IDs and failing to fit into
  1352. // 28 bits would mean that more than 268,435,456 threads were currently
  1353. // active. This is improbable in the extreme as NT runs out of
  1354. // resources if there are more than a few thousands threads in
  1355. // existence and the overhead of context swapping becomes unbearable.
  1356. inline static LONG _CurrentThreadId()
  1357. {
  1358. DWORD dwTid = ::GetCurrentThreadId();
  1359. // Thread ID 0 is used by the System Process (Process ID 0).
  1360. // We use a thread-id of zero to indicate lock is unowned.
  1361. // NT uses +ve thread ids, Win9x uses -ve ids
  1362. IRTLASSERT(dwTid != 0
  1363. && ((dwTid <= SL_THREAD_MASK) || (dwTid > ~SL_THREAD_MASK)));
  1364. return (LONG) (dwTid & SL_THREAD_MASK);
  1365. }
  1366. LOCK_FORCEINLINE bool _TryWriteLock(
  1367. LONG nIncr)
  1368. {
  1369. // The common case: the writelock has no owner
  1370. if (m_lTid == 0)
  1371. {
  1372. // IRTLASSERT((m_lRW & SL_STATE_MASK) != SL_EXCLUSIVE);
  1373. LONG l = m_lRW;
  1374. // Grab exclusive access to the lock if it's free. Works even
  1375. // if there are other writers queued up.
  1376. if ((l & SL_STATE_MASK) == SL_FREE
  1377. && _CmpExch((l + nIncr) | SL_EXCLUSIVE, l))
  1378. {
  1379. l = Lock_AtomicExchange(const_cast<LONG*>(&m_lTid),
  1380. _CurrentThreadId() | SL_OWNER_INCR);
  1381. IRTLASSERT(l == 0);
  1382. return true;
  1383. }
  1384. }
  1385. return _TryWriteLock2();
  1386. }
  1387. // split into a separate function to make _TryWriteLock more inlineable
  1388. bool _TryWriteLock2()
  1389. {
  1390. if ((m_lTid & SL_THREAD_MASK) == _CurrentThreadId())
  1391. {
  1392. IRTLASSERT((m_lRW & SL_STATE_MASK) == SL_EXCLUSIVE);
  1393. IRTLASSERT((m_lTid & SL_OWNER_MASK) != SL_OWNER_MASK);
  1394. Lock_AtomicExchangeAdd(const_cast<LONG*>(&m_lTid), SL_OWNER_INCR);
  1395. return true;
  1396. }
  1397. return false;
  1398. }
  1399. LOCK_FORCEINLINE bool _TryReadLock()
  1400. {
  1401. LONG l = m_lRW;
  1402. // Give writers priority
  1403. bool f = ((l & SL_WRITERS_MASK) == 0
  1404. && _CmpExch(l + SL_READER_INCR, l));
  1405. IRTLASSERT(!f || m_lTid == 0);
  1406. return f;
  1407. }
  1408. public:
  1409. CReaderWriterLock3()
  1410. : m_lRW(SL_FREE),
  1411. m_lTid(0)
  1412. {}
  1413. #ifdef LOCK_INSTRUMENTATION
  1414. CReaderWriterLock3(
  1415. const char* pszName)
  1416. : m_lRW(SL_FREE),
  1417. m_lTid(0)
  1418. {
  1419. LOCK_INSTRUMENTATION_INIT(pszName);
  1420. }
  1421. #endif // LOCK_INSTRUMENTATION
  1422. #ifdef _DEBUG
  1423. ~CReaderWriterLock3()
  1424. {
  1425. IRTLASSERT(m_lRW == SL_FREE && m_lTid == 0);
  1426. }
  1427. #endif // _DEBUG
  1428. inline void WriteLock()
  1429. {
  1430. LOCK_WRITELOCK_INSTRUMENTATION();
  1431. // Optimize for the common case
  1432. if (_TryWriteLock(SL_WRITER_INCR))
  1433. return;
  1434. _WriteLockSpin();
  1435. }
  1436. inline void ReadLock()
  1437. {
  1438. LOCK_READLOCK_INSTRUMENTATION();
  1439. // Optimize for the common case
  1440. if (_TryReadLock())
  1441. return;
  1442. _ReadLockSpin();
  1443. }
  1444. // If already locked, recursively acquires another lock of the same
  1445. // kind (read or write). Otherwise, just acquires a read lock.
  1446. // Needed for cases like this.
  1447. // pTable->WriteLock();
  1448. // if (!pTable->FindKey(&SomeKey))
  1449. // InsertRecord(&Whatever);
  1450. // pTable->WriteUnlock();
  1451. // where FindKey looks like
  1452. // Table::FindKey(pKey) {
  1453. // ReadOrWriteLock();
  1454. // // find pKey if present in table
  1455. // ReadOrWriteUnlock();
  1456. // }
  1457. // and InsertRecord looks like
  1458. // Table::InsertRecord(pRecord) {
  1459. // WriteLock();
  1460. // // insert pRecord into table
  1461. // WriteUnlock();
  1462. // }
  1463. // If FindKey called ReadLock while the thread already had done a
  1464. // WriteLock, the thread would deadlock.
  1465. inline bool ReadOrWriteLock()
  1466. {
  1467. if (IsWriteLocked())
  1468. {
  1469. WriteLock();
  1470. return false;
  1471. }
  1472. else
  1473. {
  1474. ReadLock();
  1475. return true;
  1476. }
  1477. }
  1478. inline bool TryWriteLock()
  1479. {
  1480. if (_TryWriteLock(SL_WRITER_INCR))
  1481. {
  1482. LOCK_WRITELOCK_INSTRUMENTATION();
  1483. return true;
  1484. }
  1485. return false;
  1486. }
  1487. inline bool TryReadLock()
  1488. {
  1489. if (_TryReadLock())
  1490. {
  1491. LOCK_READLOCK_INSTRUMENTATION();
  1492. return true;
  1493. }
  1494. return false;
  1495. }
  1496. inline void WriteUnlock()
  1497. {
  1498. IRTLASSERT(IsWriteLocked());
  1499. LONG lNew = m_lTid - SL_OWNER_INCR;
  1500. // Last owner? Release completely, if so
  1501. if ((lNew & SL_OWNER_MASK) == 0)
  1502. {
  1503. Lock_AtomicExchange(const_cast<LONG*>(&m_lTid), 0);
  1504. for (LONG l = m_lRW;
  1505. // decrement waiter count, clear loword to SL_FREE
  1506. !_CmpExch((l - SL_WRITER_INCR) & ~SL_STATE_MASK, l);
  1507. l = m_lRW)
  1508. {
  1509. Lock_Yield();
  1510. }
  1511. }
  1512. else
  1513. Lock_AtomicExchange(const_cast<LONG*>(&m_lTid), lNew);
  1514. }
  1515. inline void ReadUnlock()
  1516. {
  1517. IRTLASSERT(IsReadLocked());
  1518. for (LONG l = m_lRW; !_CmpExch(l - SL_READER_INCR, l); l = m_lRW)
  1519. {
  1520. IRTLASSERT(IsReadLocked());
  1521. Lock_Yield();
  1522. }
  1523. }
  1524. inline void ReadOrWriteUnlock(bool fIsReadLocked)
  1525. {
  1526. if (fIsReadLocked)
  1527. ReadUnlock();
  1528. else
  1529. WriteUnlock();
  1530. }
  1531. // Does current thread hold a write lock?
  1532. bool IsWriteLocked() const
  1533. {
  1534. // bool fLocked = ((m_lTid & SL_THREAD_MASK) == _CurrentThreadId());
  1535. bool fLocked = !((m_lTid ^ GetCurrentThreadId()) & SL_THREAD_MASK);
  1536. IRTLASSERT(!fLocked || (((m_lRW & SL_STATE_MASK) == SL_EXCLUSIVE)
  1537. && ((m_lTid & SL_OWNER_MASK) > 0)));
  1538. return fLocked;
  1539. }
  1540. bool IsReadLocked() const
  1541. {return (m_lRW & SL_READER_MASK) >= SL_READER_INCR ;}
  1542. bool IsWriteUnlocked() const
  1543. {return !IsWriteLocked();}
  1544. bool IsReadUnlocked() const
  1545. {return !IsReadLocked();}
  1546. // Note: if there's more than one reader, then there's a window where
  1547. // another thread can acquire and release a writelock before this routine
  1548. // returns.
  1549. void ConvertSharedToExclusive()
  1550. {
  1551. IRTLASSERT(IsReadLocked());
  1552. // single reader?
  1553. if (m_lRW == SL_ONE_READER && _CmpExch(SL_ONE_WRITER, SL_ONE_READER))
  1554. {
  1555. Lock_AtomicExchange(const_cast<LONG*>(&m_lTid),
  1556. _CurrentThreadId() | SL_OWNER_INCR);
  1557. }
  1558. else
  1559. {
  1560. // no, multiple readers
  1561. ReadUnlock();
  1562. _WriteLockSpin();
  1563. }
  1564. IRTLASSERT(IsWriteLocked());
  1565. }
  1566. // There is no such window when converting from a writelock to a readlock
  1567. void ConvertExclusiveToShared()
  1568. {
  1569. IRTLASSERT(IsWriteLocked());
  1570. // assume writelock is not held recursively
  1571. IRTLASSERT((m_lTid & SL_OWNER_MASK) == SL_OWNER_INCR);
  1572. Lock_AtomicExchange(const_cast<LONG*>(&m_lTid), 0);
  1573. for (LONG l = m_lRW;
  1574. !_CmpExch(((l-SL_WRITER_INCR) & SL_WAITING_MASK) | SL_READER_INCR,
  1575. l);
  1576. l = m_lRW)
  1577. {
  1578. Lock_Yield();
  1579. }
  1580. IRTLASSERT(IsReadLocked());
  1581. }
  1582. bool SetSpinCount(WORD wSpins) {return false;}
  1583. WORD GetSpinCount() const {return sm_wDefaultSpinCount;}
  1584. LOCK_DEFAULT_SPIN_IMPLEMENTATION();
  1585. static const char* ClassName() {return "CReaderWriterLock3";}
  1586. }; // CReaderWriterLock3
  1587. #ifdef __LOCKS_NAMESPACE__
  1588. }
  1589. #endif // __LOCKS_NAMESPACE__
  1590. #endif // __LOCKS_H__