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.

1514 lines
46 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation, 2000
  3. Module Name:
  4. SWMR.cxx
  5. Abstract:
  6. Implementation of Single Writer Multiple Readers lock.
  7. Author:
  8. Kamen Moutafov [KamenM]
  9. Revision History:
  10. KamenM Aug 2000 Created
  11. Notes:
  12. Mechanism: Single Writer Multiple Reader Lock (SWMRLock)
  13. Design Goals:
  14. 1. Low resource usage for unused locks (i.e. one
  15. is embedded in each context handle) in the common case.
  16. 2. Fairness in dispatching, including conversions from
  17. shared to exclusive and vice versa.
  18. 3. Decent performance in the common case.
  19. 4. Efficient handling of convoys
  20. 5. Ability to claim the lock from one thread and release
  21. it from another.
  22. Design non-goals:
  23. 1. Good throughput when multiple threads claim the lock.
  24. Description:
  25. The SWMRLock contains one mini spin lock, and a queue
  26. of waiters. The spin lock is used to synchronize few
  27. enqueue/dequeue operations. The queue is used to keep
  28. all claimants of the lock, and to wake up the next
  29. waiter when necessary.
  30. Scenarios:
  31. 1. Multiple synchronized sync callers (writers). They will
  32. borrow the thread event, and will make two interlocks to
  33. get it.
  34. 2. Multiple non-synchronized sync/async callers (readers).
  35. They will use the cached waiter - 0 events, two interlocks
  36. to get it.
  37. 3. Multiple non-synchronized sync/async callers (readers)
  38. followed by one synchronzied sync caller (writer). All readers
  39. will use the cached waiter, and the sync writer who destroys
  40. the context handle will borrow the thread event.
  41. Data Structures:
  42. Generic state:
  43. *****************
  44. * *
  45. * SWMRLock *
  46. * *
  47. *****************
  48. | |
  49. FirstWaiter | | LastWaiter & ~StateMask
  50. +-----------+ +-------------------+
  51. | |
  52. | |
  53. V V
  54. ///////////////// /////////////////////
  55. / / Next Next / /
  56. / SWMRWaiter / -----> ... -------->/ SWMRWaiter /
  57. / / / /
  58. ///////////////// /////////////////////
  59. Scenario 1:
  60. *****************
  61. * *
  62. * SWMRLock *
  63. * *
  64. *****************
  65. | |
  66. FirstWaiter | | LastWaiter & ~StateMask (LastWaiter = Pointer | LastWriter)
  67. +-----------+ +-------------------+
  68. | |
  69. | |
  70. V V
  71. ///////////////// /////////////////////
  72. / / / /
  73. / Writer / / Writer /
  74. / / / /
  75. / Flags: / / Flags: /
  76. / swmrwtWriter / / swmrwtWriter /
  77. / | OwnLock / / /
  78. / RefCount: 1 / Next Next / RefCount: 1 /
  79. / Next: / -----> ... -------->/ Next: NULL /
  80. / hEvent: EVENT / / hEvent: EVENT /
  81. / or NULL / / /
  82. / / / /
  83. ///////////////// /////////////////////
  84. Scenario 2:
  85. *****************
  86. * *
  87. * SWMRLock *
  88. * *
  89. *****************
  90. |
  91. | FirstWaiter
  92. | == LastWaiter (LastWaiter = Pointer | LastReader)
  93. |
  94. |
  95. |
  96. V
  97. /////////////////
  98. / /
  99. / Reader /
  100. / /
  101. / Flags: /
  102. / swmrwtReader /
  103. / | OwnLock /
  104. / RefCount: N /
  105. / Next: NULL /
  106. / hEvent: NULL /
  107. / /
  108. /////////////////
  109. Scenario 3:
  110. *****************
  111. * *
  112. * SWMRLock *
  113. * *
  114. *****************
  115. | |
  116. FirstWaiter | | LastWaiter & ~StateMask (LastWaiter = Pointer | LastWriter)
  117. +-----------+ +-------------------+
  118. | |
  119. | |
  120. V V
  121. ///////////////// /////////////////////
  122. / / / /
  123. / Reader / / Writer /
  124. / / / /
  125. / Flags: / / Flags: /
  126. / swmrwtReader / / swmrwtWriter /
  127. / | OwnLock / / /
  128. / RefCount: N / Next / RefCount: 1 /
  129. / Next: / --------------------->/ Next: NULL /
  130. / hEvent: EVENT / / hEvent: EVENT /
  131. / or NULL / / /
  132. / / / /
  133. ///////////////// /////////////////////
  134. --*/
  135. // undef this to get unit tests for SWMR
  136. // #define _TEST_SWMR_
  137. #include <precomp.hxx>
  138. #if defined(_TEST_SWMR_)
  139. extern "C" {
  140. #include <rpcperf.h>
  141. #include <rpcrt.h>
  142. void Test (void);
  143. extern long Clients;
  144. }
  145. HANDLE ProcessHeap;
  146. void *
  147. __cdecl
  148. operator new (
  149. IN size_t size
  150. )
  151. {
  152. return(RtlAllocateHeap(ProcessHeap, 0, size));
  153. }
  154. void
  155. __cdecl
  156. operator delete (
  157. IN void * obj
  158. )
  159. {
  160. RtlFreeHeap(ProcessHeap, 0, obj);
  161. }
  162. #define _NOT_COVERED_ (0)
  163. #endif
  164. #include <swmr.hxx>
  165. void
  166. SpinOrYield (
  167. IN OUT BOOL *fNonFirstIteration,
  168. IN SWMRWaiterQueue *Queue
  169. )
  170. {
  171. int i;
  172. #if defined(_TEST_SWMR_)
  173. if ((2 > 1) && (*fNonFirstIteration == FALSE))
  174. #else
  175. if ((gNumberOfProcessors > 1) && (*fNonFirstIteration == FALSE))
  176. #endif
  177. {
  178. for (i = 0; i < 2000; i ++)
  179. {
  180. if (Queue->GetLastWaiterState() == SWMRWaiterQueue::Free)
  181. return;
  182. }
  183. }
  184. else
  185. {
  186. Sleep (10);
  187. }
  188. *fNonFirstIteration = TRUE;
  189. }
  190. #if defined(_TEST_SWMR_)
  191. long TestCounter = 0;
  192. #endif
  193. SWMRWaiter *
  194. SWMRLock::AllocateWaiter (
  195. IN SWMRWaiterType WaiterType,
  196. IN BOOL fEventRequired
  197. )
  198. /*++
  199. Routine Description:
  200. Allocates a waiter. First, it tries the cache. If this
  201. fails, it does the allocation.
  202. Arguments:
  203. WaiterType - whether the needed waiter is a reader or writer
  204. fEventRequired - if non-FALSE, the event is required and must
  205. be allocated before return. Otherwise, the event may not be allocated
  206. Return Value:
  207. The new waiter. If NULL, either a waiter could not be allocated or its
  208. event couldn't be initialized.
  209. --*/
  210. {
  211. SWMRWaiter *NewWaiter;
  212. RPC_STATUS RpcStatus;
  213. if (CachedWaiterAvailable)
  214. {
  215. if (InterlockedCompareExchange(&CachedWaiterAvailable, 0, 1) == 1)
  216. {
  217. RpcStatus = CachedWaiter.Initialize(WaiterType, fEventRequired);
  218. if (RpcStatus != RPC_S_OK)
  219. return NULL;
  220. return &CachedWaiter;
  221. }
  222. }
  223. NewWaiter = new SWMRWaiter;
  224. if (NewWaiter)
  225. {
  226. RpcStatus = NewWaiter->Initialize(WaiterType, fEventRequired);
  227. if (RpcStatus != RPC_S_OK)
  228. {
  229. delete NewWaiter;
  230. NewWaiter = NULL;
  231. }
  232. }
  233. return NewWaiter;
  234. }
  235. void
  236. SWMRLock::FreeWaiter (
  237. IN SWMRWaiter *Waiter
  238. )
  239. /*++
  240. Routine Description:
  241. Frees a waiter.
  242. Arguments:
  243. Waiter - the waiter to be freed. This cannot be the cached waiter.
  244. Return Value:
  245. --*/
  246. {
  247. ASSERT(Waiter);
  248. Waiter->FreeWaiterData();
  249. delete Waiter;
  250. }
  251. void
  252. SWMRLock::StoreOrFreeSpareWaiter (
  253. IN OUT SWMRWaiter **WaiterCache OPTIONAL,
  254. IN SWMRWaiter *Waiter OPTIONAL
  255. )
  256. /*++
  257. Routine Description:
  258. Given an optional waiter cache, this code decides what
  259. to do with a waiter that is no longer used. The policy is
  260. as follows:
  261. If the waiter is NULL, return.
  262. If the waiter is the cached waiter, mark the cached waiter
  263. as available.
  264. If it is not the cached waiter, and there is a waiter cache
  265. passed, store the waiter.
  266. Else, free the waiter.
  267. Arguments:
  268. WaiterCache - The waiter cache.
  269. Waiter - waiter to be freed.
  270. Return Value:
  271. --*/
  272. {
  273. if (Waiter)
  274. {
  275. if (Waiter != &CachedWaiter)
  276. {
  277. if (WaiterCache && (*WaiterCache == NULL))
  278. {
  279. *WaiterCache = Waiter;
  280. }
  281. else
  282. {
  283. FreeWaiter(Waiter);
  284. }
  285. }
  286. else
  287. {
  288. ASSERT(CachedWaiterAvailable == 0);
  289. InterlockedExchange(&CachedWaiterAvailable, 1);
  290. }
  291. }
  292. }
  293. void
  294. SWMRLock::LockSharedOnLastReader (
  295. IN OUT SWMRWaiter **WaiterCache OPTIONAL,
  296. IN OUT SWMRWaiter *OldWaiter,
  297. IN OUT SWMRWaiter *AllocatedWaiter
  298. )
  299. /*++
  300. Routine Description:
  301. Does the locking for shared access if the last waiter is a reader
  302. Arguments:
  303. WaiterCache - See WaiterCache in LockSharedOrExclusive
  304. OldWaiter - the OldWaiter obtained when the lock was taken
  305. AllocatedWaiter - new waiter available for use. In this case,
  306. we only free it.
  307. Return Value:
  308. Notes:
  309. The function is called with the lock held. It must release the lock
  310. as soon as it can.
  311. --*/
  312. {
  313. SWMRWaiter *LastWaiter;
  314. // here we know we're adding a reader to waiting readers
  315. LastWaiter = RemoveReaderStateFromWaiterPtr(OldWaiter);
  316. ASSERT((LastWaiter->Flags & SWMRWaiter::WaiterTypeMask) == swmrwtReader);
  317. ASSERT(LastWaiter->Next == NULL);
  318. ASSERT(LastWaiter->RefCount >= 1);
  319. LastWaiter->RefCount ++;
  320. if ((LastWaiter->Flags & SWMRWaiter::OwnLockMask) == 0)
  321. {
  322. UnlockAndCommit(OldWaiter);
  323. // if we have created a waiter, try to cache it or free it
  324. StoreOrFreeSpareWaiter(WaiterCache, AllocatedWaiter);
  325. // the last waiter on the queue doesn't own the lock -
  326. // wait on it. We know the block can't go away because
  327. // we have added our refcount. On return, we will
  328. // own the lock
  329. ASSERT(LastWaiter->hEvent);
  330. WaitForSingleObject(LastWaiter->hEvent, INFINITE);
  331. }
  332. else
  333. {
  334. UnlockAndCommit(OldWaiter);
  335. // if we have created a waiter, try to cache it or free it
  336. StoreOrFreeSpareWaiter(WaiterCache, AllocatedWaiter);
  337. }
  338. }
  339. void
  340. SWMRLock::LockSharedOrExclusiveOnLastWriter (
  341. IN OUT SWMRWaiter **WaiterCache OPTIONAL,
  342. IN OUT SWMRWaiter *OldWaiter,
  343. IN OUT SWMRWaiter *AllocatedWaiter,
  344. IN ULONG_PTR DesiredState
  345. )
  346. /*++
  347. Routine Description:
  348. Does the locking for both shared and exclusive access if the last waiter
  349. is a writer
  350. Arguments:
  351. WaiterCache - See WaiterCache in LockSharedOrExclusive
  352. OldWaiter - the OldWaiter obtained when the lock was taken
  353. AllocatedWaiter - new waiter available for use. In this case,
  354. we only free it.
  355. DesiredState - the state of the new last waiter
  356. Return Value:
  357. Notes:
  358. The function is called with the lock held. It must release the lock
  359. as soon as it can.
  360. --*/
  361. {
  362. SWMRWaiter *LastWaiter;
  363. ASSERT(AllocatedWaiter != NULL);
  364. // here we know we're adding a reader to writer
  365. LastWaiter = RemoveWriterStateFromWaiterPtr(OldWaiter);
  366. ASSERT((LastWaiter->Flags & SWMRWaiter::WaiterTypeMask) == swmrwtWriter);
  367. ASSERT(LastWaiter->Next == NULL);
  368. ASSERT(LastWaiter->RefCount == 1);
  369. ASSERT(LastWaiter != AllocatedWaiter);
  370. LastWaiter->Next = AllocatedWaiter;
  371. LastWaiter = SetStateInWaiterPtr(AllocatedWaiter, DesiredState);
  372. UnlockAndCommit(LastWaiter);
  373. if (WaiterCache)
  374. *WaiterCache = NULL;
  375. // Wait for it. On return, we will own the lock
  376. ASSERT(AllocatedWaiter->hEvent);
  377. WaitForSingleObject(AllocatedWaiter->hEvent, INFINITE);
  378. }
  379. void
  380. SWMRLock::LockExclusiveOnLastReader (
  381. IN OUT SWMRWaiter **WaiterCache OPTIONAL,
  382. IN OUT SWMRWaiter *OldWaiter,
  383. IN OUT SWMRWaiter *AllocatedWaiter
  384. )
  385. /*++
  386. Routine Description:
  387. Does the locking for exclusive access if the last waiter is a reader
  388. Arguments:
  389. WaiterCache - See WaiterCache in LockSharedOrExclusive
  390. OldWaiter - the OldWaiter obtained when the lock was taken
  391. AllocatedWaiter - new waiter available for use. In this case,
  392. we only free it.
  393. Return Value:
  394. Notes:
  395. The function is called with the lock held. It must release the lock
  396. as soon as it can.
  397. --*/
  398. {
  399. SWMRWaiter *LastWaiter;
  400. ASSERT(AllocatedWaiter != NULL);
  401. // here we know we're adding a writer to a reader
  402. LastWaiter = RemoveReaderStateFromWaiterPtr(OldWaiter);
  403. ASSERT((LastWaiter->Flags & SWMRWaiter::WaiterTypeMask) == swmrwtReader);
  404. ASSERT(LastWaiter->Next == NULL);
  405. ASSERT(LastWaiter->RefCount >= 1);
  406. ASSERT(LastWaiter != AllocatedWaiter);
  407. LastWaiter->Next = AllocatedWaiter;
  408. LastWaiter = AddWriterStateInWaiterPtr(AllocatedWaiter);
  409. UnlockAndCommit(LastWaiter);
  410. if (WaiterCache)
  411. *WaiterCache = NULL;
  412. // Wait for it. On return, we will own the lock
  413. ASSERT(AllocatedWaiter->hEvent);
  414. WaitForSingleObject(AllocatedWaiter->hEvent, INFINITE);
  415. }
  416. RPC_STATUS
  417. SWMRLock::LockSharedOrExclusive (
  418. IN OUT SWMRWaiter **WaiterCache OPTIONAL,
  419. IN SWMRWaiterType WaiterType,
  420. IN ULONG_PTR DesiredState
  421. )
  422. /*++
  423. Routine Description:
  424. Worker function that does most of the work association
  425. with claiming a lock for shared or exclusive access. In fact,
  426. it does all the work apart from hanlding a reader after writer,
  427. reader after reader, writer after reader and writer after writer
  428. Arguments:
  429. WaiterCache - An optional parameter allowing caching of waiter blocks.
  430. If WaiterCache == NULL, no caching will be done.
  431. If WaiterCache != NULL, but *WaiterCache == NULL, *WaiterCache
  432. may be set to the new Waiter block. If *WaiterCache != NULL,
  433. the cached value will be used. Its hEvent may or may not be set. If
  434. not set and needed, this routine may fill it in.
  435. WaiterType - the type of the caller. If this is swmrwtReader, DesiredState
  436. must be LastReader and vice versa. If this is swmrwtWriter, DesiredState
  437. must ve LastWriter and vice versa. We could have done with one argument
  438. only, except we want to save one if.
  439. DesiredState - LastReader or LastWriter
  440. Return Value:
  441. RPC_S_OK or RPC_S_* for error
  442. --*/
  443. {
  444. ULONG_PTR LastWaiterState;
  445. SWMRWaiter *Waiter;
  446. SWMRWaiter *OldWaiter;
  447. SWMRWaiter *LastWaiter;
  448. BOOL fNonFirstIteration = FALSE;
  449. RPC_STATUS RpcStatus;
  450. ASSERT((DesiredState == LastReader) || (DesiredState == LastWriter));
  451. if (DesiredState == LastReader)
  452. {
  453. ASSERT(WaiterType == swmrwtReader);
  454. }
  455. else
  456. {
  457. ASSERT(WaiterType == swmrwtWriter);
  458. }
  459. if (WaiterCache)
  460. {
  461. Waiter = *WaiterCache;
  462. if (Waiter)
  463. {
  464. *WaiterCache = NULL;
  465. Waiter->Initialize(WaiterType, FALSE);
  466. }
  467. }
  468. else
  469. {
  470. Waiter = NULL;
  471. }
  472. while (TRUE)
  473. {
  474. // on entry to this loop:
  475. // Waiter may be NULL, or non-NULL. If non-NULL,
  476. // its RefCount, Next and WaiterType are
  477. // initialized. If hEvent is non-NULL, it must
  478. // be valid too. (Flags & OwnLockMask) is 0
  479. if (Waiter)
  480. {
  481. ASSERT((Waiter->Flags & SWMRWaiter::OwnLockMask) == 0);
  482. }
  483. LastWaiterState = GetLastWaiterState();
  484. switch (LastWaiterState)
  485. {
  486. case Free:
  487. if (!Waiter)
  488. {
  489. Waiter = AllocateWaiter(WaiterType, FALSE);
  490. if (!Waiter)
  491. {
  492. return RPC_S_OUT_OF_MEMORY;
  493. }
  494. }
  495. // we know we have a waiter here
  496. OldWaiter = Lock();
  497. if (!VerifyState(OldWaiter, Free))
  498. {
  499. UnlockAndCommit(OldWaiter);
  500. // the state has changed while we were switching - loop around
  501. continue;
  502. }
  503. Waiter->Flags |= SWMRWaiter::OwnLock;
  504. SetFirstWaiter(Waiter);
  505. LastWaiter = SetStateInWaiterPtr(Waiter, DesiredState);
  506. UnlockAndCommit(LastWaiter);
  507. if (WaiterCache)
  508. *WaiterCache = NULL;
  509. if (WaiterType == swmrwtWriter)
  510. SetLastWriterThreadId();
  511. return RPC_S_OK;
  512. // no break necessary
  513. // break;
  514. case LastReader:
  515. if (WaiterType == swmrwtReader)
  516. {
  517. OldWaiter = Lock();
  518. if (!VerifyState(OldWaiter, LastReader))
  519. {
  520. UnlockAndCommit(OldWaiter);
  521. // the state has changed while we were switching - loop around
  522. continue;
  523. }
  524. LockSharedOnLastReader(WaiterCache, OldWaiter, Waiter);
  525. // cache is consumed or freed after LockSharedOnLastReader
  526. LastWaiter = RemoveReaderStateFromWaiterPtr(OldWaiter);
  527. }
  528. else
  529. {
  530. if (!Waiter)
  531. {
  532. Waiter = AllocateWaiter(WaiterType, TRUE);
  533. if (!Waiter)
  534. {
  535. return RPC_S_OUT_OF_MEMORY;
  536. }
  537. }
  538. else
  539. {
  540. RpcStatus = Waiter->InitializeEventIfNecessary();
  541. if (RpcStatus != RPC_S_OK)
  542. {
  543. StoreOrFreeSpareWaiter(WaiterCache, Waiter);
  544. return RpcStatus;
  545. }
  546. }
  547. OldWaiter = Lock();
  548. if (!VerifyState(OldWaiter, LastReader))
  549. {
  550. UnlockAndCommit(OldWaiter);
  551. // the state has changed while we were switching - loop around
  552. continue;
  553. }
  554. LockExclusiveOnLastReader(WaiterCache,
  555. OldWaiter,
  556. Waiter);
  557. // the cache has been consumed and indicated as such
  558. LastWaiter = Waiter;
  559. SetLastWriterThreadId();
  560. }
  561. // on return, we must own the lock
  562. ASSERT((LastWaiter->Flags & SWMRWaiter::OwnLockMask) == SWMRWaiter::OwnLock);
  563. SetFirstWaiter(LastWaiter);
  564. return RPC_S_OK;
  565. // break is not needed
  566. // break;
  567. case LastWriter:
  568. // the lock is owned by a writer - we must queue ourselves.
  569. // For this, we need a waiter block
  570. if (!Waiter)
  571. {
  572. Waiter = AllocateWaiter(WaiterType,
  573. TRUE);
  574. if (!Waiter)
  575. {
  576. return RPC_S_OUT_OF_MEMORY;
  577. }
  578. }
  579. else
  580. {
  581. RpcStatus = Waiter->InitializeEventIfNecessary();
  582. if (RpcStatus != RPC_S_OK)
  583. {
  584. StoreOrFreeSpareWaiter(WaiterCache, Waiter);
  585. return RpcStatus;
  586. }
  587. }
  588. OldWaiter = Lock();
  589. if (!VerifyState(OldWaiter, LastWriter))
  590. {
  591. UnlockAndCommit(OldWaiter);
  592. // the state has changed while we were switching - loop around
  593. continue;
  594. }
  595. LockSharedOrExclusiveOnLastWriter(WaiterCache,
  596. OldWaiter,
  597. Waiter,
  598. DesiredState);
  599. // the cache has been consumed and indicated as such
  600. // on return, we must own the lock
  601. ASSERT((Waiter->Flags & SWMRWaiter::OwnLockMask) == SWMRWaiter::OwnLock);
  602. SetFirstWaiter(Waiter);
  603. if (WaiterType == swmrwtWriter)
  604. SetLastWriterThreadId();
  605. return RPC_S_OK;
  606. // no need for break
  607. // break
  608. case Locked:
  609. SpinOrYield(&fNonFirstIteration, this);
  610. break;
  611. // no need for default
  612. // default:
  613. }
  614. }
  615. }
  616. void
  617. SWMRLock::UnlockShared (
  618. IN OUT SWMRWaiter **WaiterCache OPTIONAL
  619. )
  620. /*++
  621. Routine Description:
  622. Releases a lock obtained for shared access.
  623. Arguments:
  624. WaiterCache - An optional parameter allowing caching of waiter blocks.
  625. If WaiterCache == NULL, no caching will be done.
  626. If WaiterCache != NULL, but *WaiterCache == NULL, *WaiterCache
  627. may be set to the new Waiter block. If *WaiterCache != NULL,
  628. and a waiter block is produces as a result of the operation, it will
  629. be freed.
  630. Return Value:
  631. --*/
  632. {
  633. SWMRWaiter *OldWaiter;
  634. SWMRWaiter *FirstWaiter;
  635. long NewRefCount;
  636. SWMRWaiter *Next;
  637. HANDLE hEvent;
  638. FirstWaiter = GetFirstWaiter();
  639. ASSERT(FirstWaiter != NULL);
  640. OldWaiter = Lock();
  641. NewRefCount = -- FirstWaiter->RefCount;
  642. if (NewRefCount == 0)
  643. {
  644. Next = FirstWaiter->Next;
  645. if (Next)
  646. {
  647. Next->Flags |= SWMRWaiter::OwnLock;
  648. hEvent = Next->hEvent;
  649. ASSERT(hEvent);
  650. ASSERT((Next->Flags & SWMRWaiter::WaiterTypeMask) == swmrwtWriter);
  651. SetFirstWaiter(Next);
  652. UnlockAndCommit(OldWaiter);
  653. StoreOrFreeSpareWaiter(WaiterCache, FirstWaiter);
  654. SetEvent(hEvent);
  655. }
  656. else
  657. {
  658. UnlockAndCommit(NULL);
  659. StoreOrFreeSpareWaiter(WaiterCache, FirstWaiter);
  660. }
  661. }
  662. else
  663. {
  664. UnlockAndCommit(OldWaiter);
  665. }
  666. }
  667. void
  668. SWMRLock::UnlockExclusive (
  669. IN OUT SWMRWaiter **WaiterCache OPTIONAL
  670. )
  671. /*++
  672. Routine Description:
  673. Releases a lock obtained for exclusive access.
  674. Arguments:
  675. WaiterCache - An optional parameter allowing caching of waiter blocks.
  676. If WaiterCache == NULL, no caching will be done.
  677. If WaiterCache != NULL, but *WaiterCache == NULL, *WaiterCache
  678. may be set to the new Waiter block. If *WaiterCache != NULL,
  679. and a waiter block is produces as a result of the operation, it will
  680. be freed.
  681. Return Value:
  682. --*/
  683. {
  684. SWMRWaiter *OldWaiter;
  685. SWMRWaiter *FirstWaiter;
  686. SWMRWaiter *Next;
  687. FirstWaiter = GetFirstWaiter();
  688. ASSERT(FirstWaiter != NULL);
  689. OldWaiter = Lock();
  690. Next = FirstWaiter->Next;
  691. if (Next)
  692. {
  693. Next->Flags |= SWMRWaiter::OwnLock;
  694. SetFirstWaiter(Next);
  695. UnlockAndCommit(OldWaiter);
  696. StoreOrFreeSpareWaiter(WaiterCache, FirstWaiter);
  697. ASSERT(Next->hEvent);
  698. SetEvent(Next->hEvent);
  699. }
  700. else
  701. {
  702. UnlockAndCommit(NULL);
  703. StoreOrFreeSpareWaiter(WaiterCache, FirstWaiter);
  704. }
  705. }
  706. void
  707. SWMRLock::Unlock (
  708. IN OUT SWMRWaiter **WaiterCache OPTIONAL
  709. )
  710. /*++
  711. Routine Description:
  712. Releases a lock obtained for any access.
  713. Arguments:
  714. WaiterCache - An optional parameter allowing caching of waiter blocks.
  715. If WaiterCache == NULL, no caching will be done.
  716. If WaiterCache != NULL, but *WaiterCache == NULL, *WaiterCache
  717. may be set to the new Waiter block. If *WaiterCache != NULL,
  718. and a waiter block is produces as a result of the operation, it will
  719. be freed.
  720. Return Value:
  721. --*/
  722. {
  723. SWMRWaiter *OldWaiter;
  724. SWMRWaiter *FirstWaiter;
  725. SWMRWaiter *Next;
  726. FirstWaiter = GetFirstWaiter();
  727. ASSERT(FirstWaiter != NULL);
  728. if ((FirstWaiter->Flags & SWMRWaiter::WaiterTypeMask) == swmrwtReader)
  729. {
  730. UnlockShared(WaiterCache);
  731. }
  732. else
  733. {
  734. ASSERT((FirstWaiter->Flags & SWMRWaiter::WaiterTypeMask) == swmrwtWriter);
  735. UnlockExclusive(WaiterCache);
  736. }
  737. }
  738. RPC_STATUS
  739. SWMRLock::ConvertToExclusive (
  740. IN OUT SWMRWaiter **WaiterCache OPTIONAL
  741. )
  742. /*++
  743. Routine Description:
  744. Converts a shared lock to exclusive. In the process it *does not*
  745. release the shared lock (except for case described below in Return Value).
  746. This means that it is legal for the caller
  747. to walk a list in shared mode, then decide to delete an element
  748. and convert it to exclusive. If it gets ERROR_MORE_WRITES, it has the lock,
  749. but a writer may have intervened and any data structure protected by the lock
  750. may have been modified. In this case the thread must protect itself
  751. appropriately. If it gets RPC_S_OK then there is no danger of other writers
  752. coming in and changing the data structure in any way
  753. Arguments:
  754. WaiterCache - An optional parameter allowing caching of waiter blocks.
  755. If WaiterCache == NULL, no caching will be done.
  756. If WaiterCache != NULL, but *WaiterCache == NULL, *WaiterCache
  757. may be set to the new Waiter block. If *WaiterCache != NULL,
  758. the cached value will be used. Its hEvent must be set
  759. Return Value:
  760. RPC_S_OK or RPC_S_* for error. If RPC_S_CANNOT_SUPPORT is returned,
  761. this means the lock is already owned exclusively. It is safe
  762. for multiple threads to try to convert the same lock ownership to exclusive.
  763. The only peculiarity is that if two threads try to get convert the same reader
  764. lock to exclusive, one has to lose the reader lock, and it will get
  765. ERROR_MORE_WRITES. The other will get RPC_S_OK, and it knows it hasn't lost
  766. the reader lock.
  767. --*/
  768. {
  769. SWMRWaiter *CurrentOwner = GetFirstWaiter();
  770. SWMRWaiter *OldWaiter;
  771. SWMRWaiter *LastWaiter;
  772. SWMRWaiter *AllocatedWaiter = NULL;
  773. RPC_STATUS RpcStatus;
  774. SWMRWaiter *NextOwner;
  775. // make sure the current lock is not already in exclusive mode
  776. if ((CurrentOwner->Flags & SWMRWaiter::WaiterTypeMask) == swmrwtWriter)
  777. {
  778. return RPC_S_CANNOT_SUPPORT;
  779. }
  780. while (TRUE)
  781. {
  782. ASSERT(CurrentOwner->RefCount > 0);
  783. if (CurrentOwner->RefCount == 1)
  784. {
  785. OldWaiter = Lock();
  786. if (CurrentOwner->RefCount == 1)
  787. {
  788. NextOwner = CurrentOwner->Next;
  789. // if there is next, it's flags are either swmrwtReader, swmrwtWriter,
  790. // or swmrwtWriter | WriterInsertedBefore
  791. if (NextOwner)
  792. {
  793. ASSERT(
  794. (NextOwner->Flags == swmrwtReader)
  795. || (NextOwner->Flags == swmrwtWriter)
  796. || (NextOwner->Flags == (swmrwtWriter | SWMRWaiter::WriterInsertedBefore))
  797. );
  798. // we need to raise the flag only for next writers who don't have the flag raised
  799. if (NextOwner->Flags == swmrwtWriter)
  800. {
  801. NextOwner->Flags = swmrwtWriter | SWMRWaiter::WriterInsertedBefore;
  802. }
  803. }
  804. CurrentOwner->Flags = swmrwtWriter | SWMRWaiter::OwnLock;
  805. LastWaiter = RemoveStateFromWaiterPtr(OldWaiter);
  806. if (LastWaiter == CurrentOwner)
  807. {
  808. LastWaiter = AddWriterStateInWaiterPtr(LastWaiter);
  809. UnlockAndCommit(LastWaiter);
  810. }
  811. else
  812. {
  813. UnlockAndCommit(OldWaiter);
  814. }
  815. // if we have spun around, and a waiter has been allocated,
  816. // make sure it is freed
  817. StoreOrFreeSpareWaiter(WaiterCache, AllocatedWaiter);
  818. SetLastWriterThreadId();
  819. // get out of the loop
  820. break;
  821. }
  822. // somebody beat us to the punch. Just unlock and loop around
  823. UnlockAndCommit(OldWaiter);
  824. }
  825. else
  826. {
  827. // there are multiple readers on this waiter. We need to allocate
  828. // a new block to wait on.
  829. if (!AllocatedWaiter)
  830. {
  831. if (WaiterCache)
  832. {
  833. AllocatedWaiter = *WaiterCache;
  834. if (AllocatedWaiter)
  835. {
  836. *WaiterCache = NULL;
  837. RpcStatus = AllocatedWaiter->Initialize(swmrwtWriter, TRUE);
  838. if (RpcStatus != RPC_S_OK)
  839. {
  840. *WaiterCache = AllocatedWaiter;
  841. return RpcStatus;
  842. }
  843. }
  844. }
  845. // if we there was no cache, or we found nothing in the cache
  846. // create a waiter
  847. if (!AllocatedWaiter)
  848. {
  849. AllocatedWaiter = AllocateWaiter(swmrwtWriter, TRUE);
  850. if (!AllocatedWaiter)
  851. {
  852. return RPC_S_OUT_OF_MEMORY;
  853. }
  854. }
  855. }
  856. else
  857. {
  858. RpcStatus = AllocatedWaiter->InitializeEventIfNecessary();
  859. if (RpcStatus != RPC_S_OK)
  860. {
  861. StoreOrFreeSpareWaiter(WaiterCache, AllocatedWaiter);
  862. return RpcStatus;
  863. }
  864. }
  865. OldWaiter = Lock();
  866. if (CurrentOwner->RefCount > 1)
  867. {
  868. // form a separate waiter block and queue it after the current
  869. // reader block
  870. AllocatedWaiter->Next = CurrentOwner->Next;
  871. CurrentOwner->Next = AllocatedWaiter;
  872. CurrentOwner->RefCount --;
  873. ASSERT(CurrentOwner->RefCount >= 1);
  874. ASSERT(CurrentOwner->Flags == (swmrwtReader | SWMRWaiter::OwnLock));
  875. LastWaiter = RemoveStateFromWaiterPtr(OldWaiter);
  876. // if we were the last waiter ...
  877. if (LastWaiter == CurrentOwner)
  878. {
  879. // ... update the last to point to us
  880. LastWaiter = AddWriterStateInWaiterPtr(AllocatedWaiter);
  881. UnlockAndCommit(LastWaiter);
  882. }
  883. else
  884. {
  885. ASSERT (AllocatedWaiter->Next != NULL);
  886. // if the next waiter is a writer, indicate to it
  887. // that we have moved in before it. This allows
  888. // arbitraging b/n multiple converts from shared
  889. // to exclusive. Pure writers will ignore this flag
  890. if (AllocatedWaiter->Next->Flags == swmrwtWriter)
  891. {
  892. AllocatedWaiter->Next->Flags = swmrwtWriter | SWMRWaiter::WriterInsertedBefore;
  893. }
  894. else
  895. {
  896. ASSERT((AllocatedWaiter->Next->Flags & SWMRWaiter::WaiterTypeMask) != swmrwtWriter);
  897. }
  898. // just unlock
  899. UnlockAndCommit(OldWaiter);
  900. }
  901. ASSERT(AllocatedWaiter->hEvent);
  902. WaitForSingleObject(AllocatedWaiter->hEvent, INFINITE);
  903. // we must own the lock here
  904. ASSERT((AllocatedWaiter->Flags == (SWMRWaiter::OwnLock | swmrwtWriter))
  905. || (AllocatedWaiter->Flags == (SWMRWaiter::OwnLock | swmrwtWriter | SWMRWaiter::WriterInsertedBefore)));
  906. // indicate the cache has been consumed
  907. if (WaiterCache)
  908. {
  909. *WaiterCache = NULL;
  910. }
  911. SetLastWriterThreadId();
  912. if (AllocatedWaiter->Flags
  913. == (SWMRWaiter::OwnLock | swmrwtWriter | SWMRWaiter::WriterInsertedBefore))
  914. {
  915. return ERROR_MORE_WRITES;
  916. }
  917. else
  918. {
  919. return RPC_S_OK;
  920. }
  921. }
  922. else
  923. {
  924. // this has become a single reader entry (us).
  925. // just convert it to a writer - release
  926. // the lock and loop around
  927. UnlockAndCommit(OldWaiter);
  928. }
  929. }
  930. }
  931. return RPC_S_OK;
  932. }
  933. RPC_STATUS
  934. SWMRLock::ConvertToShared (
  935. IN OUT SWMRWaiter **WaiterCache OPTIONAL,
  936. IN BOOL fSyncCacheUsed
  937. )
  938. /*++
  939. Routine Description:
  940. Converts an exclusive lock to shared. In the process it *does not*
  941. release the shared lock. This means that it is legal for the caller
  942. to walk a list in exclusive mode, then decide to move to shared mode
  943. so that new readers can come in, and it is safe to assume that no
  944. new writer has intervened in the meantime.
  945. Arguments:
  946. WaiterCache - An optional parameter allowing caching of waiter blocks.
  947. If WaiterCache == NULL, no caching will be done.
  948. If WaiterCache != NULL, but *WaiterCache == NULL, *WaiterCache
  949. may be set to the new Waiter block. If *WaiterCache != NULL,
  950. the cached value will be used. Its hEvent must be set
  951. fSyncCacheUsed - if a caller was using sync cache (i.e. a thread tied
  952. or a call tied cache), it may specify non-FALSE to this parameter
  953. to cause this function to allocate a new waiter block if the waiter
  954. block may be needed by other waiters.
  955. Note that whether the cache is sync depends on previous calls to
  956. LockShared/LockExclusive/ConvertToExclusive. If you don't have
  957. enough information to determine what those calls were using,
  958. always specify TRUE. This will always work. FALSE can be used
  959. as an additional optimization if we know the history of our waiter
  960. block.
  961. Return Value:
  962. RPC_S_OK or RPC_S_* for error. If RPC_S_CANNOT_SUPPORT is returned,
  963. this means the lock is already owned in shared mode. It is safe
  964. for multiple threads to try to convert the same lock ownership to shared.
  965. --*/
  966. {
  967. SWMRWaiter *CurrentOwner = GetFirstWaiter();
  968. SWMRWaiter *OldWaiter;
  969. SWMRWaiter *LastWaiter;
  970. SWMRWaiter *AllocatedWaiter = NULL;
  971. SWMRWaiter *NextWaiter;
  972. HANDLE hEvent;
  973. // make sure the current lock is not already in shared mode
  974. if ((CurrentOwner->Flags & SWMRWaiter::WaiterTypeMask) == swmrwtReader)
  975. return RPC_S_CANNOT_SUPPORT;
  976. if (fSyncCacheUsed)
  977. {
  978. // you cannot claim that you use a sync cache if you don't have
  979. // any cache at all
  980. ASSERT(WaiterCache != NULL);
  981. }
  982. ASSERT(CurrentOwner->RefCount == 1);
  983. while (TRUE)
  984. {
  985. NextWaiter = CurrentOwner->Next;
  986. // we have three cases - last owner, non-last owner with following
  987. // readers and non-last owner with following writers
  988. if (NextWaiter == NULL)
  989. {
  990. // if a sync cache was used, we must return whatever we took
  991. // out of the cache, and recreate a waiter for the cache,
  992. // because we're going to become last readers, and other
  993. // readers may join, causing lifetime issues with our cached
  994. // entry if it is sync
  995. if (fSyncCacheUsed && (*WaiterCache == NULL) && (CurrentOwner != &CachedWaiter))
  996. {
  997. if (!AllocatedWaiter)
  998. {
  999. AllocatedWaiter = AllocateWaiter(swmrwtReader, FALSE);
  1000. if (!AllocatedWaiter)
  1001. {
  1002. return RPC_S_OUT_OF_MEMORY;
  1003. }
  1004. AllocatedWaiter->Flags = swmrwtReader | SWMRWaiter::OwnLock;
  1005. }
  1006. LastWaiter = AddReaderStateInWaiterPtr(AllocatedWaiter);
  1007. }
  1008. else
  1009. {
  1010. LastWaiter = AddReaderStateInWaiterPtr(CurrentOwner);
  1011. }
  1012. OldWaiter = Lock();
  1013. NextWaiter = CurrentOwner->Next;
  1014. if (NextWaiter)
  1015. {
  1016. // somebody managed to queue a waiter before we converted -
  1017. // unlock and loop around
  1018. UnlockAndCommit(OldWaiter);
  1019. }
  1020. else
  1021. {
  1022. if (AllocatedWaiter)
  1023. {
  1024. // the first waiter has changed - update it
  1025. SetFirstWaiter(AllocatedWaiter);
  1026. }
  1027. else
  1028. {
  1029. // update the flags of the old waiter
  1030. CurrentOwner->Flags = swmrwtReader | SWMRWaiter::OwnLock;
  1031. }
  1032. UnlockAndCommit(LastWaiter);
  1033. if (AllocatedWaiter)
  1034. {
  1035. ASSERT(CurrentOwner != &CachedWaiter);
  1036. // we were asked to free up a previous cached item
  1037. *WaiterCache = CurrentOwner;
  1038. }
  1039. // break out of the loop
  1040. break;
  1041. }
  1042. }
  1043. else if ((NextWaiter->Flags & SWMRWaiter::WaiterTypeMask) == swmrwtReader)
  1044. {
  1045. // we need to take the lock to avoid races with threads
  1046. // trying to queue themselves after the next waiter
  1047. OldWaiter = Lock();
  1048. // here we effectively join the next waiter and discard the current
  1049. // waiter block. This allows
  1050. // us to free the current item to the cache, ensuring good
  1051. // performance for sync caches
  1052. NextWaiter->Flags |= SWMRWaiter::OwnLock;
  1053. NextWaiter->RefCount ++;
  1054. hEvent = NextWaiter->hEvent;
  1055. ASSERT(hEvent);
  1056. SetFirstWaiter(NextWaiter);
  1057. UnlockAndCommit(OldWaiter);
  1058. SetEvent(hEvent);
  1059. StoreOrFreeSpareWaiter(WaiterCache, CurrentOwner);
  1060. StoreOrFreeSpareWaiter(WaiterCache, AllocatedWaiter);
  1061. break;
  1062. }
  1063. else
  1064. {
  1065. ASSERT((NextWaiter->Flags == swmrwtWriter)
  1066. || (NextWaiter->Flags == (swmrwtWriter | SWMRWaiter::WriterInsertedBefore)));
  1067. // We must reset the flags. This is because there may be pending
  1068. // WriterInsertedBefore. We want to clear this so that we don't
  1069. // return ERROR_MORE_WRITES if they convert back to exclusive and
  1070. // find this stale flag
  1071. CurrentOwner->Flags = SWMRWaiter::OwnLock | swmrwtReader;
  1072. StoreOrFreeSpareWaiter(WaiterCache, AllocatedWaiter);
  1073. break;
  1074. }
  1075. }
  1076. return RPC_S_OK;
  1077. }
  1078. #if defined(_TEST_SWMR_)
  1079. SWMRLock TestLock;
  1080. long TestCheck = 0;
  1081. HANDLE HoldEvent = NULL;
  1082. BOOL fHoldFlag = FALSE;
  1083. long HoldingThreadCount = 0;
  1084. DWORD WINAPI TestThreadProc (LPVOID )
  1085. {
  1086. RPC_STATUS RpcStatus;
  1087. long OldTestCheck;
  1088. SWMRWaiter **WaiterCache;
  1089. SWMRWaiter *CachedWaiter = NULL;
  1090. int i, j;
  1091. int RndNum;
  1092. BOOL fReader;
  1093. BOOL fSyncCache;
  1094. RndNum = rand();
  1095. if (RndNum % 2)
  1096. {
  1097. WaiterCache = NULL;
  1098. }
  1099. else
  1100. {
  1101. WaiterCache = &CachedWaiter;
  1102. }
  1103. RndNum = rand();
  1104. // one out of 3 is writer
  1105. if ((RndNum % 3) == 2)
  1106. {
  1107. fReader = FALSE;
  1108. Dump("%d is a writer\n", GetCurrentThreadId());
  1109. }
  1110. else
  1111. {
  1112. fReader = TRUE;
  1113. Dump("%d is a reader\n", GetCurrentThreadId());
  1114. }
  1115. for (; TestCounter < (signed)Iterations; )
  1116. {
  1117. if (fHoldFlag)
  1118. {
  1119. InterlockedIncrement(&HoldingThreadCount);
  1120. WaitForSingleObject(HoldEvent, INFINITE);
  1121. }
  1122. if (fReader)
  1123. {
  1124. RpcStatus = TestLock.LockShared(WaiterCache);
  1125. }
  1126. else
  1127. {
  1128. RpcStatus = TestLock.LockExclusive(WaiterCache);
  1129. }
  1130. if (RpcStatus != RPC_S_OK)
  1131. continue;
  1132. // once in a while we will swap the reader to writer and vice versa
  1133. if ((rand() % 17) == 0)
  1134. {
  1135. if (fReader)
  1136. {
  1137. // current reader. ConvertToExclusive. This can fail due to the
  1138. // fault injection. Retry
  1139. do
  1140. {
  1141. RpcStatus = TestLock.ConvertToExclusive(WaiterCache);
  1142. }
  1143. while ((RpcStatus != RPC_S_OK) && (RpcStatus != ERROR_MORE_WRITES));
  1144. fReader = FALSE;
  1145. }
  1146. else
  1147. {
  1148. if (WaiterCache)
  1149. {
  1150. fSyncCache = (rand() % 3) == 0;
  1151. }
  1152. else
  1153. {
  1154. fSyncCache = FALSE;
  1155. }
  1156. // current writer. ConvertToShared
  1157. do
  1158. {
  1159. RpcStatus = TestLock.ConvertToShared(WaiterCache, fSyncCache);
  1160. }
  1161. while (RpcStatus != RPC_S_OK);
  1162. fReader = TRUE;
  1163. }
  1164. }
  1165. if (fReader)
  1166. {
  1167. OldTestCheck = InterlockedIncrement(&TestCheck);
  1168. ASSERT(OldTestCheck >= 1);
  1169. if (OldTestCheck < 1)
  1170. DebugBreak();
  1171. }
  1172. else
  1173. {
  1174. OldTestCheck = InterlockedCompareExchange(&TestCheck, 1, 0);
  1175. ASSERT(OldTestCheck == 0);
  1176. if (OldTestCheck != 0)
  1177. DebugBreak();
  1178. }
  1179. TestCounter ++;
  1180. if ((TestCounter % 100000) == 0)
  1181. {
  1182. // print something to screen
  1183. Dump("%d: Test %ld\n", GetCurrentThreadId(), TestCounter);
  1184. }
  1185. for (i = 0; i < 50; i ++)
  1186. {
  1187. // random function call to prevent the compiler from
  1188. // optimizing the loop away
  1189. RndNum = GetCurrentThreadId();
  1190. }
  1191. if (fReader)
  1192. {
  1193. OldTestCheck = InterlockedDecrement(&TestCheck);
  1194. ASSERT(OldTestCheck >= 0);
  1195. if (OldTestCheck < 0)
  1196. DebugBreak();
  1197. }
  1198. else
  1199. {
  1200. OldTestCheck = InterlockedCompareExchange(&TestCheck, 0, 1);
  1201. if (OldTestCheck != 1)
  1202. DebugBreak();
  1203. ASSERT(OldTestCheck == 1);
  1204. }
  1205. if (fReader)
  1206. {
  1207. TestLock.UnlockShared(WaiterCache);
  1208. }
  1209. else
  1210. {
  1211. TestLock.UnlockExclusive(WaiterCache);
  1212. }
  1213. RndNum = rand() % 100;
  1214. if (RndNum == 11)
  1215. {
  1216. Sleep(10);
  1217. }
  1218. }
  1219. if (CachedWaiter)
  1220. {
  1221. TestLock.FreeWaiterCache(&CachedWaiter);
  1222. }
  1223. return 0;
  1224. }
  1225. void Test (void)
  1226. {
  1227. int i;
  1228. HANDLE hThread;
  1229. int Retries;
  1230. srand( GetTickCount() );
  1231. HoldEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  1232. ProcessHeap = GetProcessHeap();
  1233. for (i = 0; i < Clients; i ++)
  1234. {
  1235. hThread = CreateThread(NULL, 0, TestThreadProc, NULL, 0, NULL);
  1236. CloseHandle(hThread);
  1237. }
  1238. while (TRUE)
  1239. {
  1240. Sleep(rand());
  1241. if (TestCounter >= (signed)Iterations)
  1242. continue;
  1243. Retries = 0;
  1244. fHoldFlag = TRUE;
  1245. while (HoldingThreadCount < Clients)
  1246. {
  1247. if (Retries > 3)
  1248. {
  1249. Dump("Threads did not pick up the hold command! Breaking ...\n");
  1250. DebugBreak();
  1251. ASSERT(0);
  1252. }
  1253. Sleep(50);
  1254. Dump("Holding threads ...\n");
  1255. Retries ++;
  1256. }
  1257. Dump("All threads checked in. Releasing ...\n");
  1258. HoldingThreadCount = 0;
  1259. fHoldFlag = FALSE;
  1260. PulseEvent(HoldEvent);
  1261. }
  1262. Sleep(INFINITE);
  1263. }
  1264. #endif // _TEST_SWMR_