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

wait.c
Go to the documentation of this file.
00001 /*
00002  * PROJECT:         ReactOS Kernel
00003  * LICENSE:         GPL - See COPYING in the top level directory
00004  * FILE:            ntoskrnl/ke/wait.c
00005  * PURPOSE:         Manages waiting for Dispatcher Objects
00006  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
00007  *                  Gunnar Dalsnes
00008  */
00009 
00010 /* INCLUDES ******************************************************************/
00011 
00012 #include <ntoskrnl.h>
00013 #define NDEBUG
00014 #include <debug.h>
00015 
00016 /* PRIVATE FUNCTIONS *********************************************************/
00017 
00018 VOID
00019 FASTCALL
00020 KiWaitTest(IN PVOID ObjectPointer,
00021            IN KPRIORITY Increment)
00022 {
00023     PLIST_ENTRY WaitEntry, WaitList;
00024     PKWAIT_BLOCK WaitBlock;
00025     PKTHREAD WaitThread;
00026     PKMUTANT FirstObject = ObjectPointer;
00027     NTSTATUS WaitStatus;
00028 
00029     /* Loop the Wait Entries */
00030     WaitList = &FirstObject->Header.WaitListHead;
00031     WaitEntry = WaitList->Flink;
00032     while ((FirstObject->Header.SignalState > 0) && (WaitEntry != WaitList))
00033     {
00034         /* Get the current wait block */
00035         WaitBlock = CONTAINING_RECORD(WaitEntry, KWAIT_BLOCK, WaitListEntry);
00036         WaitThread = WaitBlock->Thread;
00037         WaitStatus = STATUS_KERNEL_APC;
00038 
00039         /* Check the current Wait Mode */
00040         if (WaitBlock->WaitType == WaitAny)
00041         {
00042             /* Easy case, satisfy only this wait */
00043             WaitStatus = (NTSTATUS)WaitBlock->WaitKey;
00044             KiSatisfyObjectWait(FirstObject, WaitThread);
00045         }
00046 
00047         /* Now do the rest of the unwait */
00048         KiUnwaitThread(WaitThread, WaitStatus, Increment);
00049         WaitEntry = WaitList->Flink;
00050     }
00051 }
00052 
00053 VOID
00054 FASTCALL
00055 KiUnlinkThread(IN PKTHREAD Thread,
00056                IN LONG_PTR WaitStatus)
00057 {
00058     PKWAIT_BLOCK WaitBlock;
00059     PKTIMER Timer;
00060 
00061     /* Update wait status */
00062     Thread->WaitStatus |= WaitStatus;
00063 
00064     /* Remove the Wait Blocks from the list */
00065     WaitBlock = Thread->WaitBlockList;
00066     do
00067     {
00068         /* Remove it */
00069         RemoveEntryList(&WaitBlock->WaitListEntry);
00070 
00071         /* Go to the next one */
00072         WaitBlock = WaitBlock->NextWaitBlock;
00073     } while (WaitBlock != Thread->WaitBlockList);
00074 
00075     /* Remove the thread from the wait list! */
00076     if (Thread->WaitListEntry.Flink) RemoveEntryList(&Thread->WaitListEntry);
00077 
00078     /* Check if there's a Thread Timer */
00079     Timer = &Thread->Timer;
00080     if (Timer->Header.Inserted) KxRemoveTreeTimer(Timer);
00081 
00082     /* Increment the Queue's active threads */
00083     if (Thread->Queue) Thread->Queue->CurrentCount++;
00084 }
00085 
00086 /* Must be called with the dispatcher lock held */
00087 VOID
00088 FASTCALL
00089 KiUnwaitThread(IN PKTHREAD Thread,
00090                IN LONG_PTR WaitStatus,
00091                IN KPRIORITY Increment)
00092 {
00093     /* Unlink the thread */
00094     KiUnlinkThread(Thread, WaitStatus);
00095 
00096     /* Tell the scheduler do to the increment when it readies the thread */
00097     ASSERT(Increment >= 0);
00098     Thread->AdjustIncrement = (SCHAR)Increment;
00099     Thread->AdjustReason = AdjustUnwait;
00100 
00101     /* Reschedule the Thread */
00102     KiReadyThread(Thread);
00103 }
00104 
00105 VOID
00106 FASTCALL
00107 KiAcquireFastMutex(IN PFAST_MUTEX FastMutex)
00108 {
00109     /* Increase contention count */
00110     FastMutex->Contention++;
00111 
00112     /* Wait for the event */
00113     KeWaitForSingleObject(&FastMutex->Event,
00114                           WrMutex,
00115                           KernelMode,
00116                           FALSE,
00117                           NULL);
00118 }
00119 
00120 VOID
00121 FASTCALL
00122 KiAcquireGuardedMutex(IN OUT PKGUARDED_MUTEX GuardedMutex)
00123 {
00124     ULONG BitsToRemove, BitsToAdd;
00125     LONG OldValue, NewValue;
00126 
00127     /* We depend on these bits being just right */
00128     C_ASSERT((GM_LOCK_WAITER_WOKEN * 2) == GM_LOCK_WAITER_INC);
00129     
00130     /* Increase the contention count */
00131     GuardedMutex->Contention++;
00132     
00133     /* Start by unlocking the Guarded Mutex */
00134     BitsToRemove = GM_LOCK_BIT;
00135     BitsToAdd = GM_LOCK_WAITER_INC;
00136     
00137     /* Start change loop */
00138     for (;;)
00139     {
00140         /* Loop sanity checks */
00141         ASSERT((BitsToRemove == GM_LOCK_BIT) ||
00142                (BitsToRemove == (GM_LOCK_BIT | GM_LOCK_WAITER_WOKEN)));
00143         ASSERT((BitsToAdd == GM_LOCK_WAITER_INC) ||
00144                (BitsToAdd == GM_LOCK_WAITER_WOKEN));
00145         
00146         /* Get the Count Bits */
00147         OldValue = GuardedMutex->Count;
00148         
00149         /* Start internal bit change loop */
00150         for (;;)
00151         {
00152             /* Check if the Guarded Mutex is locked */
00153             if (OldValue & GM_LOCK_BIT)
00154             {
00155                 /* Sanity check */
00156                 ASSERT((BitsToRemove == GM_LOCK_BIT) ||
00157                        ((OldValue & GM_LOCK_WAITER_WOKEN) != 0));
00158                 
00159                 /* Unlock it by removing the Lock Bit */
00160                 NewValue = OldValue ^ BitsToRemove;
00161                 NewValue = InterlockedCompareExchange(&GuardedMutex->Count,
00162                                                       NewValue,
00163                                                       OldValue);
00164                 if (NewValue == OldValue) return;
00165             }
00166             else
00167             {
00168                 /* The Guarded Mutex isn't locked, so simply set the bits */
00169                 NewValue = OldValue + BitsToAdd;
00170                 NewValue = InterlockedCompareExchange(&GuardedMutex->Count,
00171                                                       NewValue,
00172                                                       OldValue);
00173                 if (NewValue == OldValue) break;
00174             }
00175             
00176             /* Old value changed, loop again */
00177             OldValue = NewValue;
00178         }
00179         
00180         /* Now we have to wait for it */
00181         KeWaitForGate(&GuardedMutex->Gate, WrGuardedMutex, KernelMode);
00182         ASSERT((GuardedMutex->Count & GM_LOCK_WAITER_WOKEN) != 0);
00183         
00184         /* Ok, the wait is done, so set the new bits */
00185         BitsToRemove = GM_LOCK_BIT | GM_LOCK_WAITER_WOKEN;
00186         BitsToAdd = GM_LOCK_WAITER_WOKEN;
00187     }
00188 }
00189 
00190 //
00191 // This routine exits the dispatcher after a compatible operation and
00192 // swaps the context to the next scheduled thread on the current CPU if
00193 // one is available.
00194 //
00195 // It does NOT attempt to scan for a new thread to schedule.
00196 //
00197 VOID
00198 FASTCALL
00199 KiExitDispatcher(IN KIRQL OldIrql)
00200 {
00201     PKPRCB Prcb = KeGetCurrentPrcb();
00202     PKTHREAD Thread, NextThread;
00203     BOOLEAN PendingApc;
00204 
00205     /* Make sure we're at synchronization level */
00206     ASSERT(KeGetCurrentIrql() == SYNCH_LEVEL);
00207 
00208     /* Check if we have deferred threads */
00209     KiCheckDeferredReadyList(Prcb);
00210 
00211     /* Check if we were called at dispatcher level or higher */
00212     if (OldIrql >= DISPATCH_LEVEL)
00213     {
00214         /* Check if we have a thread to schedule, and that no DPC is active */
00215         if ((Prcb->NextThread) && !(Prcb->DpcRoutineActive))
00216         {
00217             /* Request DPC interrupt */
00218             HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
00219         }
00220 
00221         /* Lower IRQL and exit */
00222         goto Quickie;
00223     }
00224 
00225     /* Make sure there's a new thread scheduled */
00226     if (!Prcb->NextThread) goto Quickie;
00227 
00228     /* Lock the PRCB */
00229     KiAcquirePrcbLock(Prcb);
00230 
00231     /* Get the next and current threads now */
00232     NextThread = Prcb->NextThread;
00233     Thread = Prcb->CurrentThread;
00234 
00235     /* Set current thread's swap busy to true */
00236     KiSetThreadSwapBusy(Thread);
00237 
00238     /* Switch threads in PRCB */
00239     Prcb->NextThread = NULL;
00240     Prcb->CurrentThread = NextThread;
00241 
00242     /* Set thread to running */
00243     NextThread->State = Running;
00244 
00245     /* Queue it on the ready lists */
00246     KxQueueReadyThread(Thread, Prcb);
00247 
00248     /* Set wait IRQL */
00249     Thread->WaitIrql = OldIrql;
00250 
00251     /* Swap threads and check if APCs were pending */
00252     PendingApc = KiSwapContext(OldIrql, Thread);
00253     if (PendingApc)
00254     {
00255         /* Lower only to APC */
00256         KeLowerIrql(APC_LEVEL);
00257 
00258         /* Deliver APCs */
00259         KiDeliverApc(KernelMode, NULL, NULL);
00260         ASSERT(OldIrql == PASSIVE_LEVEL);
00261     }
00262 
00263     /* Lower IRQl back */
00264 Quickie:
00265     KeLowerIrql(OldIrql);
00266 }
00267 
00268 /* PUBLIC FUNCTIONS **********************************************************/
00269 
00270 /*
00271  * @implemented
00272  */
00273 NTSTATUS
00274 NTAPI
00275 KeDelayExecutionThread(IN KPROCESSOR_MODE WaitMode,
00276                        IN BOOLEAN Alertable,
00277                        IN PLARGE_INTEGER Interval OPTIONAL)
00278 {
00279     PKTIMER Timer;
00280     PKWAIT_BLOCK TimerBlock;
00281     PKTHREAD Thread = KeGetCurrentThread();
00282     NTSTATUS WaitStatus;
00283     BOOLEAN Swappable;
00284     PLARGE_INTEGER OriginalDueTime;
00285     LARGE_INTEGER DueTime, NewDueTime, InterruptTime;
00286     ULONG Hand = 0;
00287 
00288     /* If this is a user-mode wait of 0 seconds, yield execution */
00289     if (!(Interval->QuadPart) && (WaitMode != KernelMode))
00290     {
00291         /* Make sure the wait isn't alertable or interrupting an APC */
00292         if (!(Alertable) && !(Thread->ApcState.UserApcPending))
00293         {
00294             /* Yield execution */
00295             NtYieldExecution();
00296         }
00297     }
00298 
00299     /* Setup the original time and timer/wait blocks */
00300     OriginalDueTime = Interval;
00301     Timer = &Thread->Timer;
00302     TimerBlock = &Thread->WaitBlock[TIMER_WAIT_BLOCK];
00303 
00304     /* Check if the lock is already held */
00305     if (!Thread->WaitNext) goto WaitStart;
00306 
00307     /*  Otherwise, we already have the lock, so initialize the wait */
00308     Thread->WaitNext = FALSE;
00309     KxDelayThreadWait();
00310 
00311     /* Start wait loop */
00312     for (;;)
00313     {
00314         /* Disable pre-emption */
00315         Thread->Preempted = FALSE;
00316 
00317         /* Check if a kernel APC is pending and we're below APC_LEVEL */
00318         if ((Thread->ApcState.KernelApcPending) && !(Thread->SpecialApcDisable) &&
00319             (Thread->WaitIrql < APC_LEVEL))
00320         {
00321             /* Unlock the dispatcher */
00322             KiReleaseDispatcherLock(Thread->WaitIrql);
00323         }
00324         else
00325         {
00326             /* Check if we have to bail out due to an alerted state */
00327             WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode);
00328             if (WaitStatus != STATUS_WAIT_0) break;
00329 
00330             /* Check if the timer expired */
00331             InterruptTime.QuadPart = KeQueryInterruptTime();
00332             if ((ULONGLONG)InterruptTime.QuadPart >= Timer->DueTime.QuadPart)
00333             {
00334                 /* It did, so we don't need to wait */
00335                 goto NoWait;
00336             }
00337 
00338             /* It didn't, so activate it */
00339             Timer->Header.Inserted = TRUE;
00340 
00341             /* Handle Kernel Queues */
00342             if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue);
00343 
00344             /* Setup the wait information */
00345             Thread->State = Waiting;
00346 
00347             /* Add the thread to the wait list */
00348             KiAddThreadToWaitList(Thread, Swappable);
00349 
00350             /* Insert the timer and swap the thread */
00351             ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
00352             KiSetThreadSwapBusy(Thread);
00353             KxInsertTimer(Timer, Hand);
00354             WaitStatus = (NTSTATUS)KiSwapThread(Thread, KeGetCurrentPrcb());
00355 
00356             /* Check if were swapped ok */
00357             if (WaitStatus != STATUS_KERNEL_APC)
00358             {
00359                 /* This is a good thing */
00360                 if (WaitStatus == STATUS_TIMEOUT) WaitStatus = STATUS_SUCCESS;
00361 
00362                 /* Return Status */
00363                 return WaitStatus;
00364             }
00365 
00366             /* Recalculate due times */
00367             Interval = KiRecalculateDueTime(OriginalDueTime,
00368                                             &DueTime,
00369                                             &NewDueTime);
00370         }
00371 
00372 WaitStart:
00373         /* Setup a new wait */
00374         Thread->WaitIrql = KeRaiseIrqlToSynchLevel();
00375         KxDelayThreadWait();
00376         KiAcquireDispatcherLockAtDpcLevel();
00377     }
00378 
00379     /* We're done! */
00380     KiReleaseDispatcherLock(Thread->WaitIrql);
00381     return WaitStatus;
00382 
00383 NoWait:
00384     /* There was nothing to wait for. Did we have a wait interval? */
00385     if (!Interval->QuadPart)
00386     {
00387         /* Unlock the dispatcher and do a yield */
00388         KiReleaseDispatcherLock(Thread->WaitIrql);
00389         return NtYieldExecution();
00390     }
00391 
00392     /* Unlock the dispatcher and adjust the quantum for a no-wait */
00393     KiReleaseDispatcherLockFromDpcLevel();
00394     KiAdjustQuantumThread(Thread);
00395     return STATUS_SUCCESS;
00396 }
00397 
00398 /*
00399  * @implemented
00400  */
00401 NTSTATUS
00402 NTAPI
00403 KeWaitForSingleObject(IN PVOID Object,
00404                       IN KWAIT_REASON WaitReason,
00405                       IN KPROCESSOR_MODE WaitMode,
00406                       IN BOOLEAN Alertable,
00407                       IN PLARGE_INTEGER Timeout OPTIONAL)
00408 {
00409     PKTHREAD Thread = KeGetCurrentThread();
00410     PKMUTANT CurrentObject = (PKMUTANT)Object;
00411     PKWAIT_BLOCK WaitBlock = &Thread->WaitBlock[0];
00412     PKWAIT_BLOCK TimerBlock = &Thread->WaitBlock[TIMER_WAIT_BLOCK];
00413     PKTIMER Timer = &Thread->Timer;
00414     NTSTATUS WaitStatus;
00415     BOOLEAN Swappable;
00416     LARGE_INTEGER DueTime = {{0}}, NewDueTime, InterruptTime;
00417     PLARGE_INTEGER OriginalDueTime = Timeout;
00418     ULONG Hand = 0;
00419 
00420     /* Check if the lock is already held */
00421     if (!Thread->WaitNext) goto WaitStart;
00422 
00423     /*  Otherwise, we already have the lock, so initialize the wait */
00424     Thread->WaitNext = FALSE;
00425     KxSingleThreadWait();
00426 
00427     /* Start wait loop */
00428     for (;;)
00429     {
00430         /* Disable pre-emption */
00431         Thread->Preempted = FALSE;
00432 
00433         /* Check if a kernel APC is pending and we're below APC_LEVEL */
00434         if ((Thread->ApcState.KernelApcPending) && !(Thread->SpecialApcDisable) &&
00435             (Thread->WaitIrql < APC_LEVEL))
00436         {
00437             /* Unlock the dispatcher */
00438             KiReleaseDispatcherLock(Thread->WaitIrql);
00439         }
00440         else
00441         {
00442             /* Sanity check */
00443             ASSERT(CurrentObject->Header.Type != QueueObject);
00444 
00445             /* Check if it's a mutant */
00446             if (CurrentObject->Header.Type == MutantObject)
00447             {
00448                 /* Check its signal state or if we own it */
00449                 if ((CurrentObject->Header.SignalState > 0) ||
00450                     (Thread == CurrentObject->OwnerThread))
00451                 {
00452                     /* Just unwait this guy and exit */
00453                     if (CurrentObject->Header.SignalState != (LONG)MINLONG)
00454                     {
00455                         /* It has a normal signal state. Unwait and return */
00456                         KiSatisfyMutantWait(CurrentObject, Thread);
00457                         WaitStatus = (NTSTATUS)Thread->WaitStatus;
00458                         goto DontWait;
00459                     }
00460                     else
00461                     {
00462                         /* Raise an exception */
00463                         KiReleaseDispatcherLock(Thread->WaitIrql);
00464                         ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
00465                    }
00466                 }
00467             }
00468             else if (CurrentObject->Header.SignalState > 0)
00469             {
00470                 /* Another satisfied object */
00471                 KiSatisfyNonMutantWait(CurrentObject);
00472                 WaitStatus = STATUS_WAIT_0;
00473                 goto DontWait;
00474             }
00475 
00476             /* Make sure we can satisfy the Alertable request */
00477             WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode);
00478             if (WaitStatus != STATUS_WAIT_0) break;
00479 
00480             /* Enable the Timeout Timer if there was any specified */
00481             if (Timeout)
00482             {
00483                 /* Check if the timer expired */
00484                 InterruptTime.QuadPart = KeQueryInterruptTime();
00485                 if ((ULONGLONG)InterruptTime.QuadPart >=
00486                     Timer->DueTime.QuadPart)
00487                 {
00488                     /* It did, so we don't need to wait */
00489                     WaitStatus = STATUS_TIMEOUT;
00490                     goto DontWait;
00491                 }
00492 
00493                 /* It didn't, so activate it */
00494                 Timer->Header.Inserted = TRUE;
00495             }
00496 
00497             /* Link the Object to this Wait Block */
00498             InsertTailList(&CurrentObject->Header.WaitListHead,
00499                            &WaitBlock->WaitListEntry);
00500 
00501             /* Handle Kernel Queues */
00502             if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue);
00503 
00504             /* Setup the wait information */
00505             Thread->State = Waiting;
00506 
00507             /* Add the thread to the wait list */
00508             KiAddThreadToWaitList(Thread, Swappable);
00509 
00510             /* Activate thread swap */
00511             ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
00512             KiSetThreadSwapBusy(Thread);
00513 
00514             /* Check if we have a timer */
00515             if (Timeout)
00516             {
00517                 /* Insert it */
00518                 KxInsertTimer(Timer, Hand);
00519             }
00520             else
00521             {
00522                 /* Otherwise, unlock the dispatcher */
00523                 KiReleaseDispatcherLockFromDpcLevel();
00524             }
00525 
00526             /* Do the actual swap */
00527             WaitStatus = (NTSTATUS)KiSwapThread(Thread, KeGetCurrentPrcb());
00528 
00529             /* Check if we were executing an APC */
00530             if (WaitStatus != STATUS_KERNEL_APC) return WaitStatus;
00531 
00532             /* Check if we had a timeout */
00533             if (Timeout)
00534             {
00535                 /* Recalculate due times */
00536                 Timeout = KiRecalculateDueTime(OriginalDueTime,
00537                                                &DueTime,
00538                                                &NewDueTime);
00539             }
00540         }
00541 WaitStart:
00542         /* Setup a new wait */
00543         Thread->WaitIrql = KeRaiseIrqlToSynchLevel();
00544         KxSingleThreadWait();
00545         KiAcquireDispatcherLockAtDpcLevel();
00546     }
00547 
00548     /* Wait complete */
00549     KiReleaseDispatcherLock(Thread->WaitIrql);
00550     return WaitStatus;
00551 
00552 DontWait:
00553     /* Release dispatcher lock but maintain high IRQL */
00554     KiReleaseDispatcherLockFromDpcLevel();
00555 
00556     /* Adjust the Quantum and return the wait status */
00557     KiAdjustQuantumThread(Thread);
00558     return WaitStatus;
00559 }
00560 
00561 /*
00562  * @implemented
00563  */
00564 NTSTATUS
00565 NTAPI
00566 KeWaitForMultipleObjects(IN ULONG Count,
00567                          IN PVOID Object[],
00568                          IN WAIT_TYPE WaitType,
00569                          IN KWAIT_REASON WaitReason,
00570                          IN KPROCESSOR_MODE WaitMode,
00571                          IN BOOLEAN Alertable,
00572                          IN PLARGE_INTEGER Timeout OPTIONAL,
00573                          OUT PKWAIT_BLOCK WaitBlockArray OPTIONAL)
00574 {
00575     PKMUTANT CurrentObject;
00576     PKWAIT_BLOCK WaitBlock;
00577     PKTHREAD Thread = KeGetCurrentThread();
00578     PKWAIT_BLOCK TimerBlock = &Thread->WaitBlock[TIMER_WAIT_BLOCK];
00579     PKTIMER Timer = &Thread->Timer;
00580     NTSTATUS WaitStatus = STATUS_SUCCESS;
00581     BOOLEAN Swappable;
00582     PLARGE_INTEGER OriginalDueTime = Timeout;
00583     LARGE_INTEGER DueTime = {{0}}, NewDueTime, InterruptTime;
00584     ULONG Index, Hand = 0;
00585 
00586     /* Make sure the Wait Count is valid */
00587     if (!WaitBlockArray)
00588     {
00589         /* Check in regards to the Thread Object Limit */
00590         if (Count > THREAD_WAIT_OBJECTS)
00591         {
00592             /* Bugcheck */
00593             KeBugCheck(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
00594         }
00595 
00596         /* Use the Thread's Wait Block */
00597         WaitBlockArray = &Thread->WaitBlock[0];
00598     }
00599     else
00600     {
00601         /* Using our own Block Array, so check with the System Object Limit */
00602         if (Count > MAXIMUM_WAIT_OBJECTS)
00603         {
00604             /* Bugcheck */
00605             KeBugCheck(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
00606         }
00607     }
00608 
00609     /* Sanity check */
00610     ASSERT(Count != 0);
00611 
00612     /* Check if the lock is already held */
00613     if (!Thread->WaitNext) goto WaitStart;
00614 
00615     /*  Otherwise, we already have the lock, so initialize the wait */
00616     Thread->WaitNext = FALSE;
00617     /*  Note that KxMultiThreadWait is a macro, defined in ke_x.h, that  */
00618     /*  uses  (and modifies some of) the following local                 */
00619     /*  variables:                                                       */
00620     /*  Thread, Index, WaitBlock, Timer, Timeout, Hand and Swappable.    */
00621     /*  If it looks like this code doesn't actually wait for any objects */
00622     /*  at all, it's because the setup is done by that macro.            */
00623     KxMultiThreadWait();
00624 
00625     /* Start wait loop */
00626     for (;;)
00627     {
00628         /* Disable pre-emption */
00629         Thread->Preempted = FALSE;
00630 
00631         /* Check if a kernel APC is pending and we're below APC_LEVEL */
00632         if ((Thread->ApcState.KernelApcPending) && !(Thread->SpecialApcDisable) &&
00633             (Thread->WaitIrql < APC_LEVEL))
00634         {
00635             /* Unlock the dispatcher */
00636             KiReleaseDispatcherLock(Thread->WaitIrql);
00637         }
00638         else
00639         {
00640             /* Check what kind of wait this is */
00641             Index = 0;
00642             if (WaitType == WaitAny)
00643             {
00644                 /* Loop blocks */
00645                 do
00646                 {
00647                     /* Get the Current Object */
00648                     CurrentObject = (PKMUTANT)Object[Index];
00649                     ASSERT(CurrentObject->Header.Type != QueueObject);
00650 
00651                     /* Check if the Object is a mutant */
00652                     if (CurrentObject->Header.Type == MutantObject)
00653                     {
00654                         /* Check if it's signaled */
00655                         if ((CurrentObject->Header.SignalState > 0) ||
00656                             (Thread == CurrentObject->OwnerThread))
00657                         {
00658                             /* This is a Wait Any, so unwait this and exit */
00659                             if (CurrentObject->Header.SignalState !=
00660                                 (LONG)MINLONG)
00661                             {
00662                                 /* Normal signal state, unwait it and return */
00663                                 KiSatisfyMutantWait(CurrentObject, Thread);
00664                                 WaitStatus = (NTSTATUS)Thread->WaitStatus | Index;
00665                                 goto DontWait;
00666                             }
00667                             else
00668                             {
00669                                 /* Raise an exception (see wasm.ru) */
00670                                 KiReleaseDispatcherLock(Thread->WaitIrql);
00671                                 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
00672                             }
00673                         }
00674                     }
00675                     else if (CurrentObject->Header.SignalState > 0)
00676                     {
00677                         /* Another signaled object, unwait and return */
00678                         KiSatisfyNonMutantWait(CurrentObject);
00679                         WaitStatus = Index;
00680                         goto DontWait;
00681                     }
00682 
00683                     /* Go to the next block */
00684                     Index++;
00685                 } while (Index < Count);
00686             }
00687             else
00688             {
00689                 /* Loop blocks */
00690                 do
00691                 {
00692                     /* Get the Current Object */
00693                     CurrentObject = (PKMUTANT)Object[Index];
00694                     ASSERT(CurrentObject->Header.Type != QueueObject);
00695 
00696                     /* Check if we're dealing with a mutant again */
00697                     if (CurrentObject->Header.Type == MutantObject)
00698                     {
00699                         /* Check if it has an invalid count */
00700                         if ((Thread == CurrentObject->OwnerThread) &&
00701                             (CurrentObject->Header.SignalState == (LONG)MINLONG))
00702                         {
00703                             /* Raise an exception */
00704                             KiReleaseDispatcherLock(Thread->WaitIrql);
00705                             ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
00706                         }
00707                         else if ((CurrentObject->Header.SignalState <= 0) &&
00708                                  (Thread != CurrentObject->OwnerThread))
00709                         {
00710                             /* We don't own it, can't satisfy the wait */
00711                             break;
00712                         }
00713                     }
00714                     else if (CurrentObject->Header.SignalState <= 0)
00715                     {
00716                         /* Not signaled, can't satisfy */
00717                         break;
00718                     }
00719 
00720                     /* Go to the next block */
00721                     Index++;
00722                 } while (Index < Count);
00723 
00724                 /* Check if we've went through all the objects */
00725                 if (Index == Count)
00726                 {
00727                     /* Loop wait blocks */
00728                     WaitBlock = WaitBlockArray;
00729                     do
00730                     {
00731                         /* Get the object and satisfy it */
00732                         CurrentObject = (PKMUTANT)WaitBlock->Object;
00733                         KiSatisfyObjectWait(CurrentObject, Thread);
00734 
00735                         /* Go to the next block */
00736                         WaitBlock = WaitBlock->NextWaitBlock;
00737                     } while(WaitBlock != WaitBlockArray);
00738 
00739                     /* Set the wait status and get out */
00740                     WaitStatus = (NTSTATUS)Thread->WaitStatus;
00741                     goto DontWait;
00742                 }
00743             }
00744 
00745             /* Make sure we can satisfy the Alertable request */
00746             WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode);
00747             if (WaitStatus != STATUS_WAIT_0) break;
00748 
00749             /* Enable the Timeout Timer if there was any specified */
00750             if (Timeout)
00751             {
00752                 /* Check if the timer expired */
00753                 InterruptTime.QuadPart = KeQueryInterruptTime();
00754                 if ((ULONGLONG)InterruptTime.QuadPart >=
00755                     Timer->DueTime.QuadPart)
00756                 {
00757                     /* It did, so we don't need to wait */
00758                     WaitStatus = STATUS_TIMEOUT;
00759                     goto DontWait;
00760                 }
00761 
00762                 /* It didn't, so activate it */
00763                 Timer->Header.Inserted = TRUE;
00764 
00765                 /* Link the wait blocks */
00766                 WaitBlock->NextWaitBlock = TimerBlock;
00767             }
00768 
00769             /* Insert into Object's Wait List*/
00770             WaitBlock = WaitBlockArray;
00771             do
00772             {
00773                 /* Get the Current Object */
00774                 CurrentObject = WaitBlock->Object;
00775 
00776                 /* Link the Object to this Wait Block */
00777                 InsertTailList(&CurrentObject->Header.WaitListHead,
00778                                &WaitBlock->WaitListEntry);
00779 
00780                 /* Move to the next Wait Block */
00781                 WaitBlock = WaitBlock->NextWaitBlock;
00782             } while (WaitBlock != WaitBlockArray);
00783 
00784             /* Handle Kernel Queues */
00785             if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue);
00786 
00787             /* Setup the wait information */
00788             Thread->State = Waiting;
00789 
00790             /* Add the thread to the wait list */
00791             KiAddThreadToWaitList(Thread, Swappable);
00792 
00793             /* Activate thread swap */
00794             ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
00795             KiSetThreadSwapBusy(Thread);
00796 
00797             /* Check if we have a timer */
00798             if (Timeout)
00799             {
00800                 /* Insert it */
00801                 KxInsertTimer(Timer, Hand);
00802             }
00803             else
00804             {
00805                 /* Otherwise, unlock the dispatcher */
00806                 KiReleaseDispatcherLockFromDpcLevel();
00807             }
00808 
00809             /* Swap the thread */
00810             WaitStatus = (NTSTATUS)KiSwapThread(Thread, KeGetCurrentPrcb());
00811 
00812             /* Check if we were executing an APC */
00813             if (WaitStatus != STATUS_KERNEL_APC) return WaitStatus;
00814 
00815             /* Check if we had a timeout */
00816             if (Timeout)
00817             {
00818                 /* Recalculate due times */
00819                 Timeout = KiRecalculateDueTime(OriginalDueTime,
00820                                                &DueTime,
00821                                                &NewDueTime);
00822             }
00823         }
00824 
00825 WaitStart:
00826         /* Setup a new wait */
00827         Thread->WaitIrql = KeRaiseIrqlToSynchLevel();
00828         KxMultiThreadWait();
00829         KiAcquireDispatcherLockAtDpcLevel();
00830     }
00831 
00832     /* We are done */
00833     KiReleaseDispatcherLock(Thread->WaitIrql);
00834     return WaitStatus;
00835 
00836 DontWait:
00837     /* Release dispatcher lock but maintain high IRQL */
00838     KiReleaseDispatcherLockFromDpcLevel();
00839 
00840     /* Adjust the Quantum and return the wait status */
00841     KiAdjustQuantumThread(Thread);
00842     return WaitStatus;
00843 }
00844 
00845 NTSTATUS
00846 NTAPI
00847 NtDelayExecution(IN BOOLEAN Alertable,
00848                  IN PLARGE_INTEGER DelayInterval)
00849 {
00850     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
00851     LARGE_INTEGER SafeInterval;
00852     NTSTATUS Status;
00853 
00854     /* Check the previous mode */
00855     if (PreviousMode != KernelMode)
00856     {
00857         /* Enter SEH for probing */
00858         _SEH2_TRY
00859         {
00860             /* Probe and capture the time out */
00861             SafeInterval = ProbeForReadLargeInteger(DelayInterval);
00862             DelayInterval = &SafeInterval;
00863         }
00864         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
00865         {
00866             /* Return the exception code */
00867             _SEH2_YIELD(return _SEH2_GetExceptionCode());
00868         }
00869         _SEH2_END;
00870    }
00871 
00872    /* Call the Kernel Function */
00873    Status = KeDelayExecutionThread(PreviousMode,
00874                                    Alertable,
00875                                    DelayInterval);
00876 
00877    /* Return Status */
00878    return Status;
00879 }
00880 
00881 /* EOF */

Generated on Sun May 27 2012 04:28:08 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.