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 VOID
KiSideEffectsBeforeWake(DISPATCHER_HEADER
* hdr
,
91 * FUNCTION: Perform side effects on object before a wait for a thread is
97 case InternalSynchronizationEvent
:
101 case InternalSemaphoreType
:
105 case InternalProcessType
:
108 case InternalThreadType
:
111 case InternalNotificationEvent
:
114 case InternalSynchronizationTimer
:
115 hdr
->SignalState
= FALSE
;
118 case InternalNotificationTimer
:
121 case InternalMutexType
:
125 Mutex
= CONTAINING_RECORD(hdr
, KMUTEX
, Header
);
127 assert(hdr
->SignalState
<= 1);
128 Mutex
->OwnerThread
= Thread
;
133 DbgPrint("(%s:%d) Dispatcher object %x has unknown type\n",
134 __FILE__
,__LINE__
,hdr
);
140 static BOOLEAN
KiIsObjectSignalled(DISPATCHER_HEADER
* hdr
,
143 if (hdr
->Type
== InternalMutexType
)
147 Mutex
= CONTAINING_RECORD(hdr
, KMUTEX
, Header
);
149 assert(hdr
->SignalState
<= 1);
150 if ((hdr
->SignalState
< 1 && Mutex
->OwnerThread
== Thread
) ||
151 hdr
->SignalState
== 1)
153 KiSideEffectsBeforeWake(hdr
,
162 if (hdr
->SignalState
<= 0)
168 KiSideEffectsBeforeWake(hdr
, Thread
);
173 VOID
KeRemoveAllWaitsThread(PETHREAD Thread
, NTSTATUS WaitStatus
)
175 PKWAIT_BLOCK WaitBlock
;
176 BOOLEAN WasWaiting
= FALSE
;
178 KeAcquireDispatcherDatabaseLock(FALSE
);
180 WaitBlock
= (PKWAIT_BLOCK
)Thread
->Tcb
.WaitBlockList
;
181 if (WaitBlock
!= NULL
)
185 while (WaitBlock
!= NULL
)
187 RemoveEntryList(&WaitBlock
->WaitListEntry
);
188 WaitBlock
= WaitBlock
->NextWaitBlock
;
190 Thread
->Tcb
.WaitBlockList
= NULL
;
194 PsUnblockThread(Thread
, &WaitStatus
);
197 KeReleaseDispatcherDatabaseLock(FALSE
);
200 static BOOLEAN
KeDispatcherObjectWakeAll(DISPATCHER_HEADER
* hdr
)
202 PKWAIT_BLOCK current
;
203 PLIST_ENTRY current_entry
;
204 PKWAIT_BLOCK PrevBlock
;
207 DPRINT("KeDispatcherObjectWakeAll(hdr %x)\n",hdr
);
209 if (IsListEmpty(&hdr
->WaitListHead
))
214 while (!IsListEmpty(&(hdr
->WaitListHead
)))
216 current_entry
= RemoveHeadList(&hdr
->WaitListHead
);
217 current
= CONTAINING_RECORD(current_entry
,
220 DPRINT("Waking %x\n",current
->Thread
);
221 if (current
->WaitType
== WaitAny
)
223 DPRINT("WaitAny: Remove all wait blocks.\n");
224 for( PrevBlock
= current
->Thread
->WaitBlockList
; PrevBlock
; PrevBlock
= PrevBlock
->NextWaitBlock
)
225 if( PrevBlock
!= current
)
226 RemoveEntryList( &(PrevBlock
->WaitListEntry
) );
227 current
->Thread
->WaitBlockList
= 0;
231 DPRINT("WaitAll: Remove the current wait block only.\n");
233 PrevBlock
= current
->Thread
->WaitBlockList
;
234 if (PrevBlock
== current
)
236 DPRINT( "WaitAll: Current block is list head.\n" );
237 current
->Thread
->WaitBlockList
= current
->NextWaitBlock
;
241 DPRINT( "WaitAll: Current block is not list head.\n" );
242 while ( PrevBlock
&& PrevBlock
->NextWaitBlock
!= current
)
244 PrevBlock
= PrevBlock
->NextWaitBlock
;
248 PrevBlock
->NextWaitBlock
= current
->NextWaitBlock
;
252 KiSideEffectsBeforeWake(hdr
, current
->Thread
);
253 Status
= current
->WaitKey
;
254 if (current
->Thread
->WaitBlockList
== NULL
)
256 PsUnblockThread(CONTAINING_RECORD(current
->Thread
,ETHREAD
,Tcb
),
263 static BOOLEAN
KeDispatcherObjectWakeOne(DISPATCHER_HEADER
* hdr
)
265 PKWAIT_BLOCK current
;
266 PLIST_ENTRY current_entry
;
267 PKWAIT_BLOCK PrevBlock
;
270 DPRINT("KeDispatcherObjectWakeOn(hdr %x)\n",hdr
);
271 DPRINT("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
272 hdr
->WaitListHead
.Flink
,hdr
->WaitListHead
.Blink
);
273 if (IsListEmpty(&(hdr
->WaitListHead
)))
277 current_entry
= RemoveHeadList(&(hdr
->WaitListHead
));
278 current
= CONTAINING_RECORD(current_entry
,KWAIT_BLOCK
,
280 DPRINT("current_entry %x current %x\n",current_entry
,current
);
282 if (current
->WaitType
== WaitAny
)
284 DPRINT("WaitAny: Remove all wait blocks.\n");
285 for( PrevBlock
= current
->Thread
->WaitBlockList
; PrevBlock
; PrevBlock
= PrevBlock
->NextWaitBlock
)
286 if( PrevBlock
!= current
)
287 RemoveEntryList( &(PrevBlock
->WaitListEntry
) );
288 current
->Thread
->WaitBlockList
= 0;
292 DPRINT("WaitAll: Remove the current wait block only.\n");
294 PrevBlock
= current
->Thread
->WaitBlockList
;
295 if (PrevBlock
== current
)
297 DPRINT( "WaitAll: Current block is list head.\n" );
298 current
->Thread
->WaitBlockList
= current
->NextWaitBlock
;
302 DPRINT( "WaitAll: Current block is not list head.\n" );
303 while ( PrevBlock
&& PrevBlock
->NextWaitBlock
!= current
)
305 PrevBlock
= PrevBlock
->NextWaitBlock
;
309 PrevBlock
->NextWaitBlock
= current
->NextWaitBlock
;
314 DPRINT("Waking %x\n",current
->Thread
);
315 KiSideEffectsBeforeWake(hdr
, current
->Thread
);
316 Status
= current
->WaitKey
;
317 PsUnblockThread(CONTAINING_RECORD(current
->Thread
, ETHREAD
, Tcb
),
322 BOOLEAN
KeDispatcherObjectWake(DISPATCHER_HEADER
* hdr
)
324 * FUNCTION: Wake threads waiting on a dispatcher object
325 * NOTE: The exact semantics of waking are dependant on the type of object
330 DPRINT("Entering KeDispatcherObjectWake(hdr %x)\n",hdr
);
331 // DPRINT("hdr->WaitListHead %x hdr->WaitListHead.Flink %x\n",
332 // &hdr->WaitListHead,hdr->WaitListHead.Flink);
333 DPRINT("hdr->Type %x\n",hdr
->Type
);
336 case InternalNotificationEvent
:
337 return(KeDispatcherObjectWakeAll(hdr
));
339 case InternalNotificationTimer
:
340 return(KeDispatcherObjectWakeAll(hdr
));
342 case InternalSynchronizationEvent
:
343 return(KeDispatcherObjectWakeOne(hdr
));
346 case InternalSynchronizationTimer
:
347 return(KeDispatcherObjectWakeOne(hdr
));
349 case InternalSemaphoreType
:
350 DPRINT("hdr->SignalState %d\n", hdr
->SignalState
);
351 if(hdr
->SignalState
>0)
355 DPRINT("Waking one semaphore waiter\n");
356 Ret
= KeDispatcherObjectWakeOne(hdr
);
357 } while(hdr
->SignalState
> 0 && Ret
) ;
362 case InternalProcessType
:
363 return(KeDispatcherObjectWakeAll(hdr
));
365 case InternalThreadType
:
366 return(KeDispatcherObjectWakeAll(hdr
));
368 case InternalMutexType
:
369 return(KeDispatcherObjectWakeOne(hdr
));
371 DbgPrint("Dispatcher object %x has unknown type %d\n", hdr
, hdr
->Type
);
377 NTSTATUS STDCALL
KeWaitForSingleObject (PVOID Object
,
378 KWAIT_REASON WaitReason
,
379 KPROCESSOR_MODE WaitMode
,
381 PLARGE_INTEGER Timeout
)
383 * FUNCTION: Puts the current thread into a wait state until the
384 * given dispatcher object is set to signalled
386 * Object = Object to wait on
387 * WaitReason = Reason for the wait (debugging aid)
388 * WaitMode = Can be KernelMode or UserMode, if UserMode then
389 * user-mode APCs can be delivered and the thread's
390 * stack can be paged out
391 * Altertable = Specifies if the wait is a alertable
392 * Timeout = Optional timeout value
396 DISPATCHER_HEADER
* hdr
= (DISPATCHER_HEADER
*)Object
;
397 PKTHREAD CurrentThread
;
401 DPRINT("Entering KeWaitForSingleObject(Object %x) "
402 "PsGetCurrentThread() %x\n",Object
,PsGetCurrentThread());
404 CurrentThread
= KeGetCurrentThread();
405 WaitIrql
= KeGetCurrentIrql();
409 KeAddThreadTimeout(CurrentThread
, Timeout
);
414 KeAcquireDispatcherDatabaseLock(FALSE
);
416 DPRINT("hdr->SignalState %d\n", hdr
->SignalState
);
418 if (Alertable
&& KiTestAlert())
420 KeReleaseDispatcherDatabaseLock(FALSE
);
421 return(STATUS_USER_APC
);
424 if (KiIsObjectSignalled(hdr
, CurrentThread
))
426 KeReleaseDispatcherDatabaseLock(FALSE
);
429 KeCancelTimer(&KeGetCurrentThread()->Timer
);
431 return(STATUS_WAIT_0
);
434 CurrentThread
->WaitStatus
= STATUS_UNSUCCESSFUL
;
435 /* Append wait block to the KTHREAD wait block list */
436 CurrentThread
->WaitBlockList
= &CurrentThread
->WaitBlock
[0];
437 CurrentThread
->WaitBlock
[0].Object
= Object
;
438 CurrentThread
->WaitBlock
[0].Thread
= CurrentThread
;
439 CurrentThread
->WaitBlock
[0].WaitKey
= 0;
440 CurrentThread
->WaitBlock
[0].WaitType
= WaitAny
;
441 CurrentThread
->WaitBlock
[0].NextWaitBlock
= NULL
;
442 InsertTailList(&hdr
->WaitListHead
,
443 &CurrentThread
->WaitBlock
[0].WaitListEntry
);
444 PsBlockThread(&Status
, (UCHAR
)Alertable
, WaitMode
, TRUE
, WaitIrql
);
445 } while (Status
== STATUS_KERNEL_APC
);
449 KeCancelTimer(&KeGetCurrentThread()->Timer
);
452 DPRINT("Returning from KeWaitForSingleObject()\n");
458 KeWaitForMultipleObjects (ULONG Count
,
461 KWAIT_REASON WaitReason
,
462 KPROCESSOR_MODE WaitMode
,
464 PLARGE_INTEGER Timeout
,
465 PKWAIT_BLOCK WaitBlockArray
)
467 DISPATCHER_HEADER
* hdr
;
469 PKTHREAD CurrentThread
;
475 DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
476 "PsGetCurrentThread() %x\n",Count
,Object
,PsGetCurrentThread());
479 CurrentThread
= KeGetCurrentThread();
480 WaitIrql
= KeGetCurrentIrql();
482 if (WaitBlockArray
== NULL
)
486 DbgPrint("(%s:%d) Too many objects!\n",
488 return STATUS_UNSUCCESSFUL
;
490 blk
= &CurrentThread
->WaitBlock
[0];
496 DbgPrint("(%s:%d) Too many objects!\n",
498 return STATUS_UNSUCCESSFUL
;
500 blk
= WaitBlockArray
;
504 KeAddThreadTimeout(CurrentThread
,Timeout
);
508 KeAcquireDispatcherDatabaseLock(FALSE
);
510 for (i
= 0; i
< Count
; i
++)
512 hdr
= (DISPATCHER_HEADER
*)Object
[i
];
514 DPRINT("hdr->SignalState %d\n", hdr
->SignalState
);
516 if (KiIsObjectSignalled(hdr
, CurrentThread
))
520 if (WaitType
== WaitAny
)
522 KeReleaseDispatcherDatabaseLock(FALSE
);
523 DPRINT("One object is already signaled!\n");
524 return(STATUS_WAIT_0
+ i
);
529 if ((WaitType
== WaitAll
) && (CountSignaled
== Count
))
531 KeReleaseDispatcherDatabaseLock(FALSE
);
532 DPRINT("All objects are already signaled!\n");
533 return(STATUS_WAIT_0
);
536 /* Append wait block to the KTHREAD wait block list */
537 CurrentThread
->WaitBlockList
= blk
;
539 for (i
= 0; i
< Count
; i
++)
541 hdr
= (DISPATCHER_HEADER
*)Object
[i
];
543 DPRINT("hdr->SignalState %d\n", hdr
->SignalState
);
545 blk
->Object
= Object
[i
];
546 blk
->Thread
= CurrentThread
;
548 blk
->WaitType
= WaitType
;
550 blk
->NextWaitBlock
= NULL
;
552 blk
->NextWaitBlock
= blk
+ 1;
554 InsertTailList(&(hdr
->WaitListHead
),&(blk
->WaitListEntry
));
556 blk
= blk
->NextWaitBlock
;
559 PsBlockThread(&Status
, Alertable
, WaitMode
, TRUE
, WaitIrql
);
560 } while( Status
== STATUS_KERNEL_APC
);
562 KeCancelTimer(&KeGetCurrentThread()->Timer
);
563 DPRINT("Returning from KeWaitForMultipleObjects()\n");
567 VOID
KeInitializeDispatcher(VOID
)
569 KeInitializeSpinLock(&DispatcherDatabaseLock
);
572 NTSTATUS STDCALL
NtWaitForMultipleObjects(IN ULONG Count
,
575 IN BOOLEAN Alertable
,
576 IN PLARGE_INTEGER Time
)
578 KWAIT_BLOCK WaitBlockArray
[EX_MAXIMUM_WAIT_OBJECTS
];
579 PVOID ObjectPtrArray
[EX_MAXIMUM_WAIT_OBJECTS
];
583 DPRINT("NtWaitForMultipleObjects(Count %lu Object[] %x, Alertable %d, "
584 "Time %x)\n", Count
,Object
,Alertable
,Time
);
586 if (Count
> EX_MAXIMUM_WAIT_OBJECTS
)
587 return STATUS_UNSUCCESSFUL
;
589 /* reference all objects */
590 for (i
= 0; i
< Count
; i
++)
592 Status
= ObReferenceObjectByHandle(Object
[i
],
598 if (Status
!= STATUS_SUCCESS
)
600 /* dereference all referenced objects */
601 for (j
= 0; j
< i
; i
++)
603 ObDereferenceObject(ObjectPtrArray
[j
]);
610 Status
= KeWaitForMultipleObjects(Count
,
619 /* dereference all objects */
620 for (i
= 0; i
< Count
; i
++)
622 ObDereferenceObject(ObjectPtrArray
[i
]);
629 NTSTATUS STDCALL
NtWaitForSingleObject (IN HANDLE Object
,
630 IN BOOLEAN Alertable
,
631 IN PLARGE_INTEGER Time
)
636 DPRINT("NtWaitForSingleObject(Object %x, Alertable %d, Time %x)\n",
637 Object
,Alertable
,Time
);
639 Status
= ObReferenceObjectByHandle(Object
,
645 if (!NT_SUCCESS(Status
))
650 Status
= KeWaitForSingleObject(ObjectPtr
,
656 ObDereferenceObject(ObjectPtr
);
663 NtSignalAndWaitForSingleObject(IN HANDLE SignalObject
,
664 IN HANDLE WaitObject
,
665 IN BOOLEAN Alertable
,
666 IN PLARGE_INTEGER Time
)
668 KPROCESSOR_MODE ProcessorMode
;
669 DISPATCHER_HEADER
* hdr
;
674 ProcessorMode
= ExGetPreviousMode();
675 Status
= ObReferenceObjectByHandle(SignalObject
,
681 if (!NT_SUCCESS(Status
))
686 Status
= ObReferenceObjectByHandle(WaitObject
,
692 if (!NT_SUCCESS(Status
))
694 ObDereferenceObject(SignalObj
);
698 hdr
= (DISPATCHER_HEADER
*)SignalObj
;
701 case InternalNotificationEvent
:
702 case InternalSynchronizationEvent
:
703 KeSetEvent(SignalObj
,
708 case InternalMutexType
:
709 KeReleaseMutex(SignalObj
,
713 case InternalSemaphoreType
:
714 KeReleaseSemaphore(SignalObj
,
721 ObDereferenceObject(SignalObj
);
722 ObDereferenceObject(WaitObj
);
723 return STATUS_OBJECT_TYPE_MISMATCH
;
726 Status
= KeWaitForSingleObject(WaitObj
,
732 ObDereferenceObject(SignalObj
);
733 ObDereferenceObject(WaitObj
);