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>
24 #include <ntos/ntdef.h>
27 #include <internal/debug.h>
29 /* GLOBALS ******************************************************************/
31 static KSPIN_LOCK DispatcherDatabaseLock
;
32 static BOOLEAN WaitSet
= FALSE
;
33 static KIRQL oldlvl
= PASSIVE_LEVEL
;
34 static PKTHREAD Owner
= NULL
;
36 #define KeDispatcherObjectWakeOne(hdr) KeDispatcherObjectWakeOneOrAll(hdr, FALSE)
37 #define KeDispatcherObjectWakeAll(hdr) KeDispatcherObjectWakeOneOrAll(hdr, TRUE)
39 /* FUNCTIONS *****************************************************************/
41 VOID
KeInitializeDispatcherHeader(DISPATCHER_HEADER
* Header
,
50 Header
->SignalState
= SignalState
;
51 InitializeListHead(&(Header
->WaitListHead
));
54 VOID
KeAcquireDispatcherDatabaseLock(BOOLEAN Wait
)
56 * PURPOSE: Acquires the dispatcher database lock for the caller
59 DPRINT("KeAcquireDispatcherDatabaseLock(Wait %x)\n",Wait
);
60 if (WaitSet
&& Owner
== KeGetCurrentThread())
64 KeAcquireSpinLock(&DispatcherDatabaseLock
, &oldlvl
);
66 Owner
= KeGetCurrentThread();
69 VOID
KeReleaseDispatcherDatabaseLockAtDpcLevel(BOOLEAN Wait
)
71 DPRINT("KeReleaseDispatcherDatabaseLockAtDpcLevel(Wait %x)\n", Wait
);
72 assert(Wait
== WaitSet
);
76 KeReleaseSpinLockFromDpcLevel(&DispatcherDatabaseLock
);
80 VOID
KeReleaseDispatcherDatabaseLock(BOOLEAN Wait
)
82 DPRINT("KeReleaseDispatcherDatabaseLock(Wait %x)\n",Wait
);
83 assert(Wait
==WaitSet
);
87 KeReleaseSpinLock(&DispatcherDatabaseLock
, oldlvl
);
92 KiSideEffectsBeforeWake(DISPATCHER_HEADER
* hdr
,
95 * FUNCTION: Perform side effects on object before a wait for a thread is
99 BOOLEAN Abandoned
= FALSE
;
103 case InternalSynchronizationEvent
:
104 hdr
->SignalState
= 0;
107 case InternalQueueType
:
108 case InternalSemaphoreType
:
112 case InternalProcessType
:
115 case InternalThreadType
:
118 case InternalNotificationEvent
:
121 case InternalSynchronizationTimer
:
122 hdr
->SignalState
= FALSE
;
125 case InternalNotificationTimer
:
128 case InternalMutexType
:
132 Mutex
= CONTAINING_RECORD(hdr
, KMUTEX
, Header
);
134 assert(hdr
->SignalState
<= 1);
135 if (hdr
->SignalState
== 0)
139 DPRINT("Thread == NULL!\n");
142 Abandoned
= Mutex
->Abandoned
;
144 InsertTailList(&Thread
->MutantListHead
, &Mutex
->MutantListEntry
);
145 Mutex
->OwnerThread
= Thread
;
146 Mutex
->Abandoned
= FALSE
;
152 DbgPrint("(%s:%d) Dispatcher object %x has unknown type\n", __FILE__
, __LINE__
, hdr
);
160 KiIsObjectSignalled(DISPATCHER_HEADER
* hdr
,
163 if (hdr
->Type
== InternalMutexType
)
167 Mutex
= CONTAINING_RECORD(hdr
, KMUTEX
, Header
);
169 assert(hdr
->SignalState
<= 1);
171 if ((hdr
->SignalState
< 1 && Mutex
->OwnerThread
== Thread
) || hdr
->SignalState
== 1)
181 if (hdr
->SignalState
<= 0)
191 VOID
KeRemoveAllWaitsThread(PETHREAD Thread
, NTSTATUS WaitStatus
)
193 PKWAIT_BLOCK WaitBlock
;
194 BOOLEAN WasWaiting
= FALSE
;
196 KeAcquireDispatcherDatabaseLock(FALSE
);
198 WaitBlock
= (PKWAIT_BLOCK
)Thread
->Tcb
.WaitBlockList
;
199 if (WaitBlock
!= NULL
)
203 while (WaitBlock
!= NULL
)
205 RemoveEntryList(&WaitBlock
->WaitListEntry
);
206 WaitBlock
= WaitBlock
->NextWaitBlock
;
208 Thread
->Tcb
.WaitBlockList
= NULL
;
212 PsUnblockThread(Thread
, &WaitStatus
);
215 KeReleaseDispatcherDatabaseLock(FALSE
);
219 KeDispatcherObjectWakeOneOrAll(DISPATCHER_HEADER
* hdr
,
223 PKWAIT_BLOCK WaiterHead
;
224 PLIST_ENTRY EnumEntry
;
228 BOOLEAN WakedAny
= FALSE
;
230 DPRINT("KeDispatcherObjectWakeOnOrAll(hdr %x)\n", hdr
);
231 DPRINT ("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
232 hdr
->WaitListHead
.Flink
, hdr
->WaitListHead
.Blink
);
234 if (IsListEmpty(&hdr
->WaitListHead
))
239 //enum waiters for this dispatcher object
240 EnumEntry
= hdr
->WaitListHead
.Flink
;
241 while (EnumEntry
!= &hdr
->WaitListHead
&& (WakeAll
|| !WakedAny
))
243 WaiterHead
= CONTAINING_RECORD(EnumEntry
, KWAIT_BLOCK
, WaitListEntry
);
244 DPRINT("current_entry %x current %x\n", EnumEntry
, WaiterHead
);
245 EnumEntry
= EnumEntry
->Flink
;
246 assert(WaiterHead
->Thread
->WaitBlockList
!= NULL
);
250 if (WaiterHead
->WaitType
== WaitAny
)
252 DPRINT("WaitAny: Remove all wait blocks.\n");
253 for (Waiter
= WaiterHead
->Thread
->WaitBlockList
; Waiter
; Waiter
= Waiter
->NextWaitBlock
)
255 RemoveEntryList(&Waiter
->WaitListEntry
);
258 WaiterHead
->Thread
->WaitBlockList
= NULL
;
261 * If a WakeAll KiSideEffectsBeforeWake(hdr,.. will be called several times,
262 * but thats ok since WakeAll objects has no sideeffects.
264 Abandoned
= KiSideEffectsBeforeWake(hdr
, WaiterHead
->Thread
) ? TRUE
: Abandoned
;
268 DPRINT("WaitAll: All WaitAll objects must be signaled.\n");
272 //all WaitAll obj. for thread need to be signaled to satisfy a wake
273 for (Waiter
= WaiterHead
->Thread
->WaitBlockList
; Waiter
; Waiter
= Waiter
->NextWaitBlock
)
275 //no need to check hdr since it has to be signaled
276 if (Waiter
->WaitType
== WaitAll
&& Waiter
->Object
!= hdr
)
278 if (!KiIsObjectSignalled(Waiter
->Object
, Waiter
->Thread
))
288 for (Waiter
= WaiterHead
->Thread
->WaitBlockList
; Waiter
; Waiter
= Waiter
->NextWaitBlock
)
290 RemoveEntryList(&Waiter
->WaitListEntry
);
292 if (Waiter
->WaitType
== WaitAll
)
294 Abandoned
= KiSideEffectsBeforeWake(Waiter
->Object
, Waiter
->Thread
)
298 //no WaitAny objects can possibly be signaled since we are here
299 assert(!(Waiter
->WaitType
== WaitAny
300 && KiIsObjectSignalled(Waiter
->Object
, Waiter
->Thread
)));
303 WaiterHead
->Thread
->WaitBlockList
= NULL
;
307 if (WaiterHead
->Thread
->WaitBlockList
== NULL
)
309 Status
= WaiterHead
->WaitKey
;
312 DPRINT("Abandoned mutex among objects");
313 Status
+= STATUS_ABANDONED_WAIT_0
;
317 DPRINT("Waking %x status = %x\n", WaiterHead
->Thread
, Status
);
318 PsUnblockThread(CONTAINING_RECORD(WaiterHead
->Thread
, ETHREAD
, Tcb
), &Status
);
326 BOOLEAN
KeDispatcherObjectWake(DISPATCHER_HEADER
* hdr
)
328 * FUNCTION: Wake threads waiting on a dispatcher object
329 * NOTE: The exact semantics of waking are dependant on the type of object
334 DPRINT("Entering KeDispatcherObjectWake(hdr %x)\n",hdr
);
335 // DPRINT("hdr->WaitListHead %x hdr->WaitListHead.Flink %x\n",
336 // &hdr->WaitListHead,hdr->WaitListHead.Flink);
337 DPRINT("hdr->Type %x\n",hdr
->Type
);
340 case InternalNotificationEvent
:
341 return(KeDispatcherObjectWakeAll(hdr
));
343 case InternalNotificationTimer
:
344 return(KeDispatcherObjectWakeAll(hdr
));
346 case InternalSynchronizationEvent
:
347 return(KeDispatcherObjectWakeOne(hdr
));
349 case InternalSynchronizationTimer
:
350 return(KeDispatcherObjectWakeOne(hdr
));
352 case InternalQueueType
:
353 case InternalSemaphoreType
:
354 DPRINT("hdr->SignalState %d\n", hdr
->SignalState
);
355 if(hdr
->SignalState
>0)
359 DPRINT("Waking one semaphore waiter\n");
360 Ret
= KeDispatcherObjectWakeOne(hdr
);
361 } while(hdr
->SignalState
> 0 && Ret
) ;
366 case InternalProcessType
:
367 return(KeDispatcherObjectWakeAll(hdr
));
369 case InternalThreadType
:
370 return(KeDispatcherObjectWakeAll(hdr
));
372 case InternalMutexType
:
373 return(KeDispatcherObjectWakeOne(hdr
));
375 DbgPrint("Dispatcher object %x has unknown type %d\n", hdr
, hdr
->Type
);
385 KeWaitForSingleObject(PVOID Object
,
386 KWAIT_REASON WaitReason
,
387 KPROCESSOR_MODE WaitMode
,
389 PLARGE_INTEGER Timeout
)
391 * FUNCTION: Puts the current thread into a wait state until the
392 * given dispatcher object is set to signalled
394 * Object = Object to wait on
395 * WaitReason = Reason for the wait (debugging aid)
396 * WaitMode = Can be KernelMode or UserMode, if UserMode then
397 * user-mode APCs can be delivered and the thread's
398 * stack can be paged out
399 * Altertable = Specifies if the wait is a alertable
400 * Timeout = Optional timeout value
404 return KeWaitForMultipleObjects(1,
417 KiGetWaitableObjectFromObject(PVOID Object
)
419 //special case when waiting on file objects
420 if ( ((PDISPATCHER_HEADER
)Object
)->Type
== InternalFileType
)
422 return &((PFILE_OBJECT
)Object
)->Event
;
433 KeWaitForMultipleObjects(ULONG Count
,
436 KWAIT_REASON WaitReason
,
437 KPROCESSOR_MODE WaitMode
,
439 PLARGE_INTEGER Timeout
,
440 PKWAIT_BLOCK WaitBlockArray
)
442 DISPATCHER_HEADER
*hdr
;
444 PKTHREAD CurrentThread
;
451 DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
452 "PsGetCurrentThread() %x\n", Count
, Object
, PsGetCurrentThread());
454 CurrentThread
= KeGetCurrentThread();
455 WaitIrql
= KeGetCurrentIrql();
458 * Work out where we are going to put the wait blocks
460 if (WaitBlockArray
== NULL
)
462 if (Count
> THREAD_WAIT_OBJECTS
)
464 DPRINT("(%s:%d) Too many objects!\n", __FILE__
, __LINE__
);
465 return (STATUS_UNSUCCESSFUL
);
467 WaitBlockArray
= &CurrentThread
->WaitBlock
[0];
471 if (Count
> EX_MAXIMUM_WAIT_OBJECTS
)
473 DPRINT("(%s:%d) Too many objects!\n", __FILE__
, __LINE__
);
474 return (STATUS_UNSUCCESSFUL
);
479 * Set up the timeout if required
481 if (Timeout
!= NULL
&& Timeout
->QuadPart
!= 0)
483 KeInitializeTimer(&CurrentThread
->Timer
);
484 KeSetTimer(&CurrentThread
->Timer
, *Timeout
, NULL
);
489 KeAcquireDispatcherDatabaseLock(FALSE
);
492 * If we are going to wait alertably and a user apc is pending
495 if (Alertable
&& KiTestAlert())
497 KeReleaseDispatcherDatabaseLock(FALSE
);
498 return (STATUS_USER_APC
);
502 * Check if the wait is (already) satisfied
506 for (i
= 0; i
< Count
; i
++)
508 hdr
= (DISPATCHER_HEADER
*) KiGetWaitableObjectFromObject(Object
[i
]);
510 if (KiIsObjectSignalled(hdr
, CurrentThread
))
514 if (WaitType
== WaitAny
)
516 Abandoned
= KiSideEffectsBeforeWake(hdr
, CurrentThread
) ? TRUE
: Abandoned
;
518 if (Timeout
!= NULL
&& Timeout
->QuadPart
!= 0)
520 KeCancelTimer(&CurrentThread
->Timer
);
523 KeReleaseDispatcherDatabaseLock(FALSE
);
525 DPRINT("One object is (already) signaled!\n");
526 if (Abandoned
== TRUE
)
528 return (STATUS_ABANDONED_WAIT_0
+ i
);
531 return (STATUS_WAIT_0
+ i
);
537 if ((WaitType
== WaitAll
) && (CountSignaled
== Count
))
539 for (i
= 0; i
< Count
; i
++)
541 hdr
= (DISPATCHER_HEADER
*) KiGetWaitableObjectFromObject(Object
[i
]);
542 Abandoned
= KiSideEffectsBeforeWake(hdr
, CurrentThread
) ? TRUE
: Abandoned
;
545 if (Timeout
!= NULL
&& Timeout
->QuadPart
!= 0)
547 KeCancelTimer(&CurrentThread
->Timer
);
550 KeReleaseDispatcherDatabaseLock(FALSE
);
551 DPRINT("All objects are (already) signaled!\n");
553 if (Abandoned
== TRUE
)
555 return (STATUS_ABANDONED_WAIT_0
);
558 return (STATUS_WAIT_0
);
561 //zero timeout is used for testing if the object(s) can be immediately acquired
562 if (Timeout
!= NULL
&& Timeout
->QuadPart
== 0)
564 KeReleaseDispatcherDatabaseLock(FALSE
);
565 return STATUS_TIMEOUT
;
569 * Check if we have already timed out
571 if (Timeout
!= NULL
&& KiIsObjectSignalled(&CurrentThread
->Timer
.Header
, CurrentThread
))
573 KiSideEffectsBeforeWake(&CurrentThread
->Timer
.Header
, CurrentThread
);
574 KeCancelTimer(&CurrentThread
->Timer
);
575 KeReleaseDispatcherDatabaseLock(FALSE
);
576 return (STATUS_TIMEOUT
);
579 /* Append wait block to the KTHREAD wait block list */
580 CurrentThread
->WaitBlockList
= blk
= WaitBlockArray
;
585 CurrentThread
->WaitStatus
= STATUS_UNSUCCESSFUL
;
587 for (i
= 0; i
< Count
; i
++)
589 hdr
= (DISPATCHER_HEADER
*) KiGetWaitableObjectFromObject(Object
[i
]);
591 blk
->Object
= KiGetWaitableObjectFromObject(Object
[i
]);
592 blk
->Thread
= CurrentThread
;
593 blk
->WaitKey
= STATUS_WAIT_0
+ i
;
594 blk
->WaitType
= WaitType
;
596 if (i
== (Count
- 1))
600 blk
->NextWaitBlock
= &CurrentThread
->WaitBlock
[3];
604 blk
->NextWaitBlock
= NULL
;
609 blk
->NextWaitBlock
= blk
+ 1;
613 * add wait block to disp. obj. wait list
614 * Use FIFO for all waits except for queues which use LIFO
616 if (WaitReason
== WrQueue
)
618 InsertHeadList(&hdr
->WaitListHead
, &blk
->WaitListEntry
);
622 InsertTailList(&hdr
->WaitListHead
, &blk
->WaitListEntry
);
625 blk
= blk
->NextWaitBlock
;
630 CurrentThread
->WaitBlock
[3].Object
= (PVOID
) & CurrentThread
->Timer
;
631 CurrentThread
->WaitBlock
[3].Thread
= CurrentThread
;
632 CurrentThread
->WaitBlock
[3].WaitKey
= STATUS_TIMEOUT
;
633 CurrentThread
->WaitBlock
[3].WaitType
= WaitAny
;
634 CurrentThread
->WaitBlock
[3].NextWaitBlock
= NULL
;
636 InsertTailList(&CurrentThread
->Timer
.Header
.WaitListHead
,
637 &CurrentThread
->WaitBlock
[3].WaitListEntry
);
641 if (CurrentThread
->Queue
)
643 CurrentThread
->Queue
->CurrentCount
--;
644 if (WaitReason
!= WrQueue
&& CurrentThread
->Queue
->CurrentCount
< CurrentThread
->Queue
->MaximumCount
&&
645 !IsListEmpty(&CurrentThread
->Queue
->EntryListHead
))
647 KeDispatcherObjectWake(&CurrentThread
->Queue
->Header
);
651 PsBlockThread(&Status
, Alertable
, WaitMode
, TRUE
, WaitIrql
, WaitReason
);
654 if (CurrentThread
->Queue
)
656 CurrentThread
->Queue
->CurrentCount
++;
661 while (Status
== STATUS_KERNEL_APC
);
665 KeCancelTimer(&CurrentThread
->Timer
);
668 DPRINT("Returning from KeWaitForMultipleObjects()\n");
672 VOID
KeInitializeDispatcher(VOID
)
674 KeInitializeSpinLock(&DispatcherDatabaseLock
);
681 NtWaitForMultipleObjects(IN ULONG Count
,
683 IN WAIT_TYPE WaitType
,
684 IN BOOLEAN Alertable
,
685 IN PLARGE_INTEGER Time
)
687 KWAIT_BLOCK WaitBlockArray
[EX_MAXIMUM_WAIT_OBJECTS
];
688 PVOID ObjectPtrArray
[EX_MAXIMUM_WAIT_OBJECTS
];
691 KPROCESSOR_MODE WaitMode
;
693 DPRINT("NtWaitForMultipleObjects(Count %lu Object[] %x, Alertable %d, "
694 "Time %x)\n", Count
,Object
,Alertable
,Time
);
696 if (Count
> EX_MAXIMUM_WAIT_OBJECTS
)
697 return STATUS_UNSUCCESSFUL
;
699 WaitMode
= ExGetPreviousMode();
701 /* reference all objects */
702 for (i
= 0; i
< Count
; i
++)
704 Status
= ObReferenceObjectByHandle(Object
[i
],
710 if (Status
!= STATUS_SUCCESS
)
712 /* dereference all referenced objects */
713 for (j
= 0; j
< i
; j
++)
715 ObDereferenceObject(ObjectPtrArray
[j
]);
722 Status
= KeWaitForMultipleObjects(Count
,
731 /* dereference all objects */
732 for (i
= 0; i
< Count
; i
++)
734 ObDereferenceObject(ObjectPtrArray
[i
]);
745 NtWaitForSingleObject(IN HANDLE Object
,
746 IN BOOLEAN Alertable
,
747 IN PLARGE_INTEGER Time
)
751 KPROCESSOR_MODE WaitMode
;
753 DPRINT("NtWaitForSingleObject(Object %x, Alertable %d, Time %x)\n",
754 Object
,Alertable
,Time
);
756 WaitMode
= ExGetPreviousMode();
758 Status
= ObReferenceObjectByHandle(Object
,
764 if (!NT_SUCCESS(Status
))
769 Status
= KeWaitForSingleObject(ObjectPtr
,
775 ObDereferenceObject(ObjectPtr
);
785 NtSignalAndWaitForSingleObject(IN HANDLE SignalObject
,
786 IN HANDLE WaitObject
,
787 IN BOOLEAN Alertable
,
788 IN PLARGE_INTEGER Time
)
790 KPROCESSOR_MODE WaitMode
;
791 DISPATCHER_HEADER
* hdr
;
796 WaitMode
= ExGetPreviousMode();
797 Status
= ObReferenceObjectByHandle(SignalObject
,
803 if (!NT_SUCCESS(Status
))
808 Status
= ObReferenceObjectByHandle(WaitObject
,
814 if (!NT_SUCCESS(Status
))
816 ObDereferenceObject(SignalObj
);
820 hdr
= (DISPATCHER_HEADER
*)SignalObj
;
823 case InternalNotificationEvent
:
824 case InternalSynchronizationEvent
:
825 KeSetEvent(SignalObj
,
830 case InternalMutexType
:
831 KeReleaseMutex(SignalObj
,
835 case InternalSemaphoreType
:
836 KeReleaseSemaphore(SignalObj
,
843 ObDereferenceObject(SignalObj
);
844 ObDereferenceObject(WaitObj
);
845 return STATUS_OBJECT_TYPE_MISMATCH
;
848 Status
= KeWaitForSingleObject(WaitObj
,
854 ObDereferenceObject(SignalObj
);
855 ObDereferenceObject(WaitObj
);