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>
24 #include <internal/debug.h>
26 /* GLOBALS ******************************************************************/
28 static KSPIN_LOCK DispatcherDatabaseLock
;
29 static BOOLEAN WaitSet
= FALSE
;
30 static KIRQL oldlvl
= PASSIVE_LEVEL
;
31 static PKTHREAD Owner
= NULL
;
33 /* FUNCTIONS *****************************************************************/
35 VOID
KeInitializeDispatcherHeader(DISPATCHER_HEADER
* Header
,
44 Header
->SignalState
= SignalState
;
45 InitializeListHead(&(Header
->WaitListHead
));
48 VOID
KeAcquireDispatcherDatabaseLock(BOOLEAN Wait
)
50 * PURPOSE: Acquires the dispatcher database lock for the caller
53 DPRINT("KeAcquireDispatcherDatabaseLock(Wait %x)\n",Wait
);
54 if (WaitSet
&& Owner
== KeGetCurrentThread())
58 KeAcquireSpinLock(&DispatcherDatabaseLock
,&oldlvl
);
60 Owner
= KeGetCurrentThread();
63 VOID
KeReleaseDispatcherDatabaseLock(BOOLEAN Wait
)
65 DPRINT("KeReleaseDispatcherDatabaseLock(Wait %x)\n",Wait
);
66 assert(Wait
==WaitSet
);
70 KeReleaseSpinLock(&DispatcherDatabaseLock
, oldlvl
);
74 VOID
KiSideEffectsBeforeWake(DISPATCHER_HEADER
* hdr
)
76 * FUNCTION: Perform side effects on object before a wait for a thread is
82 case InternalSynchronizationEvent
:
83 hdr
->SignalState
= FALSE
;
86 case InternalSemaphoreType
:
90 case InternalProcessType
:
93 case InternalThreadType
:
96 case InternalNotificationEvent
:
99 case InternalSynchronizationTimer
:
100 hdr
->SignalState
= FALSE
;
103 case InternalNotificationTimer
:
107 DbgPrint("(%s:%d) Dispatcher object %x has unknown type\n",
108 __FILE__
,__LINE__
,hdr
);
114 static BOOLEAN
KiIsObjectSignalled(DISPATCHER_HEADER
* hdr
)
116 if (hdr
->SignalState
<= 0)
120 KiSideEffectsBeforeWake(hdr
);
124 VOID
KeRemoveAllWaitsThread(PETHREAD Thread
, NTSTATUS WaitStatus
)
126 PKWAIT_BLOCK WaitBlock
;
127 BOOLEAN WasWaiting
= FALSE
;
129 KeAcquireDispatcherDatabaseLock(FALSE
);
131 WaitBlock
= (PKWAIT_BLOCK
)Thread
->Tcb
.WaitBlockList
;
132 if (WaitBlock
!= NULL
)
136 while (WaitBlock
!= NULL
)
138 RemoveEntryList(&WaitBlock
->WaitListEntry
);
139 WaitBlock
= WaitBlock
->NextWaitBlock
;
141 Thread
->Tcb
.WaitBlockList
= NULL
;
145 PsUnfreezeThread(Thread
, &WaitStatus
);
148 KeReleaseDispatcherDatabaseLock(FALSE
);
151 static BOOLEAN
KeDispatcherObjectWakeAll(DISPATCHER_HEADER
* hdr
)
153 PKWAIT_BLOCK current
;
154 PLIST_ENTRY current_entry
;
155 PKWAIT_BLOCK PrevBlock
;
158 DPRINT("KeDispatcherObjectWakeAll(hdr %x)\n",hdr
);
160 if (IsListEmpty(&hdr
->WaitListHead
))
165 while (!IsListEmpty(&(hdr
->WaitListHead
)))
167 current_entry
= RemoveHeadList(&hdr
->WaitListHead
);
168 current
= CONTAINING_RECORD(current_entry
,KWAIT_BLOCK
,
170 DPRINT("Waking %x\n",current
->Thread
);
171 if (current
->WaitType
== WaitAny
)
173 DPRINT("WaitAny: Remove all wait blocks.\n");
174 for( PrevBlock
= current
->Thread
->WaitBlockList
; PrevBlock
; PrevBlock
= PrevBlock
->NextWaitBlock
)
175 if( PrevBlock
!= current
)
176 RemoveEntryList( &(PrevBlock
->WaitListEntry
) );
177 current
->Thread
->WaitBlockList
= 0;
181 DPRINT("WaitAll: Remove the current wait block only.\n");
183 PrevBlock
= current
->Thread
->WaitBlockList
;
184 if (PrevBlock
== current
)
186 DPRINT( "WaitAll: Current block is list head.\n" );
187 current
->Thread
->WaitBlockList
= current
->NextWaitBlock
;
191 DPRINT( "WaitAll: Current block is not list head.\n" );
192 while ( PrevBlock
&& PrevBlock
->NextWaitBlock
!= current
)
194 PrevBlock
= PrevBlock
->NextWaitBlock
;
198 PrevBlock
->NextWaitBlock
= current
->NextWaitBlock
;
202 KiSideEffectsBeforeWake(hdr
);
203 Status
= current
->WaitKey
;
204 if( current
->Thread
->WaitBlockList
== NULL
)
205 PsUnfreezeThread( CONTAINING_RECORD( current
->Thread
,ETHREAD
,Tcb
), &Status
);
210 static BOOLEAN
KeDispatcherObjectWakeOne(DISPATCHER_HEADER
* hdr
)
212 PKWAIT_BLOCK current
;
213 PLIST_ENTRY current_entry
;
214 PKWAIT_BLOCK PrevBlock
;
217 DPRINT("KeDispatcherObjectWakeOn(hdr %x)\n",hdr
);
218 DPRINT("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
219 hdr
->WaitListHead
.Flink
,hdr
->WaitListHead
.Blink
);
220 if (IsListEmpty(&(hdr
->WaitListHead
)))
224 current_entry
= RemoveHeadList(&(hdr
->WaitListHead
));
225 current
= CONTAINING_RECORD(current_entry
,KWAIT_BLOCK
,
227 DPRINT("current_entry %x current %x\n",current_entry
,current
);
229 if (current
->WaitType
== WaitAny
)
231 DPRINT("WaitAny: Remove all wait blocks.\n");
232 for( PrevBlock
= current
->Thread
->WaitBlockList
; PrevBlock
; PrevBlock
= PrevBlock
->NextWaitBlock
)
233 if( PrevBlock
!= current
)
234 RemoveEntryList( &(PrevBlock
->WaitListEntry
) );
235 current
->Thread
->WaitBlockList
= 0;
239 DPRINT("WaitAll: Remove the current wait block only.\n");
241 PrevBlock
= current
->Thread
->WaitBlockList
;
242 if (PrevBlock
== current
)
244 DPRINT( "WaitAll: Current block is list head.\n" );
245 current
->Thread
->WaitBlockList
= current
->NextWaitBlock
;
249 DPRINT( "WaitAll: Current block is not list head.\n" );
250 while ( PrevBlock
&& PrevBlock
->NextWaitBlock
!= current
)
252 PrevBlock
= PrevBlock
->NextWaitBlock
;
256 PrevBlock
->NextWaitBlock
= current
->NextWaitBlock
;
261 DPRINT("Waking %x\n",current
->Thread
);
262 KiSideEffectsBeforeWake(hdr
);
263 Status
= current
->WaitKey
;
264 PsUnfreezeThread( CONTAINING_RECORD( current
->Thread
, ETHREAD
, Tcb
), &Status
);
268 BOOLEAN
KeDispatcherObjectWake(DISPATCHER_HEADER
* hdr
)
270 * FUNCTION: Wake threads waiting on a dispatcher object
271 * NOTE: The exact semantics of waking are dependant on the type of object
276 DPRINT("Entering KeDispatcherObjectWake(hdr %x)\n",hdr
);
277 // DPRINT("hdr->WaitListHead %x hdr->WaitListHead.Flink %x\n",
278 // &hdr->WaitListHead,hdr->WaitListHead.Flink);
279 DPRINT("hdr->Type %x\n",hdr
->Type
);
282 case InternalNotificationEvent
:
283 return(KeDispatcherObjectWakeAll(hdr
));
285 case InternalNotificationTimer
:
286 return(KeDispatcherObjectWakeAll(hdr
));
288 case InternalSynchronizationEvent
:
289 return(KeDispatcherObjectWakeOne(hdr
));
292 case InternalSynchronizationTimer
:
293 return(KeDispatcherObjectWakeOne(hdr
));
295 case InternalSemaphoreType
:
296 DPRINT("hdr->SignalState %d\n", hdr
->SignalState
);
297 if(hdr
->SignalState
>0)
301 DPRINT("Waking one semaphore waiter\n");
302 Ret
= KeDispatcherObjectWakeOne(hdr
);
303 } while(hdr
->SignalState
> 0 && Ret
) ;
308 case InternalProcessType
:
309 return(KeDispatcherObjectWakeAll(hdr
));
311 case InternalThreadType
:
312 return(KeDispatcherObjectWakeAll(hdr
));
314 case InternalMutexType
:
315 return(KeDispatcherObjectWakeOne(hdr
));
317 DbgPrint("Dispatcher object %x has unknown type %d\n", hdr
, hdr
->Type
);
323 NTSTATUS
KeWaitForSingleObject(PVOID Object
,
324 KWAIT_REASON WaitReason
,
325 KPROCESSOR_MODE WaitMode
,
327 PLARGE_INTEGER Timeout
)
329 * FUNCTION: Puts the current thread into a wait state until the
330 * given dispatcher object is set to signalled
332 * Object = Object to wait on
333 * WaitReason = Reason for the wait (debugging aid)
334 * WaitMode = Can be KernelMode or UserMode, if UserMode then
335 * user-mode APCs can be delivered and the thread's
336 * stack can be paged out
337 * Altertable = Specifies if the wait is a alertable
338 * Timeout = Optional timeout value
342 DISPATCHER_HEADER
* hdr
= (DISPATCHER_HEADER
*)Object
;
343 PKTHREAD CurrentThread
;
346 DPRINT("Entering KeWaitForSingleObject(Object %x) "
347 "PsGetCurrentThread() %x\n",Object
,PsGetCurrentThread());
349 CurrentThread
= KeGetCurrentThread();
351 if (Alertable
&& !IsListEmpty(&CurrentThread
->ApcState
.ApcListHead
[1]))
353 DPRINT("Thread is alertable and user APCs are pending\n");
354 return(STATUS_USER_APC
);
359 KeAddThreadTimeout(CurrentThread
,Timeout
);
364 KeAcquireDispatcherDatabaseLock(FALSE
);
366 DPRINT("hdr->SignalState %d\n", hdr
->SignalState
);
368 if (KiIsObjectSignalled(hdr
))
370 KeReleaseDispatcherDatabaseLock(FALSE
);
373 KeCancelTimer(&KeGetCurrentThread()->Timer
);
375 return(STATUS_WAIT_0
);
378 /* Append wait block to the KTHREAD wait block list */
379 CurrentThread
->WaitBlockList
= &CurrentThread
->WaitBlock
[0];
381 CurrentThread
->WaitBlock
[0].Object
= Object
;
382 CurrentThread
->WaitBlock
[0].Thread
= CurrentThread
;
383 CurrentThread
->WaitBlock
[0].WaitKey
= 0;
384 CurrentThread
->WaitBlock
[0].WaitType
= WaitAny
;
385 CurrentThread
->WaitBlock
[0].NextWaitBlock
= NULL
;
386 DPRINT("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x CurrentThread->WaitBlock[0].WaitListEntry = %x\n",
387 hdr
->WaitListHead
.Flink
,hdr
->WaitListHead
.Blink
, CurrentThread
->WaitBlock
[0].WaitListEntry
);
388 InsertTailList(&hdr
->WaitListHead
, &CurrentThread
->WaitBlock
[0].WaitListEntry
);
389 KeReleaseDispatcherDatabaseLock(FALSE
);
390 DPRINT("Waiting for %x with irql %d\n", Object
, KeGetCurrentIrql());
391 PsFreezeThread(PsGetCurrentThread(),
395 DPRINT("Woke from wait\n");
396 } while (Status
== STATUS_KERNEL_APC
);
400 KeCancelTimer(&KeGetCurrentThread()->Timer
);
403 DPRINT("Returning from KeWaitForSingleObject()\n");
408 NTSTATUS
KeWaitForMultipleObjects(ULONG Count
,
411 KWAIT_REASON WaitReason
,
412 KPROCESSOR_MODE WaitMode
,
414 PLARGE_INTEGER Timeout
,
415 PKWAIT_BLOCK WaitBlockArray
)
417 DISPATCHER_HEADER
* hdr
;
419 PKTHREAD CurrentThread
;
424 DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
425 "PsGetCurrentThread() %x\n",Count
,Object
,PsGetCurrentThread());
428 CurrentThread
= KeGetCurrentThread();
430 if (WaitBlockArray
== NULL
)
434 DbgPrint("(%s:%d) Too many objects!\n",
436 return STATUS_UNSUCCESSFUL
;
438 blk
= &CurrentThread
->WaitBlock
[0];
444 DbgPrint("(%s:%d) Too many objects!\n",
446 return STATUS_UNSUCCESSFUL
;
448 blk
= WaitBlockArray
;
451 KeAcquireDispatcherDatabaseLock(FALSE
);
453 for (i
= 0; i
< Count
; i
++)
455 hdr
= (DISPATCHER_HEADER
*)Object
[i
];
457 DPRINT("hdr->SignalState %d\n", hdr
->SignalState
);
459 if (KiIsObjectSignalled(hdr
))
463 if (WaitType
== WaitAny
)
465 KeReleaseDispatcherDatabaseLock(FALSE
);
466 DPRINT("One object is already signaled!\n");
467 return(STATUS_WAIT_0
+ i
);
472 if ((WaitType
== WaitAll
) && (CountSignaled
== Count
))
474 KeReleaseDispatcherDatabaseLock(FALSE
);
475 DPRINT("All objects are already signaled!\n");
476 return(STATUS_WAIT_0
);
481 KeAddThreadTimeout(CurrentThread
,Timeout
);
484 /* Append wait block to the KTHREAD wait block list */
485 CurrentThread
->WaitBlockList
= blk
;
487 for (i
= 0; i
< Count
; i
++)
489 hdr
= (DISPATCHER_HEADER
*)Object
[i
];
491 DPRINT("hdr->SignalState %d\n", hdr
->SignalState
);
493 blk
->Object
= Object
[i
];
494 blk
->Thread
= CurrentThread
;
496 blk
->WaitType
= WaitType
;
498 blk
->NextWaitBlock
= NULL
;
500 blk
->NextWaitBlock
= blk
+ 1;
501 DPRINT("blk %p blk->NextWaitBlock %p\n",
502 blk
, blk
->NextWaitBlock
);
503 InsertTailList(&(hdr
->WaitListHead
),&(blk
->WaitListEntry
));
504 // DPRINT("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
505 // hdr->WaitListHead.Flink,hdr->WaitListHead.Blink);
507 blk
= blk
->NextWaitBlock
;
510 KeReleaseDispatcherDatabaseLock(FALSE
);
512 DPRINT("Waiting at %s:%d with irql %d\n", __FILE__
, __LINE__
,
514 PsFreezeThread(PsGetCurrentThread(),
520 KeCancelTimer(&KeGetCurrentThread()->Timer
);
521 DPRINT("Returning from KeWaitForMultipleObjects()\n");
525 VOID
KeInitializeDispatcher(VOID
)
527 KeInitializeSpinLock(&DispatcherDatabaseLock
);
530 NTSTATUS STDCALL
NtWaitForMultipleObjects(IN ULONG Count
,
533 IN BOOLEAN Alertable
,
534 IN PLARGE_INTEGER Time
)
536 KWAIT_BLOCK WaitBlockArray
[64]; /* FIXME: use MAXIMUM_WAIT_OBJECTS instead */
537 PVOID ObjectPtrArray
[64]; /* FIXME: use MAXIMUM_WAIT_OBJECTS instead */
541 DPRINT("NtWaitForMultipleObjects(Count %lu Object[] %x, Alertable %d, Time %x)\n",
542 Count
,Object
,Alertable
,Time
);
544 if (Count
> 64) /* FIXME: use MAXIMUM_WAIT_OBJECTS instead */
545 return STATUS_UNSUCCESSFUL
;
547 /* reference all objects */
548 for (i
= 0; i
< Count
; i
++)
550 Status
= ObReferenceObjectByHandle(Object
[i
],
556 if (Status
!= STATUS_SUCCESS
)
558 /* dereference all referenced objects */
559 for (j
= 0; j
< i
; i
++)
561 ObDereferenceObject(ObjectPtrArray
[j
]);
568 Status
= KeWaitForMultipleObjects(Count
,
577 /* dereference all objects */
578 for (i
= 0; i
< Count
; i
++)
580 ObDereferenceObject(ObjectPtrArray
[i
]);
587 NTSTATUS STDCALL
NtWaitForSingleObject (IN HANDLE Object
,
588 IN BOOLEAN Alertable
,
589 IN PLARGE_INTEGER Time
)
594 DPRINT("NtWaitForSingleObject(Object %x, Alertable %d, Time %x)\n",
595 Object
,Alertable
,Time
);
597 Status
= ObReferenceObjectByHandle(Object
,
603 if (Status
!= STATUS_SUCCESS
)
608 Status
= KeWaitForSingleObject(ObjectPtr
,
614 ObDereferenceObject(ObjectPtr
);
620 NTSTATUS STDCALL
NtSignalAndWaitForSingleObject (IN HANDLE EventHandle
,
621 IN BOOLEAN Alertable
,
622 IN PLARGE_INTEGER Time
,
624 NumberOfWaitingThreads OPTIONAL
)