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.

765 lines
24 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. pushlock.c
  5. Abstract:
  6. This module houses routines that do push locking.
  7. Push locks are capable of being acquired in both shared and exclusive mode.
  8. Properties include:
  9. They can not be acquired recursively.
  10. They are small (the size of a pointer) and can be used when embeded in pagable data.
  11. Acquire and release is done lock free. On lock contention the waiters are chained
  12. through the lock and local stack space.
  13. This is the structure of a push lock:
  14. E == Exclusive bit
  15. W == Waiters present
  16. SC == Share count
  17. P == Pointer to wait block
  18. +----------+---+---+
  19. | SC | E | W | E, W are single bits W == 0
  20. +----------+---+---+
  21. +--------------+---+
  22. | P | W | W == 1. Pointer is the address of a chain of stack local wait blocks
  23. +--------------+---+
  24. The non-contented acquires and releases are trivial. Interlocked operations make the following
  25. transformations.
  26. (SC=0,E=0,W=0) === Exclusive acquire ===> (SC=0,E=1,W=0)
  27. (SC=n,E=0,W=0) === Shared acquire ===> (SC=n+1,E=0,W=0)
  28. (SC=0,E=1,W=0) === Exclusive release ===> (SC=0,E=0,W=0)
  29. (SC=n,E=0,W=0) === Shared release ===> (SC=n-1,E=0,W=0) n > 0
  30. Contention causes the acquiring thread to produce a local stack based wait block and to
  31. enqueue it to the front of the list.
  32. (SC=n,E=e,W=0) === Exclusive acquire ===> (P=LWB(SSC=n,E=e),W=1) LWB = local wait block,
  33. SSC = Saved share count,
  34. n > 0 or e == 1.
  35. (SC=0,E=1,W=0) === Shared acquire ===> (P=LWB(SSC=0,E=0),W=1) LWB = local wait block,
  36. SSC = Saved share count.
  37. After contention has causes one or more threads to queue up wait blocks releases are more
  38. complicated. This following rights are granted to a releasing thread (shared or exclusive).
  39. 1) Shared release threads are allowed to search the wait list until they hit a wait block
  40. with a non-zero share count (this will be a wait block marked exclusive). This thread is
  41. allowed to use an interlocked operation to decrement this value. If this thread
  42. transitioned the value to zero then it obtains the rights of an exclusive release thread
  43. 2) Exclusive threads are allowed to search the wait list until they find a continuous chain
  44. of shared wait blocks or they find the last wait block is an exclusive waiter. This thread
  45. may then break the chain at this point or update the header to show a single exclusive
  46. owner or multiple shared owners. Breaking the list can be done with normal assignment
  47. but updating the header requires interlocked exchange compare.
  48. Author:
  49. Neill Clift (NeillC) 30-Sep-2000
  50. Revision History:
  51. --*/
  52. #include "exp.h"
  53. #pragma hdrstop
  54. #ifdef ALLOC_PRAGMA
  55. #pragma alloc_text(PAGE, ExBlockPushLock)
  56. #pragma alloc_text(PAGE, ExfAcquirePushLockExclusive)
  57. #pragma alloc_text(PAGE, ExfAcquirePushLockShared)
  58. #pragma alloc_text(PAGE, ExfUnblockPushLock)
  59. #pragma alloc_text(PAGE, ExAllocateCacheAwarePushLock)
  60. #pragma alloc_text(PAGE, ExFreeCacheAwarePushLock)
  61. #pragma alloc_text(PAGE, ExAcquireCacheAwarePushLockExclusive)
  62. #pragma alloc_text(PAGE, ExReleaseCacheAwarePushLockExclusive)
  63. #endif
  64. NTKERNELAPI
  65. VOID
  66. FASTCALL
  67. ExfAcquirePushLockExclusive (
  68. IN PEX_PUSH_LOCK PushLock
  69. )
  70. /*++
  71. Routine Description:
  72. Acquire a push lock exclusively
  73. Arguments:
  74. PushLock - Push lock to be acquired
  75. Return Value:
  76. None
  77. --*/
  78. {
  79. EX_PUSH_LOCK OldValue, NewValue;
  80. EX_PUSH_LOCK_WAIT_BLOCK WaitBlock;
  81. OldValue = *PushLock;
  82. while (1) {
  83. //
  84. // If the lock is already held exclusively/shared or there are waiters then
  85. // we need to wait.
  86. //
  87. if (OldValue.Value == 0) {
  88. NewValue.Value = OldValue.Value + EX_PUSH_LOCK_EXCLUSIVE;
  89. NewValue.Ptr = InterlockedCompareExchangePointer (&PushLock->Ptr,
  90. NewValue.Ptr,
  91. OldValue.Ptr);
  92. if (NewValue.Ptr == OldValue.Ptr) {
  93. break;
  94. }
  95. } else {
  96. KeInitializeEvent (&WaitBlock.WakeEvent, SynchronizationEvent, FALSE);
  97. WaitBlock.Exclusive = TRUE;
  98. WaitBlock.Last = NULL;
  99. WaitBlock.Previous = NULL;
  100. //
  101. // Move the sharecount to our wait block if need be.
  102. //
  103. if (OldValue.Waiting) {
  104. WaitBlock.Next = (PEX_PUSH_LOCK_WAIT_BLOCK)
  105. (OldValue.Value - EX_PUSH_LOCK_WAITING);
  106. WaitBlock.ShareCount = 0;
  107. } else {
  108. WaitBlock.Next = NULL;
  109. WaitBlock.ShareCount = (ULONG) OldValue.Shared;
  110. }
  111. NewValue.Ptr = ((PUCHAR) &WaitBlock) + EX_PUSH_LOCK_WAITING;
  112. ASSERT ((NewValue.Value & EX_PUSH_LOCK_WAITING) != 0);
  113. NewValue.Ptr = InterlockedCompareExchangePointer (&PushLock->Ptr,
  114. NewValue.Ptr,
  115. OldValue.Ptr);
  116. if (NewValue.Ptr == OldValue.Ptr) {
  117. KeWaitForSingleObject (&WaitBlock.WakeEvent,
  118. WrPushLock,
  119. KernelMode,
  120. FALSE,
  121. NULL);
  122. ASSERT ((WaitBlock.ShareCount == 0) && (WaitBlock.Next == NULL));
  123. break;
  124. }
  125. }
  126. OldValue = NewValue;
  127. }
  128. }
  129. NTKERNELAPI
  130. VOID
  131. FASTCALL
  132. ExfAcquirePushLockShared (
  133. IN PEX_PUSH_LOCK PushLock
  134. )
  135. /*++
  136. Routine Description:
  137. Acquire a push lock shared
  138. Arguments:
  139. PushLock - Push lock to be acquired
  140. Return Value:
  141. None
  142. --*/
  143. {
  144. EX_PUSH_LOCK OldValue, NewValue;
  145. EX_PUSH_LOCK_WAIT_BLOCK WaitBlock;
  146. OldValue = *PushLock;
  147. while (1) {
  148. //
  149. // If the lock is already held exclusively or there are waiters then we need to wait
  150. //
  151. if (OldValue.Exclusive || OldValue.Waiting) {
  152. KeInitializeEvent (&WaitBlock.WakeEvent, SynchronizationEvent, FALSE);
  153. WaitBlock.Exclusive = 0;
  154. WaitBlock.ShareCount = 0;
  155. WaitBlock.Last = NULL;
  156. WaitBlock.Previous = NULL;
  157. //
  158. // Chain the next block to us if there is one.
  159. //
  160. if (OldValue.Waiting) {
  161. WaitBlock.Next = (PEX_PUSH_LOCK_WAIT_BLOCK)
  162. (OldValue.Value - EX_PUSH_LOCK_WAITING);
  163. } else {
  164. WaitBlock.Next = NULL;
  165. }
  166. NewValue.Ptr = ((PUCHAR) &WaitBlock) + EX_PUSH_LOCK_WAITING;
  167. ASSERT ((NewValue.Value & EX_PUSH_LOCK_WAITING) != 0);
  168. NewValue.Ptr = InterlockedCompareExchangePointer (&PushLock->Ptr,
  169. NewValue.Ptr,
  170. OldValue.Ptr);
  171. if (NewValue.Ptr == OldValue.Ptr) {
  172. KeWaitForSingleObject (&WaitBlock.WakeEvent,
  173. WrPushLock,
  174. KernelMode,
  175. FALSE,
  176. NULL);
  177. ASSERT (WaitBlock.ShareCount == 0);
  178. break;
  179. }
  180. } else {
  181. //
  182. // We only have shared accessors at the moment. We can just update the lock to include this thread.
  183. //
  184. NewValue.Value = OldValue.Value + EX_PUSH_LOCK_SHARE_INC;
  185. ASSERT (!(NewValue.Waiting || NewValue.Exclusive));
  186. NewValue.Ptr = InterlockedCompareExchangePointer (&PushLock->Ptr,
  187. NewValue.Ptr,
  188. OldValue.Ptr);
  189. if (NewValue.Ptr == OldValue.Ptr) {
  190. break;
  191. }
  192. }
  193. OldValue = NewValue;
  194. }
  195. }
  196. NTKERNELAPI
  197. VOID
  198. FASTCALL
  199. ExfReleasePushLock (
  200. IN PEX_PUSH_LOCK PushLock
  201. )
  202. /*++
  203. Routine Description:
  204. Release a push lock that was acquired exclusively or shared
  205. Arguments:
  206. PushLock - Push lock to be released
  207. Return Value:
  208. None
  209. --*/
  210. {
  211. EX_PUSH_LOCK OldValue, NewValue;
  212. PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock, NextWaitBlock, ReleaseWaitList, Previous;
  213. PEX_PUSH_LOCK_WAIT_BLOCK LastWaitBlock, FirstWaitBlock;
  214. ULONG ShareCount;
  215. KIRQL OldIrql;
  216. OldValue = *PushLock;
  217. while (1) {
  218. if (!OldValue.Waiting) {
  219. //
  220. // Either we hold the lock exclusive or shared but not both.
  221. //
  222. ASSERT (OldValue.Exclusive ^ (OldValue.Shared > 0));
  223. //
  224. // We must hold the lock exclusive or shared. We make the assuption that
  225. // the exclusive bit is just below the share count here.
  226. //
  227. NewValue.Value = (OldValue.Value - EX_PUSH_LOCK_EXCLUSIVE) &
  228. ~EX_PUSH_LOCK_EXCLUSIVE;
  229. NewValue.Ptr = InterlockedCompareExchangePointer (&PushLock->Ptr,
  230. NewValue.Ptr,
  231. OldValue.Ptr);
  232. if (NewValue.Ptr == OldValue.Ptr) {
  233. break;
  234. }
  235. //
  236. // Either we gained a new waiter or another shared owner arrived or left
  237. //
  238. ASSERT (NewValue.Waiting || (NewValue.Shared > 0 && !NewValue.Exclusive));
  239. OldValue = NewValue;
  240. } else {
  241. //
  242. // There are waiters chained to the lock. We have to release the share count,
  243. // last exclusive or last chain of shared waiters.
  244. //
  245. WaitBlock = (PEX_PUSH_LOCK_WAIT_BLOCK)
  246. (OldValue.Value - EX_PUSH_LOCK_WAITING);
  247. FirstWaitBlock = WaitBlock;
  248. ReleaseWaitList = WaitBlock;
  249. Previous = NULL;
  250. LastWaitBlock = NULL;
  251. ShareCount = 0;
  252. do {
  253. if (WaitBlock->Last != NULL) {
  254. LastWaitBlock = WaitBlock;
  255. WaitBlock = WaitBlock->Last;
  256. Previous = WaitBlock->Previous;
  257. ReleaseWaitList = WaitBlock;
  258. ASSERT (WaitBlock->Next == NULL);
  259. ASSERT (Previous != NULL);
  260. ShareCount = 0;
  261. }
  262. if (WaitBlock->Exclusive) {
  263. //
  264. // This is an exclusive waiter. If this was the first exclusive waited to a shared acquire
  265. // then it will have the saved share count. If we acquired the lock shared then the count
  266. // must contain a bias for this thread. Release that and if we are not the last shared
  267. // accessor then exit. A later shared release thread will wake the exclusive
  268. // waiter.
  269. //
  270. if (WaitBlock->ShareCount != 0) {
  271. if (InterlockedDecrement ((PLONG)&WaitBlock->ShareCount) != 0) {
  272. return;
  273. }
  274. }
  275. //
  276. // Reset count of share acquires waiting.
  277. //
  278. ShareCount = 0;
  279. } else {
  280. //
  281. // This is a shared waiter. Record the number of these to update the head or the
  282. // previous exclusive waiter.
  283. //
  284. ShareCount++;
  285. }
  286. NextWaitBlock = WaitBlock->Next;
  287. if (NextWaitBlock != NULL) {
  288. NextWaitBlock->Previous = WaitBlock;
  289. if (NextWaitBlock->Exclusive) {
  290. //
  291. // The next block is exclusive. This may be the entry to free.
  292. //
  293. Previous = WaitBlock;
  294. ReleaseWaitList = NextWaitBlock;
  295. } else {
  296. //
  297. // The next block is shared. If the chain start is exclusive then skip to this one
  298. // as the exclusive isn't the thread we will wake up.
  299. //
  300. if (ReleaseWaitList->Exclusive) {
  301. Previous = WaitBlock;
  302. ReleaseWaitList = NextWaitBlock;
  303. }
  304. }
  305. }
  306. WaitBlock = NextWaitBlock;
  307. } while (WaitBlock != NULL);
  308. //
  309. // If our release chain is everything then we have to update the header
  310. //
  311. if (Previous == NULL) {
  312. NewValue.Value = 0;
  313. NewValue.Exclusive = ReleaseWaitList->Exclusive;
  314. NewValue.Shared = ShareCount;
  315. ASSERT (((ShareCount > 0) ^ (ReleaseWaitList->Exclusive)) && !NewValue.Waiting);
  316. NewValue.Ptr = InterlockedCompareExchangePointer (&PushLock->Ptr,
  317. NewValue.Ptr,
  318. OldValue.Ptr);
  319. if (NewValue.Ptr != OldValue.Ptr) {
  320. //
  321. // We are releasing so we could have only gained another waiter
  322. //
  323. ASSERT (NewValue.Waiting);
  324. OldValue = NewValue;
  325. continue;
  326. }
  327. } else {
  328. if (LastWaitBlock != NULL) {
  329. LastWaitBlock->Last = NULL;
  330. }
  331. //
  332. // Truncate the chain at this position and save the share count for all the shared owners to
  333. // decrement later.
  334. //
  335. Previous->Next = NULL;
  336. ASSERT (Previous->ShareCount == 0);
  337. Previous->ShareCount = ShareCount;
  338. //
  339. // Add a pointer to make future searches faster
  340. //
  341. if (Previous->Exclusive && FirstWaitBlock != Previous) {
  342. FirstWaitBlock->Last = Previous;
  343. ASSERT (Previous->Previous != NULL);
  344. }
  345. //
  346. // We are either releasing multiple share accessors or a single exclusive
  347. //
  348. ASSERT ((ShareCount > 0) ^ ReleaseWaitList->Exclusive);
  349. }
  350. //
  351. // If we are waking more than one thread then raise to DPC level to prevent us
  352. // getting rescheduled part way through the operation
  353. //
  354. OldIrql = DISPATCH_LEVEL;
  355. if (ShareCount > 1) {
  356. KeRaiseIrql (DISPATCH_LEVEL, &OldIrql);
  357. }
  358. //
  359. //
  360. // Release the chain of threads we located.
  361. //
  362. do {
  363. NextWaitBlock = ReleaseWaitList->Next;
  364. //
  365. // All the chain should have the same type (Exclusive/Shared).
  366. //
  367. ASSERT (NextWaitBlock == NULL || (ReleaseWaitList->Exclusive == NextWaitBlock->Exclusive));
  368. ASSERT (!ReleaseWaitList->Exclusive || (ReleaseWaitList->ShareCount == 0));
  369. KeSetEventBoostPriority (&ReleaseWaitList->WakeEvent, NULL);
  370. ReleaseWaitList = NextWaitBlock;
  371. } while (ReleaseWaitList != NULL);
  372. if (OldIrql != DISPATCH_LEVEL) {
  373. KeLowerIrql (OldIrql);
  374. }
  375. break;
  376. }
  377. }
  378. }
  379. NTKERNELAPI
  380. VOID
  381. FASTCALL
  382. ExBlockPushLock (
  383. IN PEX_PUSH_LOCK PushLock,
  384. IN PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock
  385. )
  386. /*++
  387. Routine Description:
  388. Block on a push lock
  389. Arguments:
  390. PushLock - Push lock to block on
  391. WaitBlock - Wait block to queue for waiting
  392. Return Value:
  393. None
  394. --*/
  395. {
  396. EX_PUSH_LOCK OldValue, NewValue;
  397. //
  398. // Push the wait block on the list.
  399. //
  400. KeInitializeEvent (&WaitBlock->WakeEvent, SynchronizationEvent, FALSE);
  401. OldValue = *PushLock;
  402. while (1) {
  403. //
  404. // Chain the next block to us if there is one.
  405. //
  406. WaitBlock->Next = OldValue.Ptr;
  407. NewValue.Ptr = InterlockedCompareExchangePointer (&PushLock->Ptr,
  408. WaitBlock,
  409. OldValue.Ptr);
  410. if (NewValue.Ptr == OldValue.Ptr) {
  411. return;
  412. }
  413. OldValue = NewValue;
  414. }
  415. }
  416. NTKERNELAPI
  417. VOID
  418. FASTCALL
  419. ExfUnblockPushLock (
  420. IN PEX_PUSH_LOCK PushLock,
  421. IN PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock OPTIONAL
  422. )
  423. /*++
  424. Routine Description:
  425. Unblock on a push lock
  426. Arguments:
  427. PushLock - Push lock to block on
  428. WaitBlock - Wait block previously queued for waiting or NULL if there wasn't one
  429. Return Value:
  430. None
  431. --*/
  432. {
  433. EX_PUSH_LOCK OldValue;
  434. PEX_PUSH_LOCK_WAIT_BLOCK tWaitBlock;
  435. BOOLEAN FoundOurBlock=FALSE;
  436. //
  437. // Pop the entire chain and wake them all up.
  438. //
  439. OldValue.Ptr = InterlockedExchangePointer (&PushLock->Ptr,
  440. NULL);
  441. while (OldValue.Ptr != NULL) {
  442. tWaitBlock = OldValue.Ptr;
  443. OldValue.Ptr = tWaitBlock->Next;
  444. if (tWaitBlock == WaitBlock) {
  445. FoundOurBlock = TRUE;
  446. } else{
  447. KeSetEvent (&tWaitBlock->WakeEvent, 0, FALSE);
  448. }
  449. }
  450. if (WaitBlock != NULL && !FoundOurBlock) {
  451. KeWaitForSingleObject (&WaitBlock->WakeEvent,
  452. WrPushLock,
  453. KernelMode,
  454. FALSE,
  455. NULL);
  456. }
  457. }
  458. NTKERNELAPI
  459. PEX_PUSH_LOCK_CACHE_AWARE
  460. ExAllocateCacheAwarePushLock (
  461. VOID
  462. )
  463. /*++
  464. Routine Description:
  465. Allocate a cache aware (cache friendly) push lock
  466. Arguments:
  467. None
  468. Return Value:
  469. None
  470. --*/
  471. {
  472. PEX_PUSH_LOCK_CACHE_AWARE PushLockCacheAware;
  473. PEX_PUSH_LOCK_CACHE_AWARE_PADDED PaddedPushLock;
  474. ULONG i, j, MaxLine;
  475. PushLockCacheAware = ExAllocatePoolWithTag (PagedPool,
  476. sizeof (EX_PUSH_LOCK_CACHE_AWARE),
  477. 'pclP');
  478. if (PushLockCacheAware != NULL) {
  479. //
  480. // If we are a non-numa machine then allocate the padded push locks as a single block
  481. //
  482. if (KeNumberNodes == 1) {
  483. PaddedPushLock = ExAllocatePoolWithTag (PagedPool,
  484. sizeof (EX_PUSH_LOCK_CACHE_AWARE_PADDED)*
  485. EX_PUSH_LOCK_FANNED_COUNT,
  486. 'lclP');
  487. if (PaddedPushLock == NULL) {
  488. ExFreePool (PushLockCacheAware);
  489. return NULL;
  490. }
  491. for (i = 0; i < EX_PUSH_LOCK_FANNED_COUNT; i++) {
  492. PaddedPushLock->Single = TRUE;
  493. ExInitializePushLock (&PaddedPushLock->Lock);
  494. PushLockCacheAware->Locks[i] = &PaddedPushLock->Lock;
  495. PaddedPushLock++;
  496. }
  497. } else {
  498. //
  499. // Allocate a different block for each lock and set affinity
  500. // so the allocation comes from that nodes memory.
  501. //
  502. MaxLine = KeNumberProcessors;
  503. if (MaxLine > EX_PUSH_LOCK_FANNED_COUNT) {
  504. MaxLine = EX_PUSH_LOCK_FANNED_COUNT;
  505. }
  506. for (i = 0; i < MaxLine; i++) {
  507. KeSetSystemAffinityThread (AFFINITY_MASK (i));
  508. PaddedPushLock = ExAllocatePoolWithTag (PagedPool,
  509. sizeof (EX_PUSH_LOCK_CACHE_AWARE_PADDED),
  510. 'lclP');
  511. if (PaddedPushLock == NULL) {
  512. for (j = 0; j < i; j++) {
  513. ExFreePool (PushLockCacheAware->Locks[j]);
  514. }
  515. KeRevertToUserAffinityThread ();
  516. ExFreePool (PushLockCacheAware);
  517. return NULL;
  518. }
  519. PaddedPushLock->Single = FALSE;
  520. ExInitializePushLock (&PaddedPushLock->Lock);
  521. PushLockCacheAware->Locks[i] = &PaddedPushLock->Lock;
  522. }
  523. KeRevertToUserAffinityThread ();
  524. }
  525. }
  526. return PushLockCacheAware;
  527. }
  528. NTKERNELAPI
  529. VOID
  530. ExFreeCacheAwarePushLock (
  531. PEX_PUSH_LOCK_CACHE_AWARE PushLock
  532. )
  533. /*++
  534. Routine Description:
  535. Frees a cache aware (cache friendly) push lock
  536. Arguments:
  537. PushLock - Cache aware push lock to be freed
  538. Return Value:
  539. None
  540. --*/
  541. {
  542. ULONG i;
  543. ULONG MaxLine;
  544. if (!CONTAINING_RECORD (PushLock->Locks[0], EX_PUSH_LOCK_CACHE_AWARE_PADDED, Lock)->Single) {
  545. MaxLine = KeNumberProcessors;
  546. if (MaxLine > EX_PUSH_LOCK_FANNED_COUNT) {
  547. MaxLine = EX_PUSH_LOCK_FANNED_COUNT;
  548. }
  549. for (i = 0; i < MaxLine; i++) {
  550. ExFreePool (PushLock->Locks[i]);
  551. }
  552. } else {
  553. ExFreePool (PushLock->Locks[0]);
  554. }
  555. ExFreePool (PushLock);
  556. }
  557. NTKERNELAPI
  558. VOID
  559. ExAcquireCacheAwarePushLockExclusive (
  560. IN PEX_PUSH_LOCK_CACHE_AWARE PushLock
  561. )
  562. /*++
  563. Routine Description:
  564. Acquire a cache aware push lock exclusive.
  565. Arguments:
  566. PushLock - Cache aware push lock to be acquired
  567. Return Value:
  568. None
  569. --*/
  570. {
  571. PEX_PUSH_LOCK *Start, *End;
  572. ULONG MaxLine;
  573. //
  574. // Exclusive acquires must obtain all the slots exclusive.
  575. // Take the first slot exclusive and then we can take the
  576. // rest of the slots in any order we want.
  577. // There is no deadlock here. A->B->C does not deadlock with A->C->B.
  578. //
  579. Start = &PushLock->Locks[1];
  580. MaxLine = KeNumberProcessors;
  581. if (MaxLine > EX_PUSH_LOCK_FANNED_COUNT) {
  582. MaxLine = EX_PUSH_LOCK_FANNED_COUNT;
  583. }
  584. End = &PushLock->Locks[MaxLine - 1];
  585. ExAcquirePushLockExclusive (PushLock->Locks[0]);
  586. while (Start <= End) {
  587. if (ExTryAcquirePushLockExclusive (*Start)) {
  588. Start++;
  589. } else {
  590. ExAcquirePushLockExclusive (*End);
  591. End--;
  592. }
  593. }
  594. }
  595. NTKERNELAPI
  596. VOID
  597. ExReleaseCacheAwarePushLockExclusive (
  598. IN PEX_PUSH_LOCK_CACHE_AWARE PushLock
  599. )
  600. /*++
  601. Routine Description:
  602. Release a cache aware push lock exclusive.
  603. Arguments:
  604. PushLock - Cache aware push lock to be released
  605. Return Value:
  606. None
  607. --*/
  608. {
  609. PEX_PUSH_LOCK *Start, *End;
  610. ULONG MaxLine;
  611. //
  612. // Release the locks in order
  613. //
  614. MaxLine = KeNumberProcessors;
  615. if (MaxLine > EX_PUSH_LOCK_FANNED_COUNT) {
  616. MaxLine = EX_PUSH_LOCK_FANNED_COUNT;
  617. }
  618. End = &PushLock->Locks[MaxLine];
  619. for (Start = &PushLock->Locks[0];
  620. Start < End;
  621. Start++) {
  622. ExReleasePushLockExclusive (*Start);
  623. }
  624. }