1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: ntoskrnl/ke/wait.c 5 * PURPOSE: Manages waiting for Dispatcher Objects 6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) 7 * Gunnar Dalsnes 8 */ 9 10 /* INCLUDES ******************************************************************/ 11 12 #include <ntoskrnl.h> 13 #define NDEBUG 14 #include <debug.h> 15 16 /* PRIVATE FUNCTIONS *********************************************************/ 17 18 VOID 19 FASTCALL 20 KiWaitTest(IN PVOID ObjectPointer, 21 IN KPRIORITY Increment) 22 { 23 PLIST_ENTRY WaitEntry, WaitList; 24 PKWAIT_BLOCK WaitBlock; 25 PKTHREAD WaitThread; 26 PKMUTANT FirstObject = ObjectPointer; 27 NTSTATUS WaitStatus; 28 29 /* Loop the Wait Entries */ 30 WaitList = &FirstObject->Header.WaitListHead; 31 WaitEntry = WaitList->Flink; 32 while ((FirstObject->Header.SignalState > 0) && (WaitEntry != WaitList)) 33 { 34 /* Get the current wait block */ 35 WaitBlock = CONTAINING_RECORD(WaitEntry, KWAIT_BLOCK, WaitListEntry); 36 WaitThread = WaitBlock->Thread; 37 WaitStatus = STATUS_KERNEL_APC; 38 39 /* Check the current Wait Mode */ 40 if (WaitBlock->WaitType == WaitAny) 41 { 42 /* Easy case, satisfy only this wait */ 43 WaitStatus = (NTSTATUS)WaitBlock->WaitKey; 44 KiSatisfyObjectWait(FirstObject, WaitThread); 45 } 46 47 /* Now do the rest of the unwait */ 48 KiUnwaitThread(WaitThread, WaitStatus, Increment); 49 WaitEntry = WaitList->Flink; 50 } 51 } 52 53 VOID 54 FASTCALL 55 KiUnlinkThread(IN PKTHREAD Thread, 56 IN LONG_PTR WaitStatus) 57 { 58 PKWAIT_BLOCK WaitBlock; 59 PKTIMER Timer; 60 61 /* Update wait status */ 62 Thread->WaitStatus |= WaitStatus; 63 64 /* Remove the Wait Blocks from the list */ 65 WaitBlock = Thread->WaitBlockList; 66 do 67 { 68 /* Remove it */ 69 RemoveEntryList(&WaitBlock->WaitListEntry); 70 71 /* Go to the next one */ 72 WaitBlock = WaitBlock->NextWaitBlock; 73 } while (WaitBlock != Thread->WaitBlockList); 74 75 /* Remove the thread from the wait list! */ 76 if (Thread->WaitListEntry.Flink) RemoveEntryList(&Thread->WaitListEntry); 77 78 /* Check if there's a Thread Timer */ 79 Timer = &Thread->Timer; 80 if (Timer->Header.Inserted) KxRemoveTreeTimer(Timer); 81 82 /* Increment the Queue's active threads */ 83 if (Thread->Queue) Thread->Queue->CurrentCount++; 84 } 85 86 /* Must be called with the dispatcher lock held */ 87 VOID 88 FASTCALL 89 KiUnwaitThread(IN PKTHREAD Thread, 90 IN LONG_PTR WaitStatus, 91 IN KPRIORITY Increment) 92 { 93 /* Unlink the thread */ 94 KiUnlinkThread(Thread, WaitStatus); 95 96 /* Tell the scheduler do to the increment when it readies the thread */ 97 ASSERT(Increment >= 0); 98 Thread->AdjustIncrement = (SCHAR)Increment; 99 Thread->AdjustReason = AdjustUnwait; 100 101 /* Reschedule the Thread */ 102 KiReadyThread(Thread); 103 } 104 105 VOID 106 FASTCALL 107 KiAcquireFastMutex(IN PFAST_MUTEX FastMutex) 108 { 109 /* Increase contention count */ 110 FastMutex->Contention++; 111 112 /* Wait for the event */ 113 KeWaitForSingleObject(&FastMutex->Event, 114 WrMutex, 115 KernelMode, 116 FALSE, 117 NULL); 118 } 119 120 VOID 121 FASTCALL 122 KiAcquireGuardedMutex(IN OUT PKGUARDED_MUTEX GuardedMutex) 123 { 124 ULONG BitsToRemove, BitsToAdd; 125 LONG OldValue, NewValue; 126 127 /* We depend on these bits being just right */ 128 C_ASSERT((GM_LOCK_WAITER_WOKEN * 2) == GM_LOCK_WAITER_INC); 129 130 /* Increase the contention count */ 131 GuardedMutex->Contention++; 132 133 /* Start by unlocking the Guarded Mutex */ 134 BitsToRemove = GM_LOCK_BIT; 135 BitsToAdd = GM_LOCK_WAITER_INC; 136 137 /* Start change loop */ 138 for (;;) 139 { 140 /* Loop sanity checks */ 141 ASSERT((BitsToRemove == GM_LOCK_BIT) || 142 (BitsToRemove == (GM_LOCK_BIT | GM_LOCK_WAITER_WOKEN))); 143 ASSERT((BitsToAdd == GM_LOCK_WAITER_INC) || 144 (BitsToAdd == GM_LOCK_WAITER_WOKEN)); 145 146 /* Get the Count Bits */ 147 OldValue = GuardedMutex->Count; 148 149 /* Start internal bit change loop */ 150 for (;;) 151 { 152 /* Check if the Guarded Mutex is locked */ 153 if (OldValue & GM_LOCK_BIT) 154 { 155 /* Sanity check */ 156 ASSERT((BitsToRemove == GM_LOCK_BIT) || 157 ((OldValue & GM_LOCK_WAITER_WOKEN) != 0)); 158 159 /* Unlock it by removing the Lock Bit */ 160 NewValue = OldValue ^ BitsToRemove; 161 NewValue = InterlockedCompareExchange(&GuardedMutex->Count, 162 NewValue, 163 OldValue); 164 if (NewValue == OldValue) return; 165 } 166 else 167 { 168 /* The Guarded Mutex isn't locked, so simply set the bits */ 169 NewValue = OldValue + BitsToAdd; 170 NewValue = InterlockedCompareExchange(&GuardedMutex->Count, 171 NewValue, 172 OldValue); 173 if (NewValue == OldValue) break; 174 } 175 176 /* Old value changed, loop again */ 177 OldValue = NewValue; 178 } 179 180 /* Now we have to wait for it */ 181 KeWaitForGate(&GuardedMutex->Gate, WrGuardedMutex, KernelMode); 182 ASSERT((GuardedMutex->Count & GM_LOCK_WAITER_WOKEN) != 0); 183 184 /* Ok, the wait is done, so set the new bits */ 185 BitsToRemove = GM_LOCK_BIT | GM_LOCK_WAITER_WOKEN; 186 BitsToAdd = GM_LOCK_WAITER_WOKEN; 187 } 188 } 189 190 // 191 // This routine exits the dispatcher after a compatible operation and 192 // swaps the context to the next scheduled thread on the current CPU if 193 // one is available. 194 // 195 // It does NOT attempt to scan for a new thread to schedule. 196 // 197 VOID 198 FASTCALL 199 KiExitDispatcher(IN KIRQL OldIrql) 200 { 201 PKPRCB Prcb = KeGetCurrentPrcb(); 202 PKTHREAD Thread, NextThread; 203 BOOLEAN PendingApc; 204 205 /* Make sure we're at synchronization level */ 206 ASSERT(KeGetCurrentIrql() == SYNCH_LEVEL); 207 208 /* Check if we have deferred threads */ 209 KiCheckDeferredReadyList(Prcb); 210 211 /* Check if we were called at dispatcher level or higher */ 212 if (OldIrql >= DISPATCH_LEVEL) 213 { 214 /* Check if we have a thread to schedule, and that no DPC is active */ 215 if ((Prcb->NextThread) && !(Prcb->DpcRoutineActive)) 216 { 217 /* Request DPC interrupt */ 218 HalRequestSoftwareInterrupt(DISPATCH_LEVEL); 219 } 220 221 /* Lower IRQL and exit */ 222 goto Quickie; 223 } 224 225 /* Make sure there's a new thread scheduled */ 226 if (!Prcb->NextThread) goto Quickie; 227 228 /* Lock the PRCB */ 229 KiAcquirePrcbLock(Prcb); 230 231 /* Get the next and current threads now */ 232 NextThread = Prcb->NextThread; 233 Thread = Prcb->CurrentThread; 234 235 /* Set current thread's swap busy to true */ 236 KiSetThreadSwapBusy(Thread); 237 238 /* Switch threads in PRCB */ 239 Prcb->NextThread = NULL; 240 Prcb->CurrentThread = NextThread; 241 242 /* Set thread to running */ 243 NextThread->State = Running; 244 245 /* Queue it on the ready lists */ 246 KxQueueReadyThread(Thread, Prcb); 247 248 /* Set wait IRQL */ 249 Thread->WaitIrql = OldIrql; 250 251 /* Swap threads and check if APCs were pending */ 252 PendingApc = KiSwapContext(OldIrql, Thread); 253 if (PendingApc) 254 { 255 /* Lower only to APC */ 256 KeLowerIrql(APC_LEVEL); 257 258 /* Deliver APCs */ 259 KiDeliverApc(KernelMode, NULL, NULL); 260 ASSERT(OldIrql == PASSIVE_LEVEL); 261 } 262 263 /* Lower IRQl back */ 264 Quickie: 265 KeLowerIrql(OldIrql); 266 } 267 268 /* PUBLIC FUNCTIONS **********************************************************/ 269 270 BOOLEAN 271 NTAPI 272 KeIsWaitListEmpty(IN PVOID Object) 273 { 274 UNIMPLEMENTED; 275 return FALSE; 276 } 277 278 /* 279 * @implemented 280 */ 281 NTSTATUS 282 NTAPI 283 KeDelayExecutionThread(IN KPROCESSOR_MODE WaitMode, 284 IN BOOLEAN Alertable, 285 IN PLARGE_INTEGER Interval OPTIONAL) 286 { 287 PKTIMER Timer; 288 PKWAIT_BLOCK TimerBlock; 289 PKTHREAD Thread = KeGetCurrentThread(); 290 NTSTATUS WaitStatus; 291 BOOLEAN Swappable; 292 PLARGE_INTEGER OriginalDueTime; 293 LARGE_INTEGER DueTime, NewDueTime, InterruptTime; 294 ULONG Hand = 0; 295 296 if (Thread->WaitNext) 297 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); 298 else 299 ASSERT(KeGetCurrentIrql() <= APC_LEVEL); 300 301 /* If this is a user-mode wait of 0 seconds, yield execution */ 302 if (!(Interval->QuadPart) && (WaitMode != KernelMode)) 303 { 304 /* Make sure the wait isn't alertable or interrupting an APC */ 305 if (!(Alertable) && !(Thread->ApcState.UserApcPending)) 306 { 307 /* Yield execution */ 308 return NtYieldExecution(); 309 } 310 } 311 312 /* Setup the original time and timer/wait blocks */ 313 OriginalDueTime = Interval; 314 Timer = &Thread->Timer; 315 TimerBlock = &Thread->WaitBlock[TIMER_WAIT_BLOCK]; 316 317 /* Check if the lock is already held */ 318 if (!Thread->WaitNext) goto WaitStart; 319 320 /* Otherwise, we already have the lock, so initialize the wait */ 321 Thread->WaitNext = FALSE; 322 KxDelayThreadWait(); 323 324 /* Start wait loop */ 325 for (;;) 326 { 327 /* Disable pre-emption */ 328 Thread->Preempted = FALSE; 329 330 /* Check if a kernel APC is pending and we're below APC_LEVEL */ 331 if ((Thread->ApcState.KernelApcPending) && !(Thread->SpecialApcDisable) && 332 (Thread->WaitIrql < APC_LEVEL)) 333 { 334 /* Unlock the dispatcher */ 335 KiReleaseDispatcherLock(Thread->WaitIrql); 336 } 337 else 338 { 339 /* Check if we have to bail out due to an alerted state */ 340 WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode); 341 if (WaitStatus != STATUS_WAIT_0) break; 342 343 /* Check if the timer expired */ 344 InterruptTime.QuadPart = KeQueryInterruptTime(); 345 if ((ULONGLONG)InterruptTime.QuadPart >= Timer->DueTime.QuadPart) 346 { 347 /* It did, so we don't need to wait */ 348 goto NoWait; 349 } 350 351 /* It didn't, so activate it */ 352 Timer->Header.Inserted = TRUE; 353 354 /* Handle Kernel Queues */ 355 if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue); 356 357 /* Setup the wait information */ 358 Thread->State = Waiting; 359 360 /* Add the thread to the wait list */ 361 KiAddThreadToWaitList(Thread, Swappable); 362 363 /* Insert the timer and swap the thread */ 364 ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL); 365 KiSetThreadSwapBusy(Thread); 366 KxInsertTimer(Timer, Hand); 367 WaitStatus = (NTSTATUS)KiSwapThread(Thread, KeGetCurrentPrcb()); 368 369 /* Check if were swapped ok */ 370 if (WaitStatus != STATUS_KERNEL_APC) 371 { 372 /* This is a good thing */ 373 if (WaitStatus == STATUS_TIMEOUT) WaitStatus = STATUS_SUCCESS; 374 375 /* Return Status */ 376 return WaitStatus; 377 } 378 379 /* Recalculate due times */ 380 Interval = KiRecalculateDueTime(OriginalDueTime, 381 &DueTime, 382 &NewDueTime); 383 } 384 385 WaitStart: 386 /* Setup a new wait */ 387 Thread->WaitIrql = KeRaiseIrqlToSynchLevel(); 388 KxDelayThreadWait(); 389 KiAcquireDispatcherLockAtSynchLevel(); 390 } 391 392 /* We're done! */ 393 KiReleaseDispatcherLock(Thread->WaitIrql); 394 return WaitStatus; 395 396 NoWait: 397 /* There was nothing to wait for. Did we have a wait interval? */ 398 if (!Interval->QuadPart) 399 { 400 /* Unlock the dispatcher and do a yield */ 401 KiReleaseDispatcherLock(Thread->WaitIrql); 402 return NtYieldExecution(); 403 } 404 405 /* Unlock the dispatcher and adjust the quantum for a no-wait */ 406 KiReleaseDispatcherLockFromSynchLevel(); 407 KiAdjustQuantumThread(Thread); 408 return STATUS_SUCCESS; 409 } 410 411 /* 412 * @implemented 413 */ 414 NTSTATUS 415 NTAPI 416 KeWaitForSingleObject(IN PVOID Object, 417 IN KWAIT_REASON WaitReason, 418 IN KPROCESSOR_MODE WaitMode, 419 IN BOOLEAN Alertable, 420 IN PLARGE_INTEGER Timeout OPTIONAL) 421 { 422 PKTHREAD Thread = KeGetCurrentThread(); 423 PKMUTANT CurrentObject = (PKMUTANT)Object; 424 PKWAIT_BLOCK WaitBlock = &Thread->WaitBlock[0]; 425 PKWAIT_BLOCK TimerBlock = &Thread->WaitBlock[TIMER_WAIT_BLOCK]; 426 PKTIMER Timer = &Thread->Timer; 427 NTSTATUS WaitStatus; 428 BOOLEAN Swappable; 429 LARGE_INTEGER DueTime = {{0}}, NewDueTime, InterruptTime; 430 PLARGE_INTEGER OriginalDueTime = Timeout; 431 ULONG Hand = 0; 432 433 if (Thread->WaitNext) 434 ASSERT(KeGetCurrentIrql() == SYNCH_LEVEL); 435 else 436 ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL || 437 (KeGetCurrentIrql() == DISPATCH_LEVEL && 438 Timeout && Timeout->QuadPart == 0)); 439 440 /* Check if the lock is already held */ 441 if (!Thread->WaitNext) goto WaitStart; 442 443 /* Otherwise, we already have the lock, so initialize the wait */ 444 Thread->WaitNext = FALSE; 445 KxSingleThreadWait(); 446 447 /* Start wait loop */ 448 for (;;) 449 { 450 /* Disable pre-emption */ 451 Thread->Preempted = FALSE; 452 453 /* Check if a kernel APC is pending and we're below APC_LEVEL */ 454 if ((Thread->ApcState.KernelApcPending) && !(Thread->SpecialApcDisable) && 455 (Thread->WaitIrql < APC_LEVEL)) 456 { 457 /* Unlock the dispatcher */ 458 KiReleaseDispatcherLock(Thread->WaitIrql); 459 } 460 else 461 { 462 /* Sanity check */ 463 ASSERT(CurrentObject->Header.Type != QueueObject); 464 465 /* Check if it's a mutant */ 466 if (CurrentObject->Header.Type == MutantObject) 467 { 468 /* Check its signal state or if we own it */ 469 if ((CurrentObject->Header.SignalState > 0) || 470 (Thread == CurrentObject->OwnerThread)) 471 { 472 /* Just unwait this guy and exit */ 473 if (CurrentObject->Header.SignalState != MINLONG) 474 { 475 /* It has a normal signal state. Unwait and return */ 476 KiSatisfyMutantWait(CurrentObject, Thread); 477 WaitStatus = (NTSTATUS)Thread->WaitStatus; 478 goto DontWait; 479 } 480 else 481 { 482 /* Raise an exception */ 483 KiReleaseDispatcherLock(Thread->WaitIrql); 484 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED); 485 } 486 } 487 } 488 else if (CurrentObject->Header.SignalState > 0) 489 { 490 /* Another satisfied object */ 491 KiSatisfyNonMutantWait(CurrentObject); 492 WaitStatus = STATUS_WAIT_0; 493 goto DontWait; 494 } 495 496 /* Make sure we can satisfy the Alertable request */ 497 WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode); 498 if (WaitStatus != STATUS_WAIT_0) break; 499 500 /* Enable the Timeout Timer if there was any specified */ 501 if (Timeout) 502 { 503 /* Check if the timer expired */ 504 InterruptTime.QuadPart = KeQueryInterruptTime(); 505 if ((ULONGLONG)InterruptTime.QuadPart >= 506 Timer->DueTime.QuadPart) 507 { 508 /* It did, so we don't need to wait */ 509 WaitStatus = STATUS_TIMEOUT; 510 goto DontWait; 511 } 512 513 /* It didn't, so activate it */ 514 Timer->Header.Inserted = TRUE; 515 } 516 517 /* Link the Object to this Wait Block */ 518 InsertTailList(&CurrentObject->Header.WaitListHead, 519 &WaitBlock->WaitListEntry); 520 521 /* Handle Kernel Queues */ 522 if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue); 523 524 /* Setup the wait information */ 525 Thread->State = Waiting; 526 527 /* Add the thread to the wait list */ 528 KiAddThreadToWaitList(Thread, Swappable); 529 530 /* Activate thread swap */ 531 ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL); 532 KiSetThreadSwapBusy(Thread); 533 534 /* Check if we have a timer */ 535 if (Timeout) 536 { 537 /* Insert it */ 538 KxInsertTimer(Timer, Hand); 539 } 540 else 541 { 542 /* Otherwise, unlock the dispatcher */ 543 KiReleaseDispatcherLockFromSynchLevel(); 544 } 545 546 /* Do the actual swap */ 547 WaitStatus = (NTSTATUS)KiSwapThread(Thread, KeGetCurrentPrcb()); 548 549 /* Check if we were executing an APC */ 550 if (WaitStatus != STATUS_KERNEL_APC) return WaitStatus; 551 552 /* Check if we had a timeout */ 553 if (Timeout) 554 { 555 /* Recalculate due times */ 556 Timeout = KiRecalculateDueTime(OriginalDueTime, 557 &DueTime, 558 &NewDueTime); 559 } 560 } 561 WaitStart: 562 /* Setup a new wait */ 563 Thread->WaitIrql = KeRaiseIrqlToSynchLevel(); 564 KxSingleThreadWait(); 565 KiAcquireDispatcherLockAtSynchLevel(); 566 } 567 568 /* Wait complete */ 569 KiReleaseDispatcherLock(Thread->WaitIrql); 570 return WaitStatus; 571 572 DontWait: 573 /* Release dispatcher lock but maintain high IRQL */ 574 KiReleaseDispatcherLockFromSynchLevel(); 575 576 /* Adjust the Quantum and return the wait status */ 577 KiAdjustQuantumThread(Thread); 578 return WaitStatus; 579 } 580 581 /* 582 * @implemented 583 */ 584 NTSTATUS 585 NTAPI 586 KeWaitForMultipleObjects(IN ULONG Count, 587 IN PVOID Object[], 588 IN WAIT_TYPE WaitType, 589 IN KWAIT_REASON WaitReason, 590 IN KPROCESSOR_MODE WaitMode, 591 IN BOOLEAN Alertable, 592 IN PLARGE_INTEGER Timeout OPTIONAL, 593 OUT PKWAIT_BLOCK WaitBlockArray OPTIONAL) 594 { 595 PKMUTANT CurrentObject; 596 PKWAIT_BLOCK WaitBlock; 597 PKTHREAD Thread = KeGetCurrentThread(); 598 PKWAIT_BLOCK TimerBlock = &Thread->WaitBlock[TIMER_WAIT_BLOCK]; 599 PKTIMER Timer = &Thread->Timer; 600 NTSTATUS WaitStatus = STATUS_SUCCESS; 601 BOOLEAN Swappable; 602 PLARGE_INTEGER OriginalDueTime = Timeout; 603 LARGE_INTEGER DueTime = {{0}}, NewDueTime, InterruptTime; 604 ULONG Index, Hand = 0; 605 606 if (Thread->WaitNext) 607 ASSERT(KeGetCurrentIrql() == SYNCH_LEVEL); 608 else if (!Timeout || (Timeout->QuadPart != 0)) 609 { 610 ASSERT(KeGetCurrentIrql() <= APC_LEVEL); 611 } 612 else 613 ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); 614 615 /* Make sure the Wait Count is valid */ 616 if (!WaitBlockArray) 617 { 618 /* Check in regards to the Thread Object Limit */ 619 if (Count > THREAD_WAIT_OBJECTS) 620 { 621 /* Bugcheck */ 622 KeBugCheck(MAXIMUM_WAIT_OBJECTS_EXCEEDED); 623 } 624 625 /* Use the Thread's Wait Block */ 626 WaitBlockArray = &Thread->WaitBlock[0]; 627 } 628 else 629 { 630 /* Using our own Block Array, so check with the System Object Limit */ 631 if (Count > MAXIMUM_WAIT_OBJECTS) 632 { 633 /* Bugcheck */ 634 KeBugCheck(MAXIMUM_WAIT_OBJECTS_EXCEEDED); 635 } 636 } 637 638 /* Sanity check */ 639 ASSERT(Count != 0); 640 641 /* Check if the lock is already held */ 642 if (!Thread->WaitNext) goto WaitStart; 643 644 /* Otherwise, we already have the lock, so initialize the wait */ 645 Thread->WaitNext = FALSE; 646 /* Note that KxMultiThreadWait is a macro, defined in ke_x.h, that */ 647 /* uses (and modifies some of) the following local */ 648 /* variables: */ 649 /* Thread, Index, WaitBlock, Timer, Timeout, Hand and Swappable. */ 650 /* If it looks like this code doesn't actually wait for any objects */ 651 /* at all, it's because the setup is done by that macro. */ 652 KxMultiThreadWait(); 653 654 /* Start wait loop */ 655 for (;;) 656 { 657 /* Disable pre-emption */ 658 Thread->Preempted = FALSE; 659 660 /* Check if a kernel APC is pending and we're below APC_LEVEL */ 661 if ((Thread->ApcState.KernelApcPending) && !(Thread->SpecialApcDisable) && 662 (Thread->WaitIrql < APC_LEVEL)) 663 { 664 /* Unlock the dispatcher */ 665 KiReleaseDispatcherLock(Thread->WaitIrql); 666 } 667 else 668 { 669 /* Check what kind of wait this is */ 670 Index = 0; 671 if (WaitType == WaitAny) 672 { 673 /* Loop blocks */ 674 do 675 { 676 /* Get the Current Object */ 677 CurrentObject = (PKMUTANT)Object[Index]; 678 ASSERT(CurrentObject->Header.Type != QueueObject); 679 680 /* Check if the Object is a mutant */ 681 if (CurrentObject->Header.Type == MutantObject) 682 { 683 /* Check if it's signaled */ 684 if ((CurrentObject->Header.SignalState > 0) || 685 (Thread == CurrentObject->OwnerThread)) 686 { 687 /* This is a Wait Any, so unwait this and exit */ 688 if (CurrentObject->Header.SignalState != 689 (LONG)MINLONG) 690 { 691 /* Normal signal state, unwait it and return */ 692 KiSatisfyMutantWait(CurrentObject, Thread); 693 WaitStatus = (NTSTATUS)Thread->WaitStatus | Index; 694 goto DontWait; 695 } 696 else 697 { 698 /* Raise an exception (see wasm.ru) */ 699 KiReleaseDispatcherLock(Thread->WaitIrql); 700 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED); 701 } 702 } 703 } 704 else if (CurrentObject->Header.SignalState > 0) 705 { 706 /* Another signaled object, unwait and return */ 707 KiSatisfyNonMutantWait(CurrentObject); 708 WaitStatus = Index; 709 goto DontWait; 710 } 711 712 /* Go to the next block */ 713 Index++; 714 } while (Index < Count); 715 } 716 else 717 { 718 /* Loop blocks */ 719 do 720 { 721 /* Get the Current Object */ 722 CurrentObject = (PKMUTANT)Object[Index]; 723 ASSERT(CurrentObject->Header.Type != QueueObject); 724 725 /* Check if we're dealing with a mutant again */ 726 if (CurrentObject->Header.Type == MutantObject) 727 { 728 /* Check if it has an invalid count */ 729 if ((Thread == CurrentObject->OwnerThread) && 730 (CurrentObject->Header.SignalState == (LONG)MINLONG)) 731 { 732 /* Raise an exception */ 733 KiReleaseDispatcherLock(Thread->WaitIrql); 734 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED); 735 } 736 else if ((CurrentObject->Header.SignalState <= 0) && 737 (Thread != CurrentObject->OwnerThread)) 738 { 739 /* We don't own it, can't satisfy the wait */ 740 break; 741 } 742 } 743 else if (CurrentObject->Header.SignalState <= 0) 744 { 745 /* Not signaled, can't satisfy */ 746 break; 747 } 748 749 /* Go to the next block */ 750 Index++; 751 } while (Index < Count); 752 753 /* Check if we've went through all the objects */ 754 if (Index == Count) 755 { 756 /* Loop wait blocks */ 757 WaitBlock = WaitBlockArray; 758 do 759 { 760 /* Get the object and satisfy it */ 761 CurrentObject = (PKMUTANT)WaitBlock->Object; 762 KiSatisfyObjectWait(CurrentObject, Thread); 763 764 /* Go to the next block */ 765 WaitBlock = WaitBlock->NextWaitBlock; 766 } while(WaitBlock != WaitBlockArray); 767 768 /* Set the wait status and get out */ 769 WaitStatus = (NTSTATUS)Thread->WaitStatus; 770 goto DontWait; 771 } 772 } 773 774 /* Make sure we can satisfy the Alertable request */ 775 WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode); 776 if (WaitStatus != STATUS_WAIT_0) break; 777 778 /* Enable the Timeout Timer if there was any specified */ 779 if (Timeout) 780 { 781 /* Check if the timer expired */ 782 InterruptTime.QuadPart = KeQueryInterruptTime(); 783 if ((ULONGLONG)InterruptTime.QuadPart >= 784 Timer->DueTime.QuadPart) 785 { 786 /* It did, so we don't need to wait */ 787 WaitStatus = STATUS_TIMEOUT; 788 goto DontWait; 789 } 790 791 /* It didn't, so activate it */ 792 Timer->Header.Inserted = TRUE; 793 794 /* Link the wait blocks */ 795 WaitBlock->NextWaitBlock = TimerBlock; 796 } 797 798 /* Insert into Object's Wait List*/ 799 WaitBlock = WaitBlockArray; 800 do 801 { 802 /* Get the Current Object */ 803 CurrentObject = WaitBlock->Object; 804 805 /* Link the Object to this Wait Block */ 806 InsertTailList(&CurrentObject->Header.WaitListHead, 807 &WaitBlock->WaitListEntry); 808 809 /* Move to the next Wait Block */ 810 WaitBlock = WaitBlock->NextWaitBlock; 811 } while (WaitBlock != WaitBlockArray); 812 813 /* Handle Kernel Queues */ 814 if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue); 815 816 /* Setup the wait information */ 817 Thread->State = Waiting; 818 819 /* Add the thread to the wait list */ 820 KiAddThreadToWaitList(Thread, Swappable); 821 822 /* Activate thread swap */ 823 ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL); 824 KiSetThreadSwapBusy(Thread); 825 826 /* Check if we have a timer */ 827 if (Timeout) 828 { 829 /* Insert it */ 830 KxInsertTimer(Timer, Hand); 831 } 832 else 833 { 834 /* Otherwise, unlock the dispatcher */ 835 KiReleaseDispatcherLockFromSynchLevel(); 836 } 837 838 /* Swap the thread */ 839 WaitStatus = (NTSTATUS)KiSwapThread(Thread, KeGetCurrentPrcb()); 840 841 /* Check if we were executing an APC */ 842 if (WaitStatus != STATUS_KERNEL_APC) return WaitStatus; 843 844 /* Check if we had a timeout */ 845 if (Timeout) 846 { 847 /* Recalculate due times */ 848 Timeout = KiRecalculateDueTime(OriginalDueTime, 849 &DueTime, 850 &NewDueTime); 851 } 852 } 853 854 WaitStart: 855 /* Setup a new wait */ 856 Thread->WaitIrql = KeRaiseIrqlToSynchLevel(); 857 KxMultiThreadWait(); 858 KiAcquireDispatcherLockAtSynchLevel(); 859 } 860 861 /* We are done */ 862 KiReleaseDispatcherLock(Thread->WaitIrql); 863 return WaitStatus; 864 865 DontWait: 866 /* Release dispatcher lock but maintain high IRQL */ 867 KiReleaseDispatcherLockFromSynchLevel(); 868 869 /* Adjust the Quantum and return the wait status */ 870 KiAdjustQuantumThread(Thread); 871 return WaitStatus; 872 } 873 874 NTSTATUS 875 NTAPI 876 NtDelayExecution(IN BOOLEAN Alertable, 877 IN PLARGE_INTEGER DelayInterval) 878 { 879 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 880 LARGE_INTEGER SafeInterval; 881 NTSTATUS Status; 882 883 /* Check the previous mode */ 884 if (PreviousMode != KernelMode) 885 { 886 /* Enter SEH for probing */ 887 _SEH2_TRY 888 { 889 /* Probe and capture the time out */ 890 SafeInterval = ProbeForReadLargeInteger(DelayInterval); 891 DelayInterval = &SafeInterval; 892 } 893 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 894 { 895 /* Return the exception code */ 896 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 897 } 898 _SEH2_END; 899 } 900 901 /* Call the Kernel Function */ 902 Status = KeDelayExecutionThread(PreviousMode, 903 Alertable, 904 DelayInterval); 905 906 /* Return Status */ 907 return Status; 908 } 909 910 /* EOF */ 911