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.

648 lines
15 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation, 2000
  3. Module Name:
  4. SWMR.hxx
  5. Abstract:
  6. Class definitions for Single Writer Multiple Readers lock. See the header
  7. in swmr.cxx for more details.
  8. Author:
  9. Kamen Moutafov [KamenM]
  10. Revision History:
  11. KamenM Aug 2000 Created
  12. --*/
  13. #if _MSC_VER >= 1200
  14. #pragma once
  15. #endif
  16. #ifndef __SWMR_HXX_
  17. #define __SWMR_HXX_
  18. #if defined(_TEST_SWMR_)
  19. extern long TestCounter;
  20. #endif
  21. // forwards
  22. class SWMRWaiterQueue;
  23. void
  24. SpinOrYield (
  25. IN OUT BOOL *fNonFirstIteration,
  26. IN SWMRWaiterQueue *Queue
  27. );
  28. typedef enum tagSWMRWaiterType
  29. {
  30. swmrwtReader = 0,
  31. swmrwtWriter,
  32. swmrwtInvalid
  33. } SWMRWaiterType;
  34. class SWMRWaiter
  35. {
  36. public:
  37. const static long OwnLock = 4;
  38. const static long OwnLockMask = 4;
  39. // the waiter types are taken from the SWMRWaiterType enum
  40. const static WaiterTypeMask = 3;
  41. // if this constant is set in flags, then another writer waiter
  42. // was inserted before the current writer after the current
  43. // writer was queued. This flag can be raised for writers only.
  44. // Note that only the code that converts the lock to exclusive
  45. // reads this lock even though it can be set to other waiters
  46. // as well.
  47. const static long WriterInsertedBefore = 8;
  48. const static long WriterInsertedBeforeMask = 8;
  49. // the flags contain three sets of flags - the type of lock, whether
  50. // the current waiter owns the lock, and whether another waiter was
  51. // inserted before the current one after the current waiter started
  52. // waiting. The flags are protected by the
  53. // queue lock and cannot change on any queued waiter block aside
  54. // from raising flags for writers losing shared lock while
  55. // converting to exclusive
  56. long Flags;
  57. // used only for reader waiter blocks. For writers it is not used.
  58. // Protected by the queue lock
  59. long RefCount;
  60. // NULL if there is no next waiter. Points to the next waiter otherwise.
  61. // Cannot change from NULL->!NULL outside the lock. The other change is
  62. // irrelevant - waiters simply fall off the queue
  63. SWMRWaiter *Next;
  64. // event to wait on
  65. HANDLE hEvent;
  66. SWMRWaiter (
  67. void
  68. )
  69. {
  70. hEvent = NULL;
  71. Initialize(swmrwtInvalid, FALSE);
  72. }
  73. RPC_STATUS
  74. Initialize (
  75. IN SWMRWaiterType WaiterType,
  76. IN BOOL fEventRequired
  77. )
  78. {
  79. Flags = WaiterType;
  80. RefCount = 1;
  81. Next = NULL;
  82. if (fEventRequired)
  83. {
  84. return InitializeEventIfNecessary();
  85. }
  86. else
  87. {
  88. return RPC_S_OK;
  89. }
  90. }
  91. RPC_STATUS
  92. InitializeEvent (
  93. void
  94. )
  95. {
  96. #if defined(_TEST_SWMR_)
  97. int RndNum;
  98. // inject failures for unit tests
  99. RndNum = rand();
  100. if ((RndNum % 20) != 11)
  101. {
  102. #endif
  103. hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  104. if (!hEvent)
  105. {
  106. return RPC_S_OUT_OF_MEMORY;
  107. }
  108. LogEvent(SU_EVENT, EV_CREATE, hEvent, this, 0, 1, 1);
  109. return RPC_S_OK;
  110. #if defined(_TEST_SWMR_)
  111. }
  112. else
  113. {
  114. return RPC_S_OUT_OF_MEMORY;
  115. }
  116. #endif
  117. }
  118. RPC_STATUS
  119. InitializeEventIfNecessary (
  120. void
  121. )
  122. {
  123. if (hEvent)
  124. {
  125. ResetEvent(hEvent);
  126. return RPC_S_OK;
  127. }
  128. else
  129. return InitializeEvent();
  130. }
  131. static void
  132. CookupWaiterFromEventAndBuffer (
  133. IN SWMRWaiter *WaiterBlock,
  134. SWMRWaiterType WaiterType,
  135. IN HANDLE hEvent)
  136. {
  137. WaiterBlock->Flags = WaiterType;
  138. WaiterBlock->RefCount = 1;
  139. WaiterBlock->Next = NULL;
  140. WaiterBlock->hEvent = hEvent;
  141. }
  142. inline void
  143. FreeWaiterData (
  144. void
  145. )
  146. {
  147. if (hEvent)
  148. {
  149. LogEvent(SU_EVENT, EV_DELETE, hEvent, this, 0, 1, 2);
  150. CloseHandle(hEvent);
  151. }
  152. }
  153. };
  154. const size_t SizeOfWaiterBlock = sizeof(SWMRWaiter);
  155. // SWMR - queued implementation
  156. class SWMRWaiterQueue
  157. {
  158. // not protected - interlocks must be used. When it is locked,
  159. // nobody can queue themselves, and nobody can unqueue themselves
  160. SWMRWaiter *LastWaiter;
  161. // A pointer to the waiter block for the current
  162. // owner(s)
  163. // not protected. If the lock is free, changed
  164. // to something by the first owner. If the lock
  165. // is not free and ownership is handed to somebody,
  166. // the owner sets this to the next owner. It is
  167. // used only during unlocking
  168. SWMRWaiter *FirstWaiter;
  169. // the thread id of the owner in case of exclusive ownership.
  170. // used only for debugging. This is not reset upon leaving ownership
  171. // so in case the lock is not owned, or owned in shared mode, the
  172. // thread id is stale
  173. // Don't use this in real processing!
  174. ULONG LastWriterThreadId;
  175. public:
  176. const static ULONG_PTR Free = 0x0;
  177. const static ULONG_PTR LastReader = 0x1;
  178. const static ULONG_PTR LastWriter = 0x2;
  179. const static ULONG_PTR Locked = 0x3;
  180. const static ULONG_PTR StateMask = 0x3;
  181. SWMRWaiterQueue (
  182. void
  183. )
  184. {
  185. LastWaiter = NULL;
  186. FirstWaiter = NULL;
  187. }
  188. ~SWMRWaiterQueue (
  189. void
  190. )
  191. {
  192. ASSERT(LastWaiter == NULL);
  193. }
  194. SWMRWaiter *
  195. Lock (
  196. void
  197. )
  198. /*++
  199. Routine Description:
  200. Locks the queue.
  201. Arguments:
  202. Return Value:
  203. The old queue lock value. This needs to be passed to Unlock
  204. --*/
  205. {
  206. SWMRWaiter *CurrentLastWaiter;
  207. BOOL fNonFirstIteration = FALSE;
  208. while (TRUE)
  209. {
  210. CurrentLastWaiter = LastWaiter;
  211. if (((ULONG_PTR)CurrentLastWaiter & StateMask) != Locked)
  212. {
  213. if (InterlockedCompareExchangePointer((PVOID *)&LastWaiter, (PVOID)((ULONG_PTR)CurrentLastWaiter | Locked),
  214. CurrentLastWaiter) == CurrentLastWaiter)
  215. {
  216. return CurrentLastWaiter;
  217. }
  218. }
  219. SpinOrYield(&fNonFirstIteration, this);
  220. }
  221. }
  222. void
  223. UnlockAndCommit (
  224. SWMRWaiter *OldLastWaiter
  225. )
  226. /*++
  227. Routine Description:
  228. Unlocks the queue and restores the old value.
  229. Arguments:
  230. The last waiter before the queue was locked.
  231. Return Value:
  232. --*/
  233. {
  234. SWMRWaiter *CurrentWaiter;
  235. SWMRWaiter *PreviousWaiter;
  236. char Buffer[256];
  237. char *BufPtr;
  238. char WaiterSymbol;
  239. BOOL fDoDump;
  240. CurrentWaiter = LastWaiter;
  241. ASSERT(((ULONG_PTR)CurrentWaiter & StateMask) == Locked);
  242. ASSERT(((ULONG_PTR)OldLastWaiter & StateMask) != Locked);
  243. #if defined(_TEST_SWMR_)
  244. // ensure we leave the chain in consistent state
  245. if (OldLastWaiter)
  246. {
  247. CurrentWaiter = FirstWaiter;
  248. BufPtr = Buffer;
  249. fDoDump = ((TestCounter & 0x3FFF) == 0);
  250. while (CurrentWaiter)
  251. {
  252. if (fDoDump)
  253. {
  254. if ((CurrentWaiter->Flags & SWMRWaiter::WaiterTypeMask) == swmrwtReader)
  255. WaiterSymbol = 'R';
  256. else
  257. WaiterSymbol = 'W';
  258. ASSERT(CurrentWaiter->RefCount != 0);
  259. if (CurrentWaiter->RefCount == 1)
  260. {
  261. *BufPtr = WaiterSymbol;
  262. BufPtr ++;
  263. }
  264. else
  265. {
  266. ASSERT(WaiterSymbol == 'R');
  267. memset(BufPtr, WaiterSymbol, CurrentWaiter->RefCount);
  268. BufPtr += CurrentWaiter->RefCount;
  269. }
  270. }
  271. PreviousWaiter = CurrentWaiter;
  272. CurrentWaiter = CurrentWaiter->Next;
  273. if (CurrentWaiter && ((CurrentWaiter->Flags & SWMRWaiter::WaiterTypeMask) == swmrwtReader))
  274. {
  275. // we don't want to have adjacent reader segments
  276. ASSERT((PreviousWaiter->Flags & SWMRWaiter::WaiterTypeMask) != swmrwtReader);
  277. }
  278. if (fDoDump)
  279. {
  280. *BufPtr = '-';
  281. BufPtr ++;
  282. }
  283. }
  284. if (fDoDump)
  285. {
  286. *BufPtr = '\0';
  287. Dump("%d: %s\n", TestCounter, Buffer);
  288. }
  289. ASSERT(PreviousWaiter == (SWMRWaiter *)((ULONG_PTR)OldLastWaiter & ~StateMask));
  290. }
  291. #endif
  292. LastWaiter = OldLastWaiter;
  293. }
  294. inline ULONG_PTR
  295. GetLastWaiterState (
  296. void
  297. ) volatile
  298. {
  299. return (((ULONG_PTR)LastWaiter) & StateMask);
  300. }
  301. inline SWMRWaiter *
  302. InterlockCompareExchangeLastWaiter (
  303. IN SWMRWaiter *Comperand,
  304. IN SWMRWaiter *Exchange,
  305. IN ULONG_PTR State
  306. )
  307. {
  308. ASSERT((State & ~StateMask) == 0);
  309. return (SWMRWaiter *)InterlockedCompareExchangePointer((PVOID *)&LastWaiter,
  310. (PVOID)((ULONG_PTR)Exchange | State), Comperand);
  311. }
  312. inline BOOL
  313. VerifyState (
  314. IN SWMRWaiter *OldWaiter,
  315. IN ULONG_PTR ExpectedState
  316. )
  317. {
  318. return (((ULONG_PTR)OldWaiter & StateMask) == ExpectedState);
  319. }
  320. inline SWMRWaiter *
  321. GetFirstWaiter (
  322. void
  323. )
  324. {
  325. return FirstWaiter;
  326. }
  327. inline void
  328. SetFirstWaiter (
  329. IN SWMRWaiter *NewFirstWaiter
  330. )
  331. {
  332. FirstWaiter = NewFirstWaiter;
  333. }
  334. inline SWMRWaiter *
  335. RemoveReaderStateFromWaiterPtr (
  336. IN SWMRWaiter *Waiter
  337. )
  338. {
  339. ASSERT(((ULONG_PTR)Waiter & StateMask) == LastReader);
  340. return (SWMRWaiter *)((ULONG_PTR)Waiter & ~StateMask);
  341. }
  342. inline SWMRWaiter *
  343. RemoveWriterStateFromWaiterPtr (
  344. IN SWMRWaiter *Waiter
  345. )
  346. {
  347. ASSERT(((ULONG_PTR)Waiter & StateMask) == LastWriter);
  348. return (SWMRWaiter *)((ULONG_PTR)Waiter & ~StateMask);
  349. }
  350. inline SWMRWaiter *
  351. RemoveStateFromWaiterPtr (
  352. IN SWMRWaiter *Waiter
  353. )
  354. {
  355. return (SWMRWaiter *)((ULONG_PTR)Waiter & ~StateMask);
  356. }
  357. inline SWMRWaiter *
  358. AddReaderStateInWaiterPtr (
  359. IN SWMRWaiter *Waiter
  360. )
  361. {
  362. ASSERT(((ULONG_PTR)Waiter & StateMask) == Free);
  363. return (SWMRWaiter *)((ULONG_PTR)Waiter | LastReader);
  364. }
  365. inline SWMRWaiter *
  366. AddWriterStateInWaiterPtr (
  367. IN SWMRWaiter *Waiter
  368. )
  369. {
  370. ASSERT(((ULONG_PTR)Waiter & StateMask) == Free);
  371. return (SWMRWaiter *)((ULONG_PTR)Waiter | LastWriter);
  372. }
  373. inline SWMRWaiter *
  374. SetStateInWaiterPtr (
  375. IN SWMRWaiter *Waiter,
  376. IN ULONG_PTR DesiredState
  377. )
  378. {
  379. ASSERT(((ULONG_PTR)Waiter & StateMask) == Free);
  380. return (SWMRWaiter *)((ULONG_PTR)Waiter | DesiredState);
  381. }
  382. inline void
  383. SetLastWriterThreadId (
  384. void
  385. )
  386. {
  387. LastWriterThreadId = HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread);
  388. }
  389. };
  390. class SWMRLock : public SWMRWaiterQueue
  391. {
  392. private:
  393. SWMRWaiter CachedWaiter;
  394. // 1 means the cached waiter is available
  395. // 0 means it is not
  396. long CachedWaiterAvailable;
  397. SWMRWaiter *
  398. AllocateWaiter (
  399. IN SWMRWaiterType WaiterType,
  400. IN BOOL fEventRequired
  401. );
  402. static void
  403. FreeWaiter (
  404. IN SWMRWaiter *Waiter
  405. );
  406. void
  407. StoreOrFreeSpareWaiter (
  408. IN OUT SWMRWaiter **WaiterCache OPTIONAL,
  409. IN SWMRWaiter *Waiter OPTIONAL
  410. );
  411. void
  412. LockSharedOnLastReader (
  413. IN OUT SWMRWaiter **WaiterCache OPTIONAL,
  414. IN OUT SWMRWaiter *OldWaiter,
  415. IN OUT SWMRWaiter *AllocatedWaiter
  416. );
  417. void
  418. LockSharedOrExclusiveOnLastWriter (
  419. IN OUT SWMRWaiter **WaiterCache OPTIONAL,
  420. IN OUT SWMRWaiter *OldWaiter,
  421. IN OUT SWMRWaiter *AllocatedWaiter,
  422. IN ULONG_PTR DesiredState
  423. );
  424. void
  425. LockExclusiveOnLastReader (
  426. IN OUT SWMRWaiter **WaiterCache OPTIONAL,
  427. IN OUT SWMRWaiter *OldWaiter,
  428. IN OUT SWMRWaiter *AllocatedWaiter
  429. );
  430. RPC_STATUS
  431. LockSharedOrExclusive (
  432. IN OUT SWMRWaiter **WaiterCache OPTIONAL,
  433. IN SWMRWaiterType WaiterType,
  434. IN ULONG_PTR DesiredState
  435. );
  436. public:
  437. SWMRLock (
  438. void
  439. )
  440. {
  441. CachedWaiterAvailable = TRUE;
  442. }
  443. ~SWMRLock (
  444. void
  445. )
  446. {
  447. ASSERT(CachedWaiterAvailable);
  448. CachedWaiter.FreeWaiterData();
  449. }
  450. inline RPC_STATUS
  451. LockShared (
  452. IN OUT SWMRWaiter **WaiterCache OPTIONAL
  453. )
  454. /*++
  455. Routine Description:
  456. Obtains a shared access lock.
  457. Arguments:
  458. WaiterCache - An optional parameter allowing caching of waiter blocks.
  459. If WaiterCache == NULL, no caching will be done.
  460. If WaiterCache != NULL, but *WaiterCache == NULL, *WaiterCache
  461. may be set to the new Waiter block. If *WaiterCache != NULL,
  462. the cached value will be used. Its hEvent must be set
  463. Return Value:
  464. RPC_S_OK or RPC_S_* for error
  465. --*/
  466. {
  467. return LockSharedOrExclusive(WaiterCache,
  468. swmrwtReader,
  469. LastReader);
  470. }
  471. inline RPC_STATUS
  472. LockExclusive (
  473. IN OUT SWMRWaiter **WaiterCache OPTIONAL
  474. )
  475. /*++
  476. Routine Description:
  477. Obtains an exclusive writer lock
  478. Arguments:
  479. WaiterCache - An optional parameter allowing caching of waiter blocks.
  480. If WaiterCache == NULL, no caching will be done.
  481. If WaiterCache != NULL, but *WaiterCache == NULL, *WaiterCache
  482. may be set to the new Waiter block. If *WaiterCache != NULL,
  483. the cached value will be used. Its hEvent must be set
  484. Return Value:
  485. RPC_S_OK or RPC_S_* for error
  486. --*/
  487. {
  488. return LockSharedOrExclusive(WaiterCache,
  489. swmrwtWriter,
  490. LastWriter);
  491. }
  492. void
  493. UnlockShared (
  494. IN OUT SWMRWaiter **WaiterCache OPTIONAL
  495. );
  496. void
  497. UnlockExclusive (
  498. IN OUT SWMRWaiter **WaiterCache OPTIONAL
  499. );
  500. void
  501. Unlock (
  502. IN OUT SWMRWaiter **WaiterCache OPTIONAL
  503. );
  504. RPC_STATUS
  505. ConvertToExclusive (
  506. IN OUT SWMRWaiter **WaiterCache OPTIONAL
  507. );
  508. RPC_STATUS
  509. ConvertToShared (
  510. IN OUT SWMRWaiter **WaiterCache OPTIONAL,
  511. IN BOOL fSyncCacheUsed
  512. );
  513. static void
  514. FreeWaiterCache (
  515. IN SWMRWaiter **WaiterCache
  516. )
  517. /*++
  518. Routine Description:
  519. Allows an external caller to free its cache. All cache owners must
  520. call this before going away.
  521. Arguments:
  522. WaiterCache - the cache. It must be non-NULL. If it points to a cached
  523. waiter, the cached waiter will be freed.
  524. Return Value:
  525. --*/
  526. {
  527. ASSERT(WaiterCache);
  528. if (*WaiterCache)
  529. FreeWaiter(*WaiterCache);
  530. }
  531. };
  532. #endif // __SWMR_HXX