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;
287 KiSideEffectsBeforeWake(hdr, current->Thread, &Abandoned);
288 Status = current->WaitKey;
290 Status += STATUS_ABANDONED_WAIT_0;
291 if (current->Thread->WaitBlockList == NULL)
293 PsUnblockThread(CONTAINING_RECORD(current->Thread,ETHREAD,Tcb),
300 static BOOLEAN KeDispatcherObjectWakeOne(DISPATCHER_HEADER* hdr)
302 PKWAIT_BLOCK current;
303 PLIST_ENTRY current_entry;
304 PKWAIT_BLOCK PrevBlock;
308 DPRINT("KeDispatcherObjectWakeOn(hdr %x)\n",hdr);
309 DPRINT("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
310 hdr->WaitListHead.Flink,hdr->WaitListHead.Blink);
311 if (IsListEmpty(&(hdr->WaitListHead)))
315 current_entry = RemoveHeadList(&(hdr->WaitListHead));
316 current = CONTAINING_RECORD(current_entry,KWAIT_BLOCK,
318 DPRINT("current_entry %x current %x\n",current_entry,current);
320 if (current->WaitType == WaitAny)
322 DPRINT("WaitAny: Remove all wait blocks.\n");
323 for( PrevBlock = current->Thread->WaitBlockList; PrevBlock; PrevBlock = PrevBlock->NextWaitBlock )
324 if( PrevBlock != current )
325 RemoveEntryList( &(PrevBlock->WaitListEntry) );
326 current->Thread->WaitBlockList = 0;
330 DPRINT("WaitAll: Remove the current wait block only.\n");
332 PrevBlock = current->Thread->WaitBlockList;
333 if (PrevBlock == current)
335 DPRINT( "WaitAll: Current block is list head.\n" );
336 current->Thread->WaitBlockList = current->NextWaitBlock;
340 DPRINT( "WaitAll: Current block is not list head.\n" );
341 while ( PrevBlock && PrevBlock->NextWaitBlock != current)
343 PrevBlock = PrevBlock->NextWaitBlock;
347 PrevBlock->NextWaitBlock = current->NextWaitBlock;
352 DPRINT("Waking %x\n",current->Thread);
353 KiSideEffectsBeforeWake(hdr, current->Thread, &Abandoned);
354 Status = current->WaitKey;
356 Status += STATUS_ABANDONED_WAIT_0;
357 PsUnblockThread(CONTAINING_RECORD(current->Thread, ETHREAD, Tcb),
362 BOOLEAN KeDispatcherObjectWake(DISPATCHER_HEADER* hdr)
364 * FUNCTION: Wake threads waiting on a dispatcher object
365 * NOTE: The exact semantics of waking are dependant on the type of object
370 DPRINT("Entering KeDispatcherObjectWake(hdr %x)\n",hdr);
371 // DPRINT("hdr->WaitListHead %x hdr->WaitListHead.Flink %x\n",
372 // &hdr->WaitListHead,hdr->WaitListHead.Flink);
373 DPRINT("hdr->Type %x\n",hdr->Type);
376 case InternalNotificationEvent:
377 return(KeDispatcherObjectWakeAll(hdr));
379 case InternalNotificationTimer:
380 return(KeDispatcherObjectWakeAll(hdr));
382 case InternalSynchronizationEvent:
383 return(KeDispatcherObjectWakeOne(hdr));
385 case InternalSynchronizationTimer:
386 return(KeDispatcherObjectWakeOne(hdr));
388 case InternalSemaphoreType:
389 DPRINT("hdr->SignalState %d\n", hdr->SignalState);
390 if(hdr->SignalState>0)
394 DPRINT("Waking one semaphore waiter\n");
395 Ret = KeDispatcherObjectWakeOne(hdr);
396 } while(hdr->SignalState > 0 && Ret) ;
401 case InternalProcessType:
402 return(KeDispatcherObjectWakeAll(hdr));
404 case InternalThreadType:
405 return(KeDispatcherObjectWakeAll(hdr));
407 case InternalMutexType:
408 return(KeDispatcherObjectWakeOne(hdr));
410 DbgPrint("Dispatcher object %x has unknown type %d\n", hdr, hdr->Type);
417 KeWaitForSingleObject(PVOID Object,
418 KWAIT_REASON WaitReason,
419 KPROCESSOR_MODE WaitMode,
421 PLARGE_INTEGER Timeout)
423 * FUNCTION: Puts the current thread into a wait state until the
424 * given dispatcher object is set to signalled
426 * Object = Object to wait on
427 * WaitReason = Reason for the wait (debugging aid)
428 * WaitMode = Can be KernelMode or UserMode, if UserMode then
429 * user-mode APCs can be delivered and the thread's
430 * stack can be paged out
431 * Altertable = Specifies if the wait is a alertable
432 * Timeout = Optional timeout value
436 DISPATCHER_HEADER* hdr = (DISPATCHER_HEADER *)Object;
437 PKTHREAD CurrentThread;
442 CurrentThread = KeGetCurrentThread();
443 WaitIrql = KeGetCurrentIrql();
447 * FIXME: Check for zero timeout
451 KeInitializeTimer(&CurrentThread->Timer);
452 KeSetTimer(&CurrentThread->Timer, *Timeout, NULL);
457 KeAcquireDispatcherDatabaseLock(FALSE);
460 * If we are going to wait alertably and a user apc is pending
463 if (Alertable && KiTestAlert())
465 KeReleaseDispatcherDatabaseLock(FALSE);
466 return(STATUS_USER_APC);
470 * If the object is signalled
472 if (KiIsObjectSignalled(hdr, CurrentThread, &Abandoned))
474 KeReleaseDispatcherDatabaseLock(FALSE);
477 KeCancelTimer(&KeGetCurrentThread()->Timer);
479 if (Abandoned == TRUE)
480 return(STATUS_ABANDONED_WAIT_0);
481 return(STATUS_WAIT_0);
485 * Check if we have already timed out
487 if (Timeout != NULL &&
488 KiIsObjectSignalled(&CurrentThread->Timer.Header, CurrentThread, NULL))
490 KeReleaseDispatcherDatabaseLock(FALSE);
493 KeCancelTimer(&KeGetCurrentThread()->Timer);
495 return(STATUS_TIMEOUT);
501 CurrentThread->WaitStatus = STATUS_UNSUCCESSFUL;
502 /* Append wait block to the KTHREAD wait block list */
503 CurrentThread->WaitBlockList = &CurrentThread->WaitBlock[0];
504 CurrentThread->WaitBlock[0].Object = Object;
505 CurrentThread->WaitBlock[0].Thread = CurrentThread;
506 CurrentThread->WaitBlock[0].WaitKey = STATUS_WAIT_0;
507 CurrentThread->WaitBlock[0].WaitType = WaitAny;
508 InsertTailList(&hdr->WaitListHead,
509 &CurrentThread->WaitBlock[0].WaitListEntry);
512 CurrentThread->WaitBlock[0].NextWaitBlock =
513 &CurrentThread->WaitBlock[1];
514 CurrentThread->WaitBlock[1].Object = (PVOID)&CurrentThread->Timer;
515 CurrentThread->WaitBlock[1].Thread = CurrentThread;
516 CurrentThread->WaitBlock[1].WaitKey = STATUS_TIMEOUT;
517 CurrentThread->WaitBlock[1].WaitType = WaitAny;
518 CurrentThread->WaitBlock[1].NextWaitBlock = NULL;
519 InsertTailList(&CurrentThread->Timer.Header.WaitListHead,
520 &CurrentThread->WaitBlock[1].WaitListEntry);
524 CurrentThread->WaitBlock[0].NextWaitBlock = NULL;
526 PsBlockThread(&Status, (UCHAR)Alertable, WaitMode, TRUE, WaitIrql);
527 } while (Status == STATUS_KERNEL_APC);
531 KeCancelTimer(&KeGetCurrentThread()->Timer);
534 DPRINT("Returning from KeWaitForSingleObject()\n");
540 KeWaitForMultipleObjects(ULONG Count,
543 KWAIT_REASON WaitReason,
544 KPROCESSOR_MODE WaitMode,
546 PLARGE_INTEGER Timeout,
547 PKWAIT_BLOCK WaitBlockArray)
549 DISPATCHER_HEADER* hdr;
551 PKTHREAD CurrentThread;
558 DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
559 "PsGetCurrentThread() %x\n",Count,Object,PsGetCurrentThread());
562 CurrentThread = KeGetCurrentThread();
563 WaitIrql = KeGetCurrentIrql();
566 * Work out where we are going to put the wait blocks
568 if (WaitBlockArray == NULL)
570 if (Count > THREAD_WAIT_OBJECTS)
572 DbgPrint("(%s:%d) Too many objects!\n",
574 return(STATUS_UNSUCCESSFUL);
576 blk = &CurrentThread->WaitBlock[0];
580 if (Count > EX_MAXIMUM_WAIT_OBJECTS)
582 DbgPrint("(%s:%d) Too many objects!\n",
584 return(STATUS_UNSUCCESSFUL);
586 blk = WaitBlockArray;
590 * Set up the timeout if required
594 KeInitializeTimer(&CurrentThread->Timer);
595 KeSetTimer(&CurrentThread->Timer, *Timeout, NULL);
600 KeAcquireDispatcherDatabaseLock(FALSE);
603 * If we are going to wait alertably and a user apc is pending
606 if (Alertable && KiTestAlert())
608 KeReleaseDispatcherDatabaseLock(FALSE);
609 return(STATUS_USER_APC);
613 * Check if the wait is already satisfied
615 for (i = 0; i < Count; i++)
617 hdr = (DISPATCHER_HEADER *)Object[i];
619 if (KiIsObjectSignalled(hdr, CurrentThread, &Abandoned))
623 if (WaitType == WaitAny)
625 KeReleaseDispatcherDatabaseLock(FALSE);
626 DPRINT("One object is already signaled!\n");
627 if (Abandoned == TRUE)
628 return(STATUS_ABANDONED_WAIT_0 + i);
629 return(STATUS_WAIT_0 + i);
634 if ((WaitType == WaitAll) && (CountSignaled == Count))
636 KeReleaseDispatcherDatabaseLock(FALSE);
637 DPRINT("All objects are already signaled!\n");
638 return(STATUS_WAIT_0);
642 * Check if we have already timed out
644 if (Timeout != NULL &&
645 KiIsObjectSignalled(&CurrentThread->Timer.Header, CurrentThread, NULL))
647 KeReleaseDispatcherDatabaseLock(FALSE);
650 KeCancelTimer(&KeGetCurrentThread()->Timer);
652 return(STATUS_TIMEOUT);
655 /* Append wait block to the KTHREAD wait block list */
656 CurrentThread->WaitBlockList = blk;
661 for (i = 0; i < Count; i++)
663 hdr = (DISPATCHER_HEADER *)Object[i];
665 blk->Object = Object[i];
666 blk->Thread = CurrentThread;
667 blk->WaitKey = STATUS_WAIT_0 + i;
668 blk->WaitType = WaitType;
669 if (i == (Count - 1))
673 blk->NextWaitBlock = &CurrentThread->WaitBlock[3];
677 blk->NextWaitBlock = NULL;
682 blk->NextWaitBlock = blk + 1;
685 InsertTailList(&hdr->WaitListHead, &blk->WaitListEntry);
687 blk = blk->NextWaitBlock;
691 CurrentThread->WaitBlock[3].Object = (PVOID)&CurrentThread->Timer;
692 CurrentThread->WaitBlock[3].Thread = CurrentThread;
693 CurrentThread->WaitBlock[3].WaitKey = STATUS_TIMEOUT;
694 CurrentThread->WaitBlock[3].WaitType = WaitAny;
695 CurrentThread->WaitBlock[3].NextWaitBlock = NULL;
696 InsertTailList(&CurrentThread->Timer.Header.WaitListHead,
697 &CurrentThread->WaitBlock[3].WaitListEntry);
700 PsBlockThread(&Status, Alertable, WaitMode, TRUE, WaitIrql);
701 } while(Status == STATUS_KERNEL_APC);
705 KeCancelTimer(&KeGetCurrentThread()->Timer);
708 DPRINT("Returning from KeWaitForMultipleObjects()\n");
712 VOID KeInitializeDispatcher(VOID)
714 KeInitializeSpinLock(&DispatcherDatabaseLock);
718 NtWaitForMultipleObjects(IN ULONG Count,
721 IN BOOLEAN Alertable,
722 IN PLARGE_INTEGER Time)
724 KWAIT_BLOCK WaitBlockArray[EX_MAXIMUM_WAIT_OBJECTS];
725 PVOID ObjectPtrArray[EX_MAXIMUM_WAIT_OBJECTS];
729 DPRINT("NtWaitForMultipleObjects(Count %lu Object[] %x, Alertable %d, "
730 "Time %x)\n", Count,Object,Alertable,Time);
732 if (Count > EX_MAXIMUM_WAIT_OBJECTS)
733 return STATUS_UNSUCCESSFUL;
735 /* reference all objects */
736 for (i = 0; i < Count; i++)
738 Status = ObReferenceObjectByHandle(Object[i],
744 if (Status != STATUS_SUCCESS)
746 /* dereference all referenced objects */
747 for (j = 0; j < i; j++)
749 ObDereferenceObject(ObjectPtrArray[j]);
756 Status = KeWaitForMultipleObjects(Count,
765 /* dereference all objects */
766 for (i = 0; i < Count; i++)
768 ObDereferenceObject(ObjectPtrArray[i]);
776 NtWaitForSingleObject(IN HANDLE Object,
777 IN BOOLEAN Alertable,
778 IN PLARGE_INTEGER Time)
783 DPRINT("NtWaitForSingleObject(Object %x, Alertable %d, Time %x)\n",
784 Object,Alertable,Time);
786 Status = ObReferenceObjectByHandle(Object,
792 if (!NT_SUCCESS(Status))
797 Status = KeWaitForSingleObject(ObjectPtr,
803 ObDereferenceObject(ObjectPtr);
810 NtSignalAndWaitForSingleObject(IN HANDLE SignalObject,
811 IN HANDLE WaitObject,
812 IN BOOLEAN Alertable,
813 IN PLARGE_INTEGER Time)
815 KPROCESSOR_MODE ProcessorMode;
816 DISPATCHER_HEADER* hdr;
821 ProcessorMode = ExGetPreviousMode();
822 Status = ObReferenceObjectByHandle(SignalObject,
828 if (!NT_SUCCESS(Status))
833 Status = ObReferenceObjectByHandle(WaitObject,
839 if (!NT_SUCCESS(Status))
841 ObDereferenceObject(SignalObj);
845 hdr = (DISPATCHER_HEADER *)SignalObj;
848 case InternalNotificationEvent:
849 case InternalSynchronizationEvent:
850 KeSetEvent(SignalObj,
855 case InternalMutexType:
856 KeReleaseMutex(SignalObj,
860 case InternalSemaphoreType:
861 KeReleaseSemaphore(SignalObj,
868 ObDereferenceObject(SignalObj);
869 ObDereferenceObject(WaitObj);
870 return STATUS_OBJECT_TYPE_MISMATCH;
873 Status = KeWaitForSingleObject(WaitObj,
879 ObDereferenceObject(SignalObj);
880 ObDereferenceObject(WaitObj);
885 #endif /* LIBCAPTIVE */