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)\n", Wait);
75 assert(Wait == WaitSet);
79 KeReleaseSpinLockFromDpcLevel(&DispatcherDatabaseLock);
83 #endif /* LIBCAPTIVE */
85 VOID KeReleaseDispatcherDatabaseLock(BOOLEAN Wait)
87 DPRINT("KeReleaseDispatcherDatabaseLock(Wait %x)\n",Wait);
88 assert(Wait==WaitSet);
92 KeReleaseSpinLock(&DispatcherDatabaseLock, oldlvl);
99 KiSideEffectsBeforeWake(DISPATCHER_HEADER * hdr,
102 * FUNCTION: Perform side effects on object before a wait for a thread is
106 BOOLEAN Abandoned = FALSE;
110 case InternalSynchronizationEvent:
111 hdr->SignalState = 0;
114 case InternalQueueType:
115 case InternalSemaphoreType:
119 case InternalProcessType:
122 case InternalThreadType:
125 case InternalNotificationEvent:
128 case InternalSynchronizationTimer:
129 hdr->SignalState = FALSE;
132 case InternalNotificationTimer:
135 case InternalMutexType:
139 Mutex = CONTAINING_RECORD(hdr, KMUTEX, Header);
141 assert(hdr->SignalState <= 1);
142 if (hdr->SignalState == 0)
146 DPRINT("Thread == NULL!\n");
149 Abandoned = Mutex->Abandoned;
151 InsertTailList(&Thread->MutantListHead, &Mutex->MutantListEntry);
152 Mutex->OwnerThread = Thread;
153 Mutex->Abandoned = FALSE;
159 DbgPrint("(%s:%d) Dispatcher object %x has unknown type\n", __FILE__, __LINE__, hdr);
167 KiIsObjectSignalled(DISPATCHER_HEADER * hdr,
170 if (hdr->Type == InternalMutexType)
174 Mutex = CONTAINING_RECORD(hdr, KMUTEX, Header);
176 assert(hdr->SignalState <= 1);
178 if ((hdr->SignalState < 1 && Mutex->OwnerThread == Thread) || hdr->SignalState == 1)
188 if (hdr->SignalState <= 0)
198 VOID KeRemoveAllWaitsThread(PETHREAD Thread, NTSTATUS WaitStatus)
200 PKWAIT_BLOCK WaitBlock;
201 BOOLEAN WasWaiting = FALSE;
203 KeAcquireDispatcherDatabaseLock(FALSE);
205 WaitBlock = (PKWAIT_BLOCK)Thread->Tcb.WaitBlockList;
206 if (WaitBlock != NULL)
210 while (WaitBlock != NULL)
212 RemoveEntryList(&WaitBlock->WaitListEntry);
213 WaitBlock = WaitBlock->NextWaitBlock;
215 Thread->Tcb.WaitBlockList = NULL;
219 PsUnblockThread(Thread, &WaitStatus);
222 KeReleaseDispatcherDatabaseLock(FALSE);
226 KeDispatcherObjectWakeOneOrAll(DISPATCHER_HEADER * hdr,
230 PKWAIT_BLOCK WaiterHead;
231 PLIST_ENTRY EnumEntry;
235 BOOLEAN WakedAny = FALSE;
237 DPRINT("KeDispatcherObjectWakeOnOrAll(hdr %x)\n", hdr);
238 DPRINT ("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
239 hdr->WaitListHead.Flink, hdr->WaitListHead.Blink);
241 if (IsListEmpty(&hdr->WaitListHead))
246 //enum waiters for this dispatcher object
247 EnumEntry = hdr->WaitListHead.Flink;
248 while (EnumEntry != &hdr->WaitListHead && (WakeAll || !WakedAny))
250 WaiterHead = CONTAINING_RECORD(EnumEntry, KWAIT_BLOCK, WaitListEntry);
251 DPRINT("current_entry %x current %x\n", EnumEntry, WaiterHead);
252 EnumEntry = EnumEntry->Flink;
253 assert(WaiterHead->Thread->WaitBlockList != NULL);
257 if (WaiterHead->WaitType == WaitAny)
259 DPRINT("WaitAny: Remove all wait blocks.\n");
260 for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock)
262 RemoveEntryList(&Waiter->WaitListEntry);
265 WaiterHead->Thread->WaitBlockList = NULL;
268 * If a WakeAll KiSideEffectsBeforeWake(hdr,.. will be called several times,
269 * but thats ok since WakeAll objects has no sideeffects.
271 Abandoned = KiSideEffectsBeforeWake(hdr, WaiterHead->Thread) ? TRUE : Abandoned;
275 DPRINT("WaitAll: All WaitAll objects must be signaled.\n");
279 //all WaitAll obj. for thread need to be signaled to satisfy a wake
280 for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock)
282 //no need to check hdr since it has to be signaled
283 if (Waiter->WaitType == WaitAll && Waiter->Object != hdr)
285 if (!KiIsObjectSignalled(Waiter->Object, Waiter->Thread))
295 for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock)
297 RemoveEntryList(&Waiter->WaitListEntry);
299 if (Waiter->WaitType == WaitAll)
301 Abandoned = KiSideEffectsBeforeWake(Waiter->Object, Waiter->Thread)
305 //no WaitAny objects can possibly be signaled since we are here
306 assert(!(Waiter->WaitType == WaitAny
307 && KiIsObjectSignalled(Waiter->Object, Waiter->Thread)));
310 WaiterHead->Thread->WaitBlockList = NULL;
314 if (WaiterHead->Thread->WaitBlockList == NULL)
316 Status = WaiterHead->WaitKey;
319 DPRINT("Abandoned mutex among objects");
320 Status += STATUS_ABANDONED_WAIT_0;
324 DPRINT("Waking %x status = %x\n", WaiterHead->Thread, Status);
325 PsUnblockThread(CONTAINING_RECORD(WaiterHead->Thread, ETHREAD, Tcb), &Status);
333 BOOLEAN KeDispatcherObjectWake(DISPATCHER_HEADER* hdr)
335 * FUNCTION: Wake threads waiting on a dispatcher object
336 * NOTE: The exact semantics of waking are dependant on the type of object
341 DPRINT("Entering KeDispatcherObjectWake(hdr %x)\n",hdr);
342 // DPRINT("hdr->WaitListHead %x hdr->WaitListHead.Flink %x\n",
343 // &hdr->WaitListHead,hdr->WaitListHead.Flink);
344 DPRINT("hdr->Type %x\n",hdr->Type);
347 case InternalNotificationEvent:
348 return(KeDispatcherObjectWakeAll(hdr));
350 case InternalNotificationTimer:
351 return(KeDispatcherObjectWakeAll(hdr));
353 case InternalSynchronizationEvent:
354 return(KeDispatcherObjectWakeOne(hdr));
356 case InternalSynchronizationTimer:
357 return(KeDispatcherObjectWakeOne(hdr));
359 case InternalQueueType:
360 case InternalSemaphoreType:
361 DPRINT("hdr->SignalState %d\n", hdr->SignalState);
362 if(hdr->SignalState>0)
366 DPRINT("Waking one semaphore waiter\n");
367 Ret = KeDispatcherObjectWakeOne(hdr);
368 } while(hdr->SignalState > 0 && Ret) ;
373 case InternalProcessType:
374 return(KeDispatcherObjectWakeAll(hdr));
376 case InternalThreadType:
377 return(KeDispatcherObjectWakeAll(hdr));
379 case InternalMutexType:
380 return(KeDispatcherObjectWakeOne(hdr));
382 DbgPrint("Dispatcher object %x has unknown type %d\n", hdr, hdr->Type);
389 KeWaitForSingleObject(PVOID Object,
390 KWAIT_REASON WaitReason,
391 KPROCESSOR_MODE WaitMode,
393 PLARGE_INTEGER Timeout)
395 * FUNCTION: Puts the current thread into a wait state until the
396 * given dispatcher object is set to signalled
398 * Object = Object to wait on
399 * WaitReason = Reason for the wait (debugging aid)
400 * WaitMode = Can be KernelMode or UserMode, if UserMode then
401 * user-mode APCs can be delivered and the thread's
402 * stack can be paged out
403 * Altertable = Specifies if the wait is a alertable
404 * Timeout = Optional timeout value
408 return KeWaitForMultipleObjects(1,
421 KiGetWaitableObjectFromObject(PVOID Object)
423 //special case when waiting on file objects
424 if ( ((PDISPATCHER_HEADER)Object)->Type == InternalFileType)
426 return &((PFILE_OBJECT)Object)->Event;
434 KeWaitForMultipleObjects(ULONG Count,
437 KWAIT_REASON WaitReason,
438 KPROCESSOR_MODE WaitMode,
440 PLARGE_INTEGER Timeout,
441 PKWAIT_BLOCK WaitBlockArray)
443 DISPATCHER_HEADER *hdr;
445 PKTHREAD CurrentThread;
452 DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
453 "PsGetCurrentThread() %x\n", Count, Object, PsGetCurrentThread());
455 CurrentThread = KeGetCurrentThread();
456 WaitIrql = KeGetCurrentIrql();
459 * Work out where we are going to put the wait blocks
461 if (WaitBlockArray == NULL)
463 if (Count > THREAD_WAIT_OBJECTS)
465 DPRINT("(%s:%d) Too many objects!\n", __FILE__, __LINE__);
466 return (STATUS_UNSUCCESSFUL);
468 WaitBlockArray = &CurrentThread->WaitBlock[0];
472 if (Count > EX_MAXIMUM_WAIT_OBJECTS)
474 DPRINT("(%s:%d) Too many objects!\n", __FILE__, __LINE__);
475 return (STATUS_UNSUCCESSFUL);
480 * Set up the timeout if required
482 if (Timeout != NULL && Timeout->QuadPart != 0)
484 KeInitializeTimer(&CurrentThread->Timer);
485 KeSetTimer(&CurrentThread->Timer, *Timeout, NULL);
490 KeAcquireDispatcherDatabaseLock(FALSE);
493 * If we are going to wait alertably and a user apc is pending
496 if (Alertable && KiTestAlert())
498 KeReleaseDispatcherDatabaseLock(FALSE);
499 return (STATUS_USER_APC);
503 * Check if the wait is (already) satisfied
507 for (i = 0; i < Count; i++)
509 hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
511 if (KiIsObjectSignalled(hdr, CurrentThread))
515 if (WaitType == WaitAny)
517 Abandoned = KiSideEffectsBeforeWake(hdr, CurrentThread) ? TRUE : Abandoned;
519 if (Timeout != NULL && Timeout->QuadPart != 0)
521 KeCancelTimer(&CurrentThread->Timer);
524 KeReleaseDispatcherDatabaseLock(FALSE);
526 DPRINT("One object is (already) signaled!\n");
527 if (Abandoned == TRUE)
529 return (STATUS_ABANDONED_WAIT_0 + i);
532 return (STATUS_WAIT_0 + i);
538 if ((WaitType == WaitAll) && (CountSignaled == Count))
540 for (i = 0; i < Count; i++)
542 hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
543 Abandoned = KiSideEffectsBeforeWake(hdr, CurrentThread) ? TRUE : Abandoned;
546 if (Timeout != NULL && Timeout->QuadPart != 0)
548 KeCancelTimer(&CurrentThread->Timer);
551 KeReleaseDispatcherDatabaseLock(FALSE);
552 DPRINT("All objects are (already) signaled!\n");
554 if (Abandoned == TRUE)
556 return (STATUS_ABANDONED_WAIT_0);
559 return (STATUS_WAIT_0);
562 //zero timeout is used for testing if the object(s) can be immediately acquired
563 if (Timeout != NULL && Timeout->QuadPart == 0)
565 KeReleaseDispatcherDatabaseLock(FALSE);
566 return STATUS_TIMEOUT;
570 * Check if we have already timed out
572 if (Timeout != NULL && KiIsObjectSignalled(&CurrentThread->Timer.Header, CurrentThread))
574 KiSideEffectsBeforeWake(&CurrentThread->Timer.Header, CurrentThread);
575 KeCancelTimer(&CurrentThread->Timer);
576 KeReleaseDispatcherDatabaseLock(FALSE);
577 return (STATUS_TIMEOUT);
580 /* Append wait block to the KTHREAD wait block list */
581 CurrentThread->WaitBlockList = blk = WaitBlockArray;
586 CurrentThread->WaitStatus = STATUS_UNSUCCESSFUL;
588 for (i = 0; i < Count; i++)
590 hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
592 blk->Object = KiGetWaitableObjectFromObject(Object[i]);
593 blk->Thread = CurrentThread;
594 blk->WaitKey = STATUS_WAIT_0 + i;
595 blk->WaitType = WaitType;
597 if (i == (Count - 1))
601 blk->NextWaitBlock = &CurrentThread->WaitBlock[3];
605 blk->NextWaitBlock = NULL;
610 blk->NextWaitBlock = blk + 1;
614 * add wait block to disp. obj. wait list
615 * Use FIFO for all waits except for queues which use LIFO
617 if (WaitReason == WrQueue)
619 InsertHeadList(&hdr->WaitListHead, &blk->WaitListEntry);
623 InsertTailList(&hdr->WaitListHead, &blk->WaitListEntry);
626 blk = blk->NextWaitBlock;
631 CurrentThread->WaitBlock[3].Object = (PVOID) & CurrentThread->Timer;
632 CurrentThread->WaitBlock[3].Thread = CurrentThread;
633 CurrentThread->WaitBlock[3].WaitKey = STATUS_TIMEOUT;
634 CurrentThread->WaitBlock[3].WaitType = WaitAny;
635 CurrentThread->WaitBlock[3].NextWaitBlock = NULL;
637 InsertTailList(&CurrentThread->Timer.Header.WaitListHead,
638 &CurrentThread->WaitBlock[3].WaitListEntry);
642 if (CurrentThread->Queue)
644 CurrentThread->Queue->RunningThreads--;
645 if (WaitReason != WrQueue && CurrentThread->Queue->RunningThreads < CurrentThread->Queue->MaximumThreads &&
646 !IsListEmpty(&CurrentThread->Queue->EntryListHead))
648 KeDispatcherObjectWake(&CurrentThread->Queue->Header);
652 PsBlockThread(&Status, Alertable, WaitMode, TRUE, WaitIrql, WaitReason);
655 if (CurrentThread->Queue)
657 CurrentThread->Queue->RunningThreads++;
662 while (Status == STATUS_KERNEL_APC);
666 KeCancelTimer(&CurrentThread->Timer);
669 DPRINT("Returning from KeWaitForMultipleObjects()\n");
673 #endif /* LIBCAPTIVE */
675 VOID KeInitializeDispatcher(VOID)
677 KeInitializeSpinLock(&DispatcherDatabaseLock);
683 NtWaitForMultipleObjects(IN ULONG Count,
685 IN WAIT_TYPE WaitType,
686 IN BOOLEAN Alertable,
687 IN PLARGE_INTEGER Time)
689 KWAIT_BLOCK WaitBlockArray[EX_MAXIMUM_WAIT_OBJECTS];
690 PVOID ObjectPtrArray[EX_MAXIMUM_WAIT_OBJECTS];
693 KPROCESSOR_MODE WaitMode;
695 DPRINT("NtWaitForMultipleObjects(Count %lu Object[] %x, Alertable %d, "
696 "Time %x)\n", Count,Object,Alertable,Time);
698 if (Count > EX_MAXIMUM_WAIT_OBJECTS)
699 return STATUS_UNSUCCESSFUL;
701 WaitMode = ExGetPreviousMode();
703 /* reference all objects */
704 for (i = 0; i < Count; i++)
706 Status = ObReferenceObjectByHandle(Object[i],
712 if (Status != STATUS_SUCCESS)
714 /* dereference all referenced objects */
715 for (j = 0; j < i; j++)
717 ObDereferenceObject(ObjectPtrArray[j]);
724 Status = KeWaitForMultipleObjects(Count,
733 /* dereference all objects */
734 for (i = 0; i < Count; i++)
736 ObDereferenceObject(ObjectPtrArray[i]);
744 NtWaitForSingleObject(IN HANDLE Object,
745 IN BOOLEAN Alertable,
746 IN PLARGE_INTEGER Time)
750 KPROCESSOR_MODE WaitMode;
752 DPRINT("NtWaitForSingleObject(Object %x, Alertable %d, Time %x)\n",
753 Object,Alertable,Time);
755 WaitMode = ExGetPreviousMode();
757 Status = ObReferenceObjectByHandle(Object,
763 if (!NT_SUCCESS(Status))
768 Status = KeWaitForSingleObject(ObjectPtr,
774 ObDereferenceObject(ObjectPtr);
781 NtSignalAndWaitForSingleObject(IN HANDLE SignalObject,
782 IN HANDLE WaitObject,
783 IN BOOLEAN Alertable,
784 IN PLARGE_INTEGER Time)
786 KPROCESSOR_MODE WaitMode;
787 DISPATCHER_HEADER* hdr;
792 WaitMode = ExGetPreviousMode();
793 Status = ObReferenceObjectByHandle(SignalObject,
799 if (!NT_SUCCESS(Status))
804 Status = ObReferenceObjectByHandle(WaitObject,
810 if (!NT_SUCCESS(Status))
812 ObDereferenceObject(SignalObj);
816 hdr = (DISPATCHER_HEADER *)SignalObj;
819 case InternalNotificationEvent:
820 case InternalSynchronizationEvent:
821 KeSetEvent(SignalObj,
826 case InternalMutexType:
827 KeReleaseMutex(SignalObj,
831 case InternalSemaphoreType:
832 KeReleaseSemaphore(SignalObj,
839 ObDereferenceObject(SignalObj);
840 ObDereferenceObject(WaitObj);
841 return STATUS_OBJECT_TYPE_MISMATCH;
844 Status = KeWaitForSingleObject(WaitObj,
850 ObDereferenceObject(SignalObj);
851 ObDereferenceObject(WaitObj);
856 #endif /* LIBCAPTIVE */