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));
54 VOID KeAcquireDispatcherDatabaseLock(BOOLEAN Wait)
56 * PURPOSE: Acquires the dispatcher database lock for the caller
59 DPRINT("KeAcquireDispatcherDatabaseLock(Wait %x)\n",Wait);
60 if (WaitSet && Owner == KeGetCurrentThread())
64 KeAcquireSpinLock(&DispatcherDatabaseLock, &oldlvl);
66 Owner = KeGetCurrentThread();
69 VOID KeReleaseDispatcherDatabaseLockAtDpcLevel(BOOLEAN Wait)
71 DPRINT("KeReleaseDispatcherDatabaseLockAtDpcLevel(Wait %x) WaitSet=%x\n", Wait, WaitSet);
75 KeReleaseSpinLockFromDpcLevel(&DispatcherDatabaseLock);
79 VOID KeReleaseDispatcherDatabaseLock(BOOLEAN Wait)
81 DPRINT("KeReleaseDispatcherDatabaseLock(Wait %x) WaitSet=%x\n",Wait,WaitSet);
85 KeReleaseSpinLock(&DispatcherDatabaseLock, oldlvl);
90 KiSideEffectsBeforeWake(DISPATCHER_HEADER * hdr,
93 * FUNCTION: Perform side effects on object before a wait for a thread is
97 BOOLEAN Abandoned = FALSE;
101 case InternalSynchronizationEvent:
102 hdr->SignalState = 0;
105 case InternalQueueType:
106 case InternalSemaphoreType:
110 case InternalProcessType:
113 case InternalThreadType:
116 case InternalNotificationEvent:
119 case InternalSynchronizationTimer:
120 hdr->SignalState = FALSE;
123 case InternalNotificationTimer:
126 case InternalMutexType:
130 Mutex = CONTAINING_RECORD(hdr, KMUTEX, Header);
132 assert(hdr->SignalState <= 1);
133 if (hdr->SignalState == 0)
137 DPRINT("Thread == NULL!\n");
140 Abandoned = Mutex->Abandoned;
142 InsertTailList(&Thread->MutantListHead, &Mutex->MutantListEntry);
143 Mutex->OwnerThread = Thread;
144 Mutex->Abandoned = FALSE;
150 DbgPrint("(%s:%d) Dispatcher object %x has unknown type\n", __FILE__, __LINE__, hdr);
158 KiIsObjectSignalled(DISPATCHER_HEADER * hdr,
161 if (hdr->Type == InternalMutexType)
165 Mutex = CONTAINING_RECORD(hdr, KMUTEX, Header);
167 assert(hdr->SignalState <= 1);
169 if ((hdr->SignalState < 1 && Mutex->OwnerThread == Thread) || hdr->SignalState == 1)
179 if (hdr->SignalState <= 0)
189 VOID KeRemoveAllWaitsThread(PETHREAD Thread, NTSTATUS WaitStatus, BOOL Unblock)
191 PKWAIT_BLOCK WaitBlock, PrevWaitBlock;
192 BOOLEAN WasWaiting = FALSE;
194 KeAcquireDispatcherDatabaseLock(FALSE);
196 WaitBlock = (PKWAIT_BLOCK)Thread->Tcb.WaitBlockList;
197 if (WaitBlock != NULL)
201 while (WaitBlock != NULL)
203 if (WaitBlock->WaitListEntry.Flink != NULL && WaitBlock->WaitListEntry.Blink != NULL)
205 RemoveEntryList (&WaitBlock->WaitListEntry);
206 WaitBlock->WaitListEntry.Flink = WaitBlock->WaitListEntry.Blink = NULL;
208 PrevWaitBlock = WaitBlock;
209 WaitBlock = WaitBlock->NextWaitBlock;
210 PrevWaitBlock->NextWaitBlock = NULL;
212 Thread->Tcb.WaitBlockList = NULL;
214 if (WasWaiting && Unblock)
216 PsUnblockThread(Thread, &WaitStatus);
219 KeReleaseDispatcherDatabaseLock(FALSE);
223 KeDispatcherObjectWakeOneOrAll(DISPATCHER_HEADER * hdr,
227 PKWAIT_BLOCK WaiterHead;
228 PLIST_ENTRY EnumEntry;
232 BOOLEAN WakedAny = FALSE;
234 DPRINT("KeDispatcherObjectWakeOnOrAll(hdr %x)\n", hdr);
235 DPRINT ("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
236 hdr->WaitListHead.Flink, hdr->WaitListHead.Blink);
238 if (IsListEmpty(&hdr->WaitListHead))
243 //enum waiters for this dispatcher object
244 EnumEntry = hdr->WaitListHead.Flink;
245 while (EnumEntry != &hdr->WaitListHead && (WakeAll || !WakedAny))
247 WaiterHead = CONTAINING_RECORD(EnumEntry, KWAIT_BLOCK, WaitListEntry);
248 DPRINT("current_entry %x current %x\n", EnumEntry, WaiterHead);
249 EnumEntry = EnumEntry->Flink;
250 assert(WaiterHead->Thread != NULL);
251 assert(WaiterHead->Thread->WaitBlockList != NULL);
255 if (WaiterHead->WaitType == WaitAny)
257 DPRINT("WaitAny: Remove all wait blocks.\n");
258 for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock)
260 if (Waiter->WaitListEntry.Flink != NULL && Waiter->WaitListEntry.Blink != NULL)
262 RemoveEntryList(&Waiter->WaitListEntry);
263 Waiter->WaitListEntry.Flink = Waiter->WaitListEntry.Blink = NULL;
267 WaiterHead->Thread->WaitBlockList = NULL;
270 * If a WakeAll KiSideEffectsBeforeWake(hdr,.. will be called several times,
271 * but thats ok since WakeAll objects has no sideeffects.
273 Abandoned = KiSideEffectsBeforeWake(hdr, WaiterHead->Thread) ? TRUE : Abandoned;
277 DPRINT("WaitAll: All WaitAll objects must be signaled.\n");
281 //all WaitAll obj. for thread need to be signaled to satisfy a wake
282 for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock)
284 //no need to check hdr since it has to be signaled
285 if (Waiter->WaitType == WaitAll && Waiter->Object != hdr)
287 if (!KiIsObjectSignalled(Waiter->Object, Waiter->Thread))
297 for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock)
299 if (Waiter->WaitListEntry.Flink != NULL && Waiter->WaitListEntry.Blink != NULL)
301 RemoveEntryList(&Waiter->WaitListEntry);
302 Waiter->WaitListEntry.Flink = Waiter->WaitListEntry.Blink = NULL;
305 if (Waiter->WaitType == WaitAll)
307 Abandoned = KiSideEffectsBeforeWake(Waiter->Object, Waiter->Thread)
311 //no WaitAny objects can possibly be signaled since we are here
312 assert(!(Waiter->WaitType == WaitAny
313 && KiIsObjectSignalled(Waiter->Object, Waiter->Thread)));
316 WaiterHead->Thread->WaitBlockList = NULL;
320 if (WaiterHead->Thread->WaitBlockList == NULL)
322 Status = WaiterHead->WaitKey;
325 DPRINT("Abandoned mutex among objects");
326 Status += STATUS_ABANDONED_WAIT_0;
330 DPRINT("Waking %x status = %x\n", WaiterHead->Thread, Status);
331 PsUnblockThread(CONTAINING_RECORD(WaiterHead->Thread, ETHREAD, Tcb), &Status);
339 BOOLEAN KeDispatcherObjectWake(DISPATCHER_HEADER* hdr)
341 * FUNCTION: Wake threads waiting on a dispatcher object
342 * NOTE: The exact semantics of waking are dependant on the type of object
347 DPRINT("Entering KeDispatcherObjectWake(hdr %x)\n",hdr);
348 // DPRINT("hdr->WaitListHead %x hdr->WaitListHead.Flink %x\n",
349 // &hdr->WaitListHead,hdr->WaitListHead.Flink);
350 DPRINT("hdr->Type %x\n",hdr->Type);
353 case InternalNotificationEvent:
354 return(KeDispatcherObjectWakeAll(hdr));
356 case InternalNotificationTimer:
357 return(KeDispatcherObjectWakeAll(hdr));
359 case InternalSynchronizationEvent:
360 return(KeDispatcherObjectWakeOne(hdr));
362 case InternalSynchronizationTimer:
363 return(KeDispatcherObjectWakeOne(hdr));
365 case InternalQueueType:
366 case InternalSemaphoreType:
367 DPRINT("hdr->SignalState %d\n", hdr->SignalState);
368 if(hdr->SignalState>0)
372 DPRINT("Waking one semaphore waiter\n");
373 Ret = KeDispatcherObjectWakeOne(hdr);
374 } while(hdr->SignalState > 0 && Ret) ;
379 case InternalProcessType:
380 return(KeDispatcherObjectWakeAll(hdr));
382 case InternalThreadType:
383 return(KeDispatcherObjectWakeAll(hdr));
385 case InternalMutexType:
386 return(KeDispatcherObjectWakeOne(hdr));
388 DbgPrint("Dispatcher object %x has unknown type %d\n", hdr, hdr->Type);
398 KeWaitForSingleObject(PVOID Object,
399 KWAIT_REASON WaitReason,
400 KPROCESSOR_MODE WaitMode,
402 PLARGE_INTEGER Timeout)
404 * FUNCTION: Puts the current thread into a wait state until the
405 * given dispatcher object is set to signalled
407 * Object = Object to wait on
408 * WaitReason = Reason for the wait (debugging aid)
409 * WaitMode = Can be KernelMode or UserMode, if UserMode then
410 * user-mode APCs can be delivered and the thread's
411 * stack can be paged out
412 * Altertable = Specifies if the wait is a alertable
413 * Timeout = Optional timeout value
417 return KeWaitForMultipleObjects(1,
430 KiGetWaitableObjectFromObject(PVOID Object)
432 //special case when waiting on file objects
433 if ( ((PDISPATCHER_HEADER)Object)->Type == InternalFileType)
435 return &((PFILE_OBJECT)Object)->Event;
446 KeWaitForMultipleObjects(ULONG Count,
449 KWAIT_REASON WaitReason,
450 KPROCESSOR_MODE WaitMode,
452 PLARGE_INTEGER Timeout,
453 PKWAIT_BLOCK WaitBlockArray)
455 DISPATCHER_HEADER *hdr;
457 PKTHREAD CurrentThread;
464 DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
465 "PsGetCurrentThread() %x\n", Count, Object, PsGetCurrentThread());
467 CurrentThread = KeGetCurrentThread();
468 WaitIrql = KeGetCurrentIrql();
471 * Work out where we are going to put the wait blocks
473 if (WaitBlockArray == NULL)
475 if (Count > THREAD_WAIT_OBJECTS)
477 DPRINT("(%s:%d) Too many objects!\n", __FILE__, __LINE__);
478 return (STATUS_UNSUCCESSFUL);
480 WaitBlockArray = &CurrentThread->WaitBlock[0];
484 if (Count > EX_MAXIMUM_WAIT_OBJECTS)
486 DPRINT("(%s:%d) Too many objects!\n", __FILE__, __LINE__);
487 return (STATUS_UNSUCCESSFUL);
492 * Set up the timeout if required
494 if (Timeout != NULL && Timeout->QuadPart != 0)
496 KeInitializeTimer(&CurrentThread->Timer);
497 KeSetTimer(&CurrentThread->Timer, *Timeout, NULL);
502 KeAcquireDispatcherDatabaseLock(FALSE);
505 * If we are going to wait alertably and a user apc is pending
508 if (Alertable && KiTestAlert())
510 KeReleaseDispatcherDatabaseLock(FALSE);
511 return (STATUS_USER_APC);
515 * Check if the wait is (already) satisfied
519 for (i = 0; i < Count; i++)
521 hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
523 if (KiIsObjectSignalled(hdr, CurrentThread))
527 if (WaitType == WaitAny)
529 Abandoned = KiSideEffectsBeforeWake(hdr, CurrentThread) ? TRUE : Abandoned;
531 if (Timeout != NULL && Timeout->QuadPart != 0)
533 KeCancelTimer(&CurrentThread->Timer);
536 KeReleaseDispatcherDatabaseLock(FALSE);
538 DPRINT("One object is (already) signaled!\n");
539 if (Abandoned == TRUE)
541 return (STATUS_ABANDONED_WAIT_0 + i);
544 return (STATUS_WAIT_0 + i);
550 if ((WaitType == WaitAll) && (CountSignaled == Count))
552 for (i = 0; i < Count; i++)
554 hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
555 Abandoned = KiSideEffectsBeforeWake(hdr, CurrentThread) ? TRUE : Abandoned;
558 if (Timeout != NULL && Timeout->QuadPart != 0)
560 KeCancelTimer(&CurrentThread->Timer);
563 KeReleaseDispatcherDatabaseLock(FALSE);
564 DPRINT("All objects are (already) signaled!\n");
566 if (Abandoned == TRUE)
568 return (STATUS_ABANDONED_WAIT_0);
571 return (STATUS_WAIT_0);
574 //zero timeout is used for testing if the object(s) can be immediately acquired
575 if (Timeout != NULL && Timeout->QuadPart == 0)
577 KeReleaseDispatcherDatabaseLock(FALSE);
578 return STATUS_TIMEOUT;
582 * Check if we have already timed out
584 if (Timeout != NULL && KiIsObjectSignalled(&CurrentThread->Timer.Header, CurrentThread))
586 KiSideEffectsBeforeWake(&CurrentThread->Timer.Header, CurrentThread);
587 KeCancelTimer(&CurrentThread->Timer);
588 KeReleaseDispatcherDatabaseLock(FALSE);
589 return (STATUS_TIMEOUT);
592 /* Append wait block to the KTHREAD wait block list */
593 CurrentThread->WaitBlockList = blk = WaitBlockArray;
598 CurrentThread->WaitStatus = STATUS_UNSUCCESSFUL;
600 for (i = 0; i < Count; i++)
602 hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
604 blk->Object = KiGetWaitableObjectFromObject(Object[i]);
605 blk->Thread = CurrentThread;
606 blk->WaitKey = STATUS_WAIT_0 + i;
607 blk->WaitType = WaitType;
609 if (i == (Count - 1))
613 blk->NextWaitBlock = &CurrentThread->WaitBlock[3];
617 blk->NextWaitBlock = NULL;
622 blk->NextWaitBlock = blk + 1;
626 * add wait block to disp. obj. wait list
627 * Use FIFO for all waits except for queues which use LIFO
629 if (WaitReason == WrQueue)
631 InsertHeadList(&hdr->WaitListHead, &blk->WaitListEntry);
635 InsertTailList(&hdr->WaitListHead, &blk->WaitListEntry);
638 blk = blk->NextWaitBlock;
643 CurrentThread->WaitBlock[3].Object = (PVOID) & CurrentThread->Timer;
644 CurrentThread->WaitBlock[3].Thread = CurrentThread;
645 CurrentThread->WaitBlock[3].WaitKey = STATUS_TIMEOUT;
646 CurrentThread->WaitBlock[3].WaitType = WaitAny;
647 CurrentThread->WaitBlock[3].NextWaitBlock = NULL;
649 InsertTailList(&CurrentThread->Timer.Header.WaitListHead,
650 &CurrentThread->WaitBlock[3].WaitListEntry);
654 if (CurrentThread->Queue)
656 CurrentThread->Queue->CurrentCount--;
657 if (WaitReason != WrQueue && CurrentThread->Queue->CurrentCount < CurrentThread->Queue->MaximumCount &&
658 !IsListEmpty(&CurrentThread->Queue->EntryListHead))
660 KeDispatcherObjectWake(&CurrentThread->Queue->Header);
664 PsBlockThread(&Status, Alertable, WaitMode, TRUE, WaitIrql, WaitReason);
667 if (CurrentThread->Queue)
669 CurrentThread->Queue->CurrentCount++;
674 while (Status == STATUS_KERNEL_APC);
678 KeCancelTimer(&CurrentThread->Timer);
681 DPRINT("Returning from KeWaitForMultipleObjects()\n");
685 VOID KeInitializeDispatcher(VOID)
687 KeInitializeSpinLock(&DispatcherDatabaseLock);
691 NtWaitForMultipleObjects(IN ULONG Count,
693 IN WAIT_TYPE WaitType,
694 IN BOOLEAN Alertable,
695 IN PLARGE_INTEGER Time)
697 KWAIT_BLOCK WaitBlockArray[EX_MAXIMUM_WAIT_OBJECTS];
698 PVOID ObjectPtrArray[EX_MAXIMUM_WAIT_OBJECTS];
701 KPROCESSOR_MODE WaitMode;
703 DPRINT("NtWaitForMultipleObjects(Count %lu Object[] %x, Alertable %d, "
704 "Time %x)\n", Count,Object,Alertable,Time);
706 if (Count > EX_MAXIMUM_WAIT_OBJECTS)
707 return STATUS_UNSUCCESSFUL;
709 WaitMode = ExGetPreviousMode();
711 /* reference all objects */
712 for (i = 0; i < Count; i++)
714 Status = ObReferenceObjectByHandle(Object[i],
720 if (Status != STATUS_SUCCESS)
722 /* dereference all referenced objects */
723 for (j = 0; j < i; j++)
725 ObDereferenceObject(ObjectPtrArray[j]);
732 Status = KeWaitForMultipleObjects(Count,
741 /* dereference all objects */
742 for (i = 0; i < Count; i++)
744 ObDereferenceObject(ObjectPtrArray[i]);
755 NtWaitForSingleObject(IN HANDLE Object,
756 IN BOOLEAN Alertable,
757 IN PLARGE_INTEGER Time)
761 KPROCESSOR_MODE WaitMode;
763 DPRINT("NtWaitForSingleObject(Object %x, Alertable %d, Time %x)\n",
764 Object,Alertable,Time);
766 WaitMode = ExGetPreviousMode();
768 Status = ObReferenceObjectByHandle(Object,
774 if (!NT_SUCCESS(Status))
779 Status = KeWaitForSingleObject(ObjectPtr,
785 ObDereferenceObject(ObjectPtr);
792 NtSignalAndWaitForSingleObject(IN HANDLE SignalObject,
793 IN HANDLE WaitObject,
794 IN BOOLEAN Alertable,
795 IN PLARGE_INTEGER Time)
797 KPROCESSOR_MODE WaitMode;
798 DISPATCHER_HEADER* hdr;
803 WaitMode = ExGetPreviousMode();
804 Status = ObReferenceObjectByHandle(SignalObject,
810 if (!NT_SUCCESS(Status))
815 Status = ObReferenceObjectByHandle(WaitObject,
821 if (!NT_SUCCESS(Status))
823 ObDereferenceObject(SignalObj);
827 hdr = (DISPATCHER_HEADER *)SignalObj;
830 case InternalNotificationEvent:
831 case InternalSynchronizationEvent:
832 KeSetEvent(SignalObj,
837 case InternalMutexType:
838 KeReleaseMutex(SignalObj,
842 case InternalSemaphoreType:
843 KeReleaseSemaphore(SignalObj,
850 ObDereferenceObject(SignalObj);
851 ObDereferenceObject(WaitObj);
852 return STATUS_OBJECT_TYPE_MISMATCH;
855 Status = KeWaitForSingleObject(WaitObj,
861 ObDereferenceObject(SignalObj);
862 ObDereferenceObject(WaitObj);