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 != (LONG)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 (KeGetCurrentIrql() == DISPATCH_LEVEL && 609 (!Timeout || Timeout->QuadPart != 0)) 610 { 611 /* HACK: tcpip is broken and waits with spinlocks acquired (CORE-6473) */ 612 DPRINT("%s called at DISPATCH_LEVEL with non-zero timeout!\n", 613 __FUNCTION__); 614 } 615 else 616 ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); 617 618 /* Make sure the Wait Count is valid */ 619 if (!WaitBlockArray) 620 { 621 /* Check in regards to the Thread Object Limit */ 622 if (Count > THREAD_WAIT_OBJECTS) 623 { 624 /* Bugcheck */ 625 KeBugCheck(MAXIMUM_WAIT_OBJECTS_EXCEEDED); 626 } 627 628 /* Use the Thread's Wait Block */ 629 WaitBlockArray = &Thread->WaitBlock[0]; 630 } 631 else 632 { 633 /* Using our own Block Array, so check with the System Object Limit */ 634 if (Count > MAXIMUM_WAIT_OBJECTS) 635 { 636 /* Bugcheck */ 637 KeBugCheck(MAXIMUM_WAIT_OBJECTS_EXCEEDED); 638 } 639 } 640 641 /* Sanity check */ 642 ASSERT(Count != 0); 643 644 /* Check if the lock is already held */ 645 if (!Thread->WaitNext) goto WaitStart; 646 647 /* Otherwise, we already have the lock, so initialize the wait */ 648 Thread->WaitNext = FALSE; 649 /* Note that KxMultiThreadWait is a macro, defined in ke_x.h, that */ 650 /* uses (and modifies some of) the following local */ 651 /* variables: */ 652 /* Thread, Index, WaitBlock, Timer, Timeout, Hand and Swappable. */ 653 /* If it looks like this code doesn't actually wait for any objects */ 654 /* at all, it's because the setup is done by that macro. */ 655 KxMultiThreadWait(); 656 657 /* Start wait loop */ 658 for (;;) 659 { 660 /* Disable pre-emption */ 661 Thread->Preempted = FALSE; 662 663 /* Check if a kernel APC is pending and we're below APC_LEVEL */ 664 if ((Thread->ApcState.KernelApcPending) && !(Thread->SpecialApcDisable) && 665 (Thread->WaitIrql < APC_LEVEL)) 666 { 667 /* Unlock the dispatcher */ 668 KiReleaseDispatcherLock(Thread->WaitIrql); 669 } 670 else 671 { 672 /* Check what kind of wait this is */ 673 Index = 0; 674 if (WaitType == WaitAny) 675 { 676 /* Loop blocks */ 677 do 678 { 679 /* Get the Current Object */ 680 CurrentObject = (PKMUTANT)Object[Index]; 681 ASSERT(CurrentObject->Header.Type != QueueObject); 682 683 /* Check if the Object is a mutant */ 684 if (CurrentObject->Header.Type == MutantObject) 685 { 686 /* Check if it's signaled */ 687 if ((CurrentObject->Header.SignalState > 0) || 688 (Thread == CurrentObject->OwnerThread)) 689 { 690 /* This is a Wait Any, so unwait this and exit */ 691 if (CurrentObject->Header.SignalState != 692 (LONG)MINLONG) 693 { 694 /* Normal signal state, unwait it and return */ 695 KiSatisfyMutantWait(CurrentObject, Thread); 696 WaitStatus = (NTSTATUS)Thread->WaitStatus | Index; 697 goto DontWait; 698 } 699 else 700 { 701 /* Raise an exception (see wasm.ru) */ 702 KiReleaseDispatcherLock(Thread->WaitIrql); 703 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED); 704 } 705 } 706 } 707 else if (CurrentObject->Header.SignalState > 0) 708 { 709 /* Another signaled object, unwait and return */ 710 KiSatisfyNonMutantWait(CurrentObject); 711 WaitStatus = Index; 712 goto DontWait; 713 } 714 715 /* Go to the next block */ 716 Index++; 717 } while (Index < Count); 718 } 719 else 720 { 721 /* Loop blocks */ 722 do 723 { 724 /* Get the Current Object */ 725 CurrentObject = (PKMUTANT)Object[Index]; 726 ASSERT(CurrentObject->Header.Type != QueueObject); 727 728 /* Check if we're dealing with a mutant again */ 729 if (CurrentObject->Header.Type == MutantObject) 730 { 731 /* Check if it has an invalid count */ 732 if ((Thread == CurrentObject->OwnerThread) && 733 (CurrentObject->Header.SignalState == (LONG)MINLONG)) 734 { 735 /* Raise an exception */ 736 KiReleaseDispatcherLock(Thread->WaitIrql); 737 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED); 738 } 739 else if ((CurrentObject->Header.SignalState <= 0) && 740 (Thread != CurrentObject->OwnerThread)) 741 { 742 /* We don't own it, can't satisfy the wait */ 743 break; 744 } 745 } 746 else if (CurrentObject->Header.SignalState <= 0) 747 { 748 /* Not signaled, can't satisfy */ 749 break; 750 } 751 752 /* Go to the next block */ 753 Index++; 754 } while (Index < Count); 755 756 /* Check if we've went through all the objects */ 757 if (Index == Count) 758 { 759 /* Loop wait blocks */ 760 WaitBlock = WaitBlockArray; 761 do 762 { 763 /* Get the object and satisfy it */ 764 CurrentObject = (PKMUTANT)WaitBlock->Object; 765 KiSatisfyObjectWait(CurrentObject, Thread); 766 767 /* Go to the next block */ 768 WaitBlock = WaitBlock->NextWaitBlock; 769 } while(WaitBlock != WaitBlockArray); 770 771 /* Set the wait status and get out */ 772 WaitStatus = (NTSTATUS)Thread->WaitStatus; 773 goto DontWait; 774 } 775 } 776 777 /* Make sure we can satisfy the Alertable request */ 778 WaitStatus = KiCheckAlertability(Thread, Alertable, WaitMode); 779 if (WaitStatus != STATUS_WAIT_0) break; 780 781 /* Enable the Timeout Timer if there was any specified */ 782 if (Timeout) 783 { 784 /* Check if the timer expired */ 785 InterruptTime.QuadPart = KeQueryInterruptTime(); 786 if ((ULONGLONG)InterruptTime.QuadPart >= 787 Timer->DueTime.QuadPart) 788 { 789 /* It did, so we don't need to wait */ 790 WaitStatus = STATUS_TIMEOUT; 791 goto DontWait; 792 } 793 794 /* It didn't, so activate it */ 795 Timer->Header.Inserted = TRUE; 796 797 /* Link the wait blocks */ 798 WaitBlock->NextWaitBlock = TimerBlock; 799 } 800 801 /* Insert into Object's Wait List*/ 802 WaitBlock = WaitBlockArray; 803 do 804 { 805 /* Get the Current Object */ 806 CurrentObject = WaitBlock->Object; 807 808 /* Link the Object to this Wait Block */ 809 InsertTailList(&CurrentObject->Header.WaitListHead, 810 &WaitBlock->WaitListEntry); 811 812 /* Move to the next Wait Block */ 813 WaitBlock = WaitBlock->NextWaitBlock; 814 } while (WaitBlock != WaitBlockArray); 815 816 /* Handle Kernel Queues */ 817 if (Thread->Queue) KiActivateWaiterQueue(Thread->Queue); 818 819 /* Setup the wait information */ 820 Thread->State = Waiting; 821 822 /* Add the thread to the wait list */ 823 KiAddThreadToWaitList(Thread, Swappable); 824 825 /* Activate thread swap */ 826 ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL); 827 KiSetThreadSwapBusy(Thread); 828 829 /* Check if we have a timer */ 830 if (Timeout) 831 { 832 /* Insert it */ 833 KxInsertTimer(Timer, Hand); 834 } 835 else 836 { 837 /* Otherwise, unlock the dispatcher */ 838 KiReleaseDispatcherLockFromSynchLevel(); 839 } 840 841 /* Swap the thread */ 842 WaitStatus = (NTSTATUS)KiSwapThread(Thread, KeGetCurrentPrcb()); 843 844 /* Check if we were executing an APC */ 845 if (WaitStatus != STATUS_KERNEL_APC) return WaitStatus; 846 847 /* Check if we had a timeout */ 848 if (Timeout) 849 { 850 /* Recalculate due times */ 851 Timeout = KiRecalculateDueTime(OriginalDueTime, 852 &DueTime, 853 &NewDueTime); 854 } 855 } 856 857 WaitStart: 858 /* Setup a new wait */ 859 Thread->WaitIrql = KeRaiseIrqlToSynchLevel(); 860 KxMultiThreadWait(); 861 KiAcquireDispatcherLockAtSynchLevel(); 862 } 863 864 /* We are done */ 865 KiReleaseDispatcherLock(Thread->WaitIrql); 866 return WaitStatus; 867 868 DontWait: 869 /* Release dispatcher lock but maintain high IRQL */ 870 KiReleaseDispatcherLockFromSynchLevel(); 871 872 /* Adjust the Quantum and return the wait status */ 873 KiAdjustQuantumThread(Thread); 874 return WaitStatus; 875 } 876 877 NTSTATUS 878 NTAPI 879 NtDelayExecution(IN BOOLEAN Alertable, 880 IN PLARGE_INTEGER DelayInterval) 881 { 882 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 883 LARGE_INTEGER SafeInterval; 884 NTSTATUS Status; 885 886 /* Check the previous mode */ 887 if (PreviousMode != KernelMode) 888 { 889 /* Enter SEH for probing */ 890 _SEH2_TRY 891 { 892 /* Probe and capture the time out */ 893 SafeInterval = ProbeForReadLargeInteger(DelayInterval); 894 DelayInterval = &SafeInterval; 895 } 896 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 897 { 898 /* Return the exception code */ 899 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 900 } 901 _SEH2_END; 902 } 903 904 /* Call the Kernel Function */ 905 Status = KeDelayExecutionThread(PreviousMode, 906 Alertable, 907 DelayInterval); 908 909 /* Return Status */ 910 return Status; 911 } 912 913 /* EOF */ 914