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.

704 lines
16 KiB

  1. /*++
  2. Copyright (c) 1997-2002 Microsoft Corporation
  3. Module Name :
  4. kLocks.h
  5. Abstract:
  6. A collection of kernel-mode locks for multithreaded access to
  7. data structures that provide the same abstractions as <locks.h>
  8. Author:
  9. George V. Reilly (GeorgeRe) 24-Oct-2000
  10. Environment:
  11. Win32 - Kernel Mode
  12. Project:
  13. LKRhash
  14. Revision History:
  15. --*/
  16. #ifndef __KLOCKS_H__
  17. #define __KLOCKS_H__
  18. #define LOCKS_KERNEL_MODE
  19. #ifdef __LOCKS_H__
  20. # error Do not #include <locks.h> before <klocks.h>
  21. #endif
  22. #include <locks.h>
  23. //--------------------------------------------------------------------
  24. // CKSpinLock is a mutex lock based on KSPIN_LOCK
  25. class IRTL_DLLEXP CKSpinLock :
  26. public CLockBase<LOCK_KSPINLOCK, LOCK_MUTEX,
  27. LOCK_NON_RECURSIVE, LOCK_WAIT_SPIN, LOCK_QUEUE_KERNEL,
  28. LOCK_CLASS_SPIN
  29. >
  30. {
  31. private:
  32. // BUGBUG: this data must live in non-paged memory
  33. mutable KSPIN_LOCK KSpinLock;
  34. volatile KIRQL m_OldIrql;
  35. LOCK_INSTRUMENTATION_DECL();
  36. public:
  37. #ifndef LOCK_INSTRUMENTATION
  38. CKSpinLock()
  39. {
  40. KeInitializeSpinLock(&KSpinLock);
  41. m_OldIrql = PASSIVE_LEVEL;
  42. }
  43. #else // LOCK_INSTRUMENTATION
  44. CKSpinLock(
  45. const TCHAR* ptszName)
  46. {
  47. KeInitializeSpinLock(&KSpinLock);
  48. m_OldIrql = PASSIVE_LEVEL;
  49. LOCK_INSTRUMENTATION_INIT(ptszName);
  50. }
  51. #endif // LOCK_INSTRUMENTATION
  52. #ifdef IRTLDEBUG
  53. ~CKSpinLock() {}
  54. #endif // IRTLDEBUG
  55. // Acquire an exclusive lock for writing.
  56. // Blocks (if needed) until acquired.
  57. void WriteLock()
  58. {
  59. KIRQL OldIrql;
  60. KeAcquireSpinLock(&KSpinLock, &OldIrql);
  61. // Now that we've acquired the spinlock, copy the stack variable
  62. // into the member variable. The member variable is only written
  63. // or read by the owner of the spinlock.
  64. m_OldIrql = OldIrql;
  65. }
  66. // Acquire a (possibly shared) lock for reading.
  67. // Blocks (if needed) until acquired.
  68. void ReadLock()
  69. {
  70. WriteLock();
  71. }
  72. // Try to acquire an exclusive lock for writing. Returns true
  73. // if successful. Non-blocking.
  74. bool TryWriteLock()
  75. {
  76. return false;
  77. }
  78. // Try to acquire a (possibly shared) lock for reading. Returns true
  79. // if successful. Non-blocking.
  80. bool TryReadLock()
  81. {
  82. return TryWriteLock();
  83. }
  84. // Unlock the lock after a successful call to {,Try}WriteLock().
  85. // Assumes caller owned the lock.
  86. void WriteUnlock()
  87. {
  88. KeReleaseSpinLock(&KSpinLock, m_OldIrql);
  89. }
  90. // Unlock the lock after a successful call to {,Try}ReadLock().
  91. // Assumes caller owned the lock.
  92. void ReadUnlock()
  93. {
  94. WriteUnlock();
  95. }
  96. // Is the lock already locked for writing by this thread?
  97. bool IsWriteLocked() const
  98. {
  99. // A KSPIN_LOCK doesn't keep track of its owning thread/processor.
  100. // It's either 0 (unlocked) or 1 (locked). We could keep track of
  101. // the owner in an additional member variable, but currently we don't.
  102. return false;
  103. }
  104. // Is the lock already locked for reading?
  105. bool IsReadLocked() const
  106. {
  107. return IsWriteLocked();
  108. }
  109. // Is the lock unlocked for writing?
  110. bool IsWriteUnlocked() const
  111. {
  112. return false;
  113. }
  114. // Is the lock unlocked for reading?
  115. bool IsReadUnlocked() const
  116. {
  117. return IsWriteUnlocked();
  118. }
  119. // Convert a reader lock to a writer lock
  120. void ConvertSharedToExclusive()
  121. {
  122. // no-op
  123. }
  124. // Convert a writer lock to a reader lock
  125. void ConvertExclusiveToShared()
  126. {
  127. // no-op
  128. }
  129. #ifdef LOCK_DEFAULT_SPIN_IMPLEMENTATION
  130. // Set the spin count for this lock.
  131. // Returns true if successfully set the per-lock spincount, false otherwise
  132. bool SetSpinCount(WORD wSpins)
  133. {
  134. IRTLASSERT((wSpins == LOCK_DONT_SPIN)
  135. || (wSpins == LOCK_USE_DEFAULT_SPINS)
  136. || (LOCK_MINIMUM_SPINS <= wSpins
  137. && wSpins <= LOCK_MAXIMUM_SPINS));
  138. return false;
  139. }
  140. // Return the spin count for this lock.
  141. WORD GetSpinCount() const
  142. {
  143. return sm_wDefaultSpinCount;
  144. }
  145. LOCK_DEFAULT_SPIN_IMPLEMENTATION();
  146. #endif // LOCK_DEFAULT_SPIN_IMPLEMENTATION
  147. static const TCHAR* ClassName() {return _TEXT("CKSpinLock");}
  148. }; // CKSpinLock
  149. //--------------------------------------------------------------------
  150. // CFastMutex is a mutex lock based on FAST_MUTEX
  151. class IRTL_DLLEXP CFastMutex :
  152. public CLockBase<LOCK_FASTMUTEX, LOCK_MUTEX,
  153. LOCK_NON_RECURSIVE, LOCK_WAIT_HANDLE, LOCK_QUEUE_KERNEL,
  154. LOCK_CLASS_SPIN
  155. >
  156. {
  157. private:
  158. mutable FAST_MUTEX FastMutex;
  159. LOCK_INSTRUMENTATION_DECL();
  160. public:
  161. #ifndef LOCK_INSTRUMENTATION
  162. CFastMutex()
  163. {
  164. ExInitializeFastMutex(&FastMutex);
  165. }
  166. #else // LOCK_INSTRUMENTATION
  167. CFastMutex(
  168. const TCHAR* ptszName)
  169. {
  170. ExInitializeFastMutex(&FastMutex);
  171. LOCK_INSTRUMENTATION_INIT(ptszName);
  172. }
  173. #endif // LOCK_INSTRUMENTATION
  174. #ifdef IRTLDEBUG
  175. ~CFastMutex() {}
  176. #endif // IRTLDEBUG
  177. // Acquire an exclusive lock for writing.
  178. // Blocks (if needed) until acquired.
  179. void WriteLock()
  180. {
  181. ExAcquireFastMutex(&FastMutex);
  182. }
  183. // Acquire a (possibly shared) lock for reading.
  184. // Blocks (if needed) until acquired.
  185. void ReadLock()
  186. {
  187. WriteLock();
  188. }
  189. // Try to acquire an exclusive lock for writing. Returns true
  190. // if successful. Non-blocking.
  191. bool TryWriteLock()
  192. {
  193. return ExTryToAcquireFastMutex(&FastMutex) != FALSE;
  194. }
  195. // Try to acquire a (possibly shared) lock for reading. Returns true
  196. // if successful. Non-blocking.
  197. bool TryReadLock()
  198. {
  199. return TryWriteLock();
  200. }
  201. // Unlock the lock after a successful call to {,Try}WriteLock().
  202. // Assumes caller owned the lock.
  203. void WriteUnlock()
  204. {
  205. ExReleaseFastMutex(&FastMutex);
  206. }
  207. // Unlock the lock after a successful call to {,Try}ReadLock().
  208. // Assumes caller owned the lock.
  209. void ReadUnlock()
  210. {
  211. WriteUnlock();
  212. }
  213. // Is the lock already locked for writing by this thread?
  214. bool IsWriteLocked() const
  215. {
  216. return false; // no way of determining this w/o auxiliary info
  217. }
  218. // Is the lock already locked for reading?
  219. bool IsReadLocked() const
  220. {
  221. return IsWriteLocked();
  222. }
  223. // Is the lock unlocked for writing?
  224. bool IsWriteUnlocked() const
  225. {
  226. return !IsWriteLocked();
  227. }
  228. // Is the lock unlocked for reading?
  229. bool IsReadUnlocked() const
  230. {
  231. return IsWriteUnlocked();
  232. }
  233. // Convert a reader lock to a writer lock
  234. void ConvertSharedToExclusive()
  235. {
  236. // no-op
  237. }
  238. // Convert a writer lock to a reader lock
  239. void ConvertExclusiveToShared()
  240. {
  241. // no-op
  242. }
  243. #ifdef LOCK_DEFAULT_SPIN_IMPLEMENTATION
  244. // Set the spin count for this lock.
  245. // Returns true if successfully set the per-lock spincount, false otherwise
  246. bool SetSpinCount(WORD wSpins)
  247. {
  248. IRTLASSERT((wSpins == LOCK_DONT_SPIN)
  249. || (wSpins == LOCK_USE_DEFAULT_SPINS)
  250. || (LOCK_MINIMUM_SPINS <= wSpins
  251. && wSpins <= LOCK_MAXIMUM_SPINS));
  252. return false;
  253. }
  254. // Return the spin count for this lock.
  255. WORD GetSpinCount() const
  256. {
  257. return sm_wDefaultSpinCount;
  258. }
  259. LOCK_DEFAULT_SPIN_IMPLEMENTATION();
  260. #endif // LOCK_DEFAULT_SPIN_IMPLEMENTATION
  261. static const TCHAR* ClassName() {return _TEXT("CFastMutex");}
  262. }; // CFastMutex
  263. //--------------------------------------------------------------------
  264. // CEResource is a multi-reader, single-writer lock based on ERESOURCE
  265. class IRTL_DLLEXP CEResource :
  266. public CLockBase<LOCK_ERESOURCE, LOCK_MRSW,
  267. LOCK_RECURSIVE, LOCK_WAIT_HANDLE, LOCK_QUEUE_KERNEL,
  268. LOCK_CLASS_SPIN
  269. >
  270. {
  271. private:
  272. mutable ERESOURCE Resource;
  273. public:
  274. CEResource()
  275. {
  276. ExInitializeResourceLite(&Resource);
  277. }
  278. #ifdef LOCK_INSTRUMENTATION
  279. CEResource(
  280. const TCHAR* ptszName)
  281. {
  282. ExInitializeResourceLite(&Resource);
  283. LOCK_INSTRUMENTATION_INIT(ptszName);
  284. }
  285. #endif // LOCK_INSTRUMENTATION
  286. ~CEResource()
  287. {
  288. ExDeleteResourceLite(&Resource);
  289. }
  290. inline void
  291. WriteLock()
  292. {
  293. KeEnterCriticalRegion();
  294. ExAcquireResourceExclusiveLite(&Resource, TRUE);
  295. }
  296. inline void
  297. ReadLock()
  298. {
  299. KeEnterCriticalRegion();
  300. ExAcquireResourceSharedLite(&Resource, TRUE);
  301. }
  302. bool ReadOrWriteLock();
  303. inline bool
  304. TryWriteLock()
  305. {
  306. KeEnterCriticalRegion();
  307. BOOLEAN fLocked = ExAcquireResourceExclusiveLite(&Resource, FALSE);
  308. if (! fLocked)
  309. KeLeaveCriticalRegion();
  310. return fLocked != FALSE;
  311. }
  312. inline bool
  313. TryReadLock()
  314. {
  315. KeEnterCriticalRegion();
  316. BOOLEAN fLocked = ExAcquireResourceSharedLite(&Resource, FALSE);
  317. if (! fLocked)
  318. KeLeaveCriticalRegion();
  319. return fLocked != FALSE;
  320. }
  321. inline void
  322. WriteUnlock()
  323. {
  324. ExReleaseResourceLite(&Resource);
  325. KeLeaveCriticalRegion();
  326. }
  327. inline void
  328. ReadUnlock()
  329. {
  330. WriteUnlock();
  331. }
  332. void ReadOrWriteUnlock(bool fIsReadLocked);
  333. // Does current thread hold a write lock?
  334. bool
  335. IsWriteLocked() const
  336. {
  337. return ExIsResourceAcquiredExclusiveLite(&Resource) != FALSE;
  338. }
  339. bool
  340. IsReadLocked() const
  341. {
  342. return ExIsResourceAcquiredSharedLite(&Resource) > 0;
  343. }
  344. bool
  345. IsWriteUnlocked() const
  346. { return !IsWriteLocked(); }
  347. bool
  348. IsReadUnlocked() const
  349. { return !IsReadLocked(); }
  350. void
  351. ConvertSharedToExclusive()
  352. {
  353. ReadUnlock();
  354. WriteLock();
  355. }
  356. // There is no such window when converting from a writelock to a readlock
  357. void
  358. ConvertExclusiveToShared()
  359. {
  360. #if 0
  361. ExConvertExclusiveToShared(&Resource);
  362. #else
  363. WriteUnlock();
  364. ReadLock();
  365. #endif
  366. }
  367. #ifdef LOCK_DEFAULT_SPIN_IMPLEMENTATION
  368. bool
  369. SetSpinCount(WORD wSpins)
  370. {return false;}
  371. WORD
  372. GetSpinCount() const
  373. {return sm_wDefaultSpinCount;}
  374. LOCK_DEFAULT_SPIN_IMPLEMENTATION();
  375. #endif // LOCK_DEFAULT_SPIN_IMPLEMENTATION
  376. static const TCHAR*
  377. ClassName()
  378. {return _TEXT("CEResource");}
  379. }; // CEResource
  380. #if 1
  381. //--------------------------------------------------------------------
  382. // CRtlMrswLock is a multi-reader, single-writer lock from the TCP team
  383. //
  384. // The following structure and routines implement a multiple reader,
  385. // single writer lock. The lock may be used from PASSIVE_LEVEL to
  386. // DISPATCH_LEVEL.
  387. //
  388. // While the lock is held by readers or writer, the IRQL is
  389. // raised to DISPATCH_LEVEL. As a result, the memory for the
  390. // CRtlMrswLock structure must reside in non-paged pool. The
  391. // lock is "fair" in the sense that waiters are granted the lock
  392. // in the order requested. This is achieved via the use of a
  393. // queued spinlock internally.
  394. //
  395. // The lock can be recursively acquired by readers, but not by writers.
  396. //
  397. // There is no support for upgrading (downgrading) a read (write) lock to
  398. // a write (read) lock.
  399. class IRTL_DLLEXP CRtlMrswLock :
  400. public CLockBase<LOCK_RTL_MRSW_LOCK, LOCK_MRSW,
  401. LOCK_READ_RECURSIVE, LOCK_WAIT_SPIN, LOCK_QUEUE_KERNEL,
  402. LOCK_CLASS_SPIN
  403. >
  404. {
  405. private:
  406. mutable KSPIN_LOCK ExclusiveLock;
  407. volatile LONG ReaderCount;
  408. public:
  409. CRtlMrswLock()
  410. {
  411. KeInitializeSpinLock(&ExclusiveLock);
  412. ReaderCount = 0;
  413. }
  414. #ifdef LOCK_INSTRUMENTATION
  415. CRtlMrswLock(
  416. const TCHAR* ptszName)
  417. {
  418. KeInitializeSpinLock(&ExclusiveLock);
  419. ReaderCount = 0;
  420. LOCK_INSTRUMENTATION_INIT(ptszName);
  421. }
  422. #endif // LOCK_INSTRUMENTATION
  423. ~CRtlMrswLock()
  424. {
  425. IRTLASSERT(ReaderCount == 0);
  426. // KeUninitializeSpinLock(&ExclusiveLock);
  427. }
  428. inline void
  429. WriteLockAtDpcLevel(
  430. OUT PKLOCK_QUEUE_HANDLE LockHandle)
  431. {
  432. //
  433. // Wait for the writer (if there is one) to release.
  434. //
  435. KeAcquireInStackQueuedSpinLockAtDpcLevel(&ExclusiveLock, LockHandle);
  436. //
  437. // Now wait for all readers to release.
  438. //
  439. while (ReaderCount != 0)
  440. {}
  441. }
  442. inline void
  443. ReadLockAtDpcLevel()
  444. {
  445. KLOCK_QUEUE_HANDLE LockHandle;
  446. //
  447. // Wait for the writer (if there is one) to release.
  448. //
  449. KeAcquireInStackQueuedSpinLockAtDpcLevel(&ExclusiveLock, &LockHandle);
  450. //
  451. // Now that we have the exclusive lock, we know there are no writers.
  452. // Bump the reader count to cause any new writer to wait.
  453. // We use an interlock here becasue the decrement path is not done
  454. // under the exclusive lock.
  455. //
  456. InterlockedIncrement(const_cast<LONG*>(&ReaderCount));
  457. KeReleaseInStackQueuedSpinLockFromDpcLevel(&LockHandle);
  458. }
  459. inline void
  460. WriteLock(
  461. OUT PKLOCK_QUEUE_HANDLE LockHandle)
  462. {
  463. //
  464. // Wait for the writer (if there is one) to release.
  465. //
  466. KeAcquireInStackQueuedSpinLock(&ExclusiveLock, LockHandle);
  467. //
  468. // Now wait for all readers to release.
  469. //
  470. while (ReaderCount != 0)
  471. {}
  472. }
  473. inline void
  474. ReadLock(
  475. OUT PKIRQL OldIrql)
  476. {
  477. *OldIrql = KeRaiseIrqlToDpcLevel();
  478. ReadLockAtDpcLevel();
  479. }
  480. inline bool
  481. TryWriteLock()
  482. {
  483. return false;
  484. }
  485. inline bool
  486. TryReadLock()
  487. {
  488. // TODO
  489. return false;
  490. }
  491. inline void
  492. WriteUnlockFromDpcLevel(
  493. IN PKLOCK_QUEUE_HANDLE LockHandle)
  494. {
  495. KeReleaseInStackQueuedSpinLockFromDpcLevel(LockHandle);
  496. }
  497. inline void
  498. ReadUnlockFromDpcLevel()
  499. {
  500. //
  501. // Decrement the reader count. This will cause any waiting writer
  502. // to wake up and acquire the lock if the reader count becomes zero.
  503. //
  504. InterlockedDecrement(const_cast<LONG*>(&ReaderCount));
  505. }
  506. inline void
  507. WriteUnlock(
  508. IN PKLOCK_QUEUE_HANDLE LockHandle)
  509. {
  510. KeReleaseInStackQueuedSpinLock(LockHandle);
  511. }
  512. inline void
  513. ReadUnlock(
  514. IN KIRQL OldIrql)
  515. {
  516. ReadUnlockFromDpcLevel();
  517. KeLowerIrql(OldIrql);
  518. }
  519. void ReadOrWriteUnlock(bool fIsReadLocked);
  520. // Does current thread hold a write lock?
  521. bool
  522. IsWriteLocked() const
  523. {
  524. return false;
  525. }
  526. bool
  527. IsReadLocked() const
  528. {
  529. return false;
  530. }
  531. bool
  532. IsWriteUnlocked() const
  533. {
  534. return false;
  535. }
  536. bool
  537. IsReadUnlocked() const
  538. {
  539. return false;
  540. }
  541. void
  542. ConvertSharedToExclusive()
  543. {
  544. // ReadUnlock();
  545. // WriteLock();
  546. }
  547. void
  548. ConvertExclusiveToShared()
  549. {
  550. // WriteUnlock();
  551. // ReadLock();
  552. }
  553. #ifdef LOCK_DEFAULT_SPIN_IMPLEMENTATION
  554. bool
  555. SetSpinCount(WORD)
  556. {return false;}
  557. WORD
  558. GetSpinCount() const
  559. {return sm_wDefaultSpinCount;}
  560. LOCK_DEFAULT_SPIN_IMPLEMENTATION();
  561. #endif // LOCK_DEFAULT_SPIN_IMPLEMENTATION
  562. static const TCHAR*
  563. ClassName()
  564. {return _TEXT("CRtlMrswLock");}
  565. }; // CRtlMrswLock
  566. #endif
  567. #endif // __KLOCKS_H__