Merge 13511:13830 from trunk
[reactos.git] / reactos / ntoskrnl / ke / wait.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS project
5 * FILE: ntoskrnl/ke/wait.c
6 * PURPOSE: Manages non-busy waiting
7 *
8 * PROGRAMMERS: David Welch (welch@mcmail.com)
9 * Phillip Susi
10 */
11
12 /* NOTES ********************************************************************
13 *
14 */
15
16 /* INCLUDES ******************************************************************/
17
18 #include <ntoskrnl.h>
19
20 #define NDEBUG
21 #include <internal/debug.h>
22
23 /* GLOBALS ******************************************************************/
24
25 static KSPIN_LOCK DispatcherDatabaseLock;
26
27 #define KeDispatcherObjectWakeOne(hdr, increment) KeDispatcherObjectWakeOneOrAll(hdr, increment, FALSE)
28 #define KeDispatcherObjectWakeAll(hdr, increment) KeDispatcherObjectWakeOneOrAll(hdr, increment, TRUE)
29
30 extern POBJECT_TYPE EXPORTED ExMutantObjectType;
31 extern POBJECT_TYPE EXPORTED ExSemaphoreObjectType;
32 extern POBJECT_TYPE EXPORTED ExTimerType;
33
34 /* FUNCTIONS *****************************************************************/
35
36 VOID KeInitializeDispatcherHeader(DISPATCHER_HEADER* Header,
37 ULONG Type,
38 ULONG Size,
39 ULONG SignalState)
40 {
41 Header->Type = (UCHAR)Type;
42 Header->Absolute = 0;
43 Header->Inserted = 0;
44 Header->Size = (UCHAR)Size;
45 Header->SignalState = SignalState;
46 InitializeListHead(&(Header->WaitListHead));
47 }
48
49
50 KIRQL
51 KeAcquireDispatcherDatabaseLock(VOID)
52 /*
53 * PURPOSE: Acquires the dispatcher database lock for the caller
54 */
55 {
56 KIRQL OldIrql;
57
58 DPRINT("KeAcquireDispatcherDatabaseLock()\n");
59
60 KeAcquireSpinLock (&DispatcherDatabaseLock, &OldIrql);
61 return OldIrql;
62 }
63
64
65 VOID
66 KeAcquireDispatcherDatabaseLockAtDpcLevel(VOID)
67 /*
68 * PURPOSE: Acquires the dispatcher database lock for the caller
69 */
70 {
71 DPRINT("KeAcquireDispatcherDatabaseLockAtDpcLevel()\n");
72
73 KeAcquireSpinLockAtDpcLevel (&DispatcherDatabaseLock);
74 }
75
76
77 VOID
78 KeReleaseDispatcherDatabaseLock(KIRQL OldIrql)
79 {
80 DPRINT("KeReleaseDispatcherDatabaseLock(OldIrql %x)\n",OldIrql);
81 if (!KeIsExecutingDpc() &&
82 OldIrql < DISPATCH_LEVEL &&
83 KeGetCurrentThread() != NULL &&
84 KeGetCurrentThread() == KeGetCurrentKPCR()->PrcbData.IdleThread)
85 {
86 PsDispatchThreadNoLock(THREAD_STATE_READY);
87 KeLowerIrql(OldIrql);
88 }
89 else
90 {
91 KeReleaseSpinLock(&DispatcherDatabaseLock, OldIrql);
92 }
93 }
94
95
96 VOID
97 KeReleaseDispatcherDatabaseLockFromDpcLevel(VOID)
98 {
99 DPRINT("KeReleaseDispatcherDatabaseLock()\n");
100
101 KeReleaseSpinLockFromDpcLevel(&DispatcherDatabaseLock);
102 }
103
104
105 static BOOLEAN
106 KiSideEffectsBeforeWake(DISPATCHER_HEADER * hdr,
107 PKTHREAD Thread)
108 /*
109 * FUNCTION: Perform side effects on object before a wait for a thread is
110 * satisfied
111 */
112 {
113 BOOLEAN Abandoned = FALSE;
114
115 switch (hdr->Type)
116 {
117 case InternalSynchronizationEvent:
118 hdr->SignalState = 0;
119 break;
120
121 case InternalQueueType:
122 break;
123
124 case InternalSemaphoreType:
125 hdr->SignalState--;
126 break;
127
128 case InternalProcessType:
129 break;
130
131 case InternalThreadType:
132 break;
133
134 case InternalNotificationEvent:
135 break;
136
137 case InternalSynchronizationTimer:
138 hdr->SignalState = FALSE;
139 break;
140
141 case InternalNotificationTimer:
142 break;
143
144 case InternalMutexType:
145 {
146 PKMUTEX Mutex;
147
148 Mutex = CONTAINING_RECORD(hdr, KMUTEX, Header);
149 hdr->SignalState--;
150 ASSERT(hdr->SignalState <= 1);
151 if (hdr->SignalState == 0)
152 {
153 if (Thread == NULL)
154 {
155 DPRINT("Thread == NULL!\n");
156 KEBUGCHECK(0);
157 }
158 Abandoned = Mutex->Abandoned;
159 if (Thread != NULL)
160 InsertTailList(&Thread->MutantListHead, &Mutex->MutantListEntry);
161 Mutex->OwnerThread = Thread;
162 Mutex->Abandoned = FALSE;
163 }
164 }
165 break;
166
167 default:
168 DbgPrint("(%s:%d) Dispatcher object %x has unknown type\n", __FILE__, __LINE__, hdr);
169 KEBUGCHECK(0);
170 }
171
172 return Abandoned;
173 }
174
175 static BOOLEAN
176 KiIsObjectSignalled(DISPATCHER_HEADER * hdr,
177 PKTHREAD Thread)
178 {
179 if (hdr->Type == InternalMutexType)
180 {
181 PKMUTEX Mutex;
182
183 Mutex = CONTAINING_RECORD(hdr, KMUTEX, Header);
184
185 ASSERT(hdr->SignalState <= 1);
186
187 if ((hdr->SignalState < 1 && Mutex->OwnerThread == Thread) || hdr->SignalState == 1)
188 {
189 return (TRUE);
190 }
191 else
192 {
193 return (FALSE);
194 }
195 }
196
197 if (hdr->SignalState <= 0)
198 {
199 return (FALSE);
200 }
201 else
202 {
203 return (TRUE);
204 }
205 }
206
207 /* Must be called with the dispatcher lock held */
208 BOOLEAN KiAbortWaitThread(PKTHREAD Thread, NTSTATUS WaitStatus)
209 {
210 PKWAIT_BLOCK WaitBlock;
211 BOOLEAN WasWaiting;
212
213 /* if we are blocked, we must be waiting on something also */
214 ASSERT((Thread->State == THREAD_STATE_BLOCKED) == (Thread->WaitBlockList != NULL));
215
216 WaitBlock = (PKWAIT_BLOCK)Thread->WaitBlockList;
217 WasWaiting = (WaitBlock != NULL);
218
219 while (WaitBlock)
220 {
221 RemoveEntryList(&WaitBlock->WaitListEntry);
222 WaitBlock = WaitBlock->NextWaitBlock;
223 }
224
225 Thread->WaitBlockList = NULL;
226
227 if (WasWaiting)
228 {
229 PsUnblockThread((PETHREAD)Thread, &WaitStatus, 0);
230 }
231 return WasWaiting;
232 }
233
234 static BOOLEAN
235 KeDispatcherObjectWakeOneOrAll(DISPATCHER_HEADER * hdr,
236 KPRIORITY increment,
237 BOOLEAN WakeAll)
238 {
239 PKWAIT_BLOCK Waiter;
240 PKWAIT_BLOCK WaiterHead;
241 PLIST_ENTRY EnumEntry;
242 NTSTATUS Status;
243 BOOLEAN Abandoned;
244 BOOLEAN AllSignaled;
245 BOOLEAN WakedAny = FALSE;
246
247 DPRINT("KeDispatcherObjectWakeOnOrAll(hdr %x)\n", hdr);
248 DPRINT ("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
249 hdr->WaitListHead.Flink, hdr->WaitListHead.Blink);
250
251 if (IsListEmpty(&hdr->WaitListHead))
252 {
253 return (FALSE);
254 }
255
256 //enum waiters for this dispatcher object
257 EnumEntry = hdr->WaitListHead.Flink;
258 while (EnumEntry != &hdr->WaitListHead && (WakeAll || !WakedAny))
259 {
260 WaiterHead = CONTAINING_RECORD(EnumEntry, KWAIT_BLOCK, WaitListEntry);
261 DPRINT("current_entry %x current %x\n", EnumEntry, WaiterHead);
262 EnumEntry = EnumEntry->Flink;
263 ASSERT(WaiterHead->Thread != NULL);
264 ASSERT(WaiterHead->Thread->WaitBlockList != NULL);
265
266 Abandoned = FALSE;
267
268 if (WaiterHead->WaitType == WaitAny)
269 {
270 DPRINT("WaitAny: Remove all wait blocks.\n");
271 for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock)
272 {
273 RemoveEntryList(&Waiter->WaitListEntry);
274 }
275
276 WaiterHead->Thread->WaitBlockList = NULL;
277
278 /*
279 * If a WakeAll KiSideEffectsBeforeWake(hdr,.. will be called several times,
280 * but thats ok since WakeAll objects has no sideeffects.
281 */
282 Abandoned |= KiSideEffectsBeforeWake(hdr, WaiterHead->Thread);
283 }
284 else
285 {
286 DPRINT("WaitAll: All WaitAll objects must be signaled.\n");
287
288 AllSignaled = TRUE;
289
290 //all WaitAll obj. for thread need to be signaled to satisfy a wake
291 for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock)
292 {
293 //no need to check hdr since it has to be signaled
294 if (Waiter->WaitType == WaitAll && Waiter->Object != hdr)
295 {
296 if (!KiIsObjectSignalled(Waiter->Object, Waiter->Thread))
297 {
298 AllSignaled = FALSE;
299 break;
300 }
301 }
302 }
303
304 if (AllSignaled)
305 {
306 for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock)
307 {
308 RemoveEntryList(&Waiter->WaitListEntry);
309
310 if (Waiter->WaitType == WaitAll)
311 {
312 Abandoned |= KiSideEffectsBeforeWake(Waiter->Object, Waiter->Thread);
313 }
314
315 //no WaitAny objects can possibly be signaled since we are here
316 ASSERT(!(Waiter->WaitType == WaitAny
317 && KiIsObjectSignalled(Waiter->Object, Waiter->Thread)));
318 }
319
320 WaiterHead->Thread->WaitBlockList = NULL;
321 }
322 }
323
324 if (WaiterHead->Thread->WaitBlockList == NULL)
325 {
326 Status = WaiterHead->WaitKey;
327 if (Abandoned)
328 {
329 DPRINT("Abandoned mutex among objects");
330 Status += STATUS_ABANDONED_WAIT_0;
331 }
332
333 WakedAny = TRUE;
334 DPRINT("Waking %x status = %x\n", WaiterHead->Thread, Status);
335 PsUnblockThread(CONTAINING_RECORD(WaiterHead->Thread, ETHREAD, Tcb),
336 &Status, increment);
337 }
338 }
339
340 return WakedAny;
341 }
342
343
344 BOOLEAN KiDispatcherObjectWake(DISPATCHER_HEADER* hdr, KPRIORITY increment)
345 /*
346 * FUNCTION: Wake threads waiting on a dispatcher object
347 * NOTE: The exact semantics of waking are dependant on the type of object
348 */
349 {
350 BOOL Ret;
351
352 DPRINT("Entering KeDispatcherObjectWake(hdr %x)\n",hdr);
353 // DPRINT("hdr->WaitListHead %x hdr->WaitListHead.Flink %x\n",
354 // &hdr->WaitListHead,hdr->WaitListHead.Flink);
355 DPRINT("hdr->Type %x\n",hdr->Type);
356 switch (hdr->Type)
357 {
358 case InternalNotificationEvent:
359 return(KeDispatcherObjectWakeAll(hdr, increment));
360
361 case InternalNotificationTimer:
362 return(KeDispatcherObjectWakeAll(hdr, increment));
363
364 case InternalSynchronizationEvent:
365 return(KeDispatcherObjectWakeOne(hdr, increment));
366
367 case InternalSynchronizationTimer:
368 return(KeDispatcherObjectWakeOne(hdr, increment));
369
370 case InternalQueueType:
371 return(KeDispatcherObjectWakeOne(hdr, increment));
372
373 case InternalSemaphoreType:
374 DPRINT("hdr->SignalState %d\n", hdr->SignalState);
375 if(hdr->SignalState>0)
376 {
377 do
378 {
379 DPRINT("Waking one semaphore waiter\n");
380 Ret = KeDispatcherObjectWakeOne(hdr, increment);
381 } while(hdr->SignalState > 0 && Ret) ;
382 return(Ret);
383 }
384 else return FALSE;
385
386 case InternalProcessType:
387 return(KeDispatcherObjectWakeAll(hdr, increment));
388
389 case InternalThreadType:
390 return(KeDispatcherObjectWakeAll(hdr, increment));
391
392 case InternalMutexType:
393 return(KeDispatcherObjectWakeOne(hdr, increment));
394 }
395 DbgPrint("Dispatcher object %x has unknown type %d\n", hdr, hdr->Type);
396 KEBUGCHECK(0);
397 return(FALSE);
398 }
399
400 /*
401 * @implemented
402 */
403 NTSTATUS STDCALL
404 KeDelayExecutionThread (KPROCESSOR_MODE WaitMode,
405 BOOLEAN Alertable,
406 PLARGE_INTEGER Interval)
407 /*
408 * FUNCTION: Puts the current thread into an alertable or nonalertable
409 * wait state for a given internal
410 * ARGUMENTS:
411 * WaitMode = Processor mode in which the caller is waiting
412 * Altertable = Specifies if the wait is alertable
413 * Interval = Specifies the interval to wait
414 * RETURNS: Status
415 */
416 {
417 PKTHREAD Thread = KeGetCurrentThread();
418
419 KeSetTimer(&Thread->Timer, *Interval, NULL);
420 return (KeWaitForSingleObject(&Thread->Timer,
421 (WaitMode == KernelMode) ? Executive : UserRequest, /* TMN: Was unconditionally Executive */
422 WaitMode, /* TMN: Was UserMode */
423 Alertable,
424 NULL));
425 }
426
427 /*
428 * @implemented
429 */
430 NTSTATUS STDCALL
431 KeWaitForSingleObject(PVOID Object,
432 KWAIT_REASON WaitReason,
433 KPROCESSOR_MODE WaitMode,
434 BOOLEAN Alertable,
435 PLARGE_INTEGER Timeout)
436 /*
437 * FUNCTION: Puts the current thread into a wait state until the
438 * given dispatcher object is set to signalled
439 * ARGUMENTS:
440 * Object = Object to wait on
441 * WaitReason = Reason for the wait (debugging aid)
442 * WaitMode = Can be KernelMode or UserMode, if UserMode then
443 * user-mode APCs can be delivered and the thread's
444 * stack can be paged out
445 * Altertable = Specifies if the wait is a alertable
446 * Timeout = Optional timeout value
447 * RETURNS: Status
448 */
449 {
450 return KeWaitForMultipleObjects(1,
451 &Object,
452 WaitAny,
453 WaitReason,
454 WaitMode,
455 Alertable,
456 Timeout,
457 NULL);
458 }
459
460
461 inline
462 PVOID
463 KiGetWaitableObjectFromObject(PVOID Object)
464 {
465 //special case when waiting on file objects
466 if ( ((PDISPATCHER_HEADER)Object)->Type == InternalFileType)
467 {
468 return &((PFILE_OBJECT)Object)->Event;
469 }
470
471 return Object;
472 }
473
474
475 inline BOOL
476 KiIsObjectWaitable(PVOID Object)
477 {
478 POBJECT_HEADER Header;
479 Header = BODY_TO_HEADER(Object);
480 if (Header->ObjectType == ExEventObjectType ||
481 Header->ObjectType == ExIoCompletionType ||
482 Header->ObjectType == ExMutantObjectType ||
483 Header->ObjectType == ExSemaphoreObjectType ||
484 Header->ObjectType == ExTimerType ||
485 Header->ObjectType == PsProcessType ||
486 Header->ObjectType == PsThreadType ||
487 Header->ObjectType == IoFileObjectType)
488 {
489 return TRUE;
490 }
491 else
492 {
493 return FALSE;
494 }
495 }
496
497 /*
498 * @implemented
499 */
500 NTSTATUS STDCALL
501 KeWaitForMultipleObjects(ULONG Count,
502 PVOID Object[],
503 WAIT_TYPE WaitType,
504 KWAIT_REASON WaitReason,
505 KPROCESSOR_MODE WaitMode,
506 BOOLEAN Alertable,
507 PLARGE_INTEGER Timeout,
508 PKWAIT_BLOCK WaitBlockArray)
509 {
510 DISPATCHER_HEADER *hdr;
511 PKWAIT_BLOCK blk;
512 PKTHREAD CurrentThread;
513 ULONG CountSignaled;
514 ULONG i;
515 NTSTATUS Status;
516 KIRQL OldIrql;
517 BOOLEAN Abandoned;
518 NTSTATUS WaitStatus;
519
520 DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
521 "PsGetCurrentThread() %x\n", Count, Object, PsGetCurrentThread());
522
523 ASSERT(0 < Count && Count <= MAXIMUM_WAIT_OBJECTS);
524
525 CurrentThread = KeGetCurrentThread();
526
527 /*
528 * Work out where we are going to put the wait blocks
529 */
530 if (WaitBlockArray == NULL)
531 {
532 if (Count > THREAD_WAIT_OBJECTS)
533 {
534 DPRINT("(%s:%d) Too many objects!\n", __FILE__, __LINE__);
535 return (STATUS_UNSUCCESSFUL);
536 }
537 WaitBlockArray = &CurrentThread->WaitBlock[0];
538 }
539 else
540 {
541 if (Count > MAXIMUM_WAIT_OBJECTS)
542 {
543 DPRINT("(%s:%d) Too many objects!\n", __FILE__, __LINE__);
544 return (STATUS_UNSUCCESSFUL);
545 }
546 }
547
548
549
550 /*
551 * Set up the timeout if required
552 */
553 if (Timeout != NULL && Timeout->QuadPart != 0)
554 {
555 KeSetTimer(&CurrentThread->Timer, *Timeout, NULL);
556 }
557
558 do
559 {
560 if (CurrentThread->WaitNext)
561 {
562 CurrentThread->WaitNext = FALSE;
563 OldIrql = CurrentThread->WaitIrql;
564 }
565 else
566 {
567 OldIrql = KeAcquireDispatcherDatabaseLock ();
568 }
569
570 /* Get the current Wait Status */
571 WaitStatus = CurrentThread->WaitStatus;
572
573 if (Alertable) {
574
575 /* If the Thread is Alerted, set the Wait Status accordingly */
576 if (CurrentThread->Alerted[(int)WaitMode]) {
577
578 CurrentThread->Alerted[(int)WaitMode] = FALSE;
579 DPRINT("Thread was Alerted\n");
580 WaitStatus = STATUS_ALERTED;
581
582 /* If there are User APCs Pending, then we can't really be alertable */
583 } else if ((!IsListEmpty(&CurrentThread->ApcState.ApcListHead[UserMode])) &&
584 (WaitMode == UserMode)) {
585
586 DPRINT1("APCs are Pending\n");
587 CurrentThread->ApcState.UserApcPending = TRUE;
588 WaitStatus = STATUS_USER_APC;
589 }
590
591 /* If there are User APCs Pending and we are waiting in usermode, then we must notify the caller */
592 } else if ((CurrentThread->ApcState.UserApcPending) && (WaitMode == UserMode)) {
593 DPRINT1("APCs are Pending\n");
594 WaitStatus = STATUS_USER_APC;
595 }
596
597 /*
598 * Check if the wait is (already) satisfied
599 */
600 CountSignaled = 0;
601 Abandoned = FALSE;
602 for (i = 0; i < Count; i++)
603 {
604 hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
605
606 if (KiIsObjectSignalled(hdr, CurrentThread))
607 {
608 CountSignaled++;
609
610 if (WaitType == WaitAny)
611 {
612 Abandoned = KiSideEffectsBeforeWake(hdr, CurrentThread) ? TRUE : Abandoned;
613
614 KeReleaseDispatcherDatabaseLock(OldIrql);
615
616 if (Timeout != NULL && Timeout->QuadPart != 0)
617 {
618 KeCancelTimer(&CurrentThread->Timer);
619 }
620
621 DPRINT("One object is (already) signaled!\n");
622 if (Abandoned == TRUE)
623 {
624 return (STATUS_ABANDONED_WAIT_0 + i);
625 }
626
627 return (STATUS_WAIT_0 + i);
628 }
629 }
630 }
631
632 Abandoned = FALSE;
633 if ((WaitType == WaitAll) && (CountSignaled == Count))
634 {
635 for (i = 0; i < Count; i++)
636 {
637 hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
638 Abandoned = KiSideEffectsBeforeWake(hdr, CurrentThread) ? TRUE : Abandoned;
639 }
640
641 KeReleaseDispatcherDatabaseLock(OldIrql);
642
643 if (Timeout != NULL && Timeout->QuadPart != 0)
644 {
645 KeCancelTimer(&CurrentThread->Timer);
646 }
647
648 DPRINT("All objects are (already) signaled!\n");
649
650 if (Abandoned == TRUE)
651 {
652 return (STATUS_ABANDONED_WAIT_0);
653 }
654
655 return (STATUS_WAIT_0);
656 }
657
658 //zero timeout is used for testing if the object(s) can be immediately acquired
659 if (Timeout != NULL && Timeout->QuadPart == 0)
660 {
661 KeReleaseDispatcherDatabaseLock(OldIrql);
662 return STATUS_TIMEOUT;
663 }
664
665 /*
666 * Check if we have already timed out
667 */
668 if (Timeout != NULL && KiIsObjectSignalled(&CurrentThread->Timer.Header, CurrentThread))
669 {
670 KiSideEffectsBeforeWake(&CurrentThread->Timer.Header, CurrentThread);
671 KeReleaseDispatcherDatabaseLock(OldIrql);
672 KeCancelTimer(&CurrentThread->Timer);
673 return (STATUS_TIMEOUT);
674 }
675
676 /* Append wait block to the KTHREAD wait block list */
677 CurrentThread->WaitBlockList = blk = WaitBlockArray;
678
679 /*
680 * Set up the wait
681 */
682 CurrentThread->WaitStatus = WaitStatus;;
683
684 for (i = 0; i < Count; i++)
685 {
686 hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
687
688 blk->Object = KiGetWaitableObjectFromObject(Object[i]);
689 blk->Thread = CurrentThread;
690 blk->WaitKey = (USHORT)(STATUS_WAIT_0 + i);
691 blk->WaitType = (USHORT)WaitType;
692
693 if (i == (Count - 1))
694 {
695 if (Timeout != NULL)
696 {
697 blk->NextWaitBlock = &CurrentThread->WaitBlock[3];
698 }
699 else
700 {
701 blk->NextWaitBlock = NULL;
702 }
703 }
704 else
705 {
706 blk->NextWaitBlock = blk + 1;
707 }
708
709 /*
710 * add wait block to disp. obj. wait list
711 * Use FIFO for all waits except for queues which use LIFO
712 */
713 if (WaitReason == WrQueue)
714 {
715 InsertHeadList(&hdr->WaitListHead, &blk->WaitListEntry);
716 }
717 else
718 {
719 InsertTailList(&hdr->WaitListHead, &blk->WaitListEntry);
720 }
721
722 blk = blk->NextWaitBlock;
723 }
724
725 if (Timeout != NULL)
726 {
727 CurrentThread->WaitBlock[3].Object = (PVOID) & CurrentThread->Timer;
728 CurrentThread->WaitBlock[3].Thread = CurrentThread;
729 CurrentThread->WaitBlock[3].WaitKey = STATUS_TIMEOUT;
730 CurrentThread->WaitBlock[3].WaitType = WaitAny;
731 CurrentThread->WaitBlock[3].NextWaitBlock = NULL;
732
733 InsertTailList(&CurrentThread->Timer.Header.WaitListHead,
734 &CurrentThread->WaitBlock[3].WaitListEntry);
735 }
736
737 //kernel queues
738 if (CurrentThread->Queue && WaitReason != WrQueue)
739 {
740 DPRINT("queue: sleep on something else\n");
741 CurrentThread->Queue->CurrentCount--;
742
743 //wake another thread
744 if (CurrentThread->Queue->CurrentCount < CurrentThread->Queue->MaximumCount &&
745 !IsListEmpty(&CurrentThread->Queue->EntryListHead))
746 {
747 KiDispatcherObjectWake(&CurrentThread->Queue->Header, IO_NO_INCREMENT);
748 }
749 }
750
751 PsBlockThread(&Status, Alertable, WaitMode, TRUE, OldIrql, (UCHAR)WaitReason);
752
753 //kernel queues
754 OldIrql = KeAcquireDispatcherDatabaseLock ();
755 if (CurrentThread->Queue && WaitReason != WrQueue)
756 {
757 DPRINT("queue: wake from something else\n");
758 CurrentThread->Queue->CurrentCount++;
759 }
760 if (Status == STATUS_KERNEL_APC)
761 {
762 CurrentThread->WaitNext = TRUE;
763 CurrentThread->WaitIrql = OldIrql;
764 }
765 else
766 {
767 KeReleaseDispatcherDatabaseLock(OldIrql);
768 }
769
770 } while (Status == STATUS_KERNEL_APC);
771
772
773 if (Timeout != NULL)
774 {
775 KeCancelTimer(&CurrentThread->Timer);
776 }
777
778 DPRINT("Returning from KeWaitForMultipleObjects()\n");
779 return (Status);
780 }
781
782 VOID KeInitializeDispatcher(VOID)
783 {
784 KeInitializeSpinLock(&DispatcherDatabaseLock);
785 }
786
787 NTSTATUS STDCALL
788 NtWaitForMultipleObjects(IN ULONG ObjectCount,
789 IN PHANDLE ObjectsArray,
790 IN WAIT_TYPE WaitType,
791 IN BOOLEAN Alertable,
792 IN PLARGE_INTEGER TimeOut OPTIONAL)
793 {
794 KWAIT_BLOCK WaitBlockArray[MAXIMUM_WAIT_OBJECTS];
795 HANDLE SafeObjectsArray[MAXIMUM_WAIT_OBJECTS];
796 PVOID ObjectPtrArray[MAXIMUM_WAIT_OBJECTS];
797 ULONG i, j;
798 KPROCESSOR_MODE PreviousMode;
799 LARGE_INTEGER SafeTimeOut;
800 NTSTATUS Status = STATUS_SUCCESS;
801
802 DPRINT("NtWaitForMultipleObjects(ObjectCount %lu ObjectsArray[] %x, Alertable %d, "
803 "TimeOut %x)\n", ObjectCount,ObjectsArray,Alertable,TimeOut);
804
805 PreviousMode = ExGetPreviousMode();
806
807 if (ObjectCount > MAXIMUM_WAIT_OBJECTS)
808 return STATUS_UNSUCCESSFUL;
809 if (0 == ObjectCount)
810 return STATUS_INVALID_PARAMETER;
811
812 if(PreviousMode != KernelMode)
813 {
814 _SEH_TRY
815 {
816 ProbeForRead(ObjectsArray,
817 ObjectCount * sizeof(ObjectsArray[0]),
818 sizeof(ULONG));
819 /* make a copy so we don't have to guard with SEH later and keep track of
820 what objects we referenced in case dereferencing pointers suddenly fails */
821 RtlCopyMemory(SafeObjectsArray, ObjectsArray, ObjectCount * sizeof(ObjectsArray[0]));
822 ObjectsArray = SafeObjectsArray;
823
824 if(TimeOut != NULL)
825 {
826 ProbeForRead(TimeOut,
827 sizeof(LARGE_INTEGER),
828 sizeof(ULONG));
829 /* make a local copy of the timeout on the stack */
830 SafeTimeOut = *TimeOut;
831 TimeOut = &SafeTimeOut;
832 }
833 }
834 _SEH_HANDLE
835 {
836 Status = _SEH_GetExceptionCode();
837 }
838 _SEH_END;
839
840 if(!NT_SUCCESS(Status))
841 {
842 return Status;
843 }
844 }
845
846 /* reference all objects */
847 for (i = 0; i < ObjectCount; i++)
848 {
849 Status = ObReferenceObjectByHandle(ObjectsArray[i],
850 SYNCHRONIZE,
851 NULL,
852 PreviousMode,
853 &ObjectPtrArray[i],
854 NULL);
855 if (!NT_SUCCESS(Status) || !KiIsObjectWaitable(ObjectPtrArray[i]))
856 {
857 if (NT_SUCCESS(Status))
858 {
859 DPRINT1("Waiting for object type '%wZ' is not supported\n",
860 &BODY_TO_HEADER(ObjectPtrArray[i])->ObjectType->TypeName);
861 Status = STATUS_HANDLE_NOT_WAITABLE;
862 i++;
863 }
864 /* dereference all referenced objects */
865 for (j = 0; j < i; j++)
866 {
867 ObDereferenceObject(ObjectPtrArray[j]);
868 }
869
870 return(Status);
871 }
872 }
873
874 Status = KeWaitForMultipleObjects(ObjectCount,
875 ObjectPtrArray,
876 WaitType,
877 UserRequest,
878 PreviousMode,
879 Alertable,
880 TimeOut,
881 WaitBlockArray);
882
883 /* dereference all objects */
884 for (i = 0; i < ObjectCount; i++)
885 {
886 ObDereferenceObject(ObjectPtrArray[i]);
887 }
888
889 return(Status);
890 }
891
892
893 /*
894 * @implemented
895 */
896 NTSTATUS STDCALL
897 NtWaitForSingleObject(IN HANDLE ObjectHandle,
898 IN BOOLEAN Alertable,
899 IN PLARGE_INTEGER TimeOut OPTIONAL)
900 {
901 PVOID ObjectPtr;
902 KPROCESSOR_MODE PreviousMode;
903 LARGE_INTEGER SafeTimeOut;
904 NTSTATUS Status = STATUS_SUCCESS;
905
906 DPRINT("NtWaitForSingleObject(ObjectHandle %x, Alertable %d, TimeOut %x)\n",
907 ObjectHandle,Alertable,TimeOut);
908
909 PreviousMode = ExGetPreviousMode();
910
911 if(TimeOut != NULL && PreviousMode != KernelMode)
912 {
913 _SEH_TRY
914 {
915 ProbeForRead(TimeOut,
916 sizeof(LARGE_INTEGER),
917 sizeof(ULONG));
918 /* make a copy on the stack */
919 SafeTimeOut = *TimeOut;
920 TimeOut = &SafeTimeOut;
921 }
922 _SEH_HANDLE
923 {
924 Status = _SEH_GetExceptionCode();
925 }
926 _SEH_END;
927
928 if(!NT_SUCCESS(Status))
929 {
930 return Status;
931 }
932 }
933
934 Status = ObReferenceObjectByHandle(ObjectHandle,
935 SYNCHRONIZE,
936 NULL,
937 PreviousMode,
938 &ObjectPtr,
939 NULL);
940 if (!NT_SUCCESS(Status))
941 {
942 return(Status);
943 }
944 if (!KiIsObjectWaitable(ObjectPtr))
945 {
946 DPRINT1("Waiting for object type '%wZ' is not supported\n",
947 &BODY_TO_HEADER(ObjectPtr)->ObjectType->TypeName);
948 Status = STATUS_HANDLE_NOT_WAITABLE;
949 }
950 else
951 {
952 Status = KeWaitForSingleObject(ObjectPtr,
953 UserRequest,
954 PreviousMode,
955 Alertable,
956 TimeOut);
957 }
958
959 ObDereferenceObject(ObjectPtr);
960
961 return(Status);
962 }
963
964
965 NTSTATUS STDCALL
966 NtSignalAndWaitForSingleObject(IN HANDLE ObjectHandleToSignal,
967 IN HANDLE WaitableObjectHandle,
968 IN BOOLEAN Alertable,
969 IN PLARGE_INTEGER TimeOut OPTIONAL)
970 {
971 KPROCESSOR_MODE PreviousMode;
972 DISPATCHER_HEADER* hdr;
973 PVOID SignalObj;
974 PVOID WaitObj;
975 LARGE_INTEGER SafeTimeOut;
976 NTSTATUS Status = STATUS_SUCCESS;
977
978 PreviousMode = ExGetPreviousMode();
979
980 if(TimeOut != NULL && PreviousMode != KernelMode)
981 {
982 _SEH_TRY
983 {
984 ProbeForRead(TimeOut,
985 sizeof(LARGE_INTEGER),
986 sizeof(ULONG));
987 /* make a copy on the stack */
988 SafeTimeOut = *TimeOut;
989 TimeOut = &SafeTimeOut;
990 }
991 _SEH_HANDLE
992 {
993 Status = _SEH_GetExceptionCode();
994 }
995 _SEH_END;
996
997 if(!NT_SUCCESS(Status))
998 {
999 return Status;
1000 }
1001 }
1002
1003 Status = ObReferenceObjectByHandle(ObjectHandleToSignal,
1004 0,
1005 NULL,
1006 PreviousMode,
1007 &SignalObj,
1008 NULL);
1009 if (!NT_SUCCESS(Status))
1010 {
1011 return Status;
1012 }
1013
1014 Status = ObReferenceObjectByHandle(WaitableObjectHandle,
1015 SYNCHRONIZE,
1016 NULL,
1017 PreviousMode,
1018 &WaitObj,
1019 NULL);
1020 if (!NT_SUCCESS(Status))
1021 {
1022 ObDereferenceObject(SignalObj);
1023 return Status;
1024 }
1025
1026 hdr = (DISPATCHER_HEADER *)SignalObj;
1027 switch (hdr->Type)
1028 {
1029 case InternalNotificationEvent:
1030 case InternalSynchronizationEvent:
1031 KeSetEvent(SignalObj,
1032 EVENT_INCREMENT,
1033 TRUE);
1034 break;
1035
1036 case InternalMutexType:
1037 KeReleaseMutex(SignalObj,
1038 TRUE);
1039 break;
1040
1041 case InternalSemaphoreType:
1042 KeReleaseSemaphore(SignalObj,
1043 SEMAPHORE_INCREMENT,
1044 1,
1045 TRUE);
1046 break;
1047
1048 default:
1049 ObDereferenceObject(SignalObj);
1050 ObDereferenceObject(WaitObj);
1051 return STATUS_OBJECT_TYPE_MISMATCH;
1052 }
1053
1054 Status = KeWaitForSingleObject(WaitObj,
1055 UserRequest,
1056 PreviousMode,
1057 Alertable,
1058 TimeOut);
1059
1060 ObDereferenceObject(SignalObj);
1061 ObDereferenceObject(WaitObj);
1062
1063 return Status;
1064 }