Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenpushlock.c
Go to the documentation of this file.
00001 /* 00002 * COPYRIGHT: See COPYING in the top level directory 00003 * PROJECT: ReactOS Kernel 00004 * FILE: ntoskrnl/ex/pushlock.c 00005 * PURPOSE: Pushlock and Cache-Aware Pushlock Implementation 00006 * PROGRAMMER: Alex Ionescu (alex.ionescu@reactos.com) 00007 */ 00008 00009 /* INCLUDES *****************************************************************/ 00010 00011 #include <ntoskrnl.h> 00012 #define NDEBUG 00013 #include <debug.h> 00014 00015 /* DATA **********************************************************************/ 00016 00017 ULONG ExPushLockSpinCount = 0; 00018 00019 #undef EX_PUSH_LOCK 00020 #undef PEX_PUSH_LOCK 00021 00022 /* PRIVATE FUNCTIONS *********************************************************/ 00023 00024 #ifdef _WIN64 00025 #define InterlockedAndPointer(ptr,val) InterlockedAnd64((PLONGLONG)ptr,(LONGLONG)val) 00026 #else 00027 #define InterlockedAndPointer(ptr,val) InterlockedAnd((PLONG)ptr,(LONG)val) 00028 #endif 00029 00030 /*++ 00031 * @name ExpInitializePushLocks 00032 * 00033 * The ExpInitializePushLocks routine initialized Pushlock support. 00034 * 00035 * @param None. 00036 * 00037 * @return None. 00038 * 00039 * @remarks The ExpInitializePushLocks routine sets up the spin on SMP machines. 00040 * 00041 *--*/ 00042 VOID 00043 NTAPI 00044 INIT_FUNCTION 00045 ExpInitializePushLocks(VOID) 00046 { 00047 #ifdef CONFIG_SMP 00048 /* Initialize an internal 1024-iteration spin for MP CPUs */ 00049 if (KeNumberProcessors > 1) 00050 ExPushLockSpinCount = 1024; 00051 #endif 00052 } 00053 00054 /*++ 00055 * @name ExfWakePushLock 00056 * 00057 * The ExfWakePushLock routine wakes a Pushlock that is in the waiting 00058 * state. 00059 * 00060 * @param PushLock 00061 * Pointer to a pushlock that is waiting. 00062 * 00063 * @param OldValue 00064 * Last known value of the pushlock before this routine was called. 00065 * 00066 * @return None. 00067 * 00068 * @remarks This is an internal routine; do not call it manually. Only the system 00069 * can properly know if the pushlock is ready to be awakened or not. 00070 * External callers should use ExfTrytoWakePushLock. 00071 * 00072 *--*/ 00073 VOID 00074 FASTCALL 00075 ExfWakePushLock(PEX_PUSH_LOCK PushLock, 00076 EX_PUSH_LOCK OldValue) 00077 { 00078 EX_PUSH_LOCK NewValue; 00079 PEX_PUSH_LOCK_WAIT_BLOCK PreviousWaitBlock, FirstWaitBlock, LastWaitBlock; 00080 PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock; 00081 KIRQL OldIrql; 00082 00083 /* Start main wake loop */ 00084 for (;;) 00085 { 00086 /* Sanity checks */ 00087 ASSERT(!OldValue.MultipleShared); 00088 00089 /* Check if it's locked */ 00090 while (OldValue.Locked) 00091 { 00092 /* It's not waking anymore */ 00093 NewValue.Value = OldValue.Value &~ EX_PUSH_LOCK_WAKING; 00094 00095 /* Sanity checks */ 00096 ASSERT(!NewValue.Waking); 00097 ASSERT(NewValue.Locked); 00098 ASSERT(NewValue.Waiting); 00099 00100 /* Write the New Value */ 00101 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr, 00102 NewValue.Ptr, 00103 OldValue.Ptr); 00104 if (NewValue.Value == OldValue.Value) return; 00105 00106 /* Someone changed the value behind our back, update it*/ 00107 OldValue = NewValue; 00108 } 00109 00110 /* Save the First Block */ 00111 FirstWaitBlock = (PEX_PUSH_LOCK_WAIT_BLOCK)(OldValue.Value & 00112 ~EX_PUSH_LOCK_PTR_BITS); 00113 WaitBlock = FirstWaitBlock; 00114 00115 /* Try to find the last block */ 00116 while (TRUE) 00117 { 00118 /* Get the last wait block */ 00119 LastWaitBlock = WaitBlock->Last; 00120 00121 /* Check if we found it */ 00122 if (LastWaitBlock) 00123 { 00124 /* Use it */ 00125 WaitBlock = LastWaitBlock; 00126 break; 00127 } 00128 00129 /* Save the previous block */ 00130 PreviousWaitBlock = WaitBlock; 00131 00132 /* Move to next block */ 00133 WaitBlock = WaitBlock->Next; 00134 00135 /* Save the previous block */ 00136 WaitBlock->Previous = PreviousWaitBlock; 00137 } 00138 00139 /* Check if the last Wait Block is not Exclusive or if it's the only one */ 00140 PreviousWaitBlock = WaitBlock->Previous; 00141 if (!(WaitBlock->Flags & EX_PUSH_LOCK_FLAGS_EXCLUSIVE) || 00142 !(PreviousWaitBlock)) 00143 { 00144 /* Destroy the pushlock */ 00145 NewValue.Value = 0; 00146 ASSERT(!NewValue.Waking); 00147 00148 /* Write the New Value */ 00149 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr, 00150 NewValue.Ptr, 00151 OldValue.Ptr); 00152 if (NewValue.Value == OldValue.Value) break; 00153 00154 /* Someone changed the value behind our back, update it*/ 00155 OldValue = NewValue; 00156 } 00157 else 00158 { 00159 /* Link the wait blocks */ 00160 FirstWaitBlock->Last = PreviousWaitBlock; 00161 WaitBlock->Previous = NULL; 00162 00163 /* Sanity checks */ 00164 ASSERT(FirstWaitBlock != WaitBlock); 00165 ASSERT(PushLock->Waiting); 00166 00167 /* Remove waking bit from pushlock */ 00168 InterlockedAndPointer(&PushLock->Value, ~EX_PUSH_LOCK_WAKING); 00169 00170 /* Leave the loop */ 00171 break; 00172 } 00173 } 00174 00175 /* Check if there's a previous block */ 00176 OldIrql = DISPATCH_LEVEL; 00177 if (WaitBlock->Previous) 00178 { 00179 /* Raise to Dispatch */ 00180 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); 00181 } 00182 00183 /* Signaling loop */ 00184 for (;;) 00185 { 00186 /* Get the previous Wait block */ 00187 PreviousWaitBlock = WaitBlock->Previous; 00188 00189 /* Sanity check */ 00190 ASSERT(!WaitBlock->Signaled); 00191 00192 #if DBG 00193 /* We are about to get signaled */ 00194 WaitBlock->Signaled = TRUE; 00195 #endif 00196 00197 /* Set the Wait Bit in the Wait Block */ 00198 if (!InterlockedBitTestAndReset(&WaitBlock->Flags, 1)) 00199 { 00200 /* Nobody signaled us, so do it */ 00201 KeSignalGateBoostPriority(&WaitBlock->WakeGate); 00202 } 00203 00204 /* Set the wait block and check if there still is one to loop*/ 00205 WaitBlock = PreviousWaitBlock; 00206 if (!WaitBlock) break; 00207 } 00208 00209 /* Check if we have to lower back the IRQL */ 00210 if (OldIrql != DISPATCH_LEVEL) KeLowerIrql(OldIrql); 00211 } 00212 00213 /*++ 00214 * @name ExpOptimizePushLockList 00215 * 00216 * The ExpOptimizePushLockList routine optimizes the list of waiters 00217 * associated to a pushlock's wait block. 00218 * 00219 * @param PushLock 00220 * Pointer to a pushlock whose waiter list needs to be optimized. 00221 * 00222 * @param OldValue 00223 * Last known value of the pushlock before this routine was called. 00224 * 00225 * @return None. 00226 * 00227 * @remarks At the end of the optimization, the pushlock will also be wakened. 00228 * 00229 *--*/ 00230 VOID 00231 FASTCALL 00232 ExpOptimizePushLockList(PEX_PUSH_LOCK PushLock, 00233 EX_PUSH_LOCK OldValue) 00234 { 00235 PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock, LastWaitBlock, PreviousWaitBlock, FirstWaitBlock; 00236 EX_PUSH_LOCK NewValue; 00237 00238 /* Start main loop */ 00239 for (;;) 00240 { 00241 /* Check if we've been unlocked */ 00242 if (!OldValue.Locked) 00243 { 00244 /* Wake us up and leave */ 00245 ExfWakePushLock(PushLock, OldValue); 00246 break; 00247 } 00248 00249 /* Get the wait block */ 00250 WaitBlock = (PEX_PUSH_LOCK_WAIT_BLOCK)(OldValue.Value & 00251 ~EX_PUSH_LOCK_PTR_BITS); 00252 00253 /* Loop the blocks */ 00254 FirstWaitBlock = WaitBlock; 00255 while (TRUE) 00256 { 00257 /* Get the last wait block */ 00258 LastWaitBlock = WaitBlock->Last; 00259 if (LastWaitBlock) 00260 { 00261 /* Set this as the new last block, we're done */ 00262 FirstWaitBlock->Last = LastWaitBlock; 00263 break; 00264 } 00265 00266 /* Save the block */ 00267 PreviousWaitBlock = WaitBlock; 00268 00269 /* Get the next block */ 00270 WaitBlock = WaitBlock->Next; 00271 00272 /* Save the previous */ 00273 WaitBlock->Previous = PreviousWaitBlock; 00274 } 00275 00276 /* Remove the wake bit */ 00277 NewValue.Value = OldValue.Value &~ EX_PUSH_LOCK_WAKING; 00278 00279 /* Sanity checks */ 00280 ASSERT(NewValue.Locked); 00281 ASSERT(!NewValue.Waking); 00282 00283 /* Update the value */ 00284 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr, 00285 NewValue.Ptr, 00286 OldValue.Ptr); 00287 00288 /* If we updated correctly, leave */ 00289 if (NewValue.Value == OldValue.Value) break; 00290 00291 /* Update value */ 00292 OldValue = NewValue; 00293 } 00294 } 00295 00296 /*++ 00297 * @name ExTimedWaitForUnblockPushLock 00298 * 00299 * The ExTimedWaitForUnblockPushLock routine waits for a pushlock 00300 * to be unblocked, for a specified internal. 00301 * 00302 * @param PushLock 00303 * Pointer to a pushlock whose waiter list needs to be optimized. 00304 * 00305 * @param WaitBlock 00306 * Pointer to the pushlock's wait block. 00307 * 00308 * @param Timeout 00309 * Amount of time to wait for this pushlock to be unblocked. 00310 * 00311 * @return STATUS_SUCCESS is the pushlock is now unblocked, otherwise the error 00312 * code returned by KeWaitForSingleObject. 00313 * 00314 * @remarks If the wait fails, then a manual unblock is attempted. 00315 * 00316 *--*/ 00317 NTSTATUS 00318 FASTCALL 00319 ExTimedWaitForUnblockPushLock(IN PEX_PUSH_LOCK PushLock, 00320 IN PVOID WaitBlock, 00321 IN PLARGE_INTEGER Timeout) 00322 { 00323 NTSTATUS Status; 00324 00325 /* Initialize the wait event */ 00326 KeInitializeEvent(&((PEX_PUSH_LOCK_WAIT_BLOCK)WaitBlock)->WakeEvent, 00327 SynchronizationEvent, 00328 FALSE); 00329 00330 #ifdef CONFIG_SMP 00331 /* Spin on the push lock if necessary */ 00332 if (ExPushLockSpinCount) 00333 { 00334 ULONG i = ExPushLockSpinCount; 00335 00336 do 00337 { 00338 /* Check if we got lucky and can leave early */ 00339 if (!(*(volatile LONG *)&((PEX_PUSH_LOCK_WAIT_BLOCK)WaitBlock)->Flags & EX_PUSH_LOCK_WAITING)) 00340 return STATUS_SUCCESS; 00341 00342 YieldProcessor(); 00343 } while (--i); 00344 } 00345 #endif 00346 00347 /* Now try to remove the wait bit */ 00348 if (InterlockedBitTestAndReset(&((PEX_PUSH_LOCK_WAIT_BLOCK)WaitBlock)->Flags, 00349 EX_PUSH_LOCK_FLAGS_WAIT_V)) 00350 { 00351 /* Nobody removed it already, let's do a full wait */ 00352 Status = KeWaitForSingleObject(&((PEX_PUSH_LOCK_WAIT_BLOCK)WaitBlock)-> 00353 WakeEvent, 00354 WrPushLock, 00355 KernelMode, 00356 FALSE, 00357 Timeout); 00358 /* Check if the wait was satisfied */ 00359 if (Status != STATUS_SUCCESS) 00360 { 00361 /* Try unblocking the pushlock if it was not */ 00362 ExfUnblockPushLock(PushLock, WaitBlock); 00363 } 00364 } 00365 else 00366 { 00367 /* Someone beat us to it, no need to wait */ 00368 Status = STATUS_SUCCESS; 00369 } 00370 00371 /* Return status */ 00372 return Status; 00373 } 00374 00375 /*++ 00376 * @name ExWaitForUnblockPushLock 00377 * 00378 * The ExWaitForUnblockPushLock routine waits for a pushlock 00379 * to be unblocked, for a specified internal. 00380 * 00381 * @param PushLock 00382 * Pointer to a pushlock whose waiter list needs to be optimized. 00383 * 00384 * @param WaitBlock 00385 * Pointer to the pushlock's wait block. 00386 * 00387 * @return STATUS_SUCCESS is the pushlock is now unblocked, otherwise the error 00388 * code returned by KeWaitForSingleObject. 00389 * 00390 * @remarks If the wait fails, then a manual unblock is attempted. 00391 * 00392 *--*/ 00393 VOID 00394 FASTCALL 00395 ExWaitForUnblockPushLock(IN PEX_PUSH_LOCK PushLock, 00396 IN PVOID WaitBlock) 00397 { 00398 /* Call the timed function with no timeout */ 00399 ExTimedWaitForUnblockPushLock(PushLock, WaitBlock, NULL); 00400 } 00401 00402 /*++ 00403 * @name ExBlockPushLock 00404 * 00405 * The ExBlockPushLock routine blocks a pushlock. 00406 * 00407 * @param PushLock 00408 * Pointer to a pushlock whose waiter list needs to be optimized. 00409 * 00410 * @param WaitBlock 00411 * Pointer to the pushlock's wait block. 00412 * 00413 * @return None. 00414 * 00415 * @remarks None. 00416 * 00417 *--*/ 00418 VOID 00419 FASTCALL 00420 ExBlockPushLock(PEX_PUSH_LOCK PushLock, 00421 PVOID pWaitBlock) 00422 { 00423 PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock = pWaitBlock; 00424 EX_PUSH_LOCK NewValue, OldValue; 00425 00426 /* Detect invalid wait block alignment */ 00427 ASSERT(((ULONG_PTR)pWaitBlock & 0xF) == 0); 00428 00429 /* Set the waiting bit */ 00430 WaitBlock->Flags = EX_PUSH_LOCK_FLAGS_WAIT; 00431 00432 /* Get the old value */ 00433 OldValue = *PushLock; 00434 00435 /* Start block loop */ 00436 for (;;) 00437 { 00438 /* Link the wait blocks */ 00439 WaitBlock->Next = OldValue.Ptr; 00440 00441 /* Set the new wait block value */ 00442 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr, 00443 WaitBlock, 00444 OldValue.Ptr); 00445 if (OldValue.Ptr == NewValue.Ptr) break; 00446 00447 /* Try again with the new value */ 00448 OldValue = NewValue; 00449 } 00450 } 00451 00452 /* PUBLIC FUNCTIONS **********************************************************/ 00453 00454 /*++ 00455 * @name ExAcquirePushLockExclusive 00456 * @implemented NT5.1 00457 * 00458 * The ExAcquirePushLockExclusive macro exclusively acquires a PushLock. 00459 * 00460 * @params PushLock 00461 * Pointer to the pushlock which is to be acquired. 00462 * 00463 * @return None. 00464 * 00465 * @remarks Callers of ExAcquirePushLockShared must be running at IRQL <= APC_LEVEL. 00466 * This macro should usually be paired up with KeAcquireCriticalRegion. 00467 * 00468 *--*/ 00469 VOID 00470 FASTCALL 00471 ExfAcquirePushLockExclusive(PEX_PUSH_LOCK PushLock) 00472 { 00473 EX_PUSH_LOCK OldValue = *PushLock, NewValue, TempValue; 00474 BOOLEAN NeedWake; 00475 DEFINE_WAIT_BLOCK(WaitBlock); 00476 00477 /* Start main loop */ 00478 for (;;) 00479 { 00480 /* Check if it's unlocked */ 00481 if (!OldValue.Locked) 00482 { 00483 /* Lock it */ 00484 NewValue.Value = OldValue.Value | EX_PUSH_LOCK_LOCK; 00485 ASSERT(NewValue.Locked); 00486 00487 /* Set the new value */ 00488 if (InterlockedCompareExchangePointer(&PushLock->Ptr, 00489 NewValue.Ptr, 00490 OldValue.Ptr) != OldValue.Ptr) 00491 { 00492 /* Retry */ 00493 OldValue = *PushLock; 00494 continue; 00495 } 00496 00497 /* Break out of the loop */ 00498 break; 00499 } 00500 else 00501 { 00502 /* We'll have to create a Waitblock */ 00503 WaitBlock->Flags = EX_PUSH_LOCK_FLAGS_EXCLUSIVE | 00504 EX_PUSH_LOCK_FLAGS_WAIT; 00505 WaitBlock->Previous = NULL; 00506 NeedWake = FALSE; 00507 00508 /* Check if there is already a waiter */ 00509 if (OldValue.Waiting) 00510 { 00511 /* Nobody is the last waiter yet */ 00512 WaitBlock->Last = NULL; 00513 00514 /* We are an exclusive waiter */ 00515 WaitBlock->ShareCount = 0; 00516 00517 /* Set the current Wait Block pointer */ 00518 WaitBlock->Next = (PEX_PUSH_LOCK_WAIT_BLOCK)( 00519 OldValue.Value &~ EX_PUSH_LOCK_PTR_BITS); 00520 00521 /* Point to ours */ 00522 NewValue.Value = (OldValue.Value & EX_PUSH_LOCK_MULTIPLE_SHARED) | 00523 EX_PUSH_LOCK_LOCK | 00524 EX_PUSH_LOCK_WAKING | 00525 EX_PUSH_LOCK_WAITING | 00526 (ULONG_PTR)WaitBlock; 00527 00528 /* Check if the pushlock was already waking */ 00529 if (!OldValue.Waking) NeedWake = TRUE; 00530 } 00531 else 00532 { 00533 /* We are the first waiter, so loop the wait block */ 00534 WaitBlock->Last = WaitBlock; 00535 00536 /* Set the share count */ 00537 WaitBlock->ShareCount = (LONG)OldValue.Shared; 00538 00539 /* Check if someone is sharing this pushlock */ 00540 if (OldValue.Shared > 1) 00541 { 00542 /* Point to our wait block */ 00543 NewValue.Value = EX_PUSH_LOCK_MULTIPLE_SHARED | 00544 EX_PUSH_LOCK_LOCK | 00545 EX_PUSH_LOCK_WAITING | 00546 (ULONG_PTR)WaitBlock; 00547 } 00548 else 00549 { 00550 /* No shared count */ 00551 WaitBlock->ShareCount = 0; 00552 00553 /* Point to our wait block */ 00554 NewValue.Value = EX_PUSH_LOCK_LOCK | 00555 EX_PUSH_LOCK_WAITING | 00556 (ULONG_PTR)WaitBlock; 00557 } 00558 } 00559 00560 #if DBG 00561 /* Setup the Debug Wait Block */ 00562 WaitBlock->Signaled = 0; 00563 WaitBlock->OldValue = OldValue; 00564 WaitBlock->NewValue = NewValue; 00565 WaitBlock->PushLock = PushLock; 00566 #endif 00567 00568 /* Sanity check */ 00569 ASSERT(NewValue.Waiting); 00570 ASSERT(NewValue.Locked); 00571 00572 /* Write the new value */ 00573 TempValue = NewValue; 00574 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr, 00575 NewValue.Ptr, 00576 OldValue.Ptr); 00577 if (NewValue.Value != OldValue.Value) 00578 { 00579 /* Retry */ 00580 OldValue = *PushLock; 00581 continue; 00582 } 00583 00584 /* Check if the pushlock needed waking */ 00585 if (NeedWake) 00586 { 00587 /* Scan the Waiters and Wake PushLocks */ 00588 ExpOptimizePushLockList(PushLock, TempValue); 00589 } 00590 00591 /* Set up the Wait Gate */ 00592 KeInitializeGate(&WaitBlock->WakeGate); 00593 00594 #ifdef CONFIG_SMP 00595 /* Now spin on the push lock if necessary */ 00596 if (ExPushLockSpinCount) 00597 { 00598 ULONG i = ExPushLockSpinCount; 00599 00600 do 00601 { 00602 if (!(*(volatile LONG *)&WaitBlock->Flags & EX_PUSH_LOCK_WAITING)) 00603 break; 00604 00605 YieldProcessor(); 00606 } while (--i); 00607 } 00608 #endif 00609 00610 /* Now try to remove the wait bit */ 00611 if (InterlockedBitTestAndReset(&WaitBlock->Flags, 1)) 00612 { 00613 /* Nobody removed it already, let's do a full wait */ 00614 KeWaitForGate(&WaitBlock->WakeGate, WrPushLock, KernelMode); 00615 ASSERT(WaitBlock->Signaled); 00616 } 00617 00618 /* We shouldn't be shared anymore */ 00619 ASSERT((WaitBlock->ShareCount == 0)); 00620 00621 /* Loop again */ 00622 OldValue = NewValue; 00623 } 00624 } 00625 } 00626 00627 /*++ 00628 * @name ExAcquirePushLockShared 00629 * @implemented NT5.1 00630 * 00631 * The ExAcquirePushLockShared routine acquires a shared PushLock. 00632 * 00633 * @params PushLock 00634 * Pointer to the pushlock which is to be acquired. 00635 * 00636 * @return None. 00637 * 00638 * @remarks Callers of ExAcquirePushLockShared must be running at IRQL <= APC_LEVEL. 00639 * This macro should usually be paired up with KeAcquireCriticalRegion. 00640 * 00641 *--*/ 00642 VOID 00643 FASTCALL 00644 ExfAcquirePushLockShared(PEX_PUSH_LOCK PushLock) 00645 { 00646 EX_PUSH_LOCK OldValue = *PushLock, NewValue; 00647 BOOLEAN NeedWake; 00648 DEFINE_WAIT_BLOCK(WaitBlock); 00649 00650 /* Start main loop */ 00651 for (;;) 00652 { 00653 /* Check if it's unlocked or if it's waiting without any sharers */ 00654 if (!(OldValue.Locked) || (!(OldValue.Waiting) && (OldValue.Shared > 0))) 00655 { 00656 /* Check if anyone is waiting on it */ 00657 if (!OldValue.Waiting) 00658 { 00659 /* Increase the share count and lock it */ 00660 NewValue.Value = OldValue.Value | EX_PUSH_LOCK_LOCK; 00661 NewValue.Shared++; 00662 } 00663 else 00664 { 00665 /* Simply set the lock bit */ 00666 NewValue.Value = OldValue.Value | EX_PUSH_LOCK_LOCK; 00667 } 00668 00669 /* Sanity check */ 00670 ASSERT(NewValue.Locked); 00671 00672 /* Set the new value */ 00673 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr, 00674 NewValue.Ptr, 00675 OldValue.Ptr); 00676 if (NewValue.Value != OldValue.Value) 00677 { 00678 /* Retry */ 00679 OldValue = *PushLock; 00680 continue; 00681 } 00682 00683 /* Break out of the loop */ 00684 break; 00685 } 00686 else 00687 { 00688 /* We'll have to create a Waitblock */ 00689 WaitBlock->Flags = EX_PUSH_LOCK_FLAGS_WAIT; 00690 WaitBlock->ShareCount = 0; 00691 NeedWake = FALSE; 00692 WaitBlock->Previous = NULL; 00693 00694 /* Check if there is already a waiter */ 00695 if (OldValue.Waiting) 00696 { 00697 /* Set the current Wait Block pointer */ 00698 WaitBlock->Next = (PEX_PUSH_LOCK_WAIT_BLOCK)( 00699 OldValue.Value &~ EX_PUSH_LOCK_PTR_BITS); 00700 00701 /* Nobody is the last waiter yet */ 00702 WaitBlock->Last = NULL; 00703 00704 /* Point to ours */ 00705 NewValue.Value = (OldValue.Value & (EX_PUSH_LOCK_MULTIPLE_SHARED | 00706 EX_PUSH_LOCK_LOCK)) | 00707 EX_PUSH_LOCK_WAKING | 00708 EX_PUSH_LOCK_WAITING | 00709 (ULONG_PTR)WaitBlock; 00710 00711 /* Check if the pushlock was already waking */ 00712 if (!OldValue.Waking) NeedWake = TRUE; 00713 } 00714 else 00715 { 00716 /* We are the first waiter, so loop the wait block */ 00717 WaitBlock->Last = WaitBlock; 00718 00719 /* Point to our wait block */ 00720 NewValue.Value = (OldValue.Value & EX_PUSH_LOCK_PTR_BITS) | 00721 EX_PUSH_LOCK_WAITING | 00722 (ULONG_PTR)WaitBlock; 00723 } 00724 00725 /* Sanity check */ 00726 ASSERT(NewValue.Waiting); 00727 00728 #if DBG 00729 /* Setup the Debug Wait Block */ 00730 WaitBlock->Signaled = 0; 00731 WaitBlock->OldValue = OldValue; 00732 WaitBlock->NewValue = NewValue; 00733 WaitBlock->PushLock = PushLock; 00734 #endif 00735 00736 /* Write the new value */ 00737 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr, 00738 NewValue.Ptr, 00739 OldValue.Ptr); 00740 if (NewValue.Ptr != OldValue.Ptr) 00741 { 00742 /* Retry */ 00743 OldValue = *PushLock; 00744 continue; 00745 } 00746 00747 /* Update the value now */ 00748 OldValue = NewValue; 00749 00750 /* Check if the pushlock needed waking */ 00751 if (NeedWake) 00752 { 00753 /* Scan the Waiters and Wake PushLocks */ 00754 ExpOptimizePushLockList(PushLock, OldValue); 00755 } 00756 00757 /* Set up the Wait Gate */ 00758 KeInitializeGate(&WaitBlock->WakeGate); 00759 00760 #ifdef CONFIG_SMP 00761 /* Now spin on the push lock if necessary */ 00762 if (ExPushLockSpinCount) 00763 { 00764 ULONG i = ExPushLockSpinCount; 00765 00766 do 00767 { 00768 if (!(*(volatile LONG *)&WaitBlock->Flags & EX_PUSH_LOCK_WAITING)) 00769 break; 00770 00771 YieldProcessor(); 00772 } while (--i); 00773 } 00774 #endif 00775 00776 /* Now try to remove the wait bit */ 00777 if (InterlockedBitTestAndReset(&WaitBlock->Flags, 1)) 00778 { 00779 /* Fast-path did not work, we need to do a full wait */ 00780 KeWaitForGate(&WaitBlock->WakeGate, WrPushLock, KernelMode); 00781 ASSERT(WaitBlock->Signaled); 00782 } 00783 00784 /* We shouldn't be shared anymore */ 00785 ASSERT((WaitBlock->ShareCount == 0)); 00786 } 00787 } 00788 } 00789 00790 /*++ 00791 * @name ExfReleasePushLock 00792 * @implemented NT5.1 00793 * 00794 * The ExReleasePushLock routine releases a previously acquired PushLock. 00795 * 00796 * 00797 * @params PushLock 00798 * Pointer to a previously acquired pushlock. 00799 * 00800 * @return None. 00801 * 00802 * @remarks Callers of ExfReleasePushLock must be running at IRQL <= APC_LEVEL. 00803 * This macro should usually be paired up with KeLeaveCriticalRegion. 00804 * 00805 *--*/ 00806 VOID 00807 FASTCALL 00808 ExfReleasePushLock(PEX_PUSH_LOCK PushLock) 00809 { 00810 EX_PUSH_LOCK OldValue = *PushLock, NewValue, WakeValue; 00811 PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock, LastWaitBlock; 00812 00813 /* Sanity check */ 00814 ASSERT(OldValue.Locked); 00815 00816 /* Start main loop */ 00817 while (TRUE) 00818 { 00819 /* Check if someone is waiting on the lock */ 00820 if (!OldValue.Waiting) 00821 { 00822 /* Check if it's shared */ 00823 if (OldValue.Shared > 1) 00824 { 00825 /* Write the Old Value but decrease share count */ 00826 NewValue = OldValue; 00827 NewValue.Shared--; 00828 } 00829 else 00830 { 00831 /* Simply clear the lock */ 00832 NewValue.Value = 0; 00833 } 00834 00835 /* Write the New Value */ 00836 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr, 00837 NewValue.Ptr, 00838 OldValue.Ptr); 00839 if (NewValue.Value == OldValue.Value) return; 00840 00841 /* Did it enter a wait state? */ 00842 OldValue = NewValue; 00843 } 00844 else 00845 { 00846 /* Ok, we do know someone is waiting on it. Are there more then one? */ 00847 if (OldValue.MultipleShared) 00848 { 00849 /* Get the wait block */ 00850 WaitBlock = (PEX_PUSH_LOCK_WAIT_BLOCK)(OldValue.Value & 00851 ~EX_PUSH_LOCK_PTR_BITS); 00852 00853 /* Loop until we find the last wait block */ 00854 while (TRUE) 00855 { 00856 /* Get the last wait block */ 00857 LastWaitBlock = WaitBlock->Last; 00858 00859 /* Did it exist? */ 00860 if (LastWaitBlock) 00861 { 00862 /* Choose it */ 00863 WaitBlock = LastWaitBlock; 00864 break; 00865 } 00866 00867 /* Keep searching */ 00868 WaitBlock = WaitBlock->Next; 00869 } 00870 00871 /* Make sure the Share Count is above 0 */ 00872 if (WaitBlock->ShareCount > 0) 00873 { 00874 /* This shouldn't be an exclusive wait block */ 00875 ASSERT(WaitBlock->Flags & EX_PUSH_LOCK_FLAGS_EXCLUSIVE); 00876 00877 /* Do the decrease and check if the lock isn't shared anymore */ 00878 if (InterlockedDecrement(&WaitBlock->ShareCount) > 0) return; 00879 } 00880 } 00881 00882 /* 00883 * If nobody was waiting on the block, then we possibly reduced the number 00884 * of times the pushlock was shared, and we unlocked it. 00885 * If someone was waiting, and more then one person is waiting, then we 00886 * reduced the number of times the pushlock is shared in the wait block. 00887 * Therefore, at this point, we can now 'satisfy' the wait. 00888 */ 00889 for (;;) 00890 { 00891 /* Now we need to see if it's waking */ 00892 if (OldValue.Waking) 00893 { 00894 /* Remove the lock and multiple shared bits */ 00895 NewValue.Value = OldValue.Value; 00896 NewValue.MultipleShared = FALSE; 00897 NewValue.Locked = FALSE; 00898 00899 /* Sanity check */ 00900 ASSERT(NewValue.Waking && !NewValue.Locked && !NewValue.MultipleShared); 00901 00902 /* Write the new value */ 00903 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr, 00904 NewValue.Ptr, 00905 OldValue.Ptr); 00906 if (NewValue.Value == OldValue.Value) return; 00907 } 00908 else 00909 { 00910 /* Remove the lock and multiple shared bits */ 00911 NewValue.Value = OldValue.Value; 00912 NewValue.MultipleShared = FALSE; 00913 NewValue.Locked = FALSE; 00914 00915 /* It's not already waking, so add the wake bit */ 00916 NewValue.Waking = TRUE; 00917 00918 /* Sanity check */ 00919 ASSERT(NewValue.Waking && !NewValue.Locked && !NewValue.MultipleShared); 00920 00921 /* Write the new value */ 00922 WakeValue = NewValue; 00923 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr, 00924 NewValue.Ptr, 00925 OldValue.Ptr); 00926 if (NewValue.Value != OldValue.Value) continue; 00927 00928 /* The write was successful. The pushlock is Unlocked and Waking */ 00929 ExfWakePushLock(PushLock, WakeValue); 00930 return; 00931 } 00932 } 00933 } 00934 } 00935 } 00936 00937 /*++ 00938 * @name ExfReleasePushLockShared 00939 * @implemented NT5.2 00940 * 00941 * The ExfReleasePushLockShared macro releases a previously acquired PushLock. 00942 * 00943 * @params PushLock 00944 * Pointer to a previously acquired pushlock. 00945 * 00946 * @return None. 00947 * 00948 * @remarks Callers of ExReleasePushLockShared must be running at IRQL <= APC_LEVEL. 00949 * This macro should usually be paired up with KeLeaveCriticalRegion. 00950 * 00951 *--*/ 00952 VOID 00953 FASTCALL 00954 ExfReleasePushLockShared(PEX_PUSH_LOCK PushLock) 00955 { 00956 EX_PUSH_LOCK OldValue = *PushLock, NewValue, WakeValue; 00957 PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock, LastWaitBlock; 00958 00959 /* Check if someone is waiting on the lock */ 00960 while (!OldValue.Waiting) 00961 { 00962 /* Check if it's shared */ 00963 if (OldValue.Shared > 1) 00964 { 00965 /* Write the Old Value but decrease share count */ 00966 NewValue = OldValue; 00967 NewValue.Shared--; 00968 } 00969 else 00970 { 00971 /* Simply clear the lock */ 00972 NewValue.Value = 0; 00973 } 00974 00975 /* Write the New Value */ 00976 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr, 00977 NewValue.Ptr, 00978 OldValue.Ptr); 00979 if (NewValue.Value == OldValue.Value) return; 00980 00981 /* Did it enter a wait state? */ 00982 OldValue = NewValue; 00983 } 00984 00985 /* Ok, we do know someone is waiting on it. Are there more then one? */ 00986 if (OldValue.MultipleShared) 00987 { 00988 /* Get the wait block */ 00989 WaitBlock = (PEX_PUSH_LOCK_WAIT_BLOCK)(OldValue.Value & 00990 ~EX_PUSH_LOCK_PTR_BITS); 00991 00992 /* Loop until we find the last wait block */ 00993 while (TRUE) 00994 { 00995 /* Get the last wait block */ 00996 LastWaitBlock = WaitBlock->Last; 00997 00998 /* Did it exist? */ 00999 if (LastWaitBlock) 01000 { 01001 /* Choose it */ 01002 WaitBlock = LastWaitBlock; 01003 break; 01004 } 01005 01006 /* Keep searching */ 01007 WaitBlock = WaitBlock->Next; 01008 } 01009 01010 /* Sanity checks */ 01011 ASSERT(WaitBlock->ShareCount > 0); 01012 ASSERT(WaitBlock->Flags & EX_PUSH_LOCK_FLAGS_EXCLUSIVE); 01013 01014 /* Do the decrease and check if the lock isn't shared anymore */ 01015 if (InterlockedDecrement(&WaitBlock->ShareCount) > 0) return; 01016 } 01017 01018 /* 01019 * If nobody was waiting on the block, then we possibly reduced the number 01020 * of times the pushlock was shared, and we unlocked it. 01021 * If someone was waiting, and more then one person is waiting, then we 01022 * reduced the number of times the pushlock is shared in the wait block. 01023 * Therefore, at this point, we can now 'satisfy' the wait. 01024 */ 01025 for (;;) 01026 { 01027 /* Now we need to see if it's waking */ 01028 if (OldValue.Waking) 01029 { 01030 /* Remove the lock and multiple shared bits */ 01031 NewValue.Value = OldValue.Value; 01032 NewValue.MultipleShared = FALSE; 01033 NewValue.Locked = FALSE; 01034 01035 /* Sanity check */ 01036 ASSERT(NewValue.Waking && !NewValue.Locked && !NewValue.MultipleShared); 01037 01038 /* Write the new value */ 01039 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr, 01040 NewValue.Ptr, 01041 OldValue.Ptr); 01042 if (NewValue.Value == OldValue.Value) return; 01043 } 01044 else 01045 { 01046 /* Remove the lock and multiple shared bits */ 01047 NewValue.Value = OldValue.Value; 01048 NewValue.MultipleShared = FALSE; 01049 NewValue.Locked = FALSE; 01050 01051 /* It's not already waking, so add the wake bit */ 01052 NewValue.Waking = TRUE; 01053 01054 /* Sanity check */ 01055 ASSERT(NewValue.Waking && !NewValue.Locked && !NewValue.MultipleShared); 01056 01057 /* Write the new value */ 01058 WakeValue = NewValue; 01059 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr, 01060 NewValue.Ptr, 01061 OldValue.Ptr); 01062 if (NewValue.Value != OldValue.Value) continue; 01063 01064 /* The write was successful. The pushlock is Unlocked and Waking */ 01065 ExfWakePushLock(PushLock, WakeValue); 01066 return; 01067 } 01068 } 01069 } 01070 01071 /*++ 01072 * ExfReleasePushLockExclusive 01073 * @implemented NT5.2 01074 * 01075 * The ExfReleasePushLockExclusive routine releases a previously 01076 * exclusively acquired PushLock. 01077 * 01078 * @params PushLock 01079 * Pointer to a previously acquired pushlock. 01080 * 01081 * @return None. 01082 * 01083 * @remarks Callers of ExReleasePushLockExclusive must be running at IRQL <= APC_LEVEL. 01084 * This macro should usually be paired up with KeLeaveCriticalRegion. 01085 * 01086 *--*/ 01087 VOID 01088 FASTCALL 01089 ExfReleasePushLockExclusive(PEX_PUSH_LOCK PushLock) 01090 { 01091 EX_PUSH_LOCK NewValue, WakeValue; 01092 EX_PUSH_LOCK OldValue = *PushLock; 01093 01094 /* Loop until we can change */ 01095 for (;;) 01096 { 01097 /* Sanity checks */ 01098 ASSERT(OldValue.Locked); 01099 ASSERT(OldValue.Waiting || OldValue.Shared == 0); 01100 01101 /* Check if it's waiting and not yet waking */ 01102 if ((OldValue.Waiting) && !(OldValue.Waking)) 01103 { 01104 /* Remove the lock bit, and add the wake bit */ 01105 NewValue.Value = (OldValue.Value &~ EX_PUSH_LOCK_LOCK) | 01106 EX_PUSH_LOCK_WAKING; 01107 01108 /* Sanity check */ 01109 ASSERT(NewValue.Waking && !NewValue.Locked); 01110 01111 /* Write the New Value. Save our original value for waking */ 01112 WakeValue = NewValue; 01113 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr, 01114 NewValue.Ptr, 01115 OldValue.Ptr); 01116 01117 /* Check if the value changed behind our back */ 01118 if (NewValue.Value == OldValue.Value) 01119 { 01120 /* Wake the Pushlock */ 01121 ExfWakePushLock(PushLock, WakeValue); 01122 break; 01123 } 01124 } 01125 else 01126 { 01127 /* A simple unlock */ 01128 NewValue.Value = OldValue.Value &~ EX_PUSH_LOCK_LOCK; 01129 01130 /* Sanity check */ 01131 ASSERT(NewValue.Waking && !NewValue.Waiting); 01132 01133 /* Write the New Value */ 01134 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr, 01135 NewValue.Ptr, 01136 OldValue.Ptr); 01137 01138 /* Check if the value changed behind our back */ 01139 if (NewValue.Value == OldValue.Value) break; 01140 } 01141 01142 /* Loop again */ 01143 OldValue = NewValue; 01144 } 01145 } 01146 01147 /*++ 01148 * @name ExfTryToWakePushLock 01149 * @implemented NT5.2 01150 * 01151 * The ExfTryToWakePushLock attemps to wake a waiting pushlock. 01152 * 01153 * @param PushLock 01154 * Pointer to a PushLock which is in the wait state. 01155 * 01156 * @return None. 01157 * 01158 * @remarks The pushlock must be in a wait state and must not be already waking. 01159 * 01160 *--*/ 01161 VOID 01162 FASTCALL 01163 ExfTryToWakePushLock(PEX_PUSH_LOCK PushLock) 01164 { 01165 EX_PUSH_LOCK OldValue = *PushLock, NewValue; 01166 01167 /* 01168 * If the Pushlock is not waiting on anything, or if it's already waking up 01169 * and locked, don't do anything 01170 */ 01171 if ((OldValue.Waking) || (OldValue.Locked) || !(OldValue.Waiting)) return; 01172 01173 /* Make it Waking */ 01174 NewValue = OldValue; 01175 NewValue.Waking = TRUE; 01176 01177 /* Write the New Value */ 01178 if (InterlockedCompareExchangePointer(&PushLock->Ptr, 01179 NewValue.Ptr, 01180 OldValue.Ptr) == OldValue.Ptr) 01181 { 01182 /* Wake the Pushlock */ 01183 ExfWakePushLock(PushLock, NewValue); 01184 } 01185 } 01186 01187 /*++ 01188 * @name ExfUnblockPushLock 01189 * @implemented NT5.1 01190 * 01191 * The ExfUnblockPushLock routine unblocks a previously blocked PushLock. 01192 * 01193 * @param PushLock 01194 * Pointer to a previously blocked PushLock. 01195 * 01196 * @return None. 01197 * 01198 * @remarks Callers of ExfUnblockPushLock can be running at any IRQL. 01199 * 01200 *--*/ 01201 VOID 01202 FASTCALL 01203 ExfUnblockPushLock(PEX_PUSH_LOCK PushLock, 01204 PVOID CurrentWaitBlock) 01205 { 01206 PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock, NextWaitBlock; 01207 KIRQL OldIrql = DISPATCH_LEVEL; 01208 01209 /* Get the wait block and erase the previous one */ 01210 WaitBlock = InterlockedExchangePointer(&PushLock->Ptr, NULL); 01211 if (WaitBlock) 01212 { 01213 /* Check if there is a linked pushlock and raise IRQL appropriately */ 01214 if (WaitBlock->Next) KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); 01215 01216 /* Start block loop */ 01217 while (WaitBlock) 01218 { 01219 /* Get the next block */ 01220 NextWaitBlock = WaitBlock->Next; 01221 01222 /* Remove the wait flag from the Wait block */ 01223 if (!InterlockedBitTestAndReset(&WaitBlock->Flags, EX_PUSH_LOCK_FLAGS_WAIT_V)) 01224 { 01225 /* Nobody removed the flag before us, so signal the event */ 01226 KeSetEventBoostPriority(&WaitBlock->WakeEvent, NULL); 01227 } 01228 01229 /* Try the next one */ 01230 WaitBlock = NextWaitBlock; 01231 } 01232 01233 /* Lower IRQL if needed */ 01234 if (OldIrql != DISPATCH_LEVEL) KeLowerIrql(OldIrql); 01235 } 01236 01237 /* Check if we got a wait block that's pending */ 01238 if ((CurrentWaitBlock) && 01239 (((PEX_PUSH_LOCK_WAIT_BLOCK)CurrentWaitBlock)->Flags & 01240 EX_PUSH_LOCK_FLAGS_WAIT)) 01241 { 01242 /* Wait for the pushlock to be unblocked */ 01243 ExWaitForUnblockPushLock(PushLock, CurrentWaitBlock); 01244 } 01245 } Generated on Sun May 27 2012 04:37:10 for ReactOS by
1.7.6.1
|