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
;
281 KiSideEffectsBeforeWake(hdr
, current
->Thread
, &Abandoned
);
282 Status
= current
->WaitKey
;
284 Status
+= STATUS_ABANDONED_WAIT_0
;
285 if (current
->Thread
->WaitBlockList
== NULL
)
287 PsUnblockThread(CONTAINING_RECORD(current
->Thread
,ETHREAD
,Tcb
),
294 static BOOLEAN
KeDispatcherObjectWakeOne(DISPATCHER_HEADER
* hdr
)
296 PKWAIT_BLOCK current
;
297 PLIST_ENTRY current_entry
;
298 PKWAIT_BLOCK PrevBlock
;
302 DPRINT("KeDispatcherObjectWakeOn(hdr %x)\n",hdr
);
303 DPRINT("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
304 hdr
->WaitListHead
.Flink
,hdr
->WaitListHead
.Blink
);
305 if (IsListEmpty(&(hdr
->WaitListHead
)))
309 current_entry
= RemoveHeadList(&(hdr
->WaitListHead
));
310 current
= CONTAINING_RECORD(current_entry
,KWAIT_BLOCK
,
312 DPRINT("current_entry %x current %x\n",current_entry
,current
);
314 if (current
->WaitType
== WaitAny
)
316 DPRINT("WaitAny: Remove all wait blocks.\n");
317 for( PrevBlock
= current
->Thread
->WaitBlockList
; PrevBlock
; PrevBlock
= PrevBlock
->NextWaitBlock
)
318 if( PrevBlock
!= current
)
319 RemoveEntryList( &(PrevBlock
->WaitListEntry
) );
320 current
->Thread
->WaitBlockList
= 0;
324 DPRINT("WaitAll: Remove the current wait block only.\n");
326 PrevBlock
= current
->Thread
->WaitBlockList
;
327 if (PrevBlock
== current
)
329 DPRINT( "WaitAll: Current block is list head.\n" );
330 current
->Thread
->WaitBlockList
= current
->NextWaitBlock
;
334 DPRINT( "WaitAll: Current block is not list head.\n" );
335 while ( PrevBlock
&& PrevBlock
->NextWaitBlock
!= current
)
337 PrevBlock
= PrevBlock
->NextWaitBlock
;
341 PrevBlock
->NextWaitBlock
= current
->NextWaitBlock
;
346 DPRINT("Waking %x\n",current
->Thread
);
347 KiSideEffectsBeforeWake(hdr
, current
->Thread
, &Abandoned
);
348 Status
= current
->WaitKey
;
350 Status
+= STATUS_ABANDONED_WAIT_0
;
351 PsUnblockThread(CONTAINING_RECORD(current
->Thread
, ETHREAD
, Tcb
),
356 BOOLEAN
KeDispatcherObjectWake(DISPATCHER_HEADER
* hdr
)
358 * FUNCTION: Wake threads waiting on a dispatcher object
359 * NOTE: The exact semantics of waking are dependant on the type of object
364 DPRINT("Entering KeDispatcherObjectWake(hdr %x)\n",hdr
);
365 // DPRINT("hdr->WaitListHead %x hdr->WaitListHead.Flink %x\n",
366 // &hdr->WaitListHead,hdr->WaitListHead.Flink);
367 DPRINT("hdr->Type %x\n",hdr
->Type
);
370 case InternalNotificationEvent
:
371 return(KeDispatcherObjectWakeAll(hdr
));
373 case InternalNotificationTimer
:
374 return(KeDispatcherObjectWakeAll(hdr
));
376 case InternalSynchronizationEvent
:
377 return(KeDispatcherObjectWakeOne(hdr
));
379 case InternalSynchronizationTimer
:
380 return(KeDispatcherObjectWakeOne(hdr
));
382 case InternalSemaphoreType
:
383 DPRINT("hdr->SignalState %d\n", hdr
->SignalState
);
384 if(hdr
->SignalState
>0)
388 DPRINT("Waking one semaphore waiter\n");
389 Ret
= KeDispatcherObjectWakeOne(hdr
);
390 } while(hdr
->SignalState
> 0 && Ret
) ;
395 case InternalProcessType
:
396 return(KeDispatcherObjectWakeAll(hdr
));
398 case InternalThreadType
:
399 return(KeDispatcherObjectWakeAll(hdr
));
401 case InternalMutexType
:
402 return(KeDispatcherObjectWakeOne(hdr
));
404 DbgPrint("Dispatcher object %x has unknown type %d\n", hdr
, hdr
->Type
);
411 KeWaitForSingleObject(PVOID Object
,
412 KWAIT_REASON WaitReason
,
413 KPROCESSOR_MODE WaitMode
,
415 PLARGE_INTEGER Timeout
)
417 * FUNCTION: Puts the current thread into a wait state until the
418 * given dispatcher object is set to signalled
420 * Object = Object to wait on
421 * WaitReason = Reason for the wait (debugging aid)
422 * WaitMode = Can be KernelMode or UserMode, if UserMode then
423 * user-mode APCs can be delivered and the thread's
424 * stack can be paged out
425 * Altertable = Specifies if the wait is a alertable
426 * Timeout = Optional timeout value
430 DISPATCHER_HEADER
* hdr
= (DISPATCHER_HEADER
*)Object
;
431 PKTHREAD CurrentThread
;
436 CurrentThread
= KeGetCurrentThread();
437 WaitIrql
= KeGetCurrentIrql();
441 * FIXME: Check for zero timeout
445 KeInitializeTimer(&CurrentThread
->Timer
);
446 KeSetTimer(&CurrentThread
->Timer
, *Timeout
, NULL
);
451 KeAcquireDispatcherDatabaseLock(FALSE
);
454 * If we are going to wait alertably and a user apc is pending
457 if (Alertable
&& KiTestAlert())
459 KeReleaseDispatcherDatabaseLock(FALSE
);
460 return(STATUS_USER_APC
);
464 * If the object is signalled
466 if (KiIsObjectSignalled(hdr
, CurrentThread
, &Abandoned
))
468 KeReleaseDispatcherDatabaseLock(FALSE
);
471 KeCancelTimer(&KeGetCurrentThread()->Timer
);
473 if (Abandoned
== TRUE
)
474 return(STATUS_ABANDONED_WAIT_0
);
475 return(STATUS_WAIT_0
);
479 * Check if we have already timed out
481 if (Timeout
!= NULL
&&
482 KiIsObjectSignalled(&CurrentThread
->Timer
.Header
, CurrentThread
, NULL
))
484 KeReleaseDispatcherDatabaseLock(FALSE
);
487 KeCancelTimer(&KeGetCurrentThread()->Timer
);
489 return(STATUS_TIMEOUT
);
495 CurrentThread
->WaitStatus
= STATUS_UNSUCCESSFUL
;
496 /* Append wait block to the KTHREAD wait block list */
497 CurrentThread
->WaitBlockList
= &CurrentThread
->WaitBlock
[0];
498 CurrentThread
->WaitBlock
[0].Object
= Object
;
499 CurrentThread
->WaitBlock
[0].Thread
= CurrentThread
;
500 CurrentThread
->WaitBlock
[0].WaitKey
= STATUS_WAIT_0
;
501 CurrentThread
->WaitBlock
[0].WaitType
= WaitAny
;
502 InsertTailList(&hdr
->WaitListHead
,
503 &CurrentThread
->WaitBlock
[0].WaitListEntry
);
506 CurrentThread
->WaitBlock
[0].NextWaitBlock
=
507 &CurrentThread
->WaitBlock
[1];
508 CurrentThread
->WaitBlock
[1].Object
= (PVOID
)&CurrentThread
->Timer
;
509 CurrentThread
->WaitBlock
[1].Thread
= CurrentThread
;
510 CurrentThread
->WaitBlock
[1].WaitKey
= STATUS_TIMEOUT
;
511 CurrentThread
->WaitBlock
[1].WaitType
= WaitAny
;
512 CurrentThread
->WaitBlock
[1].NextWaitBlock
= NULL
;
513 InsertTailList(&CurrentThread
->Timer
.Header
.WaitListHead
,
514 &CurrentThread
->WaitBlock
[1].WaitListEntry
);
518 CurrentThread
->WaitBlock
[0].NextWaitBlock
= NULL
;
520 PsBlockThread(&Status
, (UCHAR
)Alertable
, WaitMode
, TRUE
, WaitIrql
);
521 } while (Status
== STATUS_KERNEL_APC
);
525 KeCancelTimer(&KeGetCurrentThread()->Timer
);
528 DPRINT("Returning from KeWaitForSingleObject()\n");
534 KeWaitForMultipleObjects(ULONG Count
,
537 KWAIT_REASON WaitReason
,
538 KPROCESSOR_MODE WaitMode
,
540 PLARGE_INTEGER Timeout
,
541 PKWAIT_BLOCK WaitBlockArray
)
543 DISPATCHER_HEADER
* hdr
;
545 PKTHREAD CurrentThread
;
552 DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
553 "PsGetCurrentThread() %x\n",Count
,Object
,PsGetCurrentThread());
556 CurrentThread
= KeGetCurrentThread();
557 WaitIrql
= KeGetCurrentIrql();
560 * Work out where we are going to put the wait blocks
562 if (WaitBlockArray
== NULL
)
564 if (Count
> THREAD_WAIT_OBJECTS
)
566 DbgPrint("(%s:%d) Too many objects!\n",
568 return(STATUS_UNSUCCESSFUL
);
570 blk
= &CurrentThread
->WaitBlock
[0];
574 if (Count
> EX_MAXIMUM_WAIT_OBJECTS
)
576 DbgPrint("(%s:%d) Too many objects!\n",
578 return(STATUS_UNSUCCESSFUL
);
580 blk
= WaitBlockArray
;
584 * Set up the timeout if required
588 KeInitializeTimer(&CurrentThread
->Timer
);
589 KeSetTimer(&CurrentThread
->Timer
, *Timeout
, NULL
);
594 KeAcquireDispatcherDatabaseLock(FALSE
);
597 * If we are going to wait alertably and a user apc is pending
600 if (Alertable
&& KiTestAlert())
602 KeReleaseDispatcherDatabaseLock(FALSE
);
603 return(STATUS_USER_APC
);
607 * Check if the wait is already satisfied
609 for (i
= 0; i
< Count
; i
++)
611 hdr
= (DISPATCHER_HEADER
*)Object
[i
];
613 if (KiIsObjectSignalled(hdr
, CurrentThread
, &Abandoned
))
617 if (WaitType
== WaitAny
)
619 KeReleaseDispatcherDatabaseLock(FALSE
);
620 DPRINT("One object is already signaled!\n");
621 if (Abandoned
== TRUE
)
622 return(STATUS_ABANDONED_WAIT_0
+ i
);
623 return(STATUS_WAIT_0
+ i
);
628 if ((WaitType
== WaitAll
) && (CountSignaled
== Count
))
630 KeReleaseDispatcherDatabaseLock(FALSE
);
631 DPRINT("All objects are already signaled!\n");
632 return(STATUS_WAIT_0
);
636 * Check if we have already timed out
638 if (Timeout
!= NULL
&&
639 KiIsObjectSignalled(&CurrentThread
->Timer
.Header
, CurrentThread
, NULL
))
641 KeReleaseDispatcherDatabaseLock(FALSE
);
644 KeCancelTimer(&KeGetCurrentThread()->Timer
);
646 return(STATUS_TIMEOUT
);
649 /* Append wait block to the KTHREAD wait block list */
650 CurrentThread
->WaitBlockList
= blk
;
655 for (i
= 0; i
< Count
; i
++)
657 hdr
= (DISPATCHER_HEADER
*)Object
[i
];
659 blk
->Object
= Object
[i
];
660 blk
->Thread
= CurrentThread
;
661 blk
->WaitKey
= STATUS_WAIT_0
+ i
;
662 blk
->WaitType
= WaitType
;
663 if (i
== (Count
- 1))
667 blk
->NextWaitBlock
= &CurrentThread
->WaitBlock
[3];
671 blk
->NextWaitBlock
= NULL
;
676 blk
->NextWaitBlock
= blk
+ 1;
679 InsertTailList(&hdr
->WaitListHead
, &blk
->WaitListEntry
);
681 blk
= blk
->NextWaitBlock
;
685 CurrentThread
->WaitBlock
[3].Object
= (PVOID
)&CurrentThread
->Timer
;
686 CurrentThread
->WaitBlock
[3].Thread
= CurrentThread
;
687 CurrentThread
->WaitBlock
[3].WaitKey
= STATUS_TIMEOUT
;
688 CurrentThread
->WaitBlock
[3].WaitType
= WaitAny
;
689 CurrentThread
->WaitBlock
[3].NextWaitBlock
= NULL
;
690 InsertTailList(&CurrentThread
->Timer
.Header
.WaitListHead
,
691 &CurrentThread
->WaitBlock
[3].WaitListEntry
);
694 PsBlockThread(&Status
, Alertable
, WaitMode
, TRUE
, WaitIrql
);
695 } while(Status
== STATUS_KERNEL_APC
);
699 KeCancelTimer(&KeGetCurrentThread()->Timer
);
702 DPRINT("Returning from KeWaitForMultipleObjects()\n");
706 VOID
KeInitializeDispatcher(VOID
)
708 KeInitializeSpinLock(&DispatcherDatabaseLock
);
712 NtWaitForMultipleObjects(IN ULONG Count
,
715 IN BOOLEAN Alertable
,
716 IN PLARGE_INTEGER Time
)
718 KWAIT_BLOCK WaitBlockArray
[EX_MAXIMUM_WAIT_OBJECTS
];
719 PVOID ObjectPtrArray
[EX_MAXIMUM_WAIT_OBJECTS
];
723 DPRINT("NtWaitForMultipleObjects(Count %lu Object[] %x, Alertable %d, "
724 "Time %x)\n", Count
,Object
,Alertable
,Time
);
726 if (Count
> EX_MAXIMUM_WAIT_OBJECTS
)
727 return STATUS_UNSUCCESSFUL
;
729 /* reference all objects */
730 for (i
= 0; i
< Count
; i
++)
732 Status
= ObReferenceObjectByHandle(Object
[i
],
738 if (Status
!= STATUS_SUCCESS
)
740 /* dereference all referenced objects */
741 for (j
= 0; j
< i
; j
++)
743 ObDereferenceObject(ObjectPtrArray
[j
]);
750 Status
= KeWaitForMultipleObjects(Count
,
759 /* dereference all objects */
760 for (i
= 0; i
< Count
; i
++)
762 ObDereferenceObject(ObjectPtrArray
[i
]);
770 NtWaitForSingleObject(IN HANDLE Object
,
771 IN BOOLEAN Alertable
,
772 IN PLARGE_INTEGER Time
)
777 DPRINT("NtWaitForSingleObject(Object %x, Alertable %d, Time %x)\n",
778 Object
,Alertable
,Time
);
780 Status
= ObReferenceObjectByHandle(Object
,
786 if (!NT_SUCCESS(Status
))
791 Status
= KeWaitForSingleObject(ObjectPtr
,
797 ObDereferenceObject(ObjectPtr
);
804 NtSignalAndWaitForSingleObject(IN HANDLE SignalObject
,
805 IN HANDLE WaitObject
,
806 IN BOOLEAN Alertable
,
807 IN PLARGE_INTEGER Time
)
809 KPROCESSOR_MODE ProcessorMode
;
810 DISPATCHER_HEADER
* hdr
;
815 ProcessorMode
= ExGetPreviousMode();
816 Status
= ObReferenceObjectByHandle(SignalObject
,
822 if (!NT_SUCCESS(Status
))
827 Status
= ObReferenceObjectByHandle(WaitObject
,
833 if (!NT_SUCCESS(Status
))
835 ObDereferenceObject(SignalObj
);
839 hdr
= (DISPATCHER_HEADER
*)SignalObj
;
842 case InternalNotificationEvent
:
843 case InternalSynchronizationEvent
:
844 KeSetEvent(SignalObj
,
849 case InternalMutexType
:
850 KeReleaseMutex(SignalObj
,
854 case InternalSemaphoreType
:
855 KeReleaseSemaphore(SignalObj
,
862 ObDereferenceObject(SignalObj
);
863 ObDereferenceObject(WaitObj
);
864 return STATUS_OBJECT_TYPE_MISMATCH
;
867 Status
= KeWaitForSingleObject(WaitObj
,
873 ObDereferenceObject(SignalObj
);
874 ObDereferenceObject(WaitObj
);