2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS project
4 * FILE: ntoskrnl/ke/wait.c
5 * PURPOSE: Manages non-busy waiting
6 * PROGRAMMER: David Welch (welch@mcmail.com)
9 * 12/1/99: Phillip Susi: Fixed wake code in KeDispatcherObjectWake
10 * so that things like KeWaitForXXX() return the correct value
13 /* NOTES ********************************************************************
17 /* INCLUDES ******************************************************************/
19 #include <ddk/ntddk.h>
20 #include <internal/ke.h>
21 #include <internal/ps.h>
22 #include <internal/ob.h>
23 #include <internal/id.h>
24 #include <ntos/ntdef.h>
27 #include <internal/debug.h>
29 /* GLOBALS ******************************************************************/
31 static KSPIN_LOCK DispatcherDatabaseLock;
32 static BOOLEAN WaitSet = FALSE;
33 static KIRQL oldlvl = PASSIVE_LEVEL;
34 static PKTHREAD Owner = NULL;
36 #define KeDispatcherObjectWakeOne(hdr) KeDispatcherObjectWakeOneOrAll(hdr, FALSE)
37 #define KeDispatcherObjectWakeAll(hdr) KeDispatcherObjectWakeOneOrAll(hdr, TRUE)
39 /* FUNCTIONS *****************************************************************/
41 VOID KeInitializeDispatcherHeader(DISPATCHER_HEADER* Header,
50 Header->SignalState = SignalState;
51 InitializeListHead(&(Header->WaitListHead));
55 VOID KeAcquireDispatcherDatabaseLock(BOOLEAN Wait)
57 * PURPOSE: Acquires the dispatcher database lock for the caller
60 DPRINT("KeAcquireDispatcherDatabaseLock(Wait %x)\n",Wait);
61 if (WaitSet && Owner == KeGetCurrentThread())
65 KeAcquireSpinLock(&DispatcherDatabaseLock, &oldlvl);
67 Owner = KeGetCurrentThread();
72 VOID KeReleaseDispatcherDatabaseLockAtDpcLevel(BOOLEAN Wait)
74 DPRINT("KeReleaseDispatcherDatabaseLockAtDpcLevel(Wait %x) WaitSet=%x\n", Wait, WaitSet);
78 KeReleaseSpinLockFromDpcLevel(&DispatcherDatabaseLock);
82 #endif /* LIBCAPTIVE */
84 VOID KeReleaseDispatcherDatabaseLock(BOOLEAN Wait)
86 DPRINT("KeReleaseDispatcherDatabaseLock(Wait %x) WaitSet=%x\n",Wait,WaitSet);
90 KeReleaseSpinLock(&DispatcherDatabaseLock, oldlvl);
97 KiSideEffectsBeforeWake(DISPATCHER_HEADER * hdr,
100 * FUNCTION: Perform side effects on object before a wait for a thread is
104 BOOLEAN Abandoned = FALSE;
108 case InternalSynchronizationEvent:
109 hdr->SignalState = 0;
112 case InternalQueueType:
113 case InternalSemaphoreType:
117 case InternalProcessType:
120 case InternalThreadType:
123 case InternalNotificationEvent:
126 case InternalSynchronizationTimer:
127 hdr->SignalState = FALSE;
130 case InternalNotificationTimer:
133 case InternalMutexType:
137 Mutex = CONTAINING_RECORD(hdr, KMUTEX, Header);
139 assert(hdr->SignalState <= 1);
140 if (hdr->SignalState == 0)
144 DPRINT("Thread == NULL!\n");
147 Abandoned = Mutex->Abandoned;
149 InsertTailList(&Thread->MutantListHead, &Mutex->MutantListEntry);
150 Mutex->OwnerThread = Thread;
151 Mutex->Abandoned = FALSE;
157 DbgPrint("(%s:%d) Dispatcher object %x has unknown type\n", __FILE__, __LINE__, hdr);
165 KiIsObjectSignalled(DISPATCHER_HEADER * hdr,
168 if (hdr->Type == InternalMutexType)
172 Mutex = CONTAINING_RECORD(hdr, KMUTEX, Header);
174 assert(hdr->SignalState <= 1);
176 if ((hdr->SignalState < 1 && Mutex->OwnerThread == Thread) || hdr->SignalState == 1)
186 if (hdr->SignalState <= 0)
196 VOID KeRemoveAllWaitsThread(PETHREAD Thread, NTSTATUS WaitStatus, BOOL Unblock)
198 PKWAIT_BLOCK WaitBlock, PrevWaitBlock;
199 BOOLEAN WasWaiting = FALSE;
201 KeAcquireDispatcherDatabaseLock(FALSE);
203 WaitBlock = (PKWAIT_BLOCK)Thread->Tcb.WaitBlockList;
204 if (WaitBlock != NULL)
208 while (WaitBlock != NULL)
210 if (WaitBlock->WaitListEntry.Flink != NULL && WaitBlock->WaitListEntry.Blink != NULL)
212 RemoveEntryList (&WaitBlock->WaitListEntry);
213 WaitBlock->WaitListEntry.Flink = WaitBlock->WaitListEntry.Blink = NULL;
215 PrevWaitBlock = WaitBlock;
216 WaitBlock = WaitBlock->NextWaitBlock;
217 PrevWaitBlock->NextWaitBlock = NULL;
219 Thread->Tcb.WaitBlockList = NULL;
221 if (WasWaiting && Unblock)
223 PsUnblockThread(Thread, &WaitStatus);
226 KeReleaseDispatcherDatabaseLock(FALSE);
230 KeDispatcherObjectWakeOneOrAll(DISPATCHER_HEADER * hdr,
234 PKWAIT_BLOCK WaiterHead;
235 PLIST_ENTRY EnumEntry;
239 BOOLEAN WakedAny = FALSE;
241 DPRINT("KeDispatcherObjectWakeOnOrAll(hdr %x)\n", hdr);
242 DPRINT ("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
243 hdr->WaitListHead.Flink, hdr->WaitListHead.Blink);
245 if (IsListEmpty(&hdr->WaitListHead))
250 //enum waiters for this dispatcher object
251 EnumEntry = hdr->WaitListHead.Flink;
252 while (EnumEntry != &hdr->WaitListHead && (WakeAll || !WakedAny))
254 WaiterHead = CONTAINING_RECORD(EnumEntry, KWAIT_BLOCK, WaitListEntry);
255 DPRINT("current_entry %x current %x\n", EnumEntry, WaiterHead);
256 EnumEntry = EnumEntry->Flink;
257 assert(WaiterHead->Thread != NULL);
258 assert(WaiterHead->Thread->WaitBlockList != NULL);
262 if (WaiterHead->WaitType == WaitAny)
264 DPRINT("WaitAny: Remove all wait blocks.\n");
265 for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock)
267 if (Waiter->WaitListEntry.Flink != NULL && Waiter->WaitListEntry.Blink != NULL)
269 RemoveEntryList(&Waiter->WaitListEntry);
270 Waiter->WaitListEntry.Flink = Waiter->WaitListEntry.Blink = NULL;
274 WaiterHead->Thread->WaitBlockList = NULL;
277 * If a WakeAll KiSideEffectsBeforeWake(hdr,.. will be called several times,
278 * but thats ok since WakeAll objects has no sideeffects.
280 Abandoned = KiSideEffectsBeforeWake(hdr, WaiterHead->Thread) ? TRUE : Abandoned;
284 DPRINT("WaitAll: All WaitAll objects must be signaled.\n");
288 //all WaitAll obj. for thread need to be signaled to satisfy a wake
289 for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock)
291 //no need to check hdr since it has to be signaled
292 if (Waiter->WaitType == WaitAll && Waiter->Object != hdr)
294 if (!KiIsObjectSignalled(Waiter->Object, Waiter->Thread))
304 for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock)
306 if (Waiter->WaitListEntry.Flink != NULL && Waiter->WaitListEntry.Blink != NULL)
308 RemoveEntryList(&Waiter->WaitListEntry);
309 Waiter->WaitListEntry.Flink = Waiter->WaitListEntry.Blink = NULL;
312 if (Waiter->WaitType == WaitAll)
314 Abandoned = KiSideEffectsBeforeWake(Waiter->Object, Waiter->Thread)
318 //no WaitAny objects can possibly be signaled since we are here
319 assert(!(Waiter->WaitType == WaitAny
320 && KiIsObjectSignalled(Waiter->Object, Waiter->Thread)));
323 WaiterHead->Thread->WaitBlockList = NULL;
327 if (WaiterHead->Thread->WaitBlockList == NULL)
329 Status = WaiterHead->WaitKey;
332 DPRINT("Abandoned mutex among objects");
333 Status += STATUS_ABANDONED_WAIT_0;
337 DPRINT("Waking %x status = %x\n", WaiterHead->Thread, Status);
338 PsUnblockThread(CONTAINING_RECORD(WaiterHead->Thread, ETHREAD, Tcb), &Status);
346 BOOLEAN KeDispatcherObjectWake(DISPATCHER_HEADER* hdr)
348 * FUNCTION: Wake threads waiting on a dispatcher object
349 * NOTE: The exact semantics of waking are dependant on the type of object
354 DPRINT("Entering KeDispatcherObjectWake(hdr %x)\n",hdr);
355 // DPRINT("hdr->WaitListHead %x hdr->WaitListHead.Flink %x\n",
356 // &hdr->WaitListHead,hdr->WaitListHead.Flink);
357 DPRINT("hdr->Type %x\n",hdr->Type);
360 case InternalNotificationEvent:
361 return(KeDispatcherObjectWakeAll(hdr));
363 case InternalNotificationTimer:
364 return(KeDispatcherObjectWakeAll(hdr));
366 case InternalSynchronizationEvent:
367 return(KeDispatcherObjectWakeOne(hdr));
369 case InternalSynchronizationTimer:
370 return(KeDispatcherObjectWakeOne(hdr));
372 case InternalQueueType:
373 case InternalSemaphoreType:
374 DPRINT("hdr->SignalState %d\n", hdr->SignalState);
375 if(hdr->SignalState>0)
379 DPRINT("Waking one semaphore waiter\n");
380 Ret = KeDispatcherObjectWakeOne(hdr);
381 } while(hdr->SignalState > 0 && Ret) ;
386 case InternalProcessType:
387 return(KeDispatcherObjectWakeAll(hdr));
389 case InternalThreadType:
390 return(KeDispatcherObjectWakeAll(hdr));
392 case InternalMutexType:
393 return(KeDispatcherObjectWakeOne(hdr));
395 DbgPrint("Dispatcher object %x has unknown type %d\n", hdr, hdr->Type);
405 KeWaitForSingleObject(PVOID Object,
406 KWAIT_REASON WaitReason,
407 KPROCESSOR_MODE WaitMode,
409 PLARGE_INTEGER Timeout)
411 * FUNCTION: Puts the current thread into a wait state until the
412 * given dispatcher object is set to signalled
414 * Object = Object to wait on
415 * WaitReason = Reason for the wait (debugging aid)
416 * WaitMode = Can be KernelMode or UserMode, if UserMode then
417 * user-mode APCs can be delivered and the thread's
418 * stack can be paged out
419 * Altertable = Specifies if the wait is a alertable
420 * Timeout = Optional timeout value
424 return KeWaitForMultipleObjects(1,
437 KiGetWaitableObjectFromObject(PVOID Object)
439 //special case when waiting on file objects
440 if ( ((PDISPATCHER_HEADER)Object)->Type == InternalFileType)
442 return &((PFILE_OBJECT)Object)->Event;
453 KeWaitForMultipleObjects(ULONG Count,
456 KWAIT_REASON WaitReason,
457 KPROCESSOR_MODE WaitMode,
459 PLARGE_INTEGER Timeout,
460 PKWAIT_BLOCK WaitBlockArray)
462 DISPATCHER_HEADER *hdr;
464 PKTHREAD CurrentThread;
471 DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
472 "PsGetCurrentThread() %x\n", Count, Object, PsGetCurrentThread());
474 CurrentThread = KeGetCurrentThread();
475 WaitIrql = KeGetCurrentIrql();
478 * Work out where we are going to put the wait blocks
480 if (WaitBlockArray == NULL)
482 if (Count > THREAD_WAIT_OBJECTS)
484 DPRINT("(%s:%d) Too many objects!\n", __FILE__, __LINE__);
485 return (STATUS_UNSUCCESSFUL);
487 WaitBlockArray = &CurrentThread->WaitBlock[0];
491 if (Count > EX_MAXIMUM_WAIT_OBJECTS)
493 DPRINT("(%s:%d) Too many objects!\n", __FILE__, __LINE__);
494 return (STATUS_UNSUCCESSFUL);
499 * Set up the timeout if required
501 if (Timeout != NULL && Timeout->QuadPart != 0)
503 KeInitializeTimer(&CurrentThread->Timer);
504 KeSetTimer(&CurrentThread->Timer, *Timeout, NULL);
509 KeAcquireDispatcherDatabaseLock(FALSE);
512 * If we are going to wait alertably and a user apc is pending
515 if (Alertable && KiTestAlert())
517 KeReleaseDispatcherDatabaseLock(FALSE);
518 return (STATUS_USER_APC);
522 * Check if the wait is (already) satisfied
526 for (i = 0; i < Count; i++)
528 hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
530 if (KiIsObjectSignalled(hdr, CurrentThread))
534 if (WaitType == WaitAny)
536 Abandoned = KiSideEffectsBeforeWake(hdr, CurrentThread) ? TRUE : Abandoned;
538 if (Timeout != NULL && Timeout->QuadPart != 0)
540 KeCancelTimer(&CurrentThread->Timer);
543 KeReleaseDispatcherDatabaseLock(FALSE);
545 DPRINT("One object is (already) signaled!\n");
546 if (Abandoned == TRUE)
548 return (STATUS_ABANDONED_WAIT_0 + i);
551 return (STATUS_WAIT_0 + i);
557 if ((WaitType == WaitAll) && (CountSignaled == Count))
559 for (i = 0; i < Count; i++)
561 hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
562 Abandoned = KiSideEffectsBeforeWake(hdr, CurrentThread) ? TRUE : Abandoned;
565 if (Timeout != NULL && Timeout->QuadPart != 0)
567 KeCancelTimer(&CurrentThread->Timer);
570 KeReleaseDispatcherDatabaseLock(FALSE);
571 DPRINT("All objects are (already) signaled!\n");
573 if (Abandoned == TRUE)
575 return (STATUS_ABANDONED_WAIT_0);
578 return (STATUS_WAIT_0);
581 //zero timeout is used for testing if the object(s) can be immediately acquired
582 if (Timeout != NULL && Timeout->QuadPart == 0)
584 KeReleaseDispatcherDatabaseLock(FALSE);
585 return STATUS_TIMEOUT;
589 * Check if we have already timed out
591 if (Timeout != NULL && KiIsObjectSignalled(&CurrentThread->Timer.Header, CurrentThread))
593 KiSideEffectsBeforeWake(&CurrentThread->Timer.Header, CurrentThread);
594 KeCancelTimer(&CurrentThread->Timer);
595 KeReleaseDispatcherDatabaseLock(FALSE);
596 return (STATUS_TIMEOUT);
599 /* Append wait block to the KTHREAD wait block list */
600 CurrentThread->WaitBlockList = blk = WaitBlockArray;
605 CurrentThread->WaitStatus = STATUS_UNSUCCESSFUL;
607 for (i = 0; i < Count; i++)
609 hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
611 blk->Object = KiGetWaitableObjectFromObject(Object[i]);
612 blk->Thread = CurrentThread;
613 blk->WaitKey = STATUS_WAIT_0 + i;
614 blk->WaitType = WaitType;
616 if (i == (Count - 1))
620 blk->NextWaitBlock = &CurrentThread->WaitBlock[3];
624 blk->NextWaitBlock = NULL;
629 blk->NextWaitBlock = blk + 1;
633 * add wait block to disp. obj. wait list
634 * Use FIFO for all waits except for queues which use LIFO
636 if (WaitReason == WrQueue)
638 InsertHeadList(&hdr->WaitListHead, &blk->WaitListEntry);
642 InsertTailList(&hdr->WaitListHead, &blk->WaitListEntry);
645 blk = blk->NextWaitBlock;
650 CurrentThread->WaitBlock[3].Object = (PVOID) & CurrentThread->Timer;
651 CurrentThread->WaitBlock[3].Thread = CurrentThread;
652 CurrentThread->WaitBlock[3].WaitKey = STATUS_TIMEOUT;
653 CurrentThread->WaitBlock[3].WaitType = WaitAny;
654 CurrentThread->WaitBlock[3].NextWaitBlock = NULL;
656 InsertTailList(&CurrentThread->Timer.Header.WaitListHead,
657 &CurrentThread->WaitBlock[3].WaitListEntry);
661 if (CurrentThread->Queue)
663 CurrentThread->Queue->CurrentCount--;
664 if (WaitReason != WrQueue && CurrentThread->Queue->CurrentCount < CurrentThread->Queue->MaximumCount &&
665 !IsListEmpty(&CurrentThread->Queue->EntryListHead))
667 KeDispatcherObjectWake(&CurrentThread->Queue->Header);
671 PsBlockThread(&Status, Alertable, WaitMode, TRUE, WaitIrql, WaitReason);
674 if (CurrentThread->Queue)
676 CurrentThread->Queue->CurrentCount++;
681 while (Status == STATUS_KERNEL_APC);
685 KeCancelTimer(&CurrentThread->Timer);
688 DPRINT("Returning from KeWaitForMultipleObjects()\n");
692 #endif /* LIBCAPTIVE */
694 VOID KeInitializeDispatcher(VOID)
696 KeInitializeSpinLock(&DispatcherDatabaseLock);
702 NtWaitForMultipleObjects(IN ULONG Count,
704 IN WAIT_TYPE WaitType,
705 IN BOOLEAN Alertable,
706 IN PLARGE_INTEGER Time)
708 KWAIT_BLOCK WaitBlockArray[EX_MAXIMUM_WAIT_OBJECTS];
709 PVOID ObjectPtrArray[EX_MAXIMUM_WAIT_OBJECTS];
712 KPROCESSOR_MODE WaitMode;
714 DPRINT("NtWaitForMultipleObjects(Count %lu Object[] %x, Alertable %d, "
715 "Time %x)\n", Count,Object,Alertable,Time);
717 if (Count > EX_MAXIMUM_WAIT_OBJECTS)
718 return STATUS_UNSUCCESSFUL;
720 WaitMode = ExGetPreviousMode();
722 /* reference all objects */
723 for (i = 0; i < Count; i++)
725 Status = ObReferenceObjectByHandle(Object[i],
731 if (Status != STATUS_SUCCESS)
733 /* dereference all referenced objects */
734 for (j = 0; j < i; j++)
736 ObDereferenceObject(ObjectPtrArray[j]);
743 Status = KeWaitForMultipleObjects(Count,
752 /* dereference all objects */
753 for (i = 0; i < Count; i++)
755 ObDereferenceObject(ObjectPtrArray[i]);
766 NtWaitForSingleObject(IN HANDLE Object,
767 IN BOOLEAN Alertable,
768 IN PLARGE_INTEGER Time)
772 KPROCESSOR_MODE WaitMode;
774 DPRINT("NtWaitForSingleObject(Object %x, Alertable %d, Time %x)\n",
775 Object,Alertable,Time);
777 WaitMode = ExGetPreviousMode();
779 Status = ObReferenceObjectByHandle(Object,
785 if (!NT_SUCCESS(Status))
790 Status = KeWaitForSingleObject(ObjectPtr,
796 ObDereferenceObject(ObjectPtr);
803 NtSignalAndWaitForSingleObject(IN HANDLE SignalObject,
804 IN HANDLE WaitObject,
805 IN BOOLEAN Alertable,
806 IN PLARGE_INTEGER Time)
808 KPROCESSOR_MODE WaitMode;
809 DISPATCHER_HEADER* hdr;
814 WaitMode = ExGetPreviousMode();
815 Status = ObReferenceObjectByHandle(SignalObject,
821 if (!NT_SUCCESS(Status))
826 Status = ObReferenceObjectByHandle(WaitObject,
832 if (!NT_SUCCESS(Status))
834 ObDereferenceObject(SignalObj);
838 hdr = (DISPATCHER_HEADER *)SignalObj;
841 case InternalNotificationEvent:
842 case InternalSynchronizationEvent:
843 KeSetEvent(SignalObj,
848 case InternalMutexType:
849 KeReleaseMutex(SignalObj,
853 case InternalSemaphoreType:
854 KeReleaseSemaphore(SignalObj,
861 ObDereferenceObject(SignalObj);
862 ObDereferenceObject(WaitObj);
863 return STATUS_OBJECT_TYPE_MISMATCH;
866 Status = KeWaitForSingleObject(WaitObj,
872 ObDereferenceObject(SignalObj);
873 ObDereferenceObject(WaitObj);
878 #endif /* LIBCAPTIVE */