Leaked source code of windows server 2003
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.

1964 lines
60 KiB

  1. /*++
  2. Copyright (c) 1998-2002 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. #ifndef __IRTLDBG_H__
  43. # include <irtldbg.h>
  44. #endif
  45. enum LOCK_LOCKTYPE {
  46. LOCK_SMALLSPINLOCK = 1,
  47. LOCK_SPINLOCK,
  48. LOCK_FAKELOCK,
  49. LOCK_CRITSEC,
  50. LOCK_READERWRITERLOCK,
  51. LOCK_READERWRITERLOCK2,
  52. LOCK_READERWRITERLOCK3,
  53. };
  54. // Forward declarations
  55. class IRTL_DLLEXP CSmallSpinLock;
  56. class IRTL_DLLEXP CSpinLock;
  57. class IRTL_DLLEXP CFakeLock;
  58. class IRTL_DLLEXP CCritSec;
  59. class IRTL_DLLEXP CReaderWriterLock;
  60. class IRTL_DLLEXP CReaderWriterLock2;
  61. class IRTL_DLLEXP CReaderWriterLock3;
  62. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  63. // __forceinline keyword new to VC6
  64. # define LOCK_FORCEINLINE __forceinline
  65. #else
  66. # define LOCK_FORCEINLINE inline
  67. #endif
  68. #ifdef _M_IX86
  69. // The compiler will warn that the assembly language versions of the
  70. // Lock_Atomic* functions don't return a value. Actually, they do: in EAX.
  71. # pragma warning(disable: 4035)
  72. #endif
  73. // Workarounds for certain useful interlocked operations that are not
  74. // available on Windows 95. Note: the CMPXCHG and XADD instructions were
  75. // introduced in the 80486. If you still need to run on a 386 (unlikely in
  76. // 2000), you'll need to use something else.
  77. LOCK_FORCEINLINE
  78. LONG
  79. Lock_AtomicIncrement(
  80. IN OUT PLONG plAddend)
  81. {
  82. #ifdef _M_IX86
  83. __asm
  84. {
  85. mov ecx, plAddend
  86. mov eax, 1
  87. lock xadd [ecx], eax
  88. inc eax // correct result
  89. }
  90. #else
  91. return InterlockedIncrement(plAddend);
  92. #endif
  93. }
  94. LOCK_FORCEINLINE
  95. LONG
  96. Lock_AtomicDecrement(
  97. IN OUT PLONG plAddend)
  98. {
  99. #ifdef _M_IX86
  100. __asm
  101. {
  102. mov ecx, plAddend
  103. mov eax, -1
  104. lock xadd [ecx], eax
  105. dec eax // correct result
  106. }
  107. #else
  108. return InterlockedDecrement(plAddend);
  109. #endif
  110. }
  111. LOCK_FORCEINLINE
  112. LONG
  113. Lock_AtomicExchange(
  114. IN OUT PLONG plAddr,
  115. IN LONG lNew)
  116. {
  117. #ifdef _M_IX86
  118. __asm
  119. {
  120. mov ecx, plAddr
  121. mov edx, lNew
  122. mov eax, [ecx]
  123. LAEloop:
  124. lock cmpxchg [ecx], edx
  125. jnz LAEloop
  126. }
  127. #else
  128. return InterlockedExchange(plAddr, lNew);
  129. #endif
  130. }
  131. LOCK_FORCEINLINE
  132. LONG
  133. Lock_AtomicCompareExchange(
  134. IN OUT PLONG plAddr,
  135. IN LONG lNew,
  136. IN LONG lCurrent)
  137. {
  138. #ifdef _M_IX86
  139. __asm
  140. {
  141. mov ecx, plAddr
  142. mov edx, lNew
  143. mov eax, lCurrent
  144. lock cmpxchg [ecx], edx
  145. }
  146. #else
  147. return InterlockedCompareExchange(plAddr, lNew, lCurrent);
  148. #endif
  149. }
  150. LOCK_FORCEINLINE
  151. LONG
  152. Lock_AtomicExchangeAdd(
  153. IN OUT LPLONG plAddr,
  154. IN LONG lValue)
  155. {
  156. #ifdef _M_IX86
  157. __asm
  158. {
  159. mov ecx, plAddr
  160. mov eax, lValue
  161. lock xadd [ecx], eax
  162. }
  163. #else
  164. return InterlockedExchangeAdd(plAddr, lValue);
  165. #endif
  166. }
  167. #ifdef _M_IX86
  168. # pragma warning(default: 4035)
  169. // Makes tight loops a little more cache friendly and reduces power
  170. // consumption. Needed on Willamette processors.
  171. # define Lock_Yield() _asm { rep nop }
  172. #else
  173. # define Lock_Yield() ((void) 0)
  174. #endif
  175. //--------------------------------------------------------------------
  176. // Spin count values.
  177. enum LOCK_SPINS {
  178. LOCK_MAXIMUM_SPINS = 10000, // maximum allowable spin count
  179. LOCK_DEFAULT_SPINS = 4000, // default spin count
  180. LOCK_MINIMUM_SPINS = 100, // minimum allowable spin count
  181. LOCK_USE_DEFAULT_SPINS = 0xFFFF, // use class default spin count
  182. LOCK_DONT_SPIN = 0, // don't spin at all
  183. };
  184. // Boilerplate code for the per-class default spincount and spinfactor
  185. #define LOCK_DEFAULT_SPIN_IMPLEMENTATION() \
  186. protected: \
  187. /* per-class variables */ \
  188. static WORD sm_wDefaultSpinCount; /* global default spin count */ \
  189. static double sm_dblDfltSpinAdjFctr; /* global spin adjustment factor*/\
  190. \
  191. public: \
  192. /* Set the default spin count for all locks */ \
  193. static void SetDefaultSpinCount(WORD wSpins) \
  194. { \
  195. IRTLASSERT((wSpins == LOCK_DONT_SPIN) \
  196. || (wSpins == LOCK_USE_DEFAULT_SPINS) \
  197. || (LOCK_MINIMUM_SPINS <= wSpins \
  198. && wSpins <= LOCK_MAXIMUM_SPINS)); \
  199. \
  200. if ((LOCK_MINIMUM_SPINS <= wSpins && wSpins <= LOCK_MAXIMUM_SPINS)\
  201. || (wSpins == LOCK_DONT_SPIN)) \
  202. sm_wDefaultSpinCount = wSpins; \
  203. else if (wSpins == LOCK_USE_DEFAULT_SPINS) \
  204. sm_wDefaultSpinCount = LOCK_DEFAULT_SPINS; \
  205. } \
  206. \
  207. /* Return the default spin count for all locks */ \
  208. static WORD GetDefaultSpinCount() \
  209. { \
  210. return sm_wDefaultSpinCount; \
  211. } \
  212. \
  213. /* Set the adjustment factor for the spincount, used in each iteration */\
  214. /* of countdown-and-sleep by the backoff algorithm. */ \
  215. static void SetDefaultSpinAdjustmentFactor(double dblAdjFactor) \
  216. { \
  217. IRTLASSERT(0.1 <= dblAdjFactor && dblAdjFactor <= 10.0); \
  218. if (0.1 <= dblAdjFactor && dblAdjFactor <= 10.0) \
  219. sm_dblDfltSpinAdjFctr = dblAdjFactor; \
  220. } \
  221. \
  222. /* Return the default spin count for all locks */ \
  223. static double GetDefaultSpinAdjustmentFactor() \
  224. { \
  225. return sm_dblDfltSpinAdjFctr; \
  226. } \
  227. //--------------------------------------------------------------------
  228. // Various Lock Traits
  229. // Is the lock a simple mutex or a multi-reader/single-writer lock?
  230. enum LOCK_RW_MUTEX {
  231. LOCK_MUTEX = 1, // mutexes allow only one thread to hold the lock
  232. LOCK_MRSW, // multi-reader, single-writer
  233. };
  234. // Can the lock be recursively acquired?
  235. enum LOCK_RECURSION {
  236. LOCK_RECURSIVE = 1, // Write and Read locks can be recursively acquired
  237. LOCK_READ_RECURSIVE, // Read locks can be reacquired, but not Write
  238. LOCK_NON_RECURSIVE, // Will deadlock if attempt to acquire recursively
  239. };
  240. // Does the lock Sleep in a loop or block on a kernel synch object handle?
  241. // May (or may not) spin first before sleeping/blocking.
  242. enum LOCK_WAIT_TYPE {
  243. LOCK_WAIT_SLEEP = 1, // Calls Sleep() in a loop
  244. LOCK_WAIT_HANDLE, // Blocks on a kernel mutex, semaphore, or event
  245. };
  246. // When the lock is taken, how are the waiters dequeued?
  247. enum LOCK_QUEUE_TYPE {
  248. LOCK_QUEUE_FIFO = 1, // First in, first out. Fair.
  249. LOCK_QUEUE_LIFO, // Unfair but CPU cache friendly
  250. LOCK_QUEUE_KERNEL, // Determined by vagaries of scheduler
  251. };
  252. // Can the lock's spincount be set on a per-lock basis, or is it only
  253. // possible to modify the default spincount for all the locks in this class?
  254. enum LOCK_PERLOCK_SPIN {
  255. LOCK_NO_SPIN = 1, // The locks do not spin at all
  256. LOCK_CLASS_SPIN, // Can set class-wide spincount, not individual
  257. LOCK_INDIVIDUAL_SPIN, // Can set a spincount on an individual lock
  258. };
  259. //--------------------------------------------------------------------
  260. // CLockBase: bundle the above attributes
  261. template < LOCK_LOCKTYPE locktype,
  262. LOCK_RW_MUTEX mutextype,
  263. LOCK_RECURSION recursiontype,
  264. LOCK_WAIT_TYPE waittype,
  265. LOCK_QUEUE_TYPE queuetype,
  266. LOCK_PERLOCK_SPIN spintype
  267. >
  268. class CLockBase
  269. {
  270. public:
  271. static LOCK_LOCKTYPE LockType() {return locktype;}
  272. static LOCK_RW_MUTEX MutexType() {return mutextype;}
  273. static LOCK_RECURSION Recursion() {return recursiontype;}
  274. static LOCK_WAIT_TYPE WaitType() {return waittype;}
  275. static LOCK_QUEUE_TYPE QueueType() {return queuetype;}
  276. static LOCK_PERLOCK_SPIN PerLockSpin() {return spintype;}
  277. };
  278. // Lock instrumentation causes all sorts of interesting statistics about
  279. // lock contention, etc., to be gathered, but makes locks considerably fatter
  280. // and somewhat slower. Turned off by default.
  281. // #define LOCK_INSTRUMENTATION 1
  282. #ifdef LOCK_INSTRUMENTATION
  283. // We generally don't want to instrument CSmallSpinLock in addition
  284. // to CSpinLock1, as it makes a CSpinLock1 huge.
  285. // #define LOCK_SMALL_SPIN_INSTRUMENTATION 1
  286. //--------------------------------------------------------------------
  287. // CLockStatistics: statistics for an individual lock
  288. class IRTL_DLLEXP CLockStatistics
  289. {
  290. public:
  291. enum {
  292. L_NAMELEN = 8,
  293. };
  294. double m_nContentions; // #times this lock was already locked
  295. double m_nSleeps; // Total #Sleep()s needed
  296. double m_nContentionSpins; // Total iterations this lock spun
  297. double m_nAverageSpins; // Average spins each contention needed
  298. double m_nReadLocks; // Number of times lock acquired for reading
  299. double m_nWriteLocks; // Number of times lock acquired for writing
  300. TCHAR m_tszName[L_NAMELEN];// Name of this lock
  301. CLockStatistics()
  302. : m_nContentions(0),
  303. m_nSleeps(0),
  304. m_nContentionSpins(0),
  305. m_nAverageSpins(0),
  306. m_nReadLocks(0),
  307. m_nWriteLocks(0)
  308. {
  309. m_tszName[0] = _TEXT('\0');
  310. }
  311. };
  312. //--------------------------------------------------------------------
  313. // CGlobalLockStatistics: statistics for all the known locks
  314. class IRTL_DLLEXP CGlobalLockStatistics
  315. {
  316. public:
  317. LONG m_cTotalLocks; // Total number of locks created
  318. LONG m_cContendedLocks; // Total number of contended locks
  319. LONG m_nSleeps; // Total #Sleep()s needed by all locks
  320. LONGLONG m_cTotalSpins; // Total iterations all locks spun
  321. double m_nAverageSpins; // Average spins needed for each contended lock
  322. LONG m_nReadLocks; // Total ReadLocks
  323. LONG m_nWriteLocks; // Total WriteLocks
  324. CGlobalLockStatistics()
  325. : m_cTotalLocks(0),
  326. m_cContendedLocks(0),
  327. m_nSleeps(0),
  328. m_cTotalSpins(0),
  329. m_nAverageSpins(0),
  330. m_nReadLocks(0),
  331. m_nWriteLocks(0)
  332. {}
  333. };
  334. # define LOCK_INSTRUMENTATION_DECL() \
  335. private: \
  336. volatile LONG m_nContentionSpins; /* #iterations this lock spun */ \
  337. volatile WORD m_nContentions; /* #times lock was already locked */\
  338. volatile WORD m_nSleeps; /* #Sleep()s needed */ \
  339. volatile WORD m_nReadLocks; /* #ReadLocks */ \
  340. volatile WORD m_nWriteLocks; /* #WriteLocks */ \
  341. TCHAR m_tszName[CLockStatistics::L_NAMELEN]; /* Name of lock */\
  342. \
  343. static LONG sm_cTotalLocks; /* Total number of locks created */ \
  344. static LONG sm_cContendedLocks; /* Total number of contended locks */\
  345. static LONG sm_nSleeps; /* Total #Sleep()s by all locks */ \
  346. static LONGLONG sm_cTotalSpins; /* Total iterations all locks spun */\
  347. static LONG sm_nReadLocks; /* Total ReadLocks */ \
  348. static LONG sm_nWriteLocks; /* Total WriteLocks */ \
  349. \
  350. public: \
  351. const TCHAR* Name() const {return m_tszName;} \
  352. \
  353. CLockStatistics Statistics() const; \
  354. static CGlobalLockStatistics GlobalStatistics(); \
  355. static void ResetGlobalStatistics(); \
  356. private: \
  357. // Add this to constructors
  358. # define LOCK_INSTRUMENTATION_INIT(ptszName) \
  359. m_nContentionSpins = 0; \
  360. m_nContentions = 0; \
  361. m_nSleeps = 0; \
  362. m_nReadLocks = 0; \
  363. m_nWriteLocks = 0; \
  364. ++sm_cTotalLocks; \
  365. if (ptszName == NULL) \
  366. m_tszName[0] = _TEXT('\0'); \
  367. else \
  368. _tcsncpy(m_tszName, ptszName, sizeof(m_tszName)/sizeof(TCHAR))
  369. // Note: we are not using Interlocked operations for the shared
  370. // statistical counters. We'll lose perfect accuracy, but we'll
  371. // gain by reduced bus synchronization traffic.
  372. # define LOCK_READLOCK_INSTRUMENTATION() \
  373. { ++m_nReadLocks; \
  374. ++sm_nReadLocks; }
  375. # define LOCK_WRITELOCK_INSTRUMENTATION() \
  376. { ++m_nWriteLocks; \
  377. ++sm_nWriteLocks; }
  378. #else // !LOCK_INSTRUMENTATION
  379. # define LOCK_INSTRUMENTATION_DECL()
  380. # define LOCK_READLOCK_INSTRUMENTATION() ((void) 0)
  381. # define LOCK_WRITELOCK_INSTRUMENTATION() ((void) 0)
  382. #endif // !LOCK_INSTRUMENTATION
  383. //--------------------------------------------------------------------
  384. // CAutoReadLock<Lock> and CAutoWriteLock<Lock> provide exception-safe
  385. // acquisition and release of the other locks defined below
  386. template <class _Lock>
  387. class CAutoReadLock
  388. {
  389. private:
  390. bool m_fLocked;
  391. _Lock& m_Lock;
  392. public:
  393. CAutoReadLock(
  394. _Lock& rLock,
  395. bool fLockNow = true)
  396. : m_fLocked(false), m_Lock(rLock)
  397. {
  398. if (fLockNow)
  399. Lock();
  400. }
  401. ~CAutoReadLock()
  402. {
  403. Unlock();
  404. }
  405. void Lock()
  406. {
  407. // disallow recursive acquisition of the lock through this wrapper
  408. if (!m_fLocked)
  409. {
  410. m_fLocked = true;
  411. m_Lock.ReadLock();
  412. }
  413. }
  414. void Unlock()
  415. {
  416. if (m_fLocked)
  417. {
  418. m_Lock.ReadUnlock();
  419. m_fLocked = false;
  420. }
  421. }
  422. };
  423. template <class _Lock>
  424. class CAutoWriteLock
  425. {
  426. private:
  427. bool m_fLocked;
  428. _Lock& m_Lock;
  429. public:
  430. CAutoWriteLock(
  431. _Lock& rLock,
  432. bool fLockNow = true)
  433. : m_fLocked(false), m_Lock(rLock)
  434. {
  435. if (fLockNow)
  436. Lock();
  437. }
  438. ~CAutoWriteLock()
  439. {
  440. Unlock();
  441. }
  442. void Lock()
  443. {
  444. // disallow recursive acquisition of the lock through this wrapper
  445. if (!m_fLocked)
  446. {
  447. m_fLocked = true;
  448. m_Lock.WriteLock();
  449. }
  450. }
  451. void Unlock()
  452. {
  453. if (m_fLocked)
  454. {
  455. m_fLocked = false;
  456. m_Lock.WriteUnlock();
  457. }
  458. }
  459. };
  460. //--------------------------------------------------------------------
  461. // A spinlock is a sort of lightweight critical section. Its main
  462. // advantage over a true Win32 CRITICAL_SECTION is that it occupies 4 bytes
  463. // instead of 24 (+ another 32 bytes for the RTL_CRITICAL_SECTION_DEBUG data),
  464. // which is important when we have many thousands of locks
  465. // and we're trying to be L1 cache-conscious. A CRITICAL_SECTION also
  466. // contains a HANDLE to a semaphore, although this is not initialized until
  467. // the first time that the CRITICAL_SECTION blocks.
  468. //
  469. // On a multiprocessor machine, a spinlock tries to acquire the lock. If
  470. // it fails, it sits in a tight loop, testing the lock and decrementing a
  471. // counter. If the counter reaches zero, it does a Sleep(0), yielding the
  472. // processor to another thread. When control returns to the thread, the
  473. // lock is probably free. If not, the loop starts again and it is
  474. // terminated only when the lock is acquired. The theory is that it is
  475. // less costly to spin in a busy loop for a short time rather than
  476. // immediately yielding the processor, forcing an expensive context switch
  477. // that requires the old thread's state (registers, etc) be saved, the new
  478. // thread's state be reloaded, and the L1 and L2 caches be left full of
  479. // stale data.
  480. //
  481. // You can tune the spin count (global only: per-lock spin counts are
  482. // disabled) and the backoff algorithm (the factor by which the spin
  483. // count is multiplied after each Sleep).
  484. //
  485. // On a 1P machine, the loop is pointless---this thread has control,
  486. // hence no other thread can possibly release the lock while this thread
  487. // is looping---so the processor is yielded immediately.
  488. //
  489. // The kernel uses spinlocks internally and spinlocks were also added to
  490. // CRITICAL_SECTIONs in NT 4.0 sp3. In the CRITICAL_SECTION implementation,
  491. // however, the counter counts down only once and waits on a semaphore
  492. // thereafter (i.e., the same blocking behavior that it exhibits without
  493. // the spinlock).
  494. //
  495. // A disadvantage of a user-level spinlock such as this is that if the
  496. // thread that owns the spinlock blocks for any reason (or is preempted by
  497. // the scheduler), all the other threads will continue to spin on the
  498. // spinlock, wasting CPU, until the owning thread completes its wait and
  499. // releases the lock. (The kernel spinlocks, however, are smart enough to
  500. // switch to another runnable thread instead of wasting time spinning.)
  501. // The backoff algorithm decreases the spin count on each iteration in an
  502. // attempt to minimize this effect. The best policy---and this is true for
  503. // all locks---is to hold the lock for as short as time as possible.
  504. //
  505. // Note: unlike a CRITICAL_SECTION, a CSmallSpinLock cannot be recursively
  506. // acquired; i.e., if you acquire a spinlock and then attempt to acquire it
  507. // again *on the same thread* (perhaps from a different function), the
  508. // thread will hang forever. Use CSpinLock instead, which is safe though a
  509. // little slower than a CSmallSpinLock. If you own all the code
  510. // that is bracketed by Lock() and Unlock() (e.g., no callbacks or passing
  511. // back of locked data structures to callers) and know for certain that it
  512. // will not attempt to reacquire the lock, you can use CSmallSpinLock.
  513. //
  514. // See also http://muralik/work/performance/spinlocks.htm and John Vert's
  515. // MSDN article, "Writing Scalable Applications for Windows NT".
  516. //
  517. // The original implementation is due to PALarson.
  518. class IRTL_DLLEXP CSmallSpinLock :
  519. public CLockBase<LOCK_SMALLSPINLOCK, LOCK_MUTEX,
  520. LOCK_NON_RECURSIVE, LOCK_WAIT_SLEEP, LOCK_QUEUE_KERNEL,
  521. LOCK_CLASS_SPIN
  522. >
  523. {
  524. private:
  525. volatile LONG m_lTid; // The lock state variable
  526. #ifdef LOCK_SMALL_SPIN_INSTRUMENTATION
  527. LOCK_INSTRUMENTATION_DECL();
  528. #endif // LOCK_SMALL_SPIN_INSTRUMENTATION
  529. LOCK_FORCEINLINE static LONG _CurrentThreadId()
  530. {
  531. DWORD dwTid = ::GetCurrentThreadId();
  532. return (LONG) (dwTid);
  533. }
  534. private:
  535. // Does all the spinning (and instrumentation) if the lock is contended.
  536. void _LockSpin();
  537. LOCK_FORCEINLINE bool _TryLock()
  538. {
  539. if (m_lTid == 0)
  540. {
  541. LONG l = _CurrentThreadId();
  542. return (Lock_AtomicCompareExchange(const_cast<LONG*>(&m_lTid), l,0)
  543. == 0);
  544. }
  545. else
  546. return false;
  547. }
  548. public:
  549. #ifndef LOCK_SMALL_SPIN_INSTRUMENTATION
  550. CSmallSpinLock()
  551. : m_lTid(0)
  552. {}
  553. #else // LOCK_SMALL_SPIN_INSTRUMENTATION
  554. CSmallSpinLock(
  555. const TCHAR* ptszName)
  556. : m_lTid(0)
  557. {
  558. LOCK_INSTRUMENTATION_INIT(ptszName);
  559. }
  560. #endif // LOCK_SMALL_SPIN_INSTRUMENTATION
  561. #ifdef IRTLDEBUG
  562. ~CSmallSpinLock()
  563. {
  564. IRTLASSERT(m_lTid == 0);
  565. }
  566. #endif // IRTLDEBUG
  567. // Acquire an exclusive lock for writing. Blocks until acquired.
  568. inline void WriteLock()
  569. {
  570. #ifdef LOCK_SMALL_SPIN_INSTRUMENTATION
  571. LOCK_WRITELOCK_INSTRUMENTATION();
  572. #endif // LOCK_SMALL_SPIN_INSTRUMENTATION
  573. // Optimize for the common case by helping the processor's branch
  574. // prediction algorithm.
  575. if (_TryLock())
  576. return;
  577. _LockSpin();
  578. }
  579. // Acquire a (possibly shared) lock for reading. Blocks until acquired.
  580. inline void ReadLock()
  581. {
  582. #ifdef LOCK_SMALL_SPIN_INSTRUMENTATION
  583. LOCK_READLOCK_INSTRUMENTATION();
  584. #endif // LOCK_SMALL_SPIN_INSTRUMENTATION
  585. if (_TryLock())
  586. return;
  587. _LockSpin();
  588. }
  589. // Try to acquire an exclusive lock for writing. Returns true
  590. // if successful. Non-blocking.
  591. inline bool TryWriteLock()
  592. {
  593. bool fAcquired = _TryLock();
  594. #ifdef LOCK_SMALL_SPIN_INSTRUMENTATION
  595. if (fAcquired)
  596. LOCK_WRITELOCK_INSTRUMENTATION();
  597. #endif // LOCK_SMALL_SPIN_INSTRUMENTATION
  598. return fAcquired;
  599. }
  600. // Try to acquire a (possibly shared) lock for reading. Returns true
  601. // if successful. Non-blocking.
  602. inline bool TryReadLock()
  603. {
  604. bool fAcquired = _TryLock();
  605. #ifdef LOCK_SMALL_SPIN_INSTRUMENTATION
  606. if (fAcquired)
  607. LOCK_READLOCK_INSTRUMENTATION();
  608. #endif // LOCK_SMALL_SPIN_INSTRUMENTATION
  609. return fAcquired;
  610. }
  611. // Unlock the lock after a successful call to {,Try}WriteLock().
  612. // Assumes caller owned the lock.
  613. inline void WriteUnlock()
  614. {
  615. Lock_AtomicExchange(const_cast<LONG*>(&m_lTid), 0);
  616. }
  617. // Unlock the lock after a successful call to {,Try}ReadLock().
  618. // Assumes caller owned the lock.
  619. inline void ReadUnlock()
  620. {
  621. WriteUnlock();
  622. }
  623. // Is the lock already locked for writing by this thread?
  624. bool IsWriteLocked() const
  625. {
  626. return (m_lTid == _CurrentThreadId());
  627. }
  628. // Is the lock already locked for reading?
  629. bool IsReadLocked() const
  630. {
  631. return IsWriteLocked();
  632. }
  633. // Is the lock unlocked for writing?
  634. bool IsWriteUnlocked() const
  635. {
  636. return (m_lTid == 0);
  637. }
  638. // Is the lock unlocked for reading?
  639. bool IsReadUnlocked() const
  640. {
  641. return IsWriteUnlocked();
  642. }
  643. // Convert a reader lock to a writer lock
  644. void ConvertSharedToExclusive()
  645. {
  646. // no-op
  647. }
  648. // Convert a writer lock to a reader lock
  649. void ConvertExclusiveToShared()
  650. {
  651. // no-op
  652. }
  653. // Set the spin count for this lock.
  654. // Returns true if successfully set the per-lock spincount, false otherwise
  655. bool SetSpinCount(WORD wSpins)
  656. {
  657. UNREFERENCED_PARAMETER(wSpins);
  658. IRTLASSERT((wSpins == LOCK_DONT_SPIN)
  659. || (wSpins == LOCK_USE_DEFAULT_SPINS)
  660. || (LOCK_MINIMUM_SPINS <= wSpins
  661. && wSpins <= LOCK_MAXIMUM_SPINS));
  662. return false;
  663. }
  664. // Return the spin count for this lock.
  665. WORD GetSpinCount() const
  666. {
  667. return sm_wDefaultSpinCount;
  668. }
  669. LOCK_DEFAULT_SPIN_IMPLEMENTATION();
  670. static const TCHAR* ClassName() {return _TEXT("CSmallSpinLock");}
  671. }; // CSmallSpinLock
  672. //--------------------------------------------------------------------
  673. // CSpinLock is a spinlock that doesn't deadlock if recursively acquired.
  674. // This version occupies only 4 bytes. Uses 28 bits for the thread id.
  675. class IRTL_DLLEXP CSpinLock :
  676. public CLockBase<LOCK_SPINLOCK, LOCK_MUTEX,
  677. LOCK_RECURSIVE, LOCK_WAIT_SLEEP, LOCK_QUEUE_KERNEL,
  678. LOCK_CLASS_SPIN
  679. >
  680. {
  681. private:
  682. // a union for convenience
  683. volatile LONG m_lTid;
  684. enum {
  685. THREAD_SHIFT = 0,
  686. THREAD_BITS = 28,
  687. OWNER_SHIFT = THREAD_BITS,
  688. OWNER_BITS = 4,
  689. THREAD_MASK = ((1 << THREAD_BITS) - 1) << THREAD_SHIFT,
  690. OWNER_INCR = 1 << THREAD_BITS,
  691. OWNER_MASK = ((1 << OWNER_BITS) - 1) << OWNER_SHIFT,
  692. };
  693. LOCK_INSTRUMENTATION_DECL();
  694. private:
  695. // Get the current thread ID. Assumes that it can fit into 28 bits,
  696. // which is fairly safe as NT recycles thread IDs and failing to fit into
  697. // 28 bits would mean that more than 268,435,456 threads were currently
  698. // active. This is improbable in the extreme as NT runs out of
  699. // resources if there are more than a few thousands threads in
  700. // existence and the overhead of context swapping becomes unbearable.
  701. LOCK_FORCEINLINE static LONG _CurrentThreadId()
  702. {
  703. DWORD dwTid = ::GetCurrentThreadId();
  704. // Thread ID 0 is used by the System Idle Process (Process ID 0).
  705. // We use a thread-id of zero to indicate that the lock is unowned.
  706. // NT uses +ve thread ids, Win9x uses -ve ids
  707. IRTLASSERT(dwTid != 0
  708. && ((dwTid <= THREAD_MASK) || (dwTid > ~THREAD_MASK)));
  709. return (LONG) (dwTid & THREAD_MASK);
  710. }
  711. // Attempt to acquire the lock without blocking
  712. LOCK_FORCEINLINE bool _TryLock()
  713. {
  714. if (m_lTid == 0)
  715. {
  716. LONG l = _CurrentThreadId() | OWNER_INCR;
  717. return (Lock_AtomicCompareExchange(const_cast<LONG*>(&m_lTid), l,0)
  718. == 0);
  719. }
  720. else
  721. return false;
  722. }
  723. // Acquire the lock, recursively if need be
  724. void _Lock()
  725. {
  726. // Do we own the lock already? Just bump the count.
  727. if ((m_lTid & THREAD_MASK) == _CurrentThreadId())
  728. {
  729. // owner count isn't maxed out?
  730. IRTLASSERT((m_lTid & OWNER_MASK) != OWNER_MASK);
  731. Lock_AtomicExchange(const_cast<LONG*>(&m_lTid),
  732. 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 TCHAR* ptszName)
  769. : m_lTid(0)
  770. {
  771. LOCK_INSTRUMENTATION_INIT(ptszName);
  772. }
  773. #endif // LOCK_INSTRUMENTATION
  774. #ifdef IRTLDEBUG
  775. ~CSpinLock()
  776. {
  777. IRTLASSERT(m_lTid == 0);
  778. }
  779. #endif // IRTLDEBUG
  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) {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 TCHAR* ClassName() {return _TEXT("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) {return false;}
  908. WORD GetSpinCount() const {return LOCK_DONT_SPIN;}
  909. LOCK_DEFAULT_SPIN_IMPLEMENTATION();
  910. static const TCHAR* ClassName() {return _TEXT("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 TCHAR* ClassName() {return _TEXT("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 TCHAR* ptszName)
  1025. : m_nState(SL_FREE),
  1026. m_cWaiting(0)
  1027. {
  1028. LOCK_INSTRUMENTATION_INIT(ptszName);
  1029. }
  1030. #endif // LOCK_INSTRUMENTATION
  1031. #ifdef IRTLDEBUG
  1032. ~CReaderWriterLock()
  1033. {
  1034. IRTLASSERT(m_nState == SL_FREE && m_cWaiting == 0);
  1035. }
  1036. #endif // IRTLDEBUG
  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) {return false;}
  1107. WORD GetSpinCount() const {return sm_wDefaultSpinCount;}
  1108. LOCK_DEFAULT_SPIN_IMPLEMENTATION();
  1109. static const TCHAR* ClassName() {return _TEXT("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 TCHAR* ptszName)
  1184. : m_lRW(SL_FREE)
  1185. {
  1186. LOCK_INSTRUMENTATION_INIT(ptszName);
  1187. }
  1188. #endif // LOCK_INSTRUMENTATION
  1189. #ifdef IRTLDEBUG
  1190. ~CReaderWriterLock2()
  1191. {
  1192. IRTLASSERT(m_lRW == SL_FREE);
  1193. }
  1194. #endif // IRTLDEBUG
  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) {return false;}
  1284. WORD GetSpinCount() const {return sm_wDefaultSpinCount;}
  1285. LOCK_DEFAULT_SPIN_IMPLEMENTATION();
  1286. static const TCHAR* ClassName() {return _TEXT("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. enum SPIN_TYPE {
  1335. SPIN_WRITE = 1,
  1336. SPIN_READ,
  1337. SPIN_READ_RECURSIVE,
  1338. };
  1339. void _LockSpin(SPIN_TYPE st);
  1340. void _WriteLockSpin();
  1341. void _ReadLockSpin(SPIN_TYPE st) { _LockSpin(st); }
  1342. // _CmpExch is equivalent to
  1343. // LONG lTemp = m_lRW;
  1344. // if (lTemp == lCurrent) m_lRW = lNew;
  1345. // return lCurrent == lTemp;
  1346. // except it's one atomic instruction. Using this gives us the basis of
  1347. // a protocol because the update only succeeds when we knew exactly what
  1348. // used to be in m_lRW. If some other thread slips in and modifies m_lRW
  1349. // before we do, the update will fail. In other words, it's transactional.
  1350. LOCK_FORCEINLINE bool _CmpExch(LONG lNew, LONG lCurrent)
  1351. {
  1352. return lCurrent==Lock_AtomicCompareExchange(const_cast<LONG*>(&m_lRW),
  1353. lNew, lCurrent);
  1354. }
  1355. // Get the current thread ID. Assumes that it can fit into 28 bits,
  1356. // which is fairly safe as NT recycles thread IDs and failing to fit into
  1357. // 28 bits would mean that more than 268,435,456 threads were currently
  1358. // active. This is improbable in the extreme as NT runs out of
  1359. // resources if there are more than a few thousands threads in
  1360. // existence and the overhead of context swapping becomes unbearable.
  1361. inline static LONG _CurrentThreadId()
  1362. {
  1363. DWORD dwTid = ::GetCurrentThreadId();
  1364. // Thread ID 0 is used by the System Idle Process (Process ID 0).
  1365. // We use a thread-id of zero to indicate lock is unowned.
  1366. // NT uses +ve thread ids, Win9x uses -ve ids
  1367. IRTLASSERT(dwTid != 0
  1368. && ((dwTid <= SL_THREAD_MASK) || (dwTid > ~SL_THREAD_MASK)));
  1369. return (LONG) (dwTid & SL_THREAD_MASK);
  1370. }
  1371. LOCK_FORCEINLINE bool _TryWriteLock(
  1372. LONG nIncr)
  1373. {
  1374. // The common case: the writelock has no owner
  1375. if (m_lTid == 0)
  1376. {
  1377. // IRTLASSERT((m_lRW & SL_STATE_MASK) != SL_EXCLUSIVE);
  1378. LONG l = m_lRW;
  1379. // Grab exclusive access to the lock if it's free. Works even
  1380. // if there are other writers queued up.
  1381. if ((l & SL_STATE_MASK) == SL_FREE
  1382. && _CmpExch((l + nIncr) | SL_EXCLUSIVE, l))
  1383. {
  1384. l = Lock_AtomicExchange(const_cast<LONG*>(&m_lTid),
  1385. _CurrentThreadId() | SL_OWNER_INCR);
  1386. IRTLASSERT(l == 0);
  1387. return true;
  1388. }
  1389. }
  1390. return _TryWriteLock2();
  1391. }
  1392. // split into a separate function to make _TryWriteLock more inlineable
  1393. bool _TryWriteLock2()
  1394. {
  1395. if ((m_lTid & SL_THREAD_MASK) == _CurrentThreadId())
  1396. {
  1397. IRTLASSERT((m_lRW & SL_STATE_MASK) == SL_EXCLUSIVE);
  1398. IRTLASSERT((m_lTid & SL_OWNER_MASK) != SL_OWNER_MASK);
  1399. Lock_AtomicExchange(const_cast<LONG*>(&m_lTid),
  1400. m_lTid + SL_OWNER_INCR);
  1401. return true;
  1402. }
  1403. return false;
  1404. }
  1405. LOCK_FORCEINLINE bool _TryReadLock()
  1406. {
  1407. // Give writers priority
  1408. LONG l = m_lRW;
  1409. bool fLocked = (((l & SL_WRITERS_MASK) == 0)
  1410. && _CmpExch(l + SL_READER_INCR, l));
  1411. IRTLASSERT(!fLocked || m_lTid == 0);
  1412. return fLocked;
  1413. }
  1414. LOCK_FORCEINLINE bool _TryReadLockRecursive()
  1415. {
  1416. // Do *not* give writers priority. If the inner call attempts
  1417. // to reacquire the read lock while another thread is waiting on
  1418. // the write lock, we would deadlock if we waited for the queue
  1419. // of writers to empty: the writer(s) can't acquire the lock
  1420. // exclusively, as this thread holds a readlock. The inner call
  1421. // typically releases the lock very quickly, so there is no
  1422. // danger of writer starvation.
  1423. LONG l = m_lRW;
  1424. bool fLocked = (((l & SL_STATE_MASK) != SL_EXCLUSIVE)
  1425. && _CmpExch(l + SL_READER_INCR, l));
  1426. IRTLASSERT(!fLocked || m_lTid == 0);
  1427. return fLocked;
  1428. }
  1429. public:
  1430. CReaderWriterLock3()
  1431. : m_lRW(SL_FREE),
  1432. m_lTid(0)
  1433. {}
  1434. #ifdef LOCK_INSTRUMENTATION
  1435. CReaderWriterLock3(
  1436. const TCHAR* ptszName)
  1437. : m_lRW(SL_FREE),
  1438. m_lTid(0)
  1439. {
  1440. LOCK_INSTRUMENTATION_INIT(ptszName);
  1441. }
  1442. #endif // LOCK_INSTRUMENTATION
  1443. #ifdef IRTLDEBUG
  1444. ~CReaderWriterLock3()
  1445. {
  1446. IRTLASSERT(m_lRW == SL_FREE && m_lTid == 0);
  1447. }
  1448. #endif // IRTLDEBUG
  1449. inline void WriteLock()
  1450. {
  1451. LOCK_WRITELOCK_INSTRUMENTATION();
  1452. // Optimize for the common case
  1453. if (_TryWriteLock(SL_WRITER_INCR))
  1454. return;
  1455. _WriteLockSpin();
  1456. }
  1457. inline void ReadLock()
  1458. {
  1459. LOCK_READLOCK_INSTRUMENTATION();
  1460. // Optimize for the common case
  1461. if (_TryReadLock())
  1462. return;
  1463. _ReadLockSpin(SPIN_READ);
  1464. }
  1465. // If already locked, recursively acquires another lock of the same
  1466. // kind (read or write). Otherwise, just acquires a read lock.
  1467. // Needed for cases like this.
  1468. // pTable->WriteLock();
  1469. // if (!pTable->FindKey(&SomeKey))
  1470. // InsertRecord(&Whatever);
  1471. // pTable->WriteUnlock();
  1472. // where FindKey looks like
  1473. // Table::FindKey(pKey) {
  1474. // ReadOrWriteLock();
  1475. // // find pKey if present in table
  1476. // ReadOrWriteUnlock();
  1477. // }
  1478. // and InsertRecord looks like
  1479. // Table::InsertRecord(pRecord) {
  1480. // WriteLock();
  1481. // // insert pRecord into table
  1482. // WriteUnlock();
  1483. // }
  1484. // If FindKey called ReadLock while the thread already had done a
  1485. // WriteLock, the thread would deadlock.
  1486. inline bool ReadOrWriteLock()
  1487. {
  1488. if (IsWriteLocked())
  1489. {
  1490. WriteLock();
  1491. return false; // => not read locked
  1492. }
  1493. else
  1494. {
  1495. LOCK_READLOCK_INSTRUMENTATION();
  1496. if (!_TryReadLockRecursive())
  1497. _ReadLockSpin(SPIN_READ_RECURSIVE);
  1498. return true; // => is read locked
  1499. }
  1500. }
  1501. inline bool TryWriteLock()
  1502. {
  1503. if (_TryWriteLock(SL_WRITER_INCR))
  1504. {
  1505. LOCK_WRITELOCK_INSTRUMENTATION();
  1506. return true;
  1507. }
  1508. return false;
  1509. }
  1510. inline bool TryReadLock()
  1511. {
  1512. if (_TryReadLock())
  1513. {
  1514. LOCK_READLOCK_INSTRUMENTATION();
  1515. return true;
  1516. }
  1517. return false;
  1518. }
  1519. inline void WriteUnlock()
  1520. {
  1521. IRTLASSERT(IsWriteLocked());
  1522. LONG lNew = m_lTid - SL_OWNER_INCR;
  1523. // Last owner? Release completely, if so
  1524. if ((lNew & SL_OWNER_MASK) == 0)
  1525. lNew = 0;
  1526. Lock_AtomicExchange(const_cast<LONG*>(&m_lTid), lNew);
  1527. if (lNew == 0)
  1528. {
  1529. LONG l;
  1530. do
  1531. {
  1532. Lock_Yield();
  1533. l = m_lRW;
  1534. // decrement waiter count, clear loword to SL_FREE
  1535. }
  1536. while (!_CmpExch((l - SL_WRITER_INCR) & ~SL_STATE_MASK, l));
  1537. }
  1538. }
  1539. inline void ReadUnlock()
  1540. {
  1541. IRTLASSERT(IsReadLocked());
  1542. for (LONG l = m_lRW; !_CmpExch(l - SL_READER_INCR, l); l = m_lRW)
  1543. {
  1544. IRTLASSERT(IsReadLocked());
  1545. Lock_Yield();
  1546. }
  1547. }
  1548. inline void ReadOrWriteUnlock(bool fIsReadLocked)
  1549. {
  1550. if (fIsReadLocked)
  1551. ReadUnlock();
  1552. else
  1553. WriteUnlock();
  1554. }
  1555. // Does current thread hold a write lock?
  1556. bool IsWriteLocked() const
  1557. {
  1558. // bool fLocked = ((m_lTid & SL_THREAD_MASK) == _CurrentThreadId());
  1559. bool fLocked = ((m_lTid ^ GetCurrentThreadId()) & SL_THREAD_MASK) == 0;
  1560. IRTLASSERT(!fLocked || (((m_lRW & SL_STATE_MASK) == SL_EXCLUSIVE)
  1561. && ((m_lTid & SL_OWNER_MASK) > 0)));
  1562. return fLocked;
  1563. }
  1564. bool IsReadLocked() const
  1565. {return (m_lRW & SL_READER_MASK) >= SL_READER_INCR ;}
  1566. bool IsWriteUnlocked() const
  1567. {return !IsWriteLocked();}
  1568. bool IsReadUnlocked() const
  1569. {return !IsReadLocked();}
  1570. // Note: if there's more than one reader, then there's a window where
  1571. // another thread can acquire and release a writelock before this routine
  1572. // returns.
  1573. void ConvertSharedToExclusive()
  1574. {
  1575. IRTLASSERT(IsReadLocked());
  1576. // single reader?
  1577. if (m_lRW == SL_ONE_READER && _CmpExch(SL_ONE_WRITER, SL_ONE_READER))
  1578. {
  1579. Lock_AtomicExchange(const_cast<LONG*>(&m_lTid),
  1580. _CurrentThreadId() | SL_OWNER_INCR);
  1581. }
  1582. else
  1583. {
  1584. // no, multiple readers
  1585. ReadUnlock();
  1586. _WriteLockSpin();
  1587. }
  1588. IRTLASSERT(IsWriteLocked());
  1589. }
  1590. // There is no such window when converting from a writelock to a readlock
  1591. void ConvertExclusiveToShared()
  1592. {
  1593. IRTLASSERT(IsWriteLocked());
  1594. // assume writelock is not held recursively
  1595. IRTLASSERT((m_lTid & SL_OWNER_MASK) == SL_OWNER_INCR);
  1596. Lock_AtomicExchange(const_cast<LONG*>(&m_lTid), 0);
  1597. for (LONG l = m_lRW;
  1598. !_CmpExch(((l-SL_WRITER_INCR) & SL_WAITING_MASK) | SL_READER_INCR,
  1599. l);
  1600. l = m_lRW)
  1601. {
  1602. Lock_Yield();
  1603. }
  1604. IRTLASSERT(IsReadLocked());
  1605. }
  1606. bool SetSpinCount(WORD) {return false;}
  1607. WORD GetSpinCount() const {return sm_wDefaultSpinCount;}
  1608. LOCK_DEFAULT_SPIN_IMPLEMENTATION();
  1609. static const TCHAR* ClassName() {return _TEXT("CReaderWriterLock3");}
  1610. }; // CReaderWriterLock3
  1611. #endif // __LOCKS_H__