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)
11 /* NOTES ********************************************************************
15 /* INCLUDES ******************************************************************/
17 #include <ddk/ntddk.h>
18 #include <internal/ke.h>
21 #include <internal/debug.h>
23 /* GLOBALS ******************************************************************/
25 static KSPIN_LOCK DispatcherDatabaseLock
;
26 static BOOLEAN WaitSet
= FALSE
;
27 static KIRQL oldlvl
= PASSIVE_LEVEL
;
28 static PKTHREAD Owner
= NULL
;
30 /* FUNCTIONS *****************************************************************/
32 VOID
KeInitializeDispatcherHeader(DISPATCHER_HEADER
* Header
,
41 Header
->SignalState
= SignalState
;
42 InitializeListHead(&(Header
->WaitListHead
));
45 VOID
KeAcquireDispatcherDatabaseLock(BOOLEAN Wait
)
47 * PURPOSE: Acquires the dispatcher database lock for the caller
50 DPRINT("KeAcquireDispatcherDatabaseLock(Wait %x)\n",Wait
);
51 if (WaitSet
&& Owner
== KeGetCurrentThread())
55 KeAcquireSpinLock(&DispatcherDatabaseLock
,&oldlvl
);
57 Owner
= KeGetCurrentThread();
60 VOID
KeReleaseDispatcherDatabaseLock(BOOLEAN Wait
)
62 DPRINT("KeReleaseDispatcherDatabaseLock(Wait %x)\n",Wait
);
63 assert(Wait
==WaitSet
);
67 KeReleaseSpinLock(&DispatcherDatabaseLock
, oldlvl
);
71 VOID
KiSideEffectsBeforeWake(DISPATCHER_HEADER
* hdr
)
73 * FUNCTION: Perform side effects on object before a wait for a thread is
79 case InternalSynchronizationEvent
:
80 hdr
->SignalState
= FALSE
;
83 case InternalSemaphoreType
:
87 case InternalProcessType
:
90 case InternalThreadType
:
93 case InternalNotificationEvent
:
96 case InternalSynchronizationTimer
:
97 hdr
->SignalState
= FALSE
;
100 case InternalNotificationTimer
:
104 DbgPrint("(%s:%d) Dispatcher object %x has unknown type\n",
105 __FILE__
,__LINE__
,hdr
);
111 static BOOLEAN
KiIsObjectSignalled(DISPATCHER_HEADER
* hdr
)
113 if (hdr
->SignalState
<= 0)
117 KiSideEffectsBeforeWake(hdr
);
122 static BOOLEAN
KeDispatcherObjectWakeAll(DISPATCHER_HEADER
* hdr
)
124 PKWAIT_BLOCK current
;
125 PLIST_ENTRY current_entry
;
126 PKWAIT_BLOCK PrevBlock
;
128 DPRINT("KeDispatcherObjectWakeAll(hdr %x)\n",hdr
);
130 if (IsListEmpty(&hdr
->WaitListHead
))
135 while (!IsListEmpty(&(hdr
->WaitListHead
)))
137 current_entry
= RemoveHeadList(&hdr
->WaitListHead
);
138 current
= CONTAINING_RECORD(current_entry
,KWAIT_BLOCK
,
140 DPRINT("Waking %x\n",current
->Thread
);
142 if (current
->WaitType
== WaitAny
)
144 DPRINT("WaitAny: Remove all wait blocks.\n");
145 current
->Thread
->WaitBlockList
= NULL
;
149 DPRINT("WaitAll: Remove the current wait block only.\n");
151 PrevBlock
= current
->Thread
->WaitBlockList
;
154 if (PrevBlock
->NextWaitBlock
== current
)
156 DPRINT("WaitAll: Current block is list head.\n");
157 PrevBlock
->NextWaitBlock
= current
->NextWaitBlock
;
161 DPRINT("WaitAll: Current block is list head.\n");
163 PrevBlock
->NextWaitBlock
!= current
)
165 PrevBlock
= PrevBlock
->NextWaitBlock
;
170 PrevBlock
->NextWaitBlock
= current
->NextWaitBlock
;
176 DPRINT("WaitAll: Wait Block List is empty!\n");
180 KiSideEffectsBeforeWake(hdr
);
182 PsResumeThread(CONTAINING_RECORD(current
->Thread
,ETHREAD
,Tcb
),
188 static BOOLEAN
KeDispatcherObjectWakeOne(DISPATCHER_HEADER
* hdr
)
190 PKWAIT_BLOCK current
;
191 PLIST_ENTRY current_entry
;
192 PKWAIT_BLOCK PrevBlock
;
194 DPRINT("KeDispatcherObjectWakeOn(hdr %x)\n",hdr
);
195 DPRINT("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
196 hdr
->WaitListHead
.Flink
,hdr
->WaitListHead
.Blink
);
197 if (IsListEmpty(&(hdr
->WaitListHead
)))
201 current_entry
= RemoveHeadList(&(hdr
->WaitListHead
));
202 current
= CONTAINING_RECORD(current_entry
,KWAIT_BLOCK
,
204 DPRINT("current_entry %x current %x\n",current_entry
,current
);
207 if (current
->WaitType
== WaitAny
)
209 DPRINT("WaitAny: Remove all wait blocks.\n");
210 current
->Thread
->WaitBlockList
= NULL
;
214 DPRINT("WaitAll: Remove the current wait block only.\n");
216 PrevBlock
= current
->Thread
->WaitBlockList
;
219 if (PrevBlock
->NextWaitBlock
== current
)
221 DPRINT("WaitAll: Current block is list head.\n");
222 PrevBlock
->NextWaitBlock
= current
->NextWaitBlock
;
226 DPRINT("WaitAll: Current block is list head.\n");
227 while (PrevBlock
&& PrevBlock
->NextWaitBlock
!= current
)
229 PrevBlock
= PrevBlock
->NextWaitBlock
;
234 PrevBlock
->NextWaitBlock
= current
->NextWaitBlock
;
240 DPRINT("WaitAll: Wait Block List is empty!\n");
244 DPRINT("Waking %x\n",current
->Thread
);
246 KiSideEffectsBeforeWake(hdr
);
248 PsResumeThread(CONTAINING_RECORD(current
->Thread
,ETHREAD
,Tcb
),
253 BOOLEAN
KeDispatcherObjectWake(DISPATCHER_HEADER
* hdr
)
255 * FUNCTION: Wake threads waiting on a dispatcher object
256 * NOTE: The exact semantics of waking are dependant on the type of object
261 DPRINT("Entering KeDispatcherObjectWake(hdr %x)\n",hdr
);
262 // DPRINT("hdr->WaitListHead %x hdr->WaitListHead.Flink %x\n",
263 // &hdr->WaitListHead,hdr->WaitListHead.Flink);
264 DPRINT("hdr->Type %x\n",hdr
->Type
);
267 case InternalNotificationEvent
:
268 return(KeDispatcherObjectWakeAll(hdr
));
270 case InternalNotificationTimer
:
271 return(KeDispatcherObjectWakeAll(hdr
));
273 case InternalSynchronizationEvent
:
274 return(KeDispatcherObjectWakeOne(hdr
));
277 case InternalSynchronizationTimer
:
278 return(KeDispatcherObjectWakeOne(hdr
));
280 case InternalSemaphoreType
:
281 DPRINT("hdr->SignalState %d\n", hdr
->SignalState
);
282 if(hdr
->SignalState
>0)
286 DPRINT("Waking one semaphore waiter\n");
287 Ret
= KeDispatcherObjectWakeOne(hdr
);
288 } while(hdr
->SignalState
> 0 && Ret
) ;
293 case InternalProcessType
:
294 return(KeDispatcherObjectWakeAll(hdr
));
296 case InternalThreadType
:
297 return(KeDispatcherObjectWakeAll(hdr
));
299 case InternalMutexType
:
300 return(KeDispatcherObjectWakeOne(hdr
));
302 DPRINT("Dispatcher object %x has unknown type\n",hdr
);
308 NTSTATUS
KeWaitForSingleObject(PVOID Object
,
309 KWAIT_REASON WaitReason
,
310 KPROCESSOR_MODE WaitMode
,
312 PLARGE_INTEGER Timeout
)
314 * FUNCTION: Puts the current thread into a wait state until the
315 * given dispatcher object is set to signalled
317 * Object = Object to wait on
318 * WaitReason = Reason for the wait (debugging aid)
319 * WaitMode = Can be KernelMode or UserMode, if UserMode then
320 * user-mode APCs can be delivered and the thread's
321 * stack can be paged out
322 * Altertable = Specifies if the wait is a alertable
323 * Timeout = Optional timeout value
327 DISPATCHER_HEADER
* hdr
= (DISPATCHER_HEADER
*)Object
;
329 PKTHREAD CurrentThread
;
332 DPRINT("Entering KeWaitForSingleObject(Object %x) "
333 "PsGetCurrentThread() %x\n",Object
,PsGetCurrentThread());
335 CurrentThread
= KeGetCurrentThread();
337 if (Alertable
&& !IsListEmpty(&CurrentThread
->ApcState
.ApcListHead
[1]))
339 DPRINT("Thread is alertable and user APCs are pending\n");
340 return(STATUS_USER_APC
);
343 KeAcquireDispatcherDatabaseLock(FALSE
);
345 DPRINT("hdr->SignalState %d\n", hdr
->SignalState
);
347 if (KiIsObjectSignalled(hdr
))
349 KeReleaseDispatcherDatabaseLock(FALSE
);
350 return(STATUS_WAIT_0
);
355 KeAddThreadTimeout(CurrentThread
,Timeout
);
358 /* Append wait block to the KTHREAD wait block list */
359 CurrentThread
->WaitBlockList
= &blk
;
362 blk
.Thread
= CurrentThread
;
364 blk
.WaitType
= WaitAny
;
365 blk
.NextWaitBlock
= NULL
;
366 InsertTailList(&(hdr
->WaitListHead
),&(blk
.WaitListEntry
));
367 // DPRINT("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
368 // hdr->WaitListHead.Flink,hdr->WaitListHead.Blink);
369 KeReleaseDispatcherDatabaseLock(FALSE
);
370 DPRINT("Waiting at %s:%d with irql %d\n", __FILE__
, __LINE__
,
372 PsSuspendThread(PsGetCurrentThread(),
379 KeCancelTimer(&KeGetCurrentThread()->Timer
);
380 if (KeReadStateTimer(&KeGetCurrentThread()->Timer
))
381 return(STATUS_TIMEOUT
);
385 !IsListEmpty(&CurrentThread
->ApcState
.ApcListHead
[1]))
387 DPRINT("Current thread is alertable and APCs are pending\n");
388 return(STATUS_USER_APC
);
391 DPRINT("Returning from KeWaitForSingleObject()\n");
396 NTSTATUS
KeWaitForMultipleObjects(ULONG Count
,
399 KWAIT_REASON WaitReason
,
400 KPROCESSOR_MODE WaitMode
,
402 PLARGE_INTEGER Timeout
,
403 PKWAIT_BLOCK WaitBlockArray
)
405 DISPATCHER_HEADER
* hdr
;
407 PKTHREAD CurrentThread
;
412 DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
413 "PsGetCurrentThread() %x\n",Count
,Object
,PsGetCurrentThread());
416 CurrentThread
= KeGetCurrentThread();
418 if (WaitBlockArray
== NULL
)
422 DbgPrint("(%s:%d) Too many objects!\n",
424 return STATUS_UNSUCCESSFUL
;
426 blk
= &CurrentThread
->WaitBlock
[1];
432 DbgPrint("(%s:%d) Too many objects!\n",
434 return STATUS_UNSUCCESSFUL
;
436 blk
= WaitBlockArray
;
439 KeAcquireDispatcherDatabaseLock(FALSE
);
441 for (i
= 0; i
< Count
; i
++)
443 hdr
= (DISPATCHER_HEADER
*)Object
[i
];
445 DPRINT("hdr->SignalState %d\n", hdr
->SignalState
);
447 if (KiIsObjectSignalled(hdr
))
451 if (WaitType
== WaitAny
)
453 KeReleaseDispatcherDatabaseLock(FALSE
);
454 DPRINT("One object is already signaled!\n");
455 return(STATUS_WAIT_0
+ i
);
460 if ((WaitType
== WaitAll
) && (CountSignaled
== Count
))
462 KeReleaseDispatcherDatabaseLock(FALSE
);
463 DPRINT("All objects are already signaled!\n");
464 return(STATUS_WAIT_0
);
469 KeAddThreadTimeout(CurrentThread
,Timeout
);
472 /* Append wait block to the KTHREAD wait block list */
473 CurrentThread
->WaitBlockList
= blk
;
475 for (i
= 0; i
< Count
; i
++)
477 hdr
= (DISPATCHER_HEADER
*)Object
[i
];
479 DPRINT("hdr->SignalState %d\n", hdr
->SignalState
);
481 blk
->Object
= Object
[i
];
482 blk
->Thread
= CurrentThread
;
484 blk
->WaitType
= WaitType
;
486 blk
->NextWaitBlock
= NULL
;
488 blk
->NextWaitBlock
= (PVOID
)((ULONG
)blk
+sizeof(KWAIT_BLOCK
));
489 DPRINT("blk %p blk->NextWaitBlock %p\n",
490 blk
, blk
->NextWaitBlock
);
492 InsertTailList(&(hdr
->WaitListHead
),&(blk
->WaitListEntry
));
493 // DPRINT("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
494 // hdr->WaitListHead.Flink,hdr->WaitListHead.Blink);
496 blk
= blk
->NextWaitBlock
;
499 KeReleaseDispatcherDatabaseLock(FALSE
);
501 DPRINT("Waiting at %s:%d with irql %d\n", __FILE__
, __LINE__
,
503 PsSuspendThread(PsGetCurrentThread(),
510 KeCancelTimer(&KeGetCurrentThread()->Timer
);
511 if (KeReadStateTimer(&KeGetCurrentThread()->Timer
))
512 return(STATUS_TIMEOUT
);
515 DPRINT("Returning from KeWaitForMultipleObjects()\n");
517 if (WaitType
== WaitAny
)
519 for (i
= 0; i
< Count
; i
++)
521 if (((DISPATCHER_HEADER
*)Object
[i
])->SignalState
)
522 return(STATUS_WAIT_0
+i
);
529 VOID
KeInitializeDispatcher(VOID
)
531 KeInitializeSpinLock(&DispatcherDatabaseLock
);
534 NTSTATUS STDCALL
NtWaitForMultipleObjects(IN ULONG Count
,
537 IN BOOLEAN Alertable
,
538 IN PLARGE_INTEGER Time
)
540 KWAIT_BLOCK WaitBlockArray
[64]; /* FIXME: use MAXIMUM_WAIT_OBJECTS instead */
541 PVOID ObjectPtrArray
[64]; /* FIXME: use MAXIMUM_WAIT_OBJECTS instead */
545 DPRINT("NtWaitForMultipleObjects(Count %lu Object[] %x, Alertable %d, Time %x)\n",
546 Count
,Object
,Alertable
,Time
);
548 if (Count
> 64) /* FIXME: use MAXIMUM_WAIT_OBJECTS instead */
549 return STATUS_UNSUCCESSFUL
;
551 /* reference all objects */
552 for (i
= 0; i
< Count
; i
++)
554 Status
= ObReferenceObjectByHandle(Object
[i
],
560 if (Status
!= STATUS_SUCCESS
)
562 /* dereference all referenced objects */
563 for (j
= 0; j
< i
; i
++)
565 ObDereferenceObject(ObjectPtrArray
[j
]);
572 Status
= KeWaitForMultipleObjects(Count
,
581 /* dereference all objects */
582 for (i
= 0; i
< Count
; i
++)
584 ObDereferenceObject(ObjectPtrArray
[i
]);
591 NTSTATUS STDCALL
NtWaitForSingleObject (IN HANDLE Object
,
592 IN BOOLEAN Alertable
,
593 IN PLARGE_INTEGER Time
)
598 DPRINT("NtWaitForSingleObject(Object %x, Alertable %d, Time %x)\n",
599 Object
,Alertable
,Time
);
601 Status
= ObReferenceObjectByHandle(Object
,
607 if (Status
!= STATUS_SUCCESS
)
612 Status
= KeWaitForSingleObject(ObjectPtr
,
618 ObDereferenceObject(ObjectPtr
);
626 NTSTATUS STDCALL
NtSignalAndWaitForSingleObject (IN HANDLE EventHandle
,
627 IN BOOLEAN Alertable
,
628 IN PLARGE_INTEGER Time
,
630 NumberOfWaitingThreads OPTIONAL
)