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)\n", Wait);
72 assert(Wait == WaitSet);
76 KeReleaseSpinLockFromDpcLevel(&DispatcherDatabaseLock);
80 VOID KeReleaseDispatcherDatabaseLock(BOOLEAN Wait)
82 DPRINT("KeReleaseDispatcherDatabaseLock(Wait %x)\n",Wait);
83 assert(Wait==WaitSet);
87 KeReleaseSpinLock(&DispatcherDatabaseLock, oldlvl);
92 KiSideEffectsBeforeWake(DISPATCHER_HEADER * hdr,
95 * FUNCTION: Perform side effects on object before a wait for a thread is
99 BOOLEAN Abandoned = FALSE;
103 case InternalSynchronizationEvent:
104 hdr->SignalState = 0;
107 case InternalQueueType:
108 case InternalSemaphoreType:
112 case InternalProcessType:
115 case InternalThreadType:
118 case InternalNotificationEvent:
121 case InternalSynchronizationTimer:
122 hdr->SignalState = FALSE;
125 case InternalNotificationTimer:
128 case InternalMutexType:
132 Mutex = CONTAINING_RECORD(hdr, KMUTEX, Header);
134 assert(hdr->SignalState <= 1);
135 if (hdr->SignalState == 0)
139 DPRINT("Thread == NULL!\n");
142 Abandoned = Mutex->Abandoned;
144 InsertTailList(&Thread->MutantListHead, &Mutex->MutantListEntry);
145 Mutex->OwnerThread = Thread;
146 Mutex->Abandoned = FALSE;
152 DbgPrint("(%s:%d) Dispatcher object %x has unknown type\n", __FILE__, __LINE__, hdr);
160 KiIsObjectSignalled(DISPATCHER_HEADER * hdr,
163 if (hdr->Type == InternalMutexType)
167 Mutex = CONTAINING_RECORD(hdr, KMUTEX, Header);
169 assert(hdr->SignalState <= 1);
171 if ((hdr->SignalState < 1 && Mutex->OwnerThread == Thread) || hdr->SignalState == 1)
181 if (hdr->SignalState <= 0)
191 VOID KeRemoveAllWaitsThread(PETHREAD Thread, NTSTATUS WaitStatus)
193 PKWAIT_BLOCK WaitBlock;
194 BOOLEAN WasWaiting = FALSE;
196 KeAcquireDispatcherDatabaseLock(FALSE);
198 WaitBlock = (PKWAIT_BLOCK)Thread->Tcb.WaitBlockList;
199 if (WaitBlock != NULL)
203 while (WaitBlock != NULL)
205 RemoveEntryList(&WaitBlock->WaitListEntry);
206 WaitBlock = WaitBlock->NextWaitBlock;
208 Thread->Tcb.WaitBlockList = NULL;
212 PsUnblockThread(Thread, &WaitStatus);
215 KeReleaseDispatcherDatabaseLock(FALSE);
219 KeDispatcherObjectWakeOneOrAll(DISPATCHER_HEADER * hdr,
223 PKWAIT_BLOCK WaiterHead;
224 PLIST_ENTRY EnumEntry;
228 BOOLEAN WakedAny = FALSE;
230 DPRINT("KeDispatcherObjectWakeOnOrAll(hdr %x)\n", hdr);
231 DPRINT ("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
232 hdr->WaitListHead.Flink, hdr->WaitListHead.Blink);
234 if (IsListEmpty(&hdr->WaitListHead))
239 //enum waiters for this dispatcher object
240 EnumEntry = hdr->WaitListHead.Flink;
241 while (EnumEntry != &hdr->WaitListHead && (WakeAll || !WakedAny))
243 WaiterHead = CONTAINING_RECORD(EnumEntry, KWAIT_BLOCK, WaitListEntry);
244 DPRINT("current_entry %x current %x\n", EnumEntry, WaiterHead);
245 EnumEntry = EnumEntry->Flink;
246 assert(WaiterHead->Thread->WaitBlockList != NULL);
250 if (WaiterHead->WaitType == WaitAny)
252 DPRINT("WaitAny: Remove all wait blocks.\n");
253 for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock)
255 RemoveEntryList(&Waiter->WaitListEntry);
258 WaiterHead->Thread->WaitBlockList = NULL;
261 * If a WakeAll KiSideEffectsBeforeWake(hdr,.. will be called several times,
262 * but thats ok since WakeAll objects has no sideeffects.
264 Abandoned = KiSideEffectsBeforeWake(hdr, WaiterHead->Thread) ? TRUE : Abandoned;
268 DPRINT("WaitAll: All WaitAll objects must be signaled.\n");
272 //all WaitAll obj. for thread need to be signaled to satisfy a wake
273 for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock)
275 //no need to check hdr since it has to be signaled
276 if (Waiter->WaitType == WaitAll && Waiter->Object != hdr)
278 if (!KiIsObjectSignalled(Waiter->Object, Waiter->Thread))
288 for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock)
290 RemoveEntryList(&Waiter->WaitListEntry);
292 if (Waiter->WaitType == WaitAll)
294 Abandoned = KiSideEffectsBeforeWake(Waiter->Object, Waiter->Thread)
298 //no WaitAny objects can possibly be signaled since we are here
299 assert(!(Waiter->WaitType == WaitAny
300 && KiIsObjectSignalled(Waiter->Object, Waiter->Thread)));
303 WaiterHead->Thread->WaitBlockList = NULL;
307 if (WaiterHead->Thread->WaitBlockList == NULL)
309 Status = WaiterHead->WaitKey;
312 DPRINT("Abandoned mutex among objects");
313 Status += STATUS_ABANDONED_WAIT_0;
317 DPRINT("Waking %x status = %x\n", WaiterHead->Thread, Status);
318 PsUnblockThread(CONTAINING_RECORD(WaiterHead->Thread, ETHREAD, Tcb), &Status);
326 BOOLEAN KeDispatcherObjectWake(DISPATCHER_HEADER* hdr)
328 * FUNCTION: Wake threads waiting on a dispatcher object
329 * NOTE: The exact semantics of waking are dependant on the type of object
334 DPRINT("Entering KeDispatcherObjectWake(hdr %x)\n",hdr);
335 // DPRINT("hdr->WaitListHead %x hdr->WaitListHead.Flink %x\n",
336 // &hdr->WaitListHead,hdr->WaitListHead.Flink);
337 DPRINT("hdr->Type %x\n",hdr->Type);
340 case InternalNotificationEvent:
341 return(KeDispatcherObjectWakeAll(hdr));
343 case InternalNotificationTimer:
344 return(KeDispatcherObjectWakeAll(hdr));
346 case InternalSynchronizationEvent:
347 return(KeDispatcherObjectWakeOne(hdr));
349 case InternalSynchronizationTimer:
350 return(KeDispatcherObjectWakeOne(hdr));
352 case InternalQueueType:
353 case InternalSemaphoreType:
354 DPRINT("hdr->SignalState %d\n", hdr->SignalState);
355 if(hdr->SignalState>0)
359 DPRINT("Waking one semaphore waiter\n");
360 Ret = KeDispatcherObjectWakeOne(hdr);
361 } while(hdr->SignalState > 0 && Ret) ;
366 case InternalProcessType:
367 return(KeDispatcherObjectWakeAll(hdr));
369 case InternalThreadType:
370 return(KeDispatcherObjectWakeAll(hdr));
372 case InternalMutexType:
373 return(KeDispatcherObjectWakeOne(hdr));
375 DbgPrint("Dispatcher object %x has unknown type %d\n", hdr, hdr->Type);
382 KeWaitForSingleObject(PVOID Object,
383 KWAIT_REASON WaitReason,
384 KPROCESSOR_MODE WaitMode,
386 PLARGE_INTEGER Timeout)
388 * FUNCTION: Puts the current thread into a wait state until the
389 * given dispatcher object is set to signalled
391 * Object = Object to wait on
392 * WaitReason = Reason for the wait (debugging aid)
393 * WaitMode = Can be KernelMode or UserMode, if UserMode then
394 * user-mode APCs can be delivered and the thread's
395 * stack can be paged out
396 * Altertable = Specifies if the wait is a alertable
397 * Timeout = Optional timeout value
401 return KeWaitForMultipleObjects(1,
414 KiGetWaitableObjectFromObject(PVOID Object)
416 //special case when waiting on file objects
417 if ( ((PDISPATCHER_HEADER)Object)->Type == InternalFileType)
419 return &((PFILE_OBJECT)Object)->Event;
427 KeWaitForMultipleObjects(ULONG Count,
430 KWAIT_REASON WaitReason,
431 KPROCESSOR_MODE WaitMode,
433 PLARGE_INTEGER Timeout,
434 PKWAIT_BLOCK WaitBlockArray)
436 DISPATCHER_HEADER *hdr;
438 PKTHREAD CurrentThread;
445 DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
446 "PsGetCurrentThread() %x\n", Count, Object, PsGetCurrentThread());
448 CurrentThread = KeGetCurrentThread();
449 WaitIrql = KeGetCurrentIrql();
452 * Work out where we are going to put the wait blocks
454 if (WaitBlockArray == NULL)
456 if (Count > THREAD_WAIT_OBJECTS)
458 DPRINT("(%s:%d) Too many objects!\n", __FILE__, __LINE__);
459 return (STATUS_UNSUCCESSFUL);
461 WaitBlockArray = &CurrentThread->WaitBlock[0];
465 if (Count > EX_MAXIMUM_WAIT_OBJECTS)
467 DPRINT("(%s:%d) Too many objects!\n", __FILE__, __LINE__);
468 return (STATUS_UNSUCCESSFUL);
473 * Set up the timeout if required
475 if (Timeout != NULL && Timeout->QuadPart != 0)
477 KeInitializeTimer(&CurrentThread->Timer);
478 KeSetTimer(&CurrentThread->Timer, *Timeout, NULL);
483 KeAcquireDispatcherDatabaseLock(FALSE);
486 * If we are going to wait alertably and a user apc is pending
489 if (Alertable && KiTestAlert())
491 KeReleaseDispatcherDatabaseLock(FALSE);
492 return (STATUS_USER_APC);
496 * Check if the wait is (already) satisfied
500 for (i = 0; i < Count; i++)
502 hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
504 if (KiIsObjectSignalled(hdr, CurrentThread))
508 if (WaitType == WaitAny)
510 Abandoned = KiSideEffectsBeforeWake(hdr, CurrentThread) ? TRUE : Abandoned;
512 if (Timeout != NULL && Timeout->QuadPart != 0)
514 KeCancelTimer(&CurrentThread->Timer);
517 KeReleaseDispatcherDatabaseLock(FALSE);
519 DPRINT("One object is (already) signaled!\n");
520 if (Abandoned == TRUE)
522 return (STATUS_ABANDONED_WAIT_0 + i);
525 return (STATUS_WAIT_0 + i);
531 if ((WaitType == WaitAll) && (CountSignaled == Count))
533 for (i = 0; i < Count; i++)
535 hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
536 Abandoned = KiSideEffectsBeforeWake(hdr, CurrentThread) ? TRUE : Abandoned;
539 if (Timeout != NULL && Timeout->QuadPart != 0)
541 KeCancelTimer(&CurrentThread->Timer);
544 KeReleaseDispatcherDatabaseLock(FALSE);
545 DPRINT("All objects are (already) signaled!\n");
547 if (Abandoned == TRUE)
549 return (STATUS_ABANDONED_WAIT_0);
552 return (STATUS_WAIT_0);
555 //zero timeout is used for testing if the object(s) can be immediately acquired
556 if (Timeout != NULL && Timeout->QuadPart == 0)
558 KeReleaseDispatcherDatabaseLock(FALSE);
559 return STATUS_TIMEOUT;
563 * Check if we have already timed out
565 if (Timeout != NULL && KiIsObjectSignalled(&CurrentThread->Timer.Header, CurrentThread))
567 KiSideEffectsBeforeWake(&CurrentThread->Timer.Header, CurrentThread);
568 KeCancelTimer(&CurrentThread->Timer);
569 KeReleaseDispatcherDatabaseLock(FALSE);
570 return (STATUS_TIMEOUT);
573 /* Append wait block to the KTHREAD wait block list */
574 CurrentThread->WaitBlockList = blk = WaitBlockArray;
579 CurrentThread->WaitStatus = STATUS_UNSUCCESSFUL;
581 for (i = 0; i < Count; i++)
583 hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
585 blk->Object = KiGetWaitableObjectFromObject(Object[i]);
586 blk->Thread = CurrentThread;
587 blk->WaitKey = STATUS_WAIT_0 + i;
588 blk->WaitType = WaitType;
590 if (i == (Count - 1))
594 blk->NextWaitBlock = &CurrentThread->WaitBlock[3];
598 blk->NextWaitBlock = NULL;
603 blk->NextWaitBlock = blk + 1;
607 * add wait block to disp. obj. wait list
608 * Use FIFO for all waits except for queues which use LIFO
610 if (WaitReason == WrQueue)
612 InsertHeadList(&hdr->WaitListHead, &blk->WaitListEntry);
616 InsertTailList(&hdr->WaitListHead, &blk->WaitListEntry);
619 blk = blk->NextWaitBlock;
624 CurrentThread->WaitBlock[3].Object = (PVOID) & CurrentThread->Timer;
625 CurrentThread->WaitBlock[3].Thread = CurrentThread;
626 CurrentThread->WaitBlock[3].WaitKey = STATUS_TIMEOUT;
627 CurrentThread->WaitBlock[3].WaitType = WaitAny;
628 CurrentThread->WaitBlock[3].NextWaitBlock = NULL;
630 InsertTailList(&CurrentThread->Timer.Header.WaitListHead,
631 &CurrentThread->WaitBlock[3].WaitListEntry);
635 if (CurrentThread->Queue)
637 CurrentThread->Queue->RunningThreads--;
638 if (WaitReason != WrQueue && CurrentThread->Queue->RunningThreads < CurrentThread->Queue->MaximumThreads &&
639 !IsListEmpty(&CurrentThread->Queue->EntryListHead))
641 KeDispatcherObjectWake(&CurrentThread->Queue->Header);
645 PsBlockThread(&Status, Alertable, WaitMode, TRUE, WaitIrql, WaitReason);
648 if (CurrentThread->Queue)
650 CurrentThread->Queue->RunningThreads++;
655 while (Status == STATUS_KERNEL_APC);
659 KeCancelTimer(&CurrentThread->Timer);
662 DPRINT("Returning from KeWaitForMultipleObjects()\n");
666 VOID KeInitializeDispatcher(VOID)
668 KeInitializeSpinLock(&DispatcherDatabaseLock);
672 NtWaitForMultipleObjects(IN ULONG Count,
674 IN WAIT_TYPE WaitType,
675 IN BOOLEAN Alertable,
676 IN PLARGE_INTEGER Time)
678 KWAIT_BLOCK WaitBlockArray[EX_MAXIMUM_WAIT_OBJECTS];
679 PVOID ObjectPtrArray[EX_MAXIMUM_WAIT_OBJECTS];
682 KPROCESSOR_MODE WaitMode;
684 DPRINT("NtWaitForMultipleObjects(Count %lu Object[] %x, Alertable %d, "
685 "Time %x)\n", Count,Object,Alertable,Time);
687 if (Count > EX_MAXIMUM_WAIT_OBJECTS)
688 return STATUS_UNSUCCESSFUL;
690 WaitMode = ExGetPreviousMode();
692 /* reference all objects */
693 for (i = 0; i < Count; i++)
695 Status = ObReferenceObjectByHandle(Object[i],
701 if (Status != STATUS_SUCCESS)
703 /* dereference all referenced objects */
704 for (j = 0; j < i; j++)
706 ObDereferenceObject(ObjectPtrArray[j]);
713 Status = KeWaitForMultipleObjects(Count,
722 /* dereference all objects */
723 for (i = 0; i < Count; i++)
725 ObDereferenceObject(ObjectPtrArray[i]);
733 NtWaitForSingleObject(IN HANDLE Object,
734 IN BOOLEAN Alertable,
735 IN PLARGE_INTEGER Time)
739 KPROCESSOR_MODE WaitMode;
741 DPRINT("NtWaitForSingleObject(Object %x, Alertable %d, Time %x)\n",
742 Object,Alertable,Time);
744 WaitMode = ExGetPreviousMode();
746 Status = ObReferenceObjectByHandle(Object,
752 if (!NT_SUCCESS(Status))
757 Status = KeWaitForSingleObject(ObjectPtr,
763 ObDereferenceObject(ObjectPtr);
770 NtSignalAndWaitForSingleObject(IN HANDLE SignalObject,
771 IN HANDLE WaitObject,
772 IN BOOLEAN Alertable,
773 IN PLARGE_INTEGER Time)
775 KPROCESSOR_MODE WaitMode;
776 DISPATCHER_HEADER* hdr;
781 WaitMode = ExGetPreviousMode();
782 Status = ObReferenceObjectByHandle(SignalObject,
788 if (!NT_SUCCESS(Status))
793 Status = ObReferenceObjectByHandle(WaitObject,
799 if (!NT_SUCCESS(Status))
801 ObDereferenceObject(SignalObj);
805 hdr = (DISPATCHER_HEADER *)SignalObj;
808 case InternalNotificationEvent:
809 case InternalSynchronizationEvent:
810 KeSetEvent(SignalObj,
815 case InternalMutexType:
816 KeReleaseMutex(SignalObj,
820 case InternalSemaphoreType:
821 KeReleaseSemaphore(SignalObj,
828 ObDereferenceObject(SignalObj);
829 ObDereferenceObject(WaitObj);
830 return STATUS_OBJECT_TYPE_MISMATCH;
833 Status = KeWaitForSingleObject(WaitObj,
839 ObDereferenceObject(SignalObj);
840 ObDereferenceObject(WaitObj);