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
;
33 #define KeDispatcherObjectWakeOne(hdr) KeDispatcherObjectWakeOneOrAll(hdr, FALSE)
34 #define KeDispatcherObjectWakeAll(hdr) KeDispatcherObjectWakeOneOrAll(hdr, TRUE)
36 /* FUNCTIONS *****************************************************************/
38 VOID
KeInitializeDispatcherHeader(DISPATCHER_HEADER
* Header
,
47 Header
->SignalState
= SignalState
;
48 InitializeListHead(&(Header
->WaitListHead
));
53 KeAcquireDispatcherDatabaseLock(VOID
)
55 * PURPOSE: Acquires the dispatcher database lock for the caller
60 DPRINT("KeAcquireDispatcherDatabaseLock()\n");
62 KeAcquireSpinLock (&DispatcherDatabaseLock
, &OldIrql
);
68 KeAcquireDispatcherDatabaseLockAtDpcLevel(VOID
)
70 * PURPOSE: Acquires the dispatcher database lock for the caller
73 DPRINT("KeAcquireDispatcherDatabaseLockAtDpcLevel()\n");
75 KeAcquireSpinLockAtDpcLevel (&DispatcherDatabaseLock
);
80 KeReleaseDispatcherDatabaseLock(KIRQL OldIrql
)
82 DPRINT("KeReleaseDispatcherDatabaseLock(OldIrql %x)\n",OldIrql
);
84 KeReleaseSpinLock(&DispatcherDatabaseLock
, OldIrql
);
89 KeReleaseDispatcherDatabaseLockFromDpcLevel(VOID
)
91 DPRINT("KeReleaseDispatcherDatabaseLock()\n");
93 KeReleaseSpinLockFromDpcLevel(&DispatcherDatabaseLock
);
98 KiSideEffectsBeforeWake(DISPATCHER_HEADER
* hdr
,
101 * FUNCTION: Perform side effects on object before a wait for a thread is
105 BOOLEAN Abandoned
= FALSE
;
109 case InternalSynchronizationEvent
:
110 hdr
->SignalState
= 0;
113 case InternalQueueType
:
114 case InternalSemaphoreType
:
118 case InternalProcessType
:
121 case InternalThreadType
:
124 case InternalNotificationEvent
:
127 case InternalSynchronizationTimer
:
128 hdr
->SignalState
= FALSE
;
131 case InternalNotificationTimer
:
134 case InternalMutexType
:
138 Mutex
= CONTAINING_RECORD(hdr
, KMUTEX
, Header
);
140 assert(hdr
->SignalState
<= 1);
141 if (hdr
->SignalState
== 0)
145 DPRINT("Thread == NULL!\n");
148 Abandoned
= Mutex
->Abandoned
;
150 InsertTailList(&Thread
->MutantListHead
, &Mutex
->MutantListEntry
);
151 Mutex
->OwnerThread
= Thread
;
152 Mutex
->Abandoned
= FALSE
;
158 DbgPrint("(%s:%d) Dispatcher object %x has unknown type\n", __FILE__
, __LINE__
, hdr
);
166 KiIsObjectSignalled(DISPATCHER_HEADER
* hdr
,
169 if (hdr
->Type
== InternalMutexType
)
173 Mutex
= CONTAINING_RECORD(hdr
, KMUTEX
, Header
);
175 assert(hdr
->SignalState
<= 1);
177 if ((hdr
->SignalState
< 1 && Mutex
->OwnerThread
== Thread
) || hdr
->SignalState
== 1)
187 if (hdr
->SignalState
<= 0)
197 VOID
KeRemoveAllWaitsThread(PETHREAD Thread
, NTSTATUS WaitStatus
, BOOL Unblock
)
199 PKWAIT_BLOCK WaitBlock
, PrevWaitBlock
;
200 BOOLEAN WasWaiting
= FALSE
;
203 OldIrql
= KeAcquireDispatcherDatabaseLock ();
205 WaitBlock
= (PKWAIT_BLOCK
)Thread
->Tcb
.WaitBlockList
;
206 if (WaitBlock
!= NULL
)
210 while (WaitBlock
!= NULL
)
212 if (WaitBlock
->WaitListEntry
.Flink
!= NULL
&& WaitBlock
->WaitListEntry
.Blink
!= NULL
)
214 RemoveEntryList (&WaitBlock
->WaitListEntry
);
215 WaitBlock
->WaitListEntry
.Flink
= WaitBlock
->WaitListEntry
.Blink
= NULL
;
217 PrevWaitBlock
= WaitBlock
;
218 WaitBlock
= WaitBlock
->NextWaitBlock
;
219 PrevWaitBlock
->NextWaitBlock
= NULL
;
221 Thread
->Tcb
.WaitBlockList
= NULL
;
223 if (WasWaiting
&& Unblock
)
225 PsUnblockThread(Thread
, &WaitStatus
);
228 KeReleaseDispatcherDatabaseLock (OldIrql
);
232 KeDispatcherObjectWakeOneOrAll(DISPATCHER_HEADER
* hdr
,
236 PKWAIT_BLOCK WaiterHead
;
237 PLIST_ENTRY EnumEntry
;
241 BOOLEAN WakedAny
= FALSE
;
243 DPRINT("KeDispatcherObjectWakeOnOrAll(hdr %x)\n", hdr
);
244 DPRINT ("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
245 hdr
->WaitListHead
.Flink
, hdr
->WaitListHead
.Blink
);
247 if (IsListEmpty(&hdr
->WaitListHead
))
252 //enum waiters for this dispatcher object
253 EnumEntry
= hdr
->WaitListHead
.Flink
;
254 while (EnumEntry
!= &hdr
->WaitListHead
&& (WakeAll
|| !WakedAny
))
256 WaiterHead
= CONTAINING_RECORD(EnumEntry
, KWAIT_BLOCK
, WaitListEntry
);
257 DPRINT("current_entry %x current %x\n", EnumEntry
, WaiterHead
);
258 EnumEntry
= EnumEntry
->Flink
;
259 assert(WaiterHead
->Thread
!= NULL
);
260 assert(WaiterHead
->Thread
->WaitBlockList
!= NULL
);
264 if (WaiterHead
->WaitType
== WaitAny
)
266 DPRINT("WaitAny: Remove all wait blocks.\n");
267 for (Waiter
= WaiterHead
->Thread
->WaitBlockList
; Waiter
; Waiter
= Waiter
->NextWaitBlock
)
269 if (Waiter
->WaitListEntry
.Flink
!= NULL
&& Waiter
->WaitListEntry
.Blink
!= NULL
)
271 RemoveEntryList(&Waiter
->WaitListEntry
);
272 Waiter
->WaitListEntry
.Flink
= Waiter
->WaitListEntry
.Blink
= NULL
;
276 WaiterHead
->Thread
->WaitBlockList
= NULL
;
279 * If a WakeAll KiSideEffectsBeforeWake(hdr,.. will be called several times,
280 * but thats ok since WakeAll objects has no sideeffects.
282 Abandoned
= KiSideEffectsBeforeWake(hdr
, WaiterHead
->Thread
) ? TRUE
: Abandoned
;
286 DPRINT("WaitAll: All WaitAll objects must be signaled.\n");
290 //all WaitAll obj. for thread need to be signaled to satisfy a wake
291 for (Waiter
= WaiterHead
->Thread
->WaitBlockList
; Waiter
; Waiter
= Waiter
->NextWaitBlock
)
293 //no need to check hdr since it has to be signaled
294 if (Waiter
->WaitType
== WaitAll
&& Waiter
->Object
!= hdr
)
296 if (!KiIsObjectSignalled(Waiter
->Object
, Waiter
->Thread
))
306 for (Waiter
= WaiterHead
->Thread
->WaitBlockList
; Waiter
; Waiter
= Waiter
->NextWaitBlock
)
308 if (Waiter
->WaitListEntry
.Flink
!= NULL
&& Waiter
->WaitListEntry
.Blink
!= NULL
)
310 RemoveEntryList(&Waiter
->WaitListEntry
);
311 Waiter
->WaitListEntry
.Flink
= Waiter
->WaitListEntry
.Blink
= NULL
;
314 if (Waiter
->WaitType
== WaitAll
)
316 Abandoned
= KiSideEffectsBeforeWake(Waiter
->Object
, Waiter
->Thread
)
320 //no WaitAny objects can possibly be signaled since we are here
321 assert(!(Waiter
->WaitType
== WaitAny
322 && KiIsObjectSignalled(Waiter
->Object
, Waiter
->Thread
)));
325 WaiterHead
->Thread
->WaitBlockList
= NULL
;
329 if (WaiterHead
->Thread
->WaitBlockList
== NULL
)
331 Status
= WaiterHead
->WaitKey
;
334 DPRINT("Abandoned mutex among objects");
335 Status
+= STATUS_ABANDONED_WAIT_0
;
339 DPRINT("Waking %x status = %x\n", WaiterHead
->Thread
, Status
);
340 PsUnblockThread(CONTAINING_RECORD(WaiterHead
->Thread
, ETHREAD
, Tcb
), &Status
);
348 BOOLEAN
KeDispatcherObjectWake(DISPATCHER_HEADER
* hdr
)
350 * FUNCTION: Wake threads waiting on a dispatcher object
351 * NOTE: The exact semantics of waking are dependant on the type of object
356 DPRINT("Entering KeDispatcherObjectWake(hdr %x)\n",hdr
);
357 // DPRINT("hdr->WaitListHead %x hdr->WaitListHead.Flink %x\n",
358 // &hdr->WaitListHead,hdr->WaitListHead.Flink);
359 DPRINT("hdr->Type %x\n",hdr
->Type
);
362 case InternalNotificationEvent
:
363 return(KeDispatcherObjectWakeAll(hdr
));
365 case InternalNotificationTimer
:
366 return(KeDispatcherObjectWakeAll(hdr
));
368 case InternalSynchronizationEvent
:
369 return(KeDispatcherObjectWakeOne(hdr
));
371 case InternalSynchronizationTimer
:
372 return(KeDispatcherObjectWakeOne(hdr
));
374 case InternalQueueType
:
375 case InternalSemaphoreType
:
376 DPRINT("hdr->SignalState %d\n", hdr
->SignalState
);
377 if(hdr
->SignalState
>0)
381 DPRINT("Waking one semaphore waiter\n");
382 Ret
= KeDispatcherObjectWakeOne(hdr
);
383 } while(hdr
->SignalState
> 0 && Ret
) ;
388 case InternalProcessType
:
389 return(KeDispatcherObjectWakeAll(hdr
));
391 case InternalThreadType
:
392 return(KeDispatcherObjectWakeAll(hdr
));
394 case InternalMutexType
:
395 return(KeDispatcherObjectWakeOne(hdr
));
397 DbgPrint("Dispatcher object %x has unknown type %d\n", hdr
, hdr
->Type
);
407 KeWaitForSingleObject(PVOID Object
,
408 KWAIT_REASON WaitReason
,
409 KPROCESSOR_MODE WaitMode
,
411 PLARGE_INTEGER Timeout
)
413 * FUNCTION: Puts the current thread into a wait state until the
414 * given dispatcher object is set to signalled
416 * Object = Object to wait on
417 * WaitReason = Reason for the wait (debugging aid)
418 * WaitMode = Can be KernelMode or UserMode, if UserMode then
419 * user-mode APCs can be delivered and the thread's
420 * stack can be paged out
421 * Altertable = Specifies if the wait is a alertable
422 * Timeout = Optional timeout value
426 return KeWaitForMultipleObjects(1,
439 KiGetWaitableObjectFromObject(PVOID Object
)
441 //special case when waiting on file objects
442 if ( ((PDISPATCHER_HEADER
)Object
)->Type
== InternalFileType
)
444 return &((PFILE_OBJECT
)Object
)->Event
;
455 KeWaitForMultipleObjects(ULONG Count
,
458 KWAIT_REASON WaitReason
,
459 KPROCESSOR_MODE WaitMode
,
461 PLARGE_INTEGER Timeout
,
462 PKWAIT_BLOCK WaitBlockArray
)
464 DISPATCHER_HEADER
*hdr
;
466 PKTHREAD CurrentThread
;
474 DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
475 "PsGetCurrentThread() %x\n", Count
, Object
, PsGetCurrentThread());
477 CurrentThread
= KeGetCurrentThread();
478 WaitIrql
= KeGetCurrentIrql();
481 * Work out where we are going to put the wait blocks
483 if (WaitBlockArray
== NULL
)
485 if (Count
> THREAD_WAIT_OBJECTS
)
487 DPRINT("(%s:%d) Too many objects!\n", __FILE__
, __LINE__
);
488 return (STATUS_UNSUCCESSFUL
);
490 WaitBlockArray
= &CurrentThread
->WaitBlock
[0];
494 if (Count
> EX_MAXIMUM_WAIT_OBJECTS
)
496 DPRINT("(%s:%d) Too many objects!\n", __FILE__
, __LINE__
);
497 return (STATUS_UNSUCCESSFUL
);
502 * Set up the timeout if required
504 if (Timeout
!= NULL
&& Timeout
->QuadPart
!= 0)
506 KeInitializeTimer(&CurrentThread
->Timer
);
507 KeSetTimer(&CurrentThread
->Timer
, *Timeout
, NULL
);
512 if (CurrentThread
->WaitNext
)
514 OldIrql
= CurrentThread
->WaitIrql
;
515 CurrentThread
->WaitNext
= 0;
516 CurrentThread
->WaitIrql
= PASSIVE_LEVEL
;
520 OldIrql
= KeAcquireDispatcherDatabaseLock ();
524 * If we are going to wait alertably and a user apc is pending
527 if (Alertable
&& KiTestAlert())
529 KeReleaseDispatcherDatabaseLock(OldIrql
);
530 return (STATUS_USER_APC
);
534 * Check if the wait is (already) satisfied
538 for (i
= 0; i
< Count
; i
++)
540 hdr
= (DISPATCHER_HEADER
*) KiGetWaitableObjectFromObject(Object
[i
]);
542 if (KiIsObjectSignalled(hdr
, CurrentThread
))
546 if (WaitType
== WaitAny
)
548 Abandoned
= KiSideEffectsBeforeWake(hdr
, CurrentThread
) ? TRUE
: Abandoned
;
550 if (Timeout
!= NULL
&& Timeout
->QuadPart
!= 0)
552 KeCancelTimer(&CurrentThread
->Timer
);
555 KeReleaseDispatcherDatabaseLock(OldIrql
);
557 DPRINT("One object is (already) signaled!\n");
558 if (Abandoned
== TRUE
)
560 return (STATUS_ABANDONED_WAIT_0
+ i
);
563 return (STATUS_WAIT_0
+ i
);
569 if ((WaitType
== WaitAll
) && (CountSignaled
== Count
))
571 for (i
= 0; i
< Count
; i
++)
573 hdr
= (DISPATCHER_HEADER
*) KiGetWaitableObjectFromObject(Object
[i
]);
574 Abandoned
= KiSideEffectsBeforeWake(hdr
, CurrentThread
) ? TRUE
: Abandoned
;
577 if (Timeout
!= NULL
&& Timeout
->QuadPart
!= 0)
579 KeCancelTimer(&CurrentThread
->Timer
);
582 KeReleaseDispatcherDatabaseLock(OldIrql
);
583 DPRINT("All objects are (already) signaled!\n");
585 if (Abandoned
== TRUE
)
587 return (STATUS_ABANDONED_WAIT_0
);
590 return (STATUS_WAIT_0
);
593 //zero timeout is used for testing if the object(s) can be immediately acquired
594 if (Timeout
!= NULL
&& Timeout
->QuadPart
== 0)
596 KeReleaseDispatcherDatabaseLock(OldIrql
);
597 return STATUS_TIMEOUT
;
601 * Check if we have already timed out
603 if (Timeout
!= NULL
&& KiIsObjectSignalled(&CurrentThread
->Timer
.Header
, CurrentThread
))
605 KiSideEffectsBeforeWake(&CurrentThread
->Timer
.Header
, CurrentThread
);
606 KeCancelTimer(&CurrentThread
->Timer
);
607 KeReleaseDispatcherDatabaseLock(OldIrql
);
608 return (STATUS_TIMEOUT
);
611 /* Append wait block to the KTHREAD wait block list */
612 CurrentThread
->WaitBlockList
= blk
= WaitBlockArray
;
617 CurrentThread
->WaitStatus
= STATUS_UNSUCCESSFUL
;
619 for (i
= 0; i
< Count
; i
++)
621 hdr
= (DISPATCHER_HEADER
*) KiGetWaitableObjectFromObject(Object
[i
]);
623 blk
->Object
= KiGetWaitableObjectFromObject(Object
[i
]);
624 blk
->Thread
= CurrentThread
;
625 blk
->WaitKey
= STATUS_WAIT_0
+ i
;
626 blk
->WaitType
= WaitType
;
628 if (i
== (Count
- 1))
632 blk
->NextWaitBlock
= &CurrentThread
->WaitBlock
[3];
636 blk
->NextWaitBlock
= NULL
;
641 blk
->NextWaitBlock
= blk
+ 1;
645 * add wait block to disp. obj. wait list
646 * Use FIFO for all waits except for queues which use LIFO
648 if (WaitReason
== WrQueue
)
650 InsertHeadList(&hdr
->WaitListHead
, &blk
->WaitListEntry
);
654 InsertTailList(&hdr
->WaitListHead
, &blk
->WaitListEntry
);
657 blk
= blk
->NextWaitBlock
;
662 CurrentThread
->WaitBlock
[3].Object
= (PVOID
) & CurrentThread
->Timer
;
663 CurrentThread
->WaitBlock
[3].Thread
= CurrentThread
;
664 CurrentThread
->WaitBlock
[3].WaitKey
= STATUS_TIMEOUT
;
665 CurrentThread
->WaitBlock
[3].WaitType
= WaitAny
;
666 CurrentThread
->WaitBlock
[3].NextWaitBlock
= NULL
;
668 InsertTailList(&CurrentThread
->Timer
.Header
.WaitListHead
,
669 &CurrentThread
->WaitBlock
[3].WaitListEntry
);
673 if (CurrentThread
->Queue
)
675 CurrentThread
->Queue
->CurrentCount
--;
676 if (WaitReason
!= WrQueue
&& CurrentThread
->Queue
->CurrentCount
< CurrentThread
->Queue
->MaximumCount
&&
677 !IsListEmpty(&CurrentThread
->Queue
->EntryListHead
))
679 KeDispatcherObjectWake(&CurrentThread
->Queue
->Header
);
683 PsBlockThread(&Status
, Alertable
, WaitMode
, TRUE
, WaitIrql
, WaitReason
);
686 if (CurrentThread
->Queue
)
688 CurrentThread
->Queue
->CurrentCount
++;
693 while (Status
== STATUS_KERNEL_APC
);
697 KeCancelTimer(&CurrentThread
->Timer
);
700 DPRINT("Returning from KeWaitForMultipleObjects()\n");
704 VOID
KeInitializeDispatcher(VOID
)
706 KeInitializeSpinLock(&DispatcherDatabaseLock
);
710 NtWaitForMultipleObjects(IN ULONG Count
,
712 IN WAIT_TYPE WaitType
,
713 IN BOOLEAN Alertable
,
714 IN PLARGE_INTEGER Time
)
716 KWAIT_BLOCK WaitBlockArray
[EX_MAXIMUM_WAIT_OBJECTS
];
717 PVOID ObjectPtrArray
[EX_MAXIMUM_WAIT_OBJECTS
];
720 KPROCESSOR_MODE WaitMode
;
722 DPRINT("NtWaitForMultipleObjects(Count %lu Object[] %x, Alertable %d, "
723 "Time %x)\n", Count
,Object
,Alertable
,Time
);
725 if (Count
> EX_MAXIMUM_WAIT_OBJECTS
)
726 return STATUS_UNSUCCESSFUL
;
728 WaitMode
= ExGetPreviousMode();
730 /* reference all objects */
731 for (i
= 0; i
< Count
; i
++)
733 Status
= ObReferenceObjectByHandle(Object
[i
],
739 if (Status
!= STATUS_SUCCESS
)
741 /* dereference all referenced objects */
742 for (j
= 0; j
< i
; j
++)
744 ObDereferenceObject(ObjectPtrArray
[j
]);
751 Status
= KeWaitForMultipleObjects(Count
,
760 /* dereference all objects */
761 for (i
= 0; i
< Count
; i
++)
763 ObDereferenceObject(ObjectPtrArray
[i
]);
774 NtWaitForSingleObject(IN HANDLE Object
,
775 IN BOOLEAN Alertable
,
776 IN PLARGE_INTEGER Time
)
780 KPROCESSOR_MODE WaitMode
;
782 DPRINT("NtWaitForSingleObject(Object %x, Alertable %d, Time %x)\n",
783 Object
,Alertable
,Time
);
785 WaitMode
= ExGetPreviousMode();
787 Status
= ObReferenceObjectByHandle(Object
,
793 if (!NT_SUCCESS(Status
))
798 Status
= KeWaitForSingleObject(ObjectPtr
,
804 ObDereferenceObject(ObjectPtr
);
811 NtSignalAndWaitForSingleObject(IN HANDLE SignalObject
,
812 IN HANDLE WaitObject
,
813 IN BOOLEAN Alertable
,
814 IN PLARGE_INTEGER Time
)
816 KPROCESSOR_MODE WaitMode
;
817 DISPATCHER_HEADER
* hdr
;
822 WaitMode
= ExGetPreviousMode();
823 Status
= ObReferenceObjectByHandle(SignalObject
,
829 if (!NT_SUCCESS(Status
))
834 Status
= ObReferenceObjectByHandle(WaitObject
,
840 if (!NT_SUCCESS(Status
))
842 ObDereferenceObject(SignalObj
);
846 hdr
= (DISPATCHER_HEADER
*)SignalObj
;
849 case InternalNotificationEvent
:
850 case InternalSynchronizationEvent
:
851 KeSetEvent(SignalObj
,
856 case InternalMutexType
:
857 KeReleaseMutex(SignalObj
,
861 case InternalSemaphoreType
:
862 KeReleaseSemaphore(SignalObj
,
869 ObDereferenceObject(SignalObj
);
870 ObDereferenceObject(WaitObj
);
871 return STATUS_OBJECT_TYPE_MISMATCH
;
874 Status
= KeWaitForSingleObject(WaitObj
,
880 ObDereferenceObject(SignalObj
);
881 ObDereferenceObject(WaitObj
);