ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

pushlock.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 doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.