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 ******************************************************************/
33 static KSPIN_LOCK DispatcherDatabaseLock;
34 static BOOLEAN WaitSet = FALSE;
35 static KIRQL oldlvl = PASSIVE_LEVEL;
36 static PKTHREAD Owner = NULL;
38 #endif /* LIBCAPTIVE */
40 /* FUNCTIONS *****************************************************************/
42 VOID KeInitializeDispatcherHeader(DISPATCHER_HEADER* Header,
51 Header->SignalState = SignalState;
52 InitializeListHead(&(Header->WaitListHead));
57 VOID KeAcquireDispatcherDatabaseLock(BOOLEAN Wait)
59 * PURPOSE: Acquires the dispatcher database lock for the caller
62 DPRINT("KeAcquireDispatcherDatabaseLock(Wait %x)\n",Wait);
63 if (WaitSet && Owner == KeGetCurrentThread())
67 KeAcquireSpinLock(&DispatcherDatabaseLock, &oldlvl);
69 Owner = KeGetCurrentThread();
72 VOID KeReleaseDispatcherDatabaseLockAtDpcLevel(BOOLEAN Wait)
74 DPRINT("KeReleaseDispatcherDatabaseLockAtDpcLevel(Wait %x)\n", Wait);
75 assert(Wait == WaitSet);
79 KeReleaseSpinLockFromDpcLevel(&DispatcherDatabaseLock);
83 VOID KeReleaseDispatcherDatabaseLock(BOOLEAN Wait)
85 DPRINT("KeReleaseDispatcherDatabaseLock(Wait %x)\n",Wait);
86 assert(Wait==WaitSet);
90 KeReleaseSpinLock(&DispatcherDatabaseLock, oldlvl);
94 static VOID KiSideEffectsBeforeWake(DISPATCHER_HEADER* hdr,
98 * FUNCTION: Perform side effects on object before a wait for a thread is
102 if (Abandoned != NULL)
107 case InternalSynchronizationEvent:
108 hdr->SignalState = 0;
111 case InternalSemaphoreType:
115 case InternalProcessType:
118 case InternalThreadType:
121 case InternalNotificationEvent:
124 case InternalSynchronizationTimer:
125 hdr->SignalState = FALSE;
128 case InternalNotificationTimer:
131 case InternalMutexType:
135 Mutex = CONTAINING_RECORD(hdr, KMUTEX, Header);
137 assert(hdr->SignalState <= 1);
138 if (hdr->SignalState == 0)
142 DPRINT1("Thread == NULL!\n");
145 if (Abandoned != NULL)
146 *Abandoned = Mutex->Abandoned;
148 InsertTailList(&Thread->MutantListHead,
149 &Mutex->MutantListEntry);
150 Mutex->OwnerThread = Thread;
151 Mutex->Abandoned = FALSE;
157 DbgPrint("(%s:%d) Dispatcher object %x has unknown type\n",
158 __FILE__,__LINE__,hdr);
164 KiIsObjectSignalled(DISPATCHER_HEADER* hdr,
168 if (Abandoned != NULL)
171 if (hdr->Type == InternalMutexType)
175 Mutex = CONTAINING_RECORD(hdr, KMUTEX, Header);
177 assert(hdr->SignalState <= 1);
178 if ((hdr->SignalState < 1 && Mutex->OwnerThread == Thread) ||
179 hdr->SignalState == 1)
181 KiSideEffectsBeforeWake(hdr,
191 if (hdr->SignalState <= 0)
197 KiSideEffectsBeforeWake(hdr,
204 VOID KeRemoveAllWaitsThread(PETHREAD Thread, NTSTATUS WaitStatus)
206 PKWAIT_BLOCK WaitBlock;
207 BOOLEAN WasWaiting = FALSE;
209 KeAcquireDispatcherDatabaseLock(FALSE);
211 WaitBlock = (PKWAIT_BLOCK)Thread->Tcb.WaitBlockList;
212 if (WaitBlock != NULL)
216 while (WaitBlock != NULL)
218 RemoveEntryList(&WaitBlock->WaitListEntry);
219 WaitBlock = WaitBlock->NextWaitBlock;
221 Thread->Tcb.WaitBlockList = NULL;
225 PsUnblockThread(Thread, &WaitStatus);
228 KeReleaseDispatcherDatabaseLock(FALSE);
231 static BOOLEAN KeDispatcherObjectWakeAll(DISPATCHER_HEADER* hdr)
233 PKWAIT_BLOCK current;
234 PLIST_ENTRY current_entry;
235 PKWAIT_BLOCK PrevBlock;
239 DPRINT("KeDispatcherObjectWakeAll(hdr %x)\n",hdr);
241 if (IsListEmpty(&hdr->WaitListHead))
246 while (!IsListEmpty(&hdr->WaitListHead))
248 current_entry = RemoveHeadList(&hdr->WaitListHead);
249 current = CONTAINING_RECORD(current_entry,
252 DPRINT("Waking %x\n",current->Thread);
253 if (current->WaitType == WaitAny)
255 DPRINT("WaitAny: Remove all wait blocks.\n");
256 for(PrevBlock = current->Thread->WaitBlockList; PrevBlock;
257 PrevBlock = PrevBlock->NextWaitBlock)
259 if (PrevBlock != current)
260 RemoveEntryList(&PrevBlock->WaitListEntry);
262 current->Thread->WaitBlockList = 0;
266 DPRINT("WaitAll: Remove the current wait block only.\n");
268 PrevBlock = current->Thread->WaitBlockList;
269 if (PrevBlock == current)
271 DPRINT( "WaitAll: Current block is list head.\n" );
272 current->Thread->WaitBlockList = current->NextWaitBlock;
276 DPRINT( "WaitAll: Current block is not list head.\n" );
277 while (PrevBlock && PrevBlock->NextWaitBlock != current)
279 PrevBlock = PrevBlock->NextWaitBlock;
283 PrevBlock->NextWaitBlock = current->NextWaitBlock;
285 current->NextWaitBlock = NULL;
286 /* if the last block is the timeout block then remove this block */
287 PrevBlock = current->Thread->WaitBlockList;
288 if (PrevBlock == ¤t->Thread->WaitBlock[3] && PrevBlock->NextWaitBlock == NULL)
290 RemoveEntryList(¤t->Thread->WaitBlock[3].WaitListEntry);
291 current->Thread->WaitBlockList = NULL;
295 KiSideEffectsBeforeWake(hdr, current->Thread, &Abandoned);
296 Status = current->WaitKey;
298 Status += STATUS_ABANDONED_WAIT_0;
299 if (current->Thread->WaitBlockList == NULL)
301 PsUnblockThread(CONTAINING_RECORD(current->Thread,ETHREAD,Tcb),
308 static BOOLEAN KeDispatcherObjectWakeOne(DISPATCHER_HEADER* hdr)
310 PKWAIT_BLOCK current;
311 PLIST_ENTRY current_entry;
312 PKWAIT_BLOCK PrevBlock;
316 DPRINT("KeDispatcherObjectWakeOn(hdr %x)\n",hdr);
317 DPRINT("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
318 hdr->WaitListHead.Flink,hdr->WaitListHead.Blink);
319 if (IsListEmpty(&(hdr->WaitListHead)))
323 current_entry = RemoveHeadList(&(hdr->WaitListHead));
324 current = CONTAINING_RECORD(current_entry,KWAIT_BLOCK,
326 DPRINT("current_entry %x current %x\n",current_entry,current);
328 if (current->WaitType == WaitAny)
330 DPRINT("WaitAny: Remove all wait blocks.\n");
331 for( PrevBlock = current->Thread->WaitBlockList; PrevBlock; PrevBlock = PrevBlock->NextWaitBlock )
332 if( PrevBlock != current )
333 RemoveEntryList( &(PrevBlock->WaitListEntry) );
334 current->Thread->WaitBlockList = 0;
338 DPRINT("WaitAll: Remove the current wait block only.\n");
340 PrevBlock = current->Thread->WaitBlockList;
341 if (PrevBlock == current)
343 DPRINT( "WaitAll: Current block is list head.\n" );
344 current->Thread->WaitBlockList = current->NextWaitBlock;
348 DPRINT( "WaitAll: Current block is not list head.\n" );
349 while ( PrevBlock && PrevBlock->NextWaitBlock != current)
351 PrevBlock = PrevBlock->NextWaitBlock;
355 PrevBlock->NextWaitBlock = current->NextWaitBlock;
358 current->NextWaitBlock = NULL;
359 /* if the last block is the timeout block then remove this block */
360 PrevBlock = current->Thread->WaitBlockList;
361 if (PrevBlock == ¤t->Thread->WaitBlock[3] && PrevBlock->NextWaitBlock == NULL)
363 RemoveEntryList(¤t->Thread->WaitBlock[3].WaitListEntry);
364 current->Thread->WaitBlockList = NULL;
368 DPRINT("Waking %x\n",current->Thread);
369 KiSideEffectsBeforeWake(hdr, current->Thread, &Abandoned);
370 Status = current->WaitKey;
372 Status += STATUS_ABANDONED_WAIT_0;
373 if (current->Thread->WaitBlockList == NULL)
375 PsUnblockThread(CONTAINING_RECORD(current->Thread,ETHREAD,Tcb),
381 BOOLEAN KeDispatcherObjectWake(DISPATCHER_HEADER* hdr)
383 * FUNCTION: Wake threads waiting on a dispatcher object
384 * NOTE: The exact semantics of waking are dependant on the type of object
389 DPRINT("Entering KeDispatcherObjectWake(hdr %x)\n",hdr);
390 // DPRINT("hdr->WaitListHead %x hdr->WaitListHead.Flink %x\n",
391 // &hdr->WaitListHead,hdr->WaitListHead.Flink);
392 DPRINT("hdr->Type %x\n",hdr->Type);
395 case InternalNotificationEvent:
396 return(KeDispatcherObjectWakeAll(hdr));
398 case InternalNotificationTimer:
399 return(KeDispatcherObjectWakeAll(hdr));
401 case InternalSynchronizationEvent:
402 return(KeDispatcherObjectWakeOne(hdr));
404 case InternalSynchronizationTimer:
405 return(KeDispatcherObjectWakeOne(hdr));
407 case InternalSemaphoreType:
408 DPRINT("hdr->SignalState %d\n", hdr->SignalState);
409 if(hdr->SignalState>0)
413 DPRINT("Waking one semaphore waiter\n");
414 Ret = KeDispatcherObjectWakeOne(hdr);
415 } while(hdr->SignalState > 0 && Ret) ;
420 case InternalProcessType:
421 return(KeDispatcherObjectWakeAll(hdr));
423 case InternalThreadType:
424 return(KeDispatcherObjectWakeAll(hdr));
426 case InternalMutexType:
427 return(KeDispatcherObjectWakeOne(hdr));
429 DbgPrint("Dispatcher object %x has unknown type %d\n", hdr, hdr->Type);
436 KeWaitForSingleObject(PVOID Object,
437 KWAIT_REASON WaitReason,
438 KPROCESSOR_MODE WaitMode,
440 PLARGE_INTEGER Timeout)
442 * FUNCTION: Puts the current thread into a wait state until the
443 * given dispatcher object is set to signalled
445 * Object = Object to wait on
446 * WaitReason = Reason for the wait (debugging aid)
447 * WaitMode = Can be KernelMode or UserMode, if UserMode then
448 * user-mode APCs can be delivered and the thread's
449 * stack can be paged out
450 * Altertable = Specifies if the wait is a alertable
451 * Timeout = Optional timeout value
455 DISPATCHER_HEADER* hdr = (DISPATCHER_HEADER *)Object;
456 PKTHREAD CurrentThread;
461 CurrentThread = KeGetCurrentThread();
462 WaitIrql = KeGetCurrentIrql();
466 * FIXME: Check for zero timeout
470 KeInitializeTimer(&CurrentThread->Timer);
471 KeSetTimer(&CurrentThread->Timer, *Timeout, NULL);
476 KeAcquireDispatcherDatabaseLock(FALSE);
479 * If we are going to wait alertably and a user apc is pending
482 if (Alertable && KiTestAlert())
484 KeReleaseDispatcherDatabaseLock(FALSE);
485 return(STATUS_USER_APC);
489 * If the object is signalled
491 if (KiIsObjectSignalled(hdr, CurrentThread, &Abandoned))
495 KeCancelTimer(&CurrentThread->Timer);
497 KeReleaseDispatcherDatabaseLock(FALSE);
498 if (Abandoned == TRUE)
499 return(STATUS_ABANDONED_WAIT_0);
500 return(STATUS_WAIT_0);
504 * Check if we have already timed out
506 if (Timeout != NULL &&
507 KiIsObjectSignalled(&CurrentThread->Timer.Header, CurrentThread, NULL))
509 KeCancelTimer(&CurrentThread->Timer);
510 KeReleaseDispatcherDatabaseLock(FALSE);
511 return(STATUS_TIMEOUT);
517 CurrentThread->WaitStatus = STATUS_UNSUCCESSFUL;
518 /* Append wait block to the KTHREAD wait block list */
519 CurrentThread->WaitBlockList = &CurrentThread->WaitBlock[0];
520 CurrentThread->WaitBlock[0].Object = Object;
521 CurrentThread->WaitBlock[0].Thread = CurrentThread;
522 CurrentThread->WaitBlock[0].WaitKey = STATUS_WAIT_0;
523 CurrentThread->WaitBlock[0].WaitType = WaitAny;
524 InsertTailList(&hdr->WaitListHead,
525 &CurrentThread->WaitBlock[0].WaitListEntry);
528 CurrentThread->WaitBlock[0].NextWaitBlock =
529 &CurrentThread->WaitBlock[3];
530 CurrentThread->WaitBlock[3].Object = (PVOID)&CurrentThread->Timer;
531 CurrentThread->WaitBlock[3].Thread = CurrentThread;
532 CurrentThread->WaitBlock[3].WaitKey = STATUS_TIMEOUT;
533 CurrentThread->WaitBlock[3].WaitType = WaitAny;
534 CurrentThread->WaitBlock[3].NextWaitBlock = NULL;
535 InsertTailList(&CurrentThread->Timer.Header.WaitListHead,
536 &CurrentThread->WaitBlock[3].WaitListEntry);
540 CurrentThread->WaitBlock[0].NextWaitBlock = NULL;
542 PsBlockThread(&Status, (UCHAR)Alertable, WaitMode, TRUE, WaitIrql);
543 } while (Status == STATUS_KERNEL_APC);
547 KeCancelTimer(&CurrentThread->Timer);
550 DPRINT("Returning from KeWaitForSingleObject()\n");
556 KeWaitForMultipleObjects(ULONG Count,
559 KWAIT_REASON WaitReason,
560 KPROCESSOR_MODE WaitMode,
562 PLARGE_INTEGER Timeout,
563 PKWAIT_BLOCK WaitBlockArray)
565 DISPATCHER_HEADER* hdr;
567 PKTHREAD CurrentThread;
574 DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
575 "PsGetCurrentThread() %x\n",Count,Object,PsGetCurrentThread());
578 CurrentThread = KeGetCurrentThread();
579 WaitIrql = KeGetCurrentIrql();
582 * Work out where we are going to put the wait blocks
584 if (WaitBlockArray == NULL)
586 if (Count > THREAD_WAIT_OBJECTS)
588 DbgPrint("(%s:%d) Too many objects!\n",
590 return(STATUS_UNSUCCESSFUL);
592 WaitBlockArray = &CurrentThread->WaitBlock[0];
596 if (Count > EX_MAXIMUM_WAIT_OBJECTS)
598 DbgPrint("(%s:%d) Too many objects!\n",
600 return(STATUS_UNSUCCESSFUL);
605 * Set up the timeout if required
609 KeInitializeTimer(&CurrentThread->Timer);
610 KeSetTimer(&CurrentThread->Timer, *Timeout, NULL);
615 KeAcquireDispatcherDatabaseLock(FALSE);
618 * If we are going to wait alertably and a user apc is pending
621 if (Alertable && KiTestAlert())
623 KeReleaseDispatcherDatabaseLock(FALSE);
624 return(STATUS_USER_APC);
628 * Check if the wait is already satisfied
630 for (i = 0; i < Count; i++)
632 hdr = (DISPATCHER_HEADER *)Object[i];
634 if (KiIsObjectSignalled(hdr, CurrentThread, &Abandoned))
638 if (WaitType == WaitAny)
642 KeCancelTimer(&CurrentThread->Timer);
644 KeReleaseDispatcherDatabaseLock(FALSE);
645 DPRINT("One object is already signaled!\n");
646 if (Abandoned == TRUE)
647 return(STATUS_ABANDONED_WAIT_0 + i);
648 return(STATUS_WAIT_0 + i);
653 if ((WaitType == WaitAll) && (CountSignaled == Count))
657 KeCancelTimer(&CurrentThread->Timer);
659 KeReleaseDispatcherDatabaseLock(FALSE);
660 DPRINT("All objects are already signaled!\n");
661 return(STATUS_WAIT_0);
665 * Check if we have already timed out
667 if (Timeout != NULL &&
668 KiIsObjectSignalled(&CurrentThread->Timer.Header, CurrentThread, NULL))
670 KeCancelTimer(&CurrentThread->Timer);
671 KeReleaseDispatcherDatabaseLock(FALSE);
672 return(STATUS_TIMEOUT);
675 /* Append wait block to the KTHREAD wait block list */
676 CurrentThread->WaitBlockList = blk = WaitBlockArray;
681 CurrentThread->WaitStatus = STATUS_UNSUCCESSFUL;
682 for (i = 0; i < Count; i++)
684 hdr = (DISPATCHER_HEADER *)Object[i];
686 blk->Object = Object[i];
687 blk->Thread = CurrentThread;
688 blk->WaitKey = STATUS_WAIT_0 + i;
689 blk->WaitType = WaitType;
690 if (i == (Count - 1))
694 blk->NextWaitBlock = &CurrentThread->WaitBlock[3];
698 blk->NextWaitBlock = NULL;
703 blk->NextWaitBlock = blk + 1;
706 InsertTailList(&hdr->WaitListHead, &blk->WaitListEntry);
708 blk = blk->NextWaitBlock;
712 CurrentThread->WaitBlock[3].Object = (PVOID)&CurrentThread->Timer;
713 CurrentThread->WaitBlock[3].Thread = CurrentThread;
714 CurrentThread->WaitBlock[3].WaitKey = STATUS_TIMEOUT;
715 CurrentThread->WaitBlock[3].WaitType = WaitAny;
716 CurrentThread->WaitBlock[3].NextWaitBlock = NULL;
717 InsertTailList(&CurrentThread->Timer.Header.WaitListHead,
718 &CurrentThread->WaitBlock[3].WaitListEntry);
721 PsBlockThread(&Status, Alertable, WaitMode, TRUE, WaitIrql);
722 } while(Status == STATUS_KERNEL_APC);
726 KeCancelTimer(&CurrentThread->Timer);
729 DPRINT("Returning from KeWaitForMultipleObjects()\n");
733 VOID KeInitializeDispatcher(VOID)
735 KeInitializeSpinLock(&DispatcherDatabaseLock);
739 NtWaitForMultipleObjects(IN ULONG Count,
742 IN BOOLEAN Alertable,
743 IN PLARGE_INTEGER Time)
745 KWAIT_BLOCK WaitBlockArray[EX_MAXIMUM_WAIT_OBJECTS];
746 PVOID ObjectPtrArray[EX_MAXIMUM_WAIT_OBJECTS];
750 DPRINT("NtWaitForMultipleObjects(Count %lu Object[] %x, Alertable %d, "
751 "Time %x)\n", Count,Object,Alertable,Time);
753 if (Count > EX_MAXIMUM_WAIT_OBJECTS)
754 return STATUS_UNSUCCESSFUL;
756 /* reference all objects */
757 for (i = 0; i < Count; i++)
759 Status = ObReferenceObjectByHandle(Object[i],
765 if (Status != STATUS_SUCCESS)
767 /* dereference all referenced objects */
768 for (j = 0; j < i; j++)
770 ObDereferenceObject(ObjectPtrArray[j]);
777 Status = KeWaitForMultipleObjects(Count,
786 /* dereference all objects */
787 for (i = 0; i < Count; i++)
789 ObDereferenceObject(ObjectPtrArray[i]);
797 NtWaitForSingleObject(IN HANDLE Object,
798 IN BOOLEAN Alertable,
799 IN PLARGE_INTEGER Time)
804 DPRINT("NtWaitForSingleObject(Object %x, Alertable %d, Time %x)\n",
805 Object,Alertable,Time);
807 Status = ObReferenceObjectByHandle(Object,
813 if (!NT_SUCCESS(Status))
818 Status = KeWaitForSingleObject(ObjectPtr,
824 ObDereferenceObject(ObjectPtr);
831 NtSignalAndWaitForSingleObject(IN HANDLE SignalObject,
832 IN HANDLE WaitObject,
833 IN BOOLEAN Alertable,
834 IN PLARGE_INTEGER Time)
836 KPROCESSOR_MODE ProcessorMode;
837 DISPATCHER_HEADER* hdr;
842 ProcessorMode = ExGetPreviousMode();
843 Status = ObReferenceObjectByHandle(SignalObject,
849 if (!NT_SUCCESS(Status))
854 Status = ObReferenceObjectByHandle(WaitObject,
860 if (!NT_SUCCESS(Status))
862 ObDereferenceObject(SignalObj);
866 hdr = (DISPATCHER_HEADER *)SignalObj;
869 case InternalNotificationEvent:
870 case InternalSynchronizationEvent:
871 KeSetEvent(SignalObj,
876 case InternalMutexType:
877 KeReleaseMutex(SignalObj,
881 case InternalSemaphoreType:
882 KeReleaseSemaphore(SignalObj,
889 ObDereferenceObject(SignalObj);
890 ObDereferenceObject(WaitObj);
891 return STATUS_OBJECT_TYPE_MISMATCH;
894 Status = KeWaitForSingleObject(WaitObj,
900 ObDereferenceObject(SignalObj);
901 ObDereferenceObject(WaitObj);
906 #endif /* LIBCAPTIVE */