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");
401 return(STATUS_USER_APC
);
406 KeAddThreadTimeout(CurrentThread
,Timeout
);
411 KeAcquireDispatcherDatabaseLock(FALSE
);
413 DPRINT("hdr->SignalState %d\n", hdr
->SignalState
);
415 if (KiIsObjectSignalled(hdr
, CurrentThread
))
417 KeReleaseDispatcherDatabaseLock(FALSE
);
420 KeCancelTimer(&KeGetCurrentThread()->Timer
);
422 return(STATUS_WAIT_0
);
425 CurrentThread
->WaitStatus
= STATUS_UNSUCCESSFUL
;
426 /* Append wait block to the KTHREAD wait block list */
427 CurrentThread
->WaitBlockList
= &CurrentThread
->WaitBlock
[0];
428 CurrentThread
->WaitBlock
[0].Object
= Object
;
429 CurrentThread
->WaitBlock
[0].Thread
= CurrentThread
;
430 CurrentThread
->WaitBlock
[0].WaitKey
= 0;
431 CurrentThread
->WaitBlock
[0].WaitType
= WaitAny
;
432 CurrentThread
->WaitBlock
[0].NextWaitBlock
= NULL
;
433 InsertTailList(&hdr
->WaitListHead
,
434 &CurrentThread
->WaitBlock
[0].WaitListEntry
);
435 KeReleaseDispatcherDatabaseLock(FALSE
);
436 DPRINT("Waiting for %x with irql %d\n", Object
, KeGetCurrentIrql());
437 Status
= STATUS_SUCCESS
;
438 PsFreezeThread(PsGetCurrentThread(),
442 if (!NT_SUCCESS(Status
))
444 DPRINT1("Woke from wait with status %x\n", Status
);
446 } while (Status
== STATUS_KERNEL_APC
);
450 KeCancelTimer(&KeGetCurrentThread()->Timer
);
453 DPRINT("Returning from KeWaitForSingleObject()\n");
460 KeWaitForMultipleObjects (
464 KWAIT_REASON WaitReason
,
465 KPROCESSOR_MODE WaitMode
,
467 PLARGE_INTEGER Timeout
,
468 PKWAIT_BLOCK WaitBlockArray
471 DISPATCHER_HEADER
* hdr
;
473 PKTHREAD CurrentThread
;
478 DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
479 "PsGetCurrentThread() %x\n",Count
,Object
,PsGetCurrentThread());
482 CurrentThread
= KeGetCurrentThread();
484 if (WaitBlockArray
== NULL
)
488 DbgPrint("(%s:%d) Too many objects!\n",
490 return STATUS_UNSUCCESSFUL
;
492 blk
= &CurrentThread
->WaitBlock
[0];
498 DbgPrint("(%s:%d) Too many objects!\n",
500 return STATUS_UNSUCCESSFUL
;
502 blk
= WaitBlockArray
;
506 KeAddThreadTimeout(CurrentThread
,Timeout
);
510 KeAcquireDispatcherDatabaseLock(FALSE
);
512 for (i
= 0; i
< Count
; i
++)
514 hdr
= (DISPATCHER_HEADER
*)Object
[i
];
516 DPRINT("hdr->SignalState %d\n", hdr
->SignalState
);
518 if (KiIsObjectSignalled(hdr
, CurrentThread
))
522 if (WaitType
== WaitAny
)
524 KeReleaseDispatcherDatabaseLock(FALSE
);
525 DPRINT("One object is already signaled!\n");
526 return(STATUS_WAIT_0
+ i
);
531 if ((WaitType
== WaitAll
) && (CountSignaled
== Count
))
533 KeReleaseDispatcherDatabaseLock(FALSE
);
534 DPRINT("All objects are already signaled!\n");
535 return(STATUS_WAIT_0
);
538 /* Append wait block to the KTHREAD wait block list */
539 CurrentThread
->WaitBlockList
= blk
;
541 for (i
= 0; i
< Count
; i
++)
543 hdr
= (DISPATCHER_HEADER
*)Object
[i
];
545 DPRINT("hdr->SignalState %d\n", hdr
->SignalState
);
547 blk
->Object
= Object
[i
];
548 blk
->Thread
= CurrentThread
;
550 blk
->WaitType
= WaitType
;
552 blk
->NextWaitBlock
= NULL
;
554 blk
->NextWaitBlock
= blk
+ 1;
555 DPRINT("blk %p blk->NextWaitBlock %p\n",
556 blk
, blk
->NextWaitBlock
);
557 InsertTailList(&(hdr
->WaitListHead
),&(blk
->WaitListEntry
));
558 // DPRINT("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
559 // hdr->WaitListHead.Flink,hdr->WaitListHead.Blink);
561 blk
= blk
->NextWaitBlock
;
564 KeReleaseDispatcherDatabaseLock(FALSE
);
566 DPRINT("Waiting at %s:%d with irql %d\n", __FILE__
, __LINE__
,
568 PsFreezeThread(PsGetCurrentThread(),
572 } while( Status
== STATUS_KERNEL_APC
);
574 KeCancelTimer(&KeGetCurrentThread()->Timer
);
575 DPRINT("Returning from KeWaitForMultipleObjects()\n");
579 VOID
KeInitializeDispatcher(VOID
)
581 KeInitializeSpinLock(&DispatcherDatabaseLock
);
584 NTSTATUS STDCALL
NtWaitForMultipleObjects(IN ULONG Count
,
587 IN BOOLEAN Alertable
,
588 IN PLARGE_INTEGER Time
)
590 KWAIT_BLOCK WaitBlockArray
[64]; /* FIXME: use MAXIMUM_WAIT_OBJECTS instead */
591 PVOID ObjectPtrArray
[64]; /* FIXME: use MAXIMUM_WAIT_OBJECTS instead */
595 DPRINT("NtWaitForMultipleObjects(Count %lu Object[] %x, Alertable %d, Time %x)\n",
596 Count
,Object
,Alertable
,Time
);
598 if (Count
> 64) /* FIXME: use MAXIMUM_WAIT_OBJECTS instead */
599 return STATUS_UNSUCCESSFUL
;
601 /* reference all objects */
602 for (i
= 0; i
< Count
; i
++)
604 Status
= ObReferenceObjectByHandle(Object
[i
],
610 if (Status
!= STATUS_SUCCESS
)
612 /* dereference all referenced objects */
613 for (j
= 0; j
< i
; i
++)
615 ObDereferenceObject(ObjectPtrArray
[j
]);
622 Status
= KeWaitForMultipleObjects(Count
,
631 /* dereference all objects */
632 for (i
= 0; i
< Count
; i
++)
634 ObDereferenceObject(ObjectPtrArray
[i
]);
641 NTSTATUS STDCALL
NtWaitForSingleObject (IN HANDLE Object
,
642 IN BOOLEAN Alertable
,
643 IN PLARGE_INTEGER Time
)
648 DPRINT("NtWaitForSingleObject(Object %x, Alertable %d, Time %x)\n",
649 Object
,Alertable
,Time
);
651 Status
= ObReferenceObjectByHandle(Object
,
657 if (!NT_SUCCESS(Status
))
662 DPRINT("ObjectPtr %x\n", ObjectPtr
);
664 Status
= KeWaitForSingleObject(ObjectPtr
,
670 DPRINT("Returned from wait (status is %x) ObjectPtr %x(%d)\n",
671 Status
, ObjectPtr
, ObGetReferenceCount(ObjectPtr
));
673 ObDereferenceObject(ObjectPtr
);
680 NtSignalAndWaitForSingleObject(IN HANDLE SignalObject
,
681 IN HANDLE WaitObject
,
682 IN BOOLEAN Alertable
,
683 IN PLARGE_INTEGER Time
)
685 KPROCESSOR_MODE ProcessorMode
;
686 DISPATCHER_HEADER
* hdr
;
691 ProcessorMode
= CURRENT_KPCR
->CurrentThread
->PreviousMode
;
692 Status
= ObReferenceObjectByHandle(SignalObject
,
698 if (!NT_SUCCESS(Status
))
703 Status
= ObReferenceObjectByHandle(WaitObject
,
709 if (!NT_SUCCESS(Status
))
711 ObDereferenceObject(SignalObj
);
715 hdr
= (DISPATCHER_HEADER
*)SignalObj
;
718 case InternalNotificationEvent
:
719 case InternalSynchronizationEvent
:
720 KeSetEvent(SignalObj
,
725 case InternalMutexType
:
726 KeReleaseMutex(SignalObj
,
730 case InternalSemaphoreType
:
731 KeReleaseSemaphore(SignalObj
,
738 ObDereferenceObject(SignalObj
);
739 ObDereferenceObject(WaitObj
);
740 return STATUS_OBJECT_TYPE_MISMATCH
;
743 Status
= KeWaitForSingleObject(WaitObj
,
749 ObDereferenceObject(SignalObj
);
750 ObDereferenceObject(WaitObj
);