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 assert(hdr
->SignalState
<= 1);
118 Mutex
->OwnerThread
= Thread
;
123 DbgPrint("(%s:%d) Dispatcher object %x has unknown type\n",
124 __FILE__
,__LINE__
,hdr
);
130 static BOOLEAN
KiIsObjectSignalled(DISPATCHER_HEADER
* hdr
,
133 if (hdr
->Type
== InternalMutexType
)
137 Mutex
= CONTAINING_RECORD(hdr
,
141 assert(hdr
->SignalState
<= 1);
142 if ((hdr
->SignalState
< 1 && Mutex
->OwnerThread
== Thread
) ||
143 hdr
->SignalState
== 1)
145 KiSideEffectsBeforeWake(hdr
,
154 if (hdr
->SignalState
<= 0)
160 KiSideEffectsBeforeWake(hdr
, Thread
);
165 VOID
KeRemoveAllWaitsThread(PETHREAD Thread
, NTSTATUS WaitStatus
)
167 PKWAIT_BLOCK WaitBlock
;
168 BOOLEAN WasWaiting
= FALSE
;
170 KeAcquireDispatcherDatabaseLock(FALSE
);
172 WaitBlock
= (PKWAIT_BLOCK
)Thread
->Tcb
.WaitBlockList
;
173 if (WaitBlock
!= NULL
)
177 while (WaitBlock
!= NULL
)
179 RemoveEntryList(&WaitBlock
->WaitListEntry
);
180 WaitBlock
= WaitBlock
->NextWaitBlock
;
182 Thread
->Tcb
.WaitBlockList
= NULL
;
186 PsUnfreezeThread(Thread
, &WaitStatus
);
189 KeReleaseDispatcherDatabaseLock(FALSE
);
192 static BOOLEAN
KeDispatcherObjectWakeAll(DISPATCHER_HEADER
* hdr
)
194 PKWAIT_BLOCK current
;
195 PLIST_ENTRY current_entry
;
196 PKWAIT_BLOCK PrevBlock
;
199 DPRINT("KeDispatcherObjectWakeAll(hdr %x)\n",hdr
);
201 if (IsListEmpty(&hdr
->WaitListHead
))
206 while (!IsListEmpty(&(hdr
->WaitListHead
)))
208 current_entry
= RemoveHeadList(&hdr
->WaitListHead
);
209 current
= CONTAINING_RECORD(current_entry
,
212 DPRINT("Waking %x\n",current
->Thread
);
213 if (current
->WaitType
== WaitAny
)
215 DPRINT("WaitAny: Remove all wait blocks.\n");
216 for( PrevBlock
= current
->Thread
->WaitBlockList
; PrevBlock
; PrevBlock
= PrevBlock
->NextWaitBlock
)
217 if( PrevBlock
!= current
)
218 RemoveEntryList( &(PrevBlock
->WaitListEntry
) );
219 current
->Thread
->WaitBlockList
= 0;
223 DPRINT("WaitAll: Remove the current wait block only.\n");
225 PrevBlock
= current
->Thread
->WaitBlockList
;
226 if (PrevBlock
== current
)
228 DPRINT( "WaitAll: Current block is list head.\n" );
229 current
->Thread
->WaitBlockList
= current
->NextWaitBlock
;
233 DPRINT( "WaitAll: Current block is not list head.\n" );
234 while ( PrevBlock
&& PrevBlock
->NextWaitBlock
!= current
)
236 PrevBlock
= PrevBlock
->NextWaitBlock
;
240 PrevBlock
->NextWaitBlock
= current
->NextWaitBlock
;
244 KiSideEffectsBeforeWake(hdr
, current
->Thread
);
245 Status
= current
->WaitKey
;
246 if (current
->Thread
->WaitBlockList
== NULL
)
248 PsUnfreezeThread(CONTAINING_RECORD(current
->Thread
,ETHREAD
,Tcb
),
255 static BOOLEAN
KeDispatcherObjectWakeOne(DISPATCHER_HEADER
* hdr
)
257 PKWAIT_BLOCK current
;
258 PLIST_ENTRY current_entry
;
259 PKWAIT_BLOCK PrevBlock
;
262 DPRINT("KeDispatcherObjectWakeOn(hdr %x)\n",hdr
);
263 DPRINT("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
264 hdr
->WaitListHead
.Flink
,hdr
->WaitListHead
.Blink
);
265 if (IsListEmpty(&(hdr
->WaitListHead
)))
269 current_entry
= RemoveHeadList(&(hdr
->WaitListHead
));
270 current
= CONTAINING_RECORD(current_entry
,KWAIT_BLOCK
,
272 DPRINT("current_entry %x current %x\n",current_entry
,current
);
274 if (current
->WaitType
== WaitAny
)
276 DPRINT("WaitAny: Remove all wait blocks.\n");
277 for( PrevBlock
= current
->Thread
->WaitBlockList
; PrevBlock
; PrevBlock
= PrevBlock
->NextWaitBlock
)
278 if( PrevBlock
!= current
)
279 RemoveEntryList( &(PrevBlock
->WaitListEntry
) );
280 current
->Thread
->WaitBlockList
= 0;
284 DPRINT("WaitAll: Remove the current wait block only.\n");
286 PrevBlock
= current
->Thread
->WaitBlockList
;
287 if (PrevBlock
== current
)
289 DPRINT( "WaitAll: Current block is list head.\n" );
290 current
->Thread
->WaitBlockList
= current
->NextWaitBlock
;
294 DPRINT( "WaitAll: Current block is not list head.\n" );
295 while ( PrevBlock
&& PrevBlock
->NextWaitBlock
!= current
)
297 PrevBlock
= PrevBlock
->NextWaitBlock
;
301 PrevBlock
->NextWaitBlock
= current
->NextWaitBlock
;
306 DPRINT("Waking %x\n",current
->Thread
);
307 KiSideEffectsBeforeWake(hdr
, current
->Thread
);
308 Status
= current
->WaitKey
;
309 PsUnfreezeThread(CONTAINING_RECORD(current
->Thread
, ETHREAD
, Tcb
),
314 BOOLEAN
KeDispatcherObjectWake(DISPATCHER_HEADER
* hdr
)
316 * FUNCTION: Wake threads waiting on a dispatcher object
317 * NOTE: The exact semantics of waking are dependant on the type of object
322 DPRINT("Entering KeDispatcherObjectWake(hdr %x)\n",hdr
);
323 // DPRINT("hdr->WaitListHead %x hdr->WaitListHead.Flink %x\n",
324 // &hdr->WaitListHead,hdr->WaitListHead.Flink);
325 DPRINT("hdr->Type %x\n",hdr
->Type
);
328 case InternalNotificationEvent
:
329 return(KeDispatcherObjectWakeAll(hdr
));
331 case InternalNotificationTimer
:
332 return(KeDispatcherObjectWakeAll(hdr
));
334 case InternalSynchronizationEvent
:
335 return(KeDispatcherObjectWakeOne(hdr
));
338 case InternalSynchronizationTimer
:
339 return(KeDispatcherObjectWakeOne(hdr
));
341 case InternalSemaphoreType
:
342 DPRINT("hdr->SignalState %d\n", hdr
->SignalState
);
343 if(hdr
->SignalState
>0)
347 DPRINT("Waking one semaphore waiter\n");
348 Ret
= KeDispatcherObjectWakeOne(hdr
);
349 } while(hdr
->SignalState
> 0 && Ret
) ;
354 case InternalProcessType
:
355 return(KeDispatcherObjectWakeAll(hdr
));
357 case InternalThreadType
:
358 return(KeDispatcherObjectWakeAll(hdr
));
360 case InternalMutexType
:
361 return(KeDispatcherObjectWakeOne(hdr
));
363 DbgPrint("Dispatcher object %x has unknown type %d\n", hdr
, hdr
->Type
);
369 NTSTATUS STDCALL
KeWaitForSingleObject (PVOID Object
,
370 KWAIT_REASON WaitReason
,
371 KPROCESSOR_MODE WaitMode
,
373 PLARGE_INTEGER Timeout
)
375 * FUNCTION: Puts the current thread into a wait state until the
376 * given dispatcher object is set to signalled
378 * Object = Object to wait on
379 * WaitReason = Reason for the wait (debugging aid)
380 * WaitMode = Can be KernelMode or UserMode, if UserMode then
381 * user-mode APCs can be delivered and the thread's
382 * stack can be paged out
383 * Altertable = Specifies if the wait is a alertable
384 * Timeout = Optional timeout value
388 DISPATCHER_HEADER
* hdr
= (DISPATCHER_HEADER
*)Object
;
389 PKTHREAD CurrentThread
;
392 DPRINT("Entering KeWaitForSingleObject(Object %x) "
393 "PsGetCurrentThread() %x\n",Object
,PsGetCurrentThread());
395 CurrentThread
= KeGetCurrentThread();
397 if (Alertable
&& !IsListEmpty(&CurrentThread
->ApcState
.ApcListHead
[1]))
399 DPRINT("Thread is alertable and user APCs are pending\n");
400 return(STATUS_USER_APC
);
405 KeAddThreadTimeout(CurrentThread
,Timeout
);
410 KeAcquireDispatcherDatabaseLock(FALSE
);
412 DPRINT("hdr->SignalState %d\n", hdr
->SignalState
);
414 if (KiIsObjectSignalled(hdr
, CurrentThread
))
416 KeReleaseDispatcherDatabaseLock(FALSE
);
419 KeCancelTimer(&KeGetCurrentThread()->Timer
);
421 return(STATUS_WAIT_0
);
424 /* Append wait block to the KTHREAD wait block list */
425 CurrentThread
->WaitBlockList
= &CurrentThread
->WaitBlock
[0];
427 CurrentThread
->WaitBlock
[0].Object
= Object
;
428 CurrentThread
->WaitBlock
[0].Thread
= CurrentThread
;
429 CurrentThread
->WaitBlock
[0].WaitKey
= 0;
430 CurrentThread
->WaitBlock
[0].WaitType
= WaitAny
;
431 CurrentThread
->WaitBlock
[0].NextWaitBlock
= NULL
;
432 DPRINT("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x CurrentThread->WaitBlock[0].WaitListEntry = %x\n",
433 hdr
->WaitListHead
.Flink
,hdr
->WaitListHead
.Blink
, CurrentThread
->WaitBlock
[0].WaitListEntry
);
434 InsertTailList(&hdr
->WaitListHead
, &CurrentThread
->WaitBlock
[0].WaitListEntry
);
435 KeReleaseDispatcherDatabaseLock(FALSE
);
436 DPRINT("Waiting for %x with irql %d\n", Object
, KeGetCurrentIrql());
437 PsFreezeThread(PsGetCurrentThread(),
441 DPRINT("Woke from wait\n");
442 } while (Status
== STATUS_KERNEL_APC
);
446 KeCancelTimer(&KeGetCurrentThread()->Timer
);
449 DPRINT("Returning from KeWaitForSingleObject()\n");
456 KeWaitForMultipleObjects (
460 KWAIT_REASON WaitReason
,
461 KPROCESSOR_MODE WaitMode
,
463 PLARGE_INTEGER Timeout
,
464 PKWAIT_BLOCK WaitBlockArray
467 DISPATCHER_HEADER
* hdr
;
469 PKTHREAD CurrentThread
;
474 DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
475 "PsGetCurrentThread() %x\n",Count
,Object
,PsGetCurrentThread());
478 CurrentThread
= KeGetCurrentThread();
480 if (WaitBlockArray
== NULL
)
484 DbgPrint("(%s:%d) Too many objects!\n",
486 return STATUS_UNSUCCESSFUL
;
488 blk
= &CurrentThread
->WaitBlock
[0];
494 DbgPrint("(%s:%d) Too many objects!\n",
496 return STATUS_UNSUCCESSFUL
;
498 blk
= WaitBlockArray
;
502 KeAddThreadTimeout(CurrentThread
,Timeout
);
506 KeAcquireDispatcherDatabaseLock(FALSE
);
508 for (i
= 0; i
< Count
; i
++)
510 hdr
= (DISPATCHER_HEADER
*)Object
[i
];
512 DPRINT("hdr->SignalState %d\n", hdr
->SignalState
);
514 if (KiIsObjectSignalled(hdr
, CurrentThread
))
518 if (WaitType
== WaitAny
)
520 KeReleaseDispatcherDatabaseLock(FALSE
);
521 DPRINT("One object is already signaled!\n");
522 return(STATUS_WAIT_0
+ i
);
527 if ((WaitType
== WaitAll
) && (CountSignaled
== Count
))
529 KeReleaseDispatcherDatabaseLock(FALSE
);
530 DPRINT("All objects are already signaled!\n");
531 return(STATUS_WAIT_0
);
534 /* Append wait block to the KTHREAD wait block list */
535 CurrentThread
->WaitBlockList
= blk
;
537 for (i
= 0; i
< Count
; i
++)
539 hdr
= (DISPATCHER_HEADER
*)Object
[i
];
541 DPRINT("hdr->SignalState %d\n", hdr
->SignalState
);
543 blk
->Object
= Object
[i
];
544 blk
->Thread
= CurrentThread
;
546 blk
->WaitType
= WaitType
;
548 blk
->NextWaitBlock
= NULL
;
550 blk
->NextWaitBlock
= blk
+ 1;
551 DPRINT("blk %p blk->NextWaitBlock %p\n",
552 blk
, blk
->NextWaitBlock
);
553 InsertTailList(&(hdr
->WaitListHead
),&(blk
->WaitListEntry
));
554 // DPRINT("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
555 // hdr->WaitListHead.Flink,hdr->WaitListHead.Blink);
557 blk
= blk
->NextWaitBlock
;
560 KeReleaseDispatcherDatabaseLock(FALSE
);
562 DPRINT("Waiting at %s:%d with irql %d\n", __FILE__
, __LINE__
,
564 PsFreezeThread(PsGetCurrentThread(),
568 } while( Status
== STATUS_KERNEL_APC
);
570 KeCancelTimer(&KeGetCurrentThread()->Timer
);
571 DPRINT("Returning from KeWaitForMultipleObjects()\n");
575 VOID
KeInitializeDispatcher(VOID
)
577 KeInitializeSpinLock(&DispatcherDatabaseLock
);
580 NTSTATUS STDCALL
NtWaitForMultipleObjects(IN ULONG Count
,
583 IN BOOLEAN Alertable
,
584 IN PLARGE_INTEGER Time
)
586 KWAIT_BLOCK WaitBlockArray
[64]; /* FIXME: use MAXIMUM_WAIT_OBJECTS instead */
587 PVOID ObjectPtrArray
[64]; /* FIXME: use MAXIMUM_WAIT_OBJECTS instead */
591 DPRINT("NtWaitForMultipleObjects(Count %lu Object[] %x, Alertable %d, Time %x)\n",
592 Count
,Object
,Alertable
,Time
);
594 if (Count
> 64) /* FIXME: use MAXIMUM_WAIT_OBJECTS instead */
595 return STATUS_UNSUCCESSFUL
;
597 /* reference all objects */
598 for (i
= 0; i
< Count
; i
++)
600 Status
= ObReferenceObjectByHandle(Object
[i
],
606 if (Status
!= STATUS_SUCCESS
)
608 /* dereference all referenced objects */
609 for (j
= 0; j
< i
; i
++)
611 ObDereferenceObject(ObjectPtrArray
[j
]);
618 Status
= KeWaitForMultipleObjects(Count
,
627 /* dereference all objects */
628 for (i
= 0; i
< Count
; i
++)
630 ObDereferenceObject(ObjectPtrArray
[i
]);
637 NTSTATUS STDCALL
NtWaitForSingleObject (IN HANDLE Object
,
638 IN BOOLEAN Alertable
,
639 IN PLARGE_INTEGER Time
)
644 DPRINT("NtWaitForSingleObject(Object %x, Alertable %d, Time %x)\n",
645 Object
,Alertable
,Time
);
647 Status
= ObReferenceObjectByHandle(Object
,
653 if (!NT_SUCCESS(Status
))
658 DPRINT("ObjectPtr %x\n", ObjectPtr
);
660 Status
= KeWaitForSingleObject(ObjectPtr
,
666 DPRINT("Returned from wait (status is %x) ObjectPtr %x(%d)\n",
667 Status
, ObjectPtr
, ObGetReferenceCount(ObjectPtr
));
669 ObDereferenceObject(ObjectPtr
);
675 NTSTATUS STDCALL
NtSignalAndWaitForSingleObject (IN HANDLE EventHandle
,
676 IN BOOLEAN Alertable
,
677 IN PLARGE_INTEGER Time
,
679 NumberOfWaitingThreads OPTIONAL
)