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>
26 #include <internal/debug.h>
28 /* GLOBALS ******************************************************************/
30 static KSPIN_LOCK DispatcherDatabaseLock
;
31 static BOOLEAN WaitSet
= FALSE
;
32 static KIRQL oldlvl
= PASSIVE_LEVEL
;
33 static PKTHREAD Owner
= NULL
;
35 /* FUNCTIONS *****************************************************************/
37 VOID
KeInitializeDispatcherHeader(DISPATCHER_HEADER
* Header
,
46 Header
->SignalState
= SignalState
;
47 InitializeListHead(&(Header
->WaitListHead
));
50 VOID
KeAcquireDispatcherDatabaseLock(BOOLEAN Wait
)
52 * PURPOSE: Acquires the dispatcher database lock for the caller
55 DPRINT("KeAcquireDispatcherDatabaseLock(Wait %x)\n",Wait
);
56 if (WaitSet
&& Owner
== KeGetCurrentThread())
60 KeAcquireSpinLock(&DispatcherDatabaseLock
,&oldlvl
);
62 Owner
= KeGetCurrentThread();
65 VOID
KeReleaseDispatcherDatabaseLock(BOOLEAN Wait
)
67 DPRINT("KeReleaseDispatcherDatabaseLock(Wait %x)\n",Wait
);
68 assert(Wait
==WaitSet
);
72 KeReleaseSpinLock(&DispatcherDatabaseLock
, oldlvl
);
76 VOID
KiSideEffectsBeforeWake(DISPATCHER_HEADER
* hdr
,
79 * FUNCTION: Perform side effects on object before a wait for a thread is
85 case InternalSynchronizationEvent
:
89 case InternalSemaphoreType
:
93 case InternalProcessType
:
96 case InternalThreadType
:
99 case InternalNotificationEvent
:
102 case InternalSynchronizationTimer
:
103 hdr
->SignalState
= FALSE
;
106 case InternalNotificationTimer
:
109 case InternalMutexType
:
113 Mutex
= CONTAINING_RECORD(hdr
,
117 Mutex
->OwnerThread
= Thread
;
122 DbgPrint("(%s:%d) Dispatcher object %x has unknown type\n",
123 __FILE__
,__LINE__
,hdr
);
129 static BOOLEAN
KiIsObjectSignalled(DISPATCHER_HEADER
* hdr
,
132 if (hdr
->Type
== InternalMutexType
)
136 Mutex
= CONTAINING_RECORD(hdr
,
140 assert(hdr
->SignalState
<= 1);
141 if ((hdr
->SignalState
< 1 && Mutex
->OwnerThread
== Thread
) ||
142 hdr
->SignalState
== 1)
144 KiSideEffectsBeforeWake(hdr
,
153 if (hdr
->SignalState
<= 0)
159 KiSideEffectsBeforeWake(hdr
, Thread
);
164 VOID
KeRemoveAllWaitsThread(PETHREAD Thread
, NTSTATUS WaitStatus
)
166 PKWAIT_BLOCK WaitBlock
;
167 BOOLEAN WasWaiting
= FALSE
;
169 KeAcquireDispatcherDatabaseLock(FALSE
);
171 WaitBlock
= (PKWAIT_BLOCK
)Thread
->Tcb
.WaitBlockList
;
172 if (WaitBlock
!= NULL
)
176 while (WaitBlock
!= NULL
)
178 RemoveEntryList(&WaitBlock
->WaitListEntry
);
179 WaitBlock
= WaitBlock
->NextWaitBlock
;
181 Thread
->Tcb
.WaitBlockList
= NULL
;
185 PsUnfreezeThread(Thread
, &WaitStatus
);
188 KeReleaseDispatcherDatabaseLock(FALSE
);
191 static BOOLEAN
KeDispatcherObjectWakeAll(DISPATCHER_HEADER
* hdr
)
193 PKWAIT_BLOCK current
;
194 PLIST_ENTRY current_entry
;
195 PKWAIT_BLOCK PrevBlock
;
198 DPRINT("KeDispatcherObjectWakeAll(hdr %x)\n",hdr
);
200 if (IsListEmpty(&hdr
->WaitListHead
))
205 while (!IsListEmpty(&(hdr
->WaitListHead
)))
207 current_entry
= RemoveHeadList(&hdr
->WaitListHead
);
208 current
= CONTAINING_RECORD(current_entry
,
211 DPRINT("Waking %x\n",current
->Thread
);
212 if (current
->WaitType
== WaitAny
)
214 DPRINT("WaitAny: Remove all wait blocks.\n");
215 for( PrevBlock
= current
->Thread
->WaitBlockList
; PrevBlock
; PrevBlock
= PrevBlock
->NextWaitBlock
)
216 if( PrevBlock
!= current
)
217 RemoveEntryList( &(PrevBlock
->WaitListEntry
) );
218 current
->Thread
->WaitBlockList
= 0;
222 DPRINT("WaitAll: Remove the current wait block only.\n");
224 PrevBlock
= current
->Thread
->WaitBlockList
;
225 if (PrevBlock
== current
)
227 DPRINT( "WaitAll: Current block is list head.\n" );
228 current
->Thread
->WaitBlockList
= current
->NextWaitBlock
;
232 DPRINT( "WaitAll: Current block is not list head.\n" );
233 while ( PrevBlock
&& PrevBlock
->NextWaitBlock
!= current
)
235 PrevBlock
= PrevBlock
->NextWaitBlock
;
239 PrevBlock
->NextWaitBlock
= current
->NextWaitBlock
;
243 KiSideEffectsBeforeWake(hdr
, current
->Thread
);
244 Status
= current
->WaitKey
;
245 if (current
->Thread
->WaitBlockList
== NULL
)
247 PsUnfreezeThread(CONTAINING_RECORD(current
->Thread
,ETHREAD
,Tcb
),
254 static BOOLEAN
KeDispatcherObjectWakeOne(DISPATCHER_HEADER
* hdr
)
256 PKWAIT_BLOCK current
;
257 PLIST_ENTRY current_entry
;
258 PKWAIT_BLOCK PrevBlock
;
261 DPRINT("KeDispatcherObjectWakeOn(hdr %x)\n",hdr
);
262 DPRINT("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
263 hdr
->WaitListHead
.Flink
,hdr
->WaitListHead
.Blink
);
264 if (IsListEmpty(&(hdr
->WaitListHead
)))
268 current_entry
= RemoveHeadList(&(hdr
->WaitListHead
));
269 current
= CONTAINING_RECORD(current_entry
,KWAIT_BLOCK
,
271 DPRINT("current_entry %x current %x\n",current_entry
,current
);
273 if (current
->WaitType
== WaitAny
)
275 DPRINT("WaitAny: Remove all wait blocks.\n");
276 for( PrevBlock
= current
->Thread
->WaitBlockList
; PrevBlock
; PrevBlock
= PrevBlock
->NextWaitBlock
)
277 if( PrevBlock
!= current
)
278 RemoveEntryList( &(PrevBlock
->WaitListEntry
) );
279 current
->Thread
->WaitBlockList
= 0;
283 DPRINT("WaitAll: Remove the current wait block only.\n");
285 PrevBlock
= current
->Thread
->WaitBlockList
;
286 if (PrevBlock
== current
)
288 DPRINT( "WaitAll: Current block is list head.\n" );
289 current
->Thread
->WaitBlockList
= current
->NextWaitBlock
;
293 DPRINT( "WaitAll: Current block is not list head.\n" );
294 while ( PrevBlock
&& PrevBlock
->NextWaitBlock
!= current
)
296 PrevBlock
= PrevBlock
->NextWaitBlock
;
300 PrevBlock
->NextWaitBlock
= current
->NextWaitBlock
;
305 DPRINT("Waking %x\n",current
->Thread
);
306 KiSideEffectsBeforeWake(hdr
, current
->Thread
);
307 Status
= current
->WaitKey
;
308 PsUnfreezeThread(CONTAINING_RECORD(current
->Thread
, ETHREAD
, Tcb
),
313 BOOLEAN
KeDispatcherObjectWake(DISPATCHER_HEADER
* hdr
)
315 * FUNCTION: Wake threads waiting on a dispatcher object
316 * NOTE: The exact semantics of waking are dependant on the type of object
321 DPRINT("Entering KeDispatcherObjectWake(hdr %x)\n",hdr
);
322 // DPRINT("hdr->WaitListHead %x hdr->WaitListHead.Flink %x\n",
323 // &hdr->WaitListHead,hdr->WaitListHead.Flink);
324 DPRINT("hdr->Type %x\n",hdr
->Type
);
327 case InternalNotificationEvent
:
328 return(KeDispatcherObjectWakeAll(hdr
));
330 case InternalNotificationTimer
:
331 return(KeDispatcherObjectWakeAll(hdr
));
333 case InternalSynchronizationEvent
:
334 return(KeDispatcherObjectWakeOne(hdr
));
337 case InternalSynchronizationTimer
:
338 return(KeDispatcherObjectWakeOne(hdr
));
340 case InternalSemaphoreType
:
341 DPRINT("hdr->SignalState %d\n", hdr
->SignalState
);
342 if(hdr
->SignalState
>0)
346 DPRINT("Waking one semaphore waiter\n");
347 Ret
= KeDispatcherObjectWakeOne(hdr
);
348 } while(hdr
->SignalState
> 0 && Ret
) ;
353 case InternalProcessType
:
354 return(KeDispatcherObjectWakeAll(hdr
));
356 case InternalThreadType
:
357 return(KeDispatcherObjectWakeAll(hdr
));
359 case InternalMutexType
:
360 return(KeDispatcherObjectWakeOne(hdr
));
362 DbgPrint("Dispatcher object %x has unknown type %d\n", hdr
, hdr
->Type
);
368 NTSTATUS STDCALL
KeWaitForSingleObject (PVOID Object
,
369 KWAIT_REASON WaitReason
,
370 KPROCESSOR_MODE WaitMode
,
372 PLARGE_INTEGER Timeout
)
374 * FUNCTION: Puts the current thread into a wait state until the
375 * given dispatcher object is set to signalled
377 * Object = Object to wait on
378 * WaitReason = Reason for the wait (debugging aid)
379 * WaitMode = Can be KernelMode or UserMode, if UserMode then
380 * user-mode APCs can be delivered and the thread's
381 * stack can be paged out
382 * Altertable = Specifies if the wait is a alertable
383 * Timeout = Optional timeout value
387 DISPATCHER_HEADER
* hdr
= (DISPATCHER_HEADER
*)Object
;
388 PKTHREAD CurrentThread
;
391 DPRINT("Entering KeWaitForSingleObject(Object %x) "
392 "PsGetCurrentThread() %x\n",Object
,PsGetCurrentThread());
394 CurrentThread
= KeGetCurrentThread();
396 if (Alertable
&& !IsListEmpty(&CurrentThread
->ApcState
.ApcListHead
[1]))
398 DPRINT("Thread is alertable and user APCs are pending\n");
399 return(STATUS_USER_APC
);
404 KeAddThreadTimeout(CurrentThread
,Timeout
);
409 KeAcquireDispatcherDatabaseLock(FALSE
);
411 DPRINT("hdr->SignalState %d\n", hdr
->SignalState
);
413 if (KiIsObjectSignalled(hdr
, CurrentThread
))
415 KeReleaseDispatcherDatabaseLock(FALSE
);
418 KeCancelTimer(&KeGetCurrentThread()->Timer
);
420 return(STATUS_WAIT_0
);
423 /* Append wait block to the KTHREAD wait block list */
424 CurrentThread
->WaitBlockList
= &CurrentThread
->WaitBlock
[0];
426 CurrentThread
->WaitBlock
[0].Object
= Object
;
427 CurrentThread
->WaitBlock
[0].Thread
= CurrentThread
;
428 CurrentThread
->WaitBlock
[0].WaitKey
= 0;
429 CurrentThread
->WaitBlock
[0].WaitType
= WaitAny
;
430 CurrentThread
->WaitBlock
[0].NextWaitBlock
= NULL
;
431 DPRINT("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x CurrentThread->WaitBlock[0].WaitListEntry = %x\n",
432 hdr
->WaitListHead
.Flink
,hdr
->WaitListHead
.Blink
, CurrentThread
->WaitBlock
[0].WaitListEntry
);
433 InsertTailList(&hdr
->WaitListHead
, &CurrentThread
->WaitBlock
[0].WaitListEntry
);
434 KeReleaseDispatcherDatabaseLock(FALSE
);
435 DPRINT("Waiting for %x with irql %d\n", Object
, KeGetCurrentIrql());
436 PsFreezeThread(PsGetCurrentThread(),
440 DPRINT("Woke from wait\n");
441 } while (Status
== STATUS_KERNEL_APC
);
445 KeCancelTimer(&KeGetCurrentThread()->Timer
);
448 DPRINT("Returning from KeWaitForSingleObject()\n");
455 KeWaitForMultipleObjects (
459 KWAIT_REASON WaitReason
,
460 KPROCESSOR_MODE WaitMode
,
462 PLARGE_INTEGER Timeout
,
463 PKWAIT_BLOCK WaitBlockArray
466 DISPATCHER_HEADER
* hdr
;
468 PKTHREAD CurrentThread
;
473 DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
474 "PsGetCurrentThread() %x\n",Count
,Object
,PsGetCurrentThread());
477 CurrentThread
= KeGetCurrentThread();
479 if (WaitBlockArray
== NULL
)
483 DbgPrint("(%s:%d) Too many objects!\n",
485 return STATUS_UNSUCCESSFUL
;
487 blk
= &CurrentThread
->WaitBlock
[0];
493 DbgPrint("(%s:%d) Too many objects!\n",
495 return STATUS_UNSUCCESSFUL
;
497 blk
= WaitBlockArray
;
501 KeAddThreadTimeout(CurrentThread
,Timeout
);
505 KeAcquireDispatcherDatabaseLock(FALSE
);
507 for (i
= 0; i
< Count
; i
++)
509 hdr
= (DISPATCHER_HEADER
*)Object
[i
];
511 DPRINT("hdr->SignalState %d\n", hdr
->SignalState
);
513 if (KiIsObjectSignalled(hdr
, CurrentThread
))
517 if (WaitType
== WaitAny
)
519 KeReleaseDispatcherDatabaseLock(FALSE
);
520 DPRINT("One object is already signaled!\n");
521 return(STATUS_WAIT_0
+ i
);
526 if ((WaitType
== WaitAll
) && (CountSignaled
== Count
))
528 KeReleaseDispatcherDatabaseLock(FALSE
);
529 DPRINT("All objects are already signaled!\n");
530 return(STATUS_WAIT_0
);
533 /* Append wait block to the KTHREAD wait block list */
534 CurrentThread
->WaitBlockList
= blk
;
536 for (i
= 0; i
< Count
; i
++)
538 hdr
= (DISPATCHER_HEADER
*)Object
[i
];
540 DPRINT("hdr->SignalState %d\n", hdr
->SignalState
);
542 blk
->Object
= Object
[i
];
543 blk
->Thread
= CurrentThread
;
545 blk
->WaitType
= WaitType
;
547 blk
->NextWaitBlock
= NULL
;
549 blk
->NextWaitBlock
= blk
+ 1;
550 DPRINT("blk %p blk->NextWaitBlock %p\n",
551 blk
, blk
->NextWaitBlock
);
552 InsertTailList(&(hdr
->WaitListHead
),&(blk
->WaitListEntry
));
553 // DPRINT("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
554 // hdr->WaitListHead.Flink,hdr->WaitListHead.Blink);
556 blk
= blk
->NextWaitBlock
;
559 KeReleaseDispatcherDatabaseLock(FALSE
);
561 DPRINT("Waiting at %s:%d with irql %d\n", __FILE__
, __LINE__
,
563 PsFreezeThread(PsGetCurrentThread(),
567 } while( Status
== STATUS_KERNEL_APC
);
569 KeCancelTimer(&KeGetCurrentThread()->Timer
);
570 DPRINT("Returning from KeWaitForMultipleObjects()\n");
574 VOID
KeInitializeDispatcher(VOID
)
576 KeInitializeSpinLock(&DispatcherDatabaseLock
);
579 NTSTATUS STDCALL
NtWaitForMultipleObjects(IN ULONG Count
,
582 IN BOOLEAN Alertable
,
583 IN PLARGE_INTEGER Time
)
585 KWAIT_BLOCK WaitBlockArray
[64]; /* FIXME: use MAXIMUM_WAIT_OBJECTS instead */
586 PVOID ObjectPtrArray
[64]; /* FIXME: use MAXIMUM_WAIT_OBJECTS instead */
590 DPRINT("NtWaitForMultipleObjects(Count %lu Object[] %x, Alertable %d, Time %x)\n",
591 Count
,Object
,Alertable
,Time
);
593 if (Count
> 64) /* FIXME: use MAXIMUM_WAIT_OBJECTS instead */
594 return STATUS_UNSUCCESSFUL
;
596 /* reference all objects */
597 for (i
= 0; i
< Count
; i
++)
599 Status
= ObReferenceObjectByHandle(Object
[i
],
605 if (Status
!= STATUS_SUCCESS
)
607 /* dereference all referenced objects */
608 for (j
= 0; j
< i
; i
++)
610 ObDereferenceObject(ObjectPtrArray
[j
]);
617 Status
= KeWaitForMultipleObjects(Count
,
626 /* dereference all objects */
627 for (i
= 0; i
< Count
; i
++)
629 ObDereferenceObject(ObjectPtrArray
[i
]);
636 NTSTATUS STDCALL
NtWaitForSingleObject (IN HANDLE Object
,
637 IN BOOLEAN Alertable
,
638 IN PLARGE_INTEGER Time
)
643 DPRINT("NtWaitForSingleObject(Object %x, Alertable %d, Time %x)\n",
644 Object
,Alertable
,Time
);
646 Status
= ObReferenceObjectByHandle(Object
,
652 if (!NT_SUCCESS(Status
))
657 DPRINT("ObjectPtr %x\n", ObjectPtr
);
659 Status
= KeWaitForSingleObject(ObjectPtr
,
665 DPRINT("Returned from wait (status is %x) ObjectPtr %x(%d)\n",
666 Status
, ObjectPtr
, ObGetReferenceCount(ObjectPtr
));
668 ObDereferenceObject(ObjectPtr
);
674 NTSTATUS STDCALL
NtSignalAndWaitForSingleObject (IN HANDLE EventHandle
,
675 IN BOOLEAN Alertable
,
676 IN PLARGE_INTEGER Time
,
678 NumberOfWaitingThreads OPTIONAL
)