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));
51 VOID KeAcquireDispatcherDatabaseLock(BOOLEAN Wait)
53 * PURPOSE: Acquires the dispatcher database lock for the caller
56 DPRINT("KeAcquireDispatcherDatabaseLock(Wait %x)\n",Wait);
57 if (WaitSet && Owner == KeGetCurrentThread())
61 KeAcquireSpinLock(&DispatcherDatabaseLock, &oldlvl);
63 Owner = KeGetCurrentThread();
66 VOID KeReleaseDispatcherDatabaseLockAtDpcLevel(BOOLEAN Wait)
68 DPRINT("KeReleaseDispatcherDatabaseLockAtDpcLevel(Wait %x)\n", Wait);
69 assert(Wait == WaitSet);
73 KeReleaseSpinLockFromDpcLevel(&DispatcherDatabaseLock);
77 VOID KeReleaseDispatcherDatabaseLock(BOOLEAN Wait)
79 DPRINT("KeReleaseDispatcherDatabaseLock(Wait %x)\n",Wait);
80 assert(Wait==WaitSet);
84 KeReleaseSpinLock(&DispatcherDatabaseLock, oldlvl);
88 static VOID KiSideEffectsBeforeWake(DISPATCHER_HEADER* hdr,
92 * FUNCTION: Perform side effects on object before a wait for a thread is
96 if (Abandoned != NULL)
101 case InternalSynchronizationEvent:
102 hdr->SignalState = 0;
105 case InternalSemaphoreType:
109 case InternalProcessType:
112 case InternalThreadType:
115 case InternalNotificationEvent:
118 case InternalSynchronizationTimer:
119 hdr->SignalState = FALSE;
122 case InternalNotificationTimer:
125 case InternalMutexType:
129 Mutex = CONTAINING_RECORD(hdr, KMUTEX, Header);
131 assert(hdr->SignalState <= 1);
132 if (hdr->SignalState == 0)
136 DPRINT1("Thread == NULL!\n");
139 if (Abandoned != NULL)
140 *Abandoned = Mutex->Abandoned;
142 InsertTailList(&Thread->MutantListHead,
143 &Mutex->MutantListEntry);
144 Mutex->OwnerThread = Thread;
145 Mutex->Abandoned = FALSE;
151 DbgPrint("(%s:%d) Dispatcher object %x has unknown type\n",
152 __FILE__,__LINE__,hdr);
158 KiIsObjectSignalled(DISPATCHER_HEADER* hdr,
162 if (Abandoned != NULL)
165 if (hdr->Type == InternalMutexType)
169 Mutex = CONTAINING_RECORD(hdr, KMUTEX, Header);
171 assert(hdr->SignalState <= 1);
172 if ((hdr->SignalState < 1 && Mutex->OwnerThread == Thread) ||
173 hdr->SignalState == 1)
175 KiSideEffectsBeforeWake(hdr,
185 if (hdr->SignalState <= 0)
191 KiSideEffectsBeforeWake(hdr,
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);
225 static BOOLEAN KeDispatcherObjectWakeAll(DISPATCHER_HEADER* hdr)
227 PKWAIT_BLOCK current;
228 PLIST_ENTRY current_entry;
229 PKWAIT_BLOCK PrevBlock;
233 DPRINT("KeDispatcherObjectWakeAll(hdr %x)\n",hdr);
235 if (IsListEmpty(&hdr->WaitListHead))
240 while (!IsListEmpty(&hdr->WaitListHead))
242 current_entry = RemoveHeadList(&hdr->WaitListHead);
243 current = CONTAINING_RECORD(current_entry,
246 DPRINT("Waking %x\n",current->Thread);
247 if (current->WaitType == WaitAny)
249 DPRINT("WaitAny: Remove all wait blocks.\n");
250 for(PrevBlock = current->Thread->WaitBlockList; PrevBlock;
251 PrevBlock = PrevBlock->NextWaitBlock)
253 if (PrevBlock != current)
254 RemoveEntryList(&PrevBlock->WaitListEntry);
256 current->Thread->WaitBlockList = 0;
260 DPRINT("WaitAll: Remove the current wait block only.\n");
262 PrevBlock = current->Thread->WaitBlockList;
263 if (PrevBlock == current)
265 DPRINT( "WaitAll: Current block is list head.\n" );
266 current->Thread->WaitBlockList = current->NextWaitBlock;
270 DPRINT( "WaitAll: Current block is not list head.\n" );
271 while (PrevBlock && PrevBlock->NextWaitBlock != current)
273 PrevBlock = PrevBlock->NextWaitBlock;
277 PrevBlock->NextWaitBlock = current->NextWaitBlock;
279 current->NextWaitBlock = NULL;
280 /* if the last block is the timeout block then remove this block */
281 PrevBlock = current->Thread->WaitBlockList;
282 if (PrevBlock == ¤t->Thread->WaitBlock[3] && PrevBlock->NextWaitBlock == NULL)
284 RemoveEntryList(¤t->Thread->WaitBlock[3].WaitListEntry);
285 current->Thread->WaitBlockList = NULL;
289 KiSideEffectsBeforeWake(hdr, current->Thread, &Abandoned);
290 Status = current->WaitKey;
292 Status += STATUS_ABANDONED_WAIT_0;
293 if (current->Thread->WaitBlockList == NULL)
295 PsUnblockThread(CONTAINING_RECORD(current->Thread,ETHREAD,Tcb),
302 static BOOLEAN KeDispatcherObjectWakeOne(DISPATCHER_HEADER* hdr)
304 PKWAIT_BLOCK current;
305 PLIST_ENTRY current_entry;
306 PKWAIT_BLOCK PrevBlock;
310 DPRINT("KeDispatcherObjectWakeOn(hdr %x)\n",hdr);
311 DPRINT("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
312 hdr->WaitListHead.Flink,hdr->WaitListHead.Blink);
313 if (IsListEmpty(&(hdr->WaitListHead)))
317 current_entry = RemoveHeadList(&(hdr->WaitListHead));
318 current = CONTAINING_RECORD(current_entry,KWAIT_BLOCK,
320 DPRINT("current_entry %x current %x\n",current_entry,current);
322 if (current->WaitType == WaitAny)
324 DPRINT("WaitAny: Remove all wait blocks.\n");
325 for( PrevBlock = current->Thread->WaitBlockList; PrevBlock; PrevBlock = PrevBlock->NextWaitBlock )
326 if( PrevBlock != current )
327 RemoveEntryList( &(PrevBlock->WaitListEntry) );
328 current->Thread->WaitBlockList = 0;
332 DPRINT("WaitAll: Remove the current wait block only.\n");
334 PrevBlock = current->Thread->WaitBlockList;
335 if (PrevBlock == current)
337 DPRINT( "WaitAll: Current block is list head.\n" );
338 current->Thread->WaitBlockList = current->NextWaitBlock;
342 DPRINT( "WaitAll: Current block is not list head.\n" );
343 while ( PrevBlock && PrevBlock->NextWaitBlock != current)
345 PrevBlock = PrevBlock->NextWaitBlock;
349 PrevBlock->NextWaitBlock = current->NextWaitBlock;
352 current->NextWaitBlock = NULL;
353 /* if the last block is the timeout block then remove this block */
354 PrevBlock = current->Thread->WaitBlockList;
355 if (PrevBlock == ¤t->Thread->WaitBlock[3] && PrevBlock->NextWaitBlock == NULL)
357 RemoveEntryList(¤t->Thread->WaitBlock[3].WaitListEntry);
358 current->Thread->WaitBlockList = NULL;
362 DPRINT("Waking %x\n",current->Thread);
363 KiSideEffectsBeforeWake(hdr, current->Thread, &Abandoned);
364 Status = current->WaitKey;
366 Status += STATUS_ABANDONED_WAIT_0;
367 if (current->Thread->WaitBlockList == NULL)
369 PsUnblockThread(CONTAINING_RECORD(current->Thread,ETHREAD,Tcb),
375 BOOLEAN KeDispatcherObjectWake(DISPATCHER_HEADER* hdr)
377 * FUNCTION: Wake threads waiting on a dispatcher object
378 * NOTE: The exact semantics of waking are dependant on the type of object
383 DPRINT("Entering KeDispatcherObjectWake(hdr %x)\n",hdr);
384 // DPRINT("hdr->WaitListHead %x hdr->WaitListHead.Flink %x\n",
385 // &hdr->WaitListHead,hdr->WaitListHead.Flink);
386 DPRINT("hdr->Type %x\n",hdr->Type);
389 case InternalNotificationEvent:
390 return(KeDispatcherObjectWakeAll(hdr));
392 case InternalNotificationTimer:
393 return(KeDispatcherObjectWakeAll(hdr));
395 case InternalSynchronizationEvent:
396 return(KeDispatcherObjectWakeOne(hdr));
398 case InternalSynchronizationTimer:
399 return(KeDispatcherObjectWakeOne(hdr));
401 case InternalSemaphoreType:
402 DPRINT("hdr->SignalState %d\n", hdr->SignalState);
403 if(hdr->SignalState>0)
407 DPRINT("Waking one semaphore waiter\n");
408 Ret = KeDispatcherObjectWakeOne(hdr);
409 } while(hdr->SignalState > 0 && Ret) ;
414 case InternalProcessType:
415 return(KeDispatcherObjectWakeAll(hdr));
417 case InternalThreadType:
418 return(KeDispatcherObjectWakeAll(hdr));
420 case InternalMutexType:
421 return(KeDispatcherObjectWakeOne(hdr));
423 DbgPrint("Dispatcher object %x has unknown type %d\n", hdr, hdr->Type);
430 KeWaitForSingleObject(PVOID Object,
431 KWAIT_REASON WaitReason,
432 KPROCESSOR_MODE WaitMode,
434 PLARGE_INTEGER Timeout)
436 * FUNCTION: Puts the current thread into a wait state until the
437 * given dispatcher object is set to signalled
439 * Object = Object to wait on
440 * WaitReason = Reason for the wait (debugging aid)
441 * WaitMode = Can be KernelMode or UserMode, if UserMode then
442 * user-mode APCs can be delivered and the thread's
443 * stack can be paged out
444 * Altertable = Specifies if the wait is a alertable
445 * Timeout = Optional timeout value
449 DISPATCHER_HEADER* hdr = (DISPATCHER_HEADER *)Object;
450 PKTHREAD CurrentThread;
455 CurrentThread = KeGetCurrentThread();
456 WaitIrql = KeGetCurrentIrql();
460 * FIXME: Check for zero timeout
464 KeInitializeTimer(&CurrentThread->Timer);
465 KeSetTimer(&CurrentThread->Timer, *Timeout, NULL);
470 KeAcquireDispatcherDatabaseLock(FALSE);
473 * If we are going to wait alertably and a user apc is pending
476 if (Alertable && KiTestAlert())
478 KeReleaseDispatcherDatabaseLock(FALSE);
479 return(STATUS_USER_APC);
483 * If the object is signalled
485 if (KiIsObjectSignalled(hdr, CurrentThread, &Abandoned))
489 KeCancelTimer(&CurrentThread->Timer);
491 KeReleaseDispatcherDatabaseLock(FALSE);
492 if (Abandoned == TRUE)
493 return(STATUS_ABANDONED_WAIT_0);
494 return(STATUS_WAIT_0);
498 * Check if we have already timed out
500 if (Timeout != NULL &&
501 KiIsObjectSignalled(&CurrentThread->Timer.Header, CurrentThread, NULL))
503 KeCancelTimer(&CurrentThread->Timer);
504 KeReleaseDispatcherDatabaseLock(FALSE);
505 return(STATUS_TIMEOUT);
511 CurrentThread->WaitStatus = STATUS_UNSUCCESSFUL;
512 /* Append wait block to the KTHREAD wait block list */
513 CurrentThread->WaitBlockList = &CurrentThread->WaitBlock[0];
514 CurrentThread->WaitBlock[0].Object = Object;
515 CurrentThread->WaitBlock[0].Thread = CurrentThread;
516 CurrentThread->WaitBlock[0].WaitKey = STATUS_WAIT_0;
517 CurrentThread->WaitBlock[0].WaitType = WaitAny;
518 InsertTailList(&hdr->WaitListHead,
519 &CurrentThread->WaitBlock[0].WaitListEntry);
522 CurrentThread->WaitBlock[0].NextWaitBlock =
523 &CurrentThread->WaitBlock[3];
524 CurrentThread->WaitBlock[3].Object = (PVOID)&CurrentThread->Timer;
525 CurrentThread->WaitBlock[3].Thread = CurrentThread;
526 CurrentThread->WaitBlock[3].WaitKey = STATUS_TIMEOUT;
527 CurrentThread->WaitBlock[3].WaitType = WaitAny;
528 CurrentThread->WaitBlock[3].NextWaitBlock = NULL;
529 InsertTailList(&CurrentThread->Timer.Header.WaitListHead,
530 &CurrentThread->WaitBlock[3].WaitListEntry);
534 CurrentThread->WaitBlock[0].NextWaitBlock = NULL;
536 PsBlockThread(&Status, (UCHAR)Alertable, WaitMode, TRUE, WaitIrql);
537 } while (Status == STATUS_KERNEL_APC);
541 KeCancelTimer(&CurrentThread->Timer);
544 DPRINT("Returning from KeWaitForSingleObject()\n");
550 KeWaitForMultipleObjects(ULONG Count,
553 KWAIT_REASON WaitReason,
554 KPROCESSOR_MODE WaitMode,
556 PLARGE_INTEGER Timeout,
557 PKWAIT_BLOCK WaitBlockArray)
559 DISPATCHER_HEADER* hdr;
561 PKTHREAD CurrentThread;
568 DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
569 "PsGetCurrentThread() %x\n",Count,Object,PsGetCurrentThread());
572 CurrentThread = KeGetCurrentThread();
573 WaitIrql = KeGetCurrentIrql();
576 * Work out where we are going to put the wait blocks
578 if (WaitBlockArray == NULL)
580 if (Count > THREAD_WAIT_OBJECTS)
582 DbgPrint("(%s:%d) Too many objects!\n",
584 return(STATUS_UNSUCCESSFUL);
586 WaitBlockArray = &CurrentThread->WaitBlock[0];
590 if (Count > EX_MAXIMUM_WAIT_OBJECTS)
592 DbgPrint("(%s:%d) Too many objects!\n",
594 return(STATUS_UNSUCCESSFUL);
599 * Set up the timeout if required
603 KeInitializeTimer(&CurrentThread->Timer);
604 KeSetTimer(&CurrentThread->Timer, *Timeout, NULL);
609 KeAcquireDispatcherDatabaseLock(FALSE);
612 * If we are going to wait alertably and a user apc is pending
615 if (Alertable && KiTestAlert())
617 KeReleaseDispatcherDatabaseLock(FALSE);
618 return(STATUS_USER_APC);
622 * Check if the wait is already satisfied
624 for (i = 0; i < Count; i++)
626 hdr = (DISPATCHER_HEADER *)Object[i];
628 if (KiIsObjectSignalled(hdr, CurrentThread, &Abandoned))
632 if (WaitType == WaitAny)
636 KeCancelTimer(&CurrentThread->Timer);
638 KeReleaseDispatcherDatabaseLock(FALSE);
639 DPRINT("One object is already signaled!\n");
640 if (Abandoned == TRUE)
641 return(STATUS_ABANDONED_WAIT_0 + i);
642 return(STATUS_WAIT_0 + i);
647 if ((WaitType == WaitAll) && (CountSignaled == Count))
651 KeCancelTimer(&CurrentThread->Timer);
653 KeReleaseDispatcherDatabaseLock(FALSE);
654 DPRINT("All objects are already signaled!\n");
655 return(STATUS_WAIT_0);
659 * Check if we have already timed out
661 if (Timeout != NULL &&
662 KiIsObjectSignalled(&CurrentThread->Timer.Header, CurrentThread, NULL))
664 KeCancelTimer(&CurrentThread->Timer);
665 KeReleaseDispatcherDatabaseLock(FALSE);
666 return(STATUS_TIMEOUT);
669 /* Append wait block to the KTHREAD wait block list */
670 CurrentThread->WaitBlockList = blk = WaitBlockArray;
675 CurrentThread->WaitStatus = STATUS_UNSUCCESSFUL;
676 for (i = 0; i < Count; i++)
678 hdr = (DISPATCHER_HEADER *)Object[i];
680 blk->Object = Object[i];
681 blk->Thread = CurrentThread;
682 blk->WaitKey = STATUS_WAIT_0 + i;
683 blk->WaitType = WaitType;
684 if (i == (Count - 1))
688 blk->NextWaitBlock = &CurrentThread->WaitBlock[3];
692 blk->NextWaitBlock = NULL;
697 blk->NextWaitBlock = blk + 1;
700 InsertTailList(&hdr->WaitListHead, &blk->WaitListEntry);
702 blk = blk->NextWaitBlock;
706 CurrentThread->WaitBlock[3].Object = (PVOID)&CurrentThread->Timer;
707 CurrentThread->WaitBlock[3].Thread = CurrentThread;
708 CurrentThread->WaitBlock[3].WaitKey = STATUS_TIMEOUT;
709 CurrentThread->WaitBlock[3].WaitType = WaitAny;
710 CurrentThread->WaitBlock[3].NextWaitBlock = NULL;
711 InsertTailList(&CurrentThread->Timer.Header.WaitListHead,
712 &CurrentThread->WaitBlock[3].WaitListEntry);
715 PsBlockThread(&Status, Alertable, WaitMode, TRUE, WaitIrql);
716 } while(Status == STATUS_KERNEL_APC);
720 KeCancelTimer(&CurrentThread->Timer);
723 DPRINT("Returning from KeWaitForMultipleObjects()\n");
727 VOID KeInitializeDispatcher(VOID)
729 KeInitializeSpinLock(&DispatcherDatabaseLock);
733 NtWaitForMultipleObjects(IN ULONG Count,
736 IN BOOLEAN Alertable,
737 IN PLARGE_INTEGER Time)
739 KWAIT_BLOCK WaitBlockArray[EX_MAXIMUM_WAIT_OBJECTS];
740 PVOID ObjectPtrArray[EX_MAXIMUM_WAIT_OBJECTS];
744 DPRINT("NtWaitForMultipleObjects(Count %lu Object[] %x, Alertable %d, "
745 "Time %x)\n", Count,Object,Alertable,Time);
747 if (Count > EX_MAXIMUM_WAIT_OBJECTS)
748 return STATUS_UNSUCCESSFUL;
750 /* reference all objects */
751 for (i = 0; i < Count; i++)
753 Status = ObReferenceObjectByHandle(Object[i],
759 if (Status != STATUS_SUCCESS)
761 /* dereference all referenced objects */
762 for (j = 0; j < i; j++)
764 ObDereferenceObject(ObjectPtrArray[j]);
771 Status = KeWaitForMultipleObjects(Count,
780 /* dereference all objects */
781 for (i = 0; i < Count; i++)
783 ObDereferenceObject(ObjectPtrArray[i]);
791 NtWaitForSingleObject(IN HANDLE Object,
792 IN BOOLEAN Alertable,
793 IN PLARGE_INTEGER Time)
798 DPRINT("NtWaitForSingleObject(Object %x, Alertable %d, Time %x)\n",
799 Object,Alertable,Time);
801 Status = ObReferenceObjectByHandle(Object,
807 if (!NT_SUCCESS(Status))
812 Status = KeWaitForSingleObject(ObjectPtr,
818 ObDereferenceObject(ObjectPtr);
825 NtSignalAndWaitForSingleObject(IN HANDLE SignalObject,
826 IN HANDLE WaitObject,
827 IN BOOLEAN Alertable,
828 IN PLARGE_INTEGER Time)
830 KPROCESSOR_MODE ProcessorMode;
831 DISPATCHER_HEADER* hdr;
836 ProcessorMode = ExGetPreviousMode();
837 Status = ObReferenceObjectByHandle(SignalObject,
843 if (!NT_SUCCESS(Status))
848 Status = ObReferenceObjectByHandle(WaitObject,
854 if (!NT_SUCCESS(Status))
856 ObDereferenceObject(SignalObj);
860 hdr = (DISPATCHER_HEADER *)SignalObj;
863 case InternalNotificationEvent:
864 case InternalSynchronizationEvent:
865 KeSetEvent(SignalObj,
870 case InternalMutexType:
871 KeReleaseMutex(SignalObj,
875 case InternalSemaphoreType:
876 KeReleaseSemaphore(SignalObj,
883 ObDereferenceObject(SignalObj);
884 ObDereferenceObject(WaitObj);
885 return STATUS_OBJECT_TYPE_MISMATCH;
888 Status = KeWaitForSingleObject(WaitObj,
894 ObDereferenceObject(SignalObj);
895 ObDereferenceObject(WaitObj);