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 /* FUNCTIONS *****************************************************************/
38 VOID KeInitializeDispatcherHeader(DISPATCHER_HEADER* Header,
47 Header->SignalState = SignalState;
48 InitializeListHead(&(Header->WaitListHead));
52 VOID KeAcquireDispatcherDatabaseLock(BOOLEAN Wait)
54 * PURPOSE: Acquires the dispatcher database lock for the caller
57 DPRINT("KeAcquireDispatcherDatabaseLock(Wait %x)\n",Wait);
58 if (WaitSet && Owner == KeGetCurrentThread())
62 KeAcquireSpinLock(&DispatcherDatabaseLock, &oldlvl);
64 Owner = KeGetCurrentThread();
69 VOID KeReleaseDispatcherDatabaseLockAtDpcLevel(BOOLEAN Wait)
71 DPRINT("KeReleaseDispatcherDatabaseLockAtDpcLevel(Wait %x)\n", Wait);
72 assert(Wait == WaitSet);
76 KeReleaseSpinLockFromDpcLevel(&DispatcherDatabaseLock);
80 #endif /* LIBCAPTIVE */
82 VOID KeReleaseDispatcherDatabaseLock(BOOLEAN Wait)
84 DPRINT("KeReleaseDispatcherDatabaseLock(Wait %x)\n",Wait);
85 assert(Wait==WaitSet);
89 KeReleaseSpinLock(&DispatcherDatabaseLock, oldlvl);
95 static VOID KiSideEffectsBeforeWake(DISPATCHER_HEADER* hdr,
99 * FUNCTION: Perform side effects on object before a wait for a thread is
103 if (Abandoned != NULL)
108 case InternalSynchronizationEvent:
109 hdr->SignalState = 0;
112 case InternalSemaphoreType:
116 case InternalProcessType:
119 case InternalThreadType:
122 case InternalNotificationEvent:
125 case InternalSynchronizationTimer:
126 hdr->SignalState = FALSE;
129 case InternalNotificationTimer:
132 case InternalMutexType:
136 Mutex = CONTAINING_RECORD(hdr, KMUTEX, Header);
138 assert(hdr->SignalState <= 1);
139 if (hdr->SignalState == 0)
143 DPRINT1("Thread == NULL!\n");
146 if (Abandoned != NULL)
147 *Abandoned = Mutex->Abandoned;
149 InsertTailList(&Thread->MutantListHead,
150 &Mutex->MutantListEntry);
151 Mutex->OwnerThread = Thread;
152 Mutex->Abandoned = FALSE;
158 DbgPrint("(%s:%d) Dispatcher object %x has unknown type\n",
159 __FILE__,__LINE__,hdr);
165 KiIsObjectSignalled(DISPATCHER_HEADER* hdr,
169 if (Abandoned != NULL)
172 if (hdr->Type == InternalMutexType)
176 Mutex = CONTAINING_RECORD(hdr, KMUTEX, Header);
178 assert(hdr->SignalState <= 1);
179 if ((hdr->SignalState < 1 && Mutex->OwnerThread == Thread) ||
180 hdr->SignalState == 1)
182 KiSideEffectsBeforeWake(hdr,
192 if (hdr->SignalState <= 0)
198 KiSideEffectsBeforeWake(hdr,
205 VOID KeRemoveAllWaitsThread(PETHREAD Thread, NTSTATUS WaitStatus)
207 PKWAIT_BLOCK WaitBlock;
208 BOOLEAN WasWaiting = FALSE;
210 KeAcquireDispatcherDatabaseLock(FALSE);
212 WaitBlock = (PKWAIT_BLOCK)Thread->Tcb.WaitBlockList;
213 if (WaitBlock != NULL)
217 while (WaitBlock != NULL)
219 RemoveEntryList(&WaitBlock->WaitListEntry);
220 WaitBlock = WaitBlock->NextWaitBlock;
222 Thread->Tcb.WaitBlockList = NULL;
226 PsUnblockThread(Thread, &WaitStatus);
229 KeReleaseDispatcherDatabaseLock(FALSE);
232 static BOOLEAN KeDispatcherObjectWakeAll(DISPATCHER_HEADER* hdr)
234 PKWAIT_BLOCK current;
235 PLIST_ENTRY current_entry;
236 PKWAIT_BLOCK PrevBlock;
240 DPRINT("KeDispatcherObjectWakeAll(hdr %x)\n",hdr);
242 if (IsListEmpty(&hdr->WaitListHead))
247 while (!IsListEmpty(&hdr->WaitListHead))
249 current_entry = RemoveHeadList(&hdr->WaitListHead);
250 current = CONTAINING_RECORD(current_entry,
253 DPRINT("Waking %x\n",current->Thread);
254 if (current->WaitType == WaitAny)
256 DPRINT("WaitAny: Remove all wait blocks.\n");
257 for(PrevBlock = current->Thread->WaitBlockList; PrevBlock;
258 PrevBlock = PrevBlock->NextWaitBlock)
260 if (PrevBlock != current)
261 RemoveEntryList(&PrevBlock->WaitListEntry);
263 current->Thread->WaitBlockList = 0;
267 DPRINT("WaitAll: Remove the current wait block only.\n");
269 PrevBlock = current->Thread->WaitBlockList;
270 if (PrevBlock == current)
272 DPRINT( "WaitAll: Current block is list head.\n" );
273 current->Thread->WaitBlockList = current->NextWaitBlock;
277 DPRINT( "WaitAll: Current block is not list head.\n" );
278 while (PrevBlock && PrevBlock->NextWaitBlock != current)
280 PrevBlock = PrevBlock->NextWaitBlock;
284 PrevBlock->NextWaitBlock = current->NextWaitBlock;
286 current->NextWaitBlock = NULL;
287 /* if the last block is the timeout block then remove this block */
288 PrevBlock = current->Thread->WaitBlockList;
289 if (PrevBlock == ¤t->Thread->WaitBlock[3] && PrevBlock->NextWaitBlock == NULL)
291 RemoveEntryList(¤t->Thread->WaitBlock[3].WaitListEntry);
292 current->Thread->WaitBlockList = NULL;
296 KiSideEffectsBeforeWake(hdr, current->Thread, &Abandoned);
297 Status = current->WaitKey;
299 Status += STATUS_ABANDONED_WAIT_0;
300 if (current->Thread->WaitBlockList == NULL)
302 PsUnblockThread(CONTAINING_RECORD(current->Thread,ETHREAD,Tcb),
309 static BOOLEAN KeDispatcherObjectWakeOne(DISPATCHER_HEADER* hdr)
311 PKWAIT_BLOCK current;
312 PLIST_ENTRY current_entry;
313 PKWAIT_BLOCK PrevBlock;
317 DPRINT("KeDispatcherObjectWakeOn(hdr %x)\n",hdr);
318 DPRINT("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
319 hdr->WaitListHead.Flink,hdr->WaitListHead.Blink);
320 if (IsListEmpty(&(hdr->WaitListHead)))
324 current_entry = RemoveHeadList(&(hdr->WaitListHead));
325 current = CONTAINING_RECORD(current_entry,KWAIT_BLOCK,
327 DPRINT("current_entry %x current %x\n",current_entry,current);
329 if (current->WaitType == WaitAny)
331 DPRINT("WaitAny: Remove all wait blocks.\n");
332 for( PrevBlock = current->Thread->WaitBlockList; PrevBlock; PrevBlock = PrevBlock->NextWaitBlock )
333 if( PrevBlock != current )
334 RemoveEntryList( &(PrevBlock->WaitListEntry) );
335 current->Thread->WaitBlockList = 0;
339 DPRINT("WaitAll: Remove the current wait block only.\n");
341 PrevBlock = current->Thread->WaitBlockList;
342 if (PrevBlock == current)
344 DPRINT( "WaitAll: Current block is list head.\n" );
345 current->Thread->WaitBlockList = current->NextWaitBlock;
349 DPRINT( "WaitAll: Current block is not list head.\n" );
350 while ( PrevBlock && PrevBlock->NextWaitBlock != current)
352 PrevBlock = PrevBlock->NextWaitBlock;
356 PrevBlock->NextWaitBlock = current->NextWaitBlock;
359 current->NextWaitBlock = NULL;
360 /* if the last block is the timeout block then remove this block */
361 PrevBlock = current->Thread->WaitBlockList;
362 if (PrevBlock == ¤t->Thread->WaitBlock[3] && PrevBlock->NextWaitBlock == NULL)
364 RemoveEntryList(¤t->Thread->WaitBlock[3].WaitListEntry);
365 current->Thread->WaitBlockList = NULL;
369 DPRINT("Waking %x\n",current->Thread);
370 KiSideEffectsBeforeWake(hdr, current->Thread, &Abandoned);
371 Status = current->WaitKey;
373 Status += STATUS_ABANDONED_WAIT_0;
374 if (current->Thread->WaitBlockList == NULL)
376 PsUnblockThread(CONTAINING_RECORD(current->Thread,ETHREAD,Tcb),
382 BOOLEAN KeDispatcherObjectWake(DISPATCHER_HEADER* hdr)
384 * FUNCTION: Wake threads waiting on a dispatcher object
385 * NOTE: The exact semantics of waking are dependant on the type of object
390 DPRINT("Entering KeDispatcherObjectWake(hdr %x)\n",hdr);
391 // DPRINT("hdr->WaitListHead %x hdr->WaitListHead.Flink %x\n",
392 // &hdr->WaitListHead,hdr->WaitListHead.Flink);
393 DPRINT("hdr->Type %x\n",hdr->Type);
396 case InternalNotificationEvent:
397 return(KeDispatcherObjectWakeAll(hdr));
399 case InternalNotificationTimer:
400 return(KeDispatcherObjectWakeAll(hdr));
402 case InternalSynchronizationEvent:
403 return(KeDispatcherObjectWakeOne(hdr));
405 case InternalSynchronizationTimer:
406 return(KeDispatcherObjectWakeOne(hdr));
408 case InternalSemaphoreType:
409 DPRINT("hdr->SignalState %d\n", hdr->SignalState);
410 if(hdr->SignalState>0)
414 DPRINT("Waking one semaphore waiter\n");
415 Ret = KeDispatcherObjectWakeOne(hdr);
416 } while(hdr->SignalState > 0 && Ret) ;
421 case InternalProcessType:
422 return(KeDispatcherObjectWakeAll(hdr));
424 case InternalThreadType:
425 return(KeDispatcherObjectWakeAll(hdr));
427 case InternalMutexType:
428 return(KeDispatcherObjectWakeOne(hdr));
430 DbgPrint("Dispatcher object %x has unknown type %d\n", hdr, hdr->Type);
437 KeWaitForSingleObject(PVOID Object,
438 KWAIT_REASON WaitReason,
439 KPROCESSOR_MODE WaitMode,
441 PLARGE_INTEGER Timeout)
443 * FUNCTION: Puts the current thread into a wait state until the
444 * given dispatcher object is set to signalled
446 * Object = Object to wait on
447 * WaitReason = Reason for the wait (debugging aid)
448 * WaitMode = Can be KernelMode or UserMode, if UserMode then
449 * user-mode APCs can be delivered and the thread's
450 * stack can be paged out
451 * Altertable = Specifies if the wait is a alertable
452 * Timeout = Optional timeout value
456 DISPATCHER_HEADER* hdr = (DISPATCHER_HEADER *)Object;
457 PKTHREAD CurrentThread;
462 CurrentThread = KeGetCurrentThread();
463 WaitIrql = KeGetCurrentIrql();
467 * FIXME: Check for zero timeout
471 KeInitializeTimer(&CurrentThread->Timer);
472 KeSetTimer(&CurrentThread->Timer, *Timeout, NULL);
477 KeAcquireDispatcherDatabaseLock(FALSE);
480 * If we are going to wait alertably and a user apc is pending
483 if (Alertable && KiTestAlert())
485 KeReleaseDispatcherDatabaseLock(FALSE);
486 return(STATUS_USER_APC);
490 * If the object is signalled
492 if (KiIsObjectSignalled(hdr, CurrentThread, &Abandoned))
496 KeCancelTimer(&CurrentThread->Timer);
498 KeReleaseDispatcherDatabaseLock(FALSE);
499 if (Abandoned == TRUE)
500 return(STATUS_ABANDONED_WAIT_0);
501 return(STATUS_WAIT_0);
505 * Check if we have already timed out
507 if (Timeout != NULL &&
508 KiIsObjectSignalled(&CurrentThread->Timer.Header, CurrentThread, NULL))
510 KeCancelTimer(&CurrentThread->Timer);
511 KeReleaseDispatcherDatabaseLock(FALSE);
512 return(STATUS_TIMEOUT);
518 CurrentThread->WaitStatus = STATUS_UNSUCCESSFUL;
519 /* Append wait block to the KTHREAD wait block list */
520 CurrentThread->WaitBlockList = &CurrentThread->WaitBlock[0];
521 CurrentThread->WaitBlock[0].Object = Object;
522 CurrentThread->WaitBlock[0].Thread = CurrentThread;
523 CurrentThread->WaitBlock[0].WaitKey = STATUS_WAIT_0;
524 CurrentThread->WaitBlock[0].WaitType = WaitAny;
525 InsertTailList(&hdr->WaitListHead,
526 &CurrentThread->WaitBlock[0].WaitListEntry);
529 CurrentThread->WaitBlock[0].NextWaitBlock =
530 &CurrentThread->WaitBlock[3];
531 CurrentThread->WaitBlock[3].Object = (PVOID)&CurrentThread->Timer;
532 CurrentThread->WaitBlock[3].Thread = CurrentThread;
533 CurrentThread->WaitBlock[3].WaitKey = STATUS_TIMEOUT;
534 CurrentThread->WaitBlock[3].WaitType = WaitAny;
535 CurrentThread->WaitBlock[3].NextWaitBlock = NULL;
536 InsertTailList(&CurrentThread->Timer.Header.WaitListHead,
537 &CurrentThread->WaitBlock[3].WaitListEntry);
541 CurrentThread->WaitBlock[0].NextWaitBlock = NULL;
543 PsBlockThread(&Status, (UCHAR)Alertable, WaitMode, TRUE, WaitIrql);
544 } while (Status == STATUS_KERNEL_APC);
548 KeCancelTimer(&CurrentThread->Timer);
551 DPRINT("Returning from KeWaitForSingleObject()\n");
557 KeWaitForMultipleObjects(ULONG Count,
560 KWAIT_REASON WaitReason,
561 KPROCESSOR_MODE WaitMode,
563 PLARGE_INTEGER Timeout,
564 PKWAIT_BLOCK WaitBlockArray)
566 DISPATCHER_HEADER* hdr;
568 PKTHREAD CurrentThread;
575 DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
576 "PsGetCurrentThread() %x\n",Count,Object,PsGetCurrentThread());
579 CurrentThread = KeGetCurrentThread();
580 WaitIrql = KeGetCurrentIrql();
583 * Work out where we are going to put the wait blocks
585 if (WaitBlockArray == NULL)
587 if (Count > THREAD_WAIT_OBJECTS)
589 DbgPrint("(%s:%d) Too many objects!\n",
591 return(STATUS_UNSUCCESSFUL);
593 WaitBlockArray = &CurrentThread->WaitBlock[0];
597 if (Count > EX_MAXIMUM_WAIT_OBJECTS)
599 DbgPrint("(%s:%d) Too many objects!\n",
601 return(STATUS_UNSUCCESSFUL);
606 * Set up the timeout if required
610 KeInitializeTimer(&CurrentThread->Timer);
611 KeSetTimer(&CurrentThread->Timer, *Timeout, NULL);
616 KeAcquireDispatcherDatabaseLock(FALSE);
619 * If we are going to wait alertably and a user apc is pending
622 if (Alertable && KiTestAlert())
624 KeReleaseDispatcherDatabaseLock(FALSE);
625 return(STATUS_USER_APC);
629 * Check if the wait is already satisfied
631 for (i = 0; i < Count; i++)
633 hdr = (DISPATCHER_HEADER *)Object[i];
635 if (KiIsObjectSignalled(hdr, CurrentThread, &Abandoned))
639 if (WaitType == WaitAny)
643 KeCancelTimer(&CurrentThread->Timer);
645 KeReleaseDispatcherDatabaseLock(FALSE);
646 DPRINT("One object is already signaled!\n");
647 if (Abandoned == TRUE)
648 return(STATUS_ABANDONED_WAIT_0 + i);
649 return(STATUS_WAIT_0 + i);
654 if ((WaitType == WaitAll) && (CountSignaled == Count))
658 KeCancelTimer(&CurrentThread->Timer);
660 KeReleaseDispatcherDatabaseLock(FALSE);
661 DPRINT("All objects are already signaled!\n");
662 return(STATUS_WAIT_0);
666 * Check if we have already timed out
668 if (Timeout != NULL &&
669 KiIsObjectSignalled(&CurrentThread->Timer.Header, CurrentThread, NULL))
671 KeCancelTimer(&CurrentThread->Timer);
672 KeReleaseDispatcherDatabaseLock(FALSE);
673 return(STATUS_TIMEOUT);
676 /* Append wait block to the KTHREAD wait block list */
677 CurrentThread->WaitBlockList = blk = WaitBlockArray;
682 CurrentThread->WaitStatus = STATUS_UNSUCCESSFUL;
683 for (i = 0; i < Count; i++)
685 hdr = (DISPATCHER_HEADER *)Object[i];
687 blk->Object = Object[i];
688 blk->Thread = CurrentThread;
689 blk->WaitKey = STATUS_WAIT_0 + i;
690 blk->WaitType = WaitType;
691 if (i == (Count - 1))
695 blk->NextWaitBlock = &CurrentThread->WaitBlock[3];
699 blk->NextWaitBlock = NULL;
704 blk->NextWaitBlock = blk + 1;
707 InsertTailList(&hdr->WaitListHead, &blk->WaitListEntry);
709 blk = blk->NextWaitBlock;
713 CurrentThread->WaitBlock[3].Object = (PVOID)&CurrentThread->Timer;
714 CurrentThread->WaitBlock[3].Thread = CurrentThread;
715 CurrentThread->WaitBlock[3].WaitKey = STATUS_TIMEOUT;
716 CurrentThread->WaitBlock[3].WaitType = WaitAny;
717 CurrentThread->WaitBlock[3].NextWaitBlock = NULL;
718 InsertTailList(&CurrentThread->Timer.Header.WaitListHead,
719 &CurrentThread->WaitBlock[3].WaitListEntry);
722 PsBlockThread(&Status, Alertable, WaitMode, TRUE, WaitIrql);
723 } while(Status == STATUS_KERNEL_APC);
727 KeCancelTimer(&CurrentThread->Timer);
730 DPRINT("Returning from KeWaitForMultipleObjects()\n");
734 #endif /* LIBCAPTIVE */
736 VOID KeInitializeDispatcher(VOID)
738 KeInitializeSpinLock(&DispatcherDatabaseLock);
744 NtWaitForMultipleObjects(IN ULONG Count,
747 IN BOOLEAN Alertable,
748 IN PLARGE_INTEGER Time)
750 KWAIT_BLOCK WaitBlockArray[EX_MAXIMUM_WAIT_OBJECTS];
751 PVOID ObjectPtrArray[EX_MAXIMUM_WAIT_OBJECTS];
755 DPRINT("NtWaitForMultipleObjects(Count %lu Object[] %x, Alertable %d, "
756 "Time %x)\n", Count,Object,Alertable,Time);
758 if (Count > EX_MAXIMUM_WAIT_OBJECTS)
759 return STATUS_UNSUCCESSFUL;
761 /* reference all objects */
762 for (i = 0; i < Count; i++)
764 Status = ObReferenceObjectByHandle(Object[i],
770 if (Status != STATUS_SUCCESS)
772 /* dereference all referenced objects */
773 for (j = 0; j < i; j++)
775 ObDereferenceObject(ObjectPtrArray[j]);
782 Status = KeWaitForMultipleObjects(Count,
791 /* dereference all objects */
792 for (i = 0; i < Count; i++)
794 ObDereferenceObject(ObjectPtrArray[i]);
802 NtWaitForSingleObject(IN HANDLE Object,
803 IN BOOLEAN Alertable,
804 IN PLARGE_INTEGER Time)
809 DPRINT("NtWaitForSingleObject(Object %x, Alertable %d, Time %x)\n",
810 Object,Alertable,Time);
812 Status = ObReferenceObjectByHandle(Object,
818 if (!NT_SUCCESS(Status))
823 Status = KeWaitForSingleObject(ObjectPtr,
829 ObDereferenceObject(ObjectPtr);
836 NtSignalAndWaitForSingleObject(IN HANDLE SignalObject,
837 IN HANDLE WaitObject,
838 IN BOOLEAN Alertable,
839 IN PLARGE_INTEGER Time)
841 KPROCESSOR_MODE ProcessorMode;
842 DISPATCHER_HEADER* hdr;
847 ProcessorMode = ExGetPreviousMode();
848 Status = ObReferenceObjectByHandle(SignalObject,
854 if (!NT_SUCCESS(Status))
859 Status = ObReferenceObjectByHandle(WaitObject,
865 if (!NT_SUCCESS(Status))
867 ObDereferenceObject(SignalObj);
871 hdr = (DISPATCHER_HEADER *)SignalObj;
874 case InternalNotificationEvent:
875 case InternalSynchronizationEvent:
876 KeSetEvent(SignalObj,
881 case InternalMutexType:
882 KeReleaseMutex(SignalObj,
886 case InternalSemaphoreType:
887 KeReleaseSemaphore(SignalObj,
894 ObDereferenceObject(SignalObj);
895 ObDereferenceObject(WaitObj);
896 return STATUS_OBJECT_TYPE_MISMATCH;
899 Status = KeWaitForSingleObject(WaitObj,
905 ObDereferenceObject(SignalObj);
906 ObDereferenceObject(WaitObj);
911 #endif /* LIBCAPTIVE */