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.

1955 lines
57 KiB

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