Reimplemented dispatcher database lock and synchronization primitives.
[reactos.git] / reactos / ntoskrnl / ke / wait.c
1 /*
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)
7 * REVISION HISTORY:
8 * 21/07/98: Created
9 * 12/1/99: Phillip Susi: Fixed wake code in KeDispatcherObjectWake
10 * so that things like KeWaitForXXX() return the correct value
11 */
12
13 /* NOTES ********************************************************************
14 *
15 */
16
17 /* INCLUDES ******************************************************************/
18
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>
25
26 #define NDEBUG
27 #include <internal/debug.h>
28
29 /* GLOBALS ******************************************************************/
30
31 static KSPIN_LOCK DispatcherDatabaseLock;
32
33 #define KeDispatcherObjectWakeOne(hdr) KeDispatcherObjectWakeOneOrAll(hdr, FALSE)
34 #define KeDispatcherObjectWakeAll(hdr) KeDispatcherObjectWakeOneOrAll(hdr, TRUE)
35
36 /* FUNCTIONS *****************************************************************/
37
38 VOID KeInitializeDispatcherHeader(DISPATCHER_HEADER* Header,
39 ULONG Type,
40 ULONG Size,
41 ULONG SignalState)
42 {
43 Header->Type = Type;
44 Header->Absolute = 0;
45 Header->Inserted = 0;
46 Header->Size = Size;
47 Header->SignalState = SignalState;
48 InitializeListHead(&(Header->WaitListHead));
49 }
50
51
52 KIRQL
53 KeAcquireDispatcherDatabaseLock(VOID)
54 /*
55 * PURPOSE: Acquires the dispatcher database lock for the caller
56 */
57 {
58 KIRQL OldIrql;
59
60 DPRINT("KeAcquireDispatcherDatabaseLock()\n");
61
62 KeAcquireSpinLock (&DispatcherDatabaseLock, &OldIrql);
63 return OldIrql;
64 }
65
66
67 VOID
68 KeAcquireDispatcherDatabaseLockAtDpcLevel(VOID)
69 /*
70 * PURPOSE: Acquires the dispatcher database lock for the caller
71 */
72 {
73 DPRINT("KeAcquireDispatcherDatabaseLockAtDpcLevel()\n");
74
75 KeAcquireSpinLockAtDpcLevel (&DispatcherDatabaseLock);
76 }
77
78
79 VOID
80 KeReleaseDispatcherDatabaseLock(KIRQL OldIrql)
81 {
82 DPRINT("KeReleaseDispatcherDatabaseLock(OldIrql %x)\n",OldIrql);
83
84 KeReleaseSpinLock(&DispatcherDatabaseLock, OldIrql);
85 }
86
87
88 VOID
89 KeReleaseDispatcherDatabaseLockFromDpcLevel(VOID)
90 {
91 DPRINT("KeReleaseDispatcherDatabaseLock()\n");
92
93 KeReleaseSpinLockFromDpcLevel(&DispatcherDatabaseLock);
94 }
95
96
97 static BOOLEAN
98 KiSideEffectsBeforeWake(DISPATCHER_HEADER * hdr,
99 PKTHREAD Thread)
100 /*
101 * FUNCTION: Perform side effects on object before a wait for a thread is
102 * satisfied
103 */
104 {
105 BOOLEAN Abandoned = FALSE;
106
107 switch (hdr->Type)
108 {
109 case InternalSynchronizationEvent:
110 hdr->SignalState = 0;
111 break;
112
113 case InternalQueueType:
114 case InternalSemaphoreType:
115 hdr->SignalState--;
116 break;
117
118 case InternalProcessType:
119 break;
120
121 case InternalThreadType:
122 break;
123
124 case InternalNotificationEvent:
125 break;
126
127 case InternalSynchronizationTimer:
128 hdr->SignalState = FALSE;
129 break;
130
131 case InternalNotificationTimer:
132 break;
133
134 case InternalMutexType:
135 {
136 PKMUTEX Mutex;
137
138 Mutex = CONTAINING_RECORD(hdr, KMUTEX, Header);
139 hdr->SignalState--;
140 assert(hdr->SignalState <= 1);
141 if (hdr->SignalState == 0)
142 {
143 if (Thread == NULL)
144 {
145 DPRINT("Thread == NULL!\n");
146 KEBUGCHECK(0);
147 }
148 Abandoned = Mutex->Abandoned;
149 if (Thread != NULL)
150 InsertTailList(&Thread->MutantListHead, &Mutex->MutantListEntry);
151 Mutex->OwnerThread = Thread;
152 Mutex->Abandoned = FALSE;
153 }
154 }
155 break;
156
157 default:
158 DbgPrint("(%s:%d) Dispatcher object %x has unknown type\n", __FILE__, __LINE__, hdr);
159 KEBUGCHECK(0);
160 }
161
162 return Abandoned;
163 }
164
165 static BOOLEAN
166 KiIsObjectSignalled(DISPATCHER_HEADER * hdr,
167 PKTHREAD Thread)
168 {
169 if (hdr->Type == InternalMutexType)
170 {
171 PKMUTEX Mutex;
172
173 Mutex = CONTAINING_RECORD(hdr, KMUTEX, Header);
174
175 assert(hdr->SignalState <= 1);
176
177 if ((hdr->SignalState < 1 && Mutex->OwnerThread == Thread) || hdr->SignalState == 1)
178 {
179 return (TRUE);
180 }
181 else
182 {
183 return (FALSE);
184 }
185 }
186
187 if (hdr->SignalState <= 0)
188 {
189 return (FALSE);
190 }
191 else
192 {
193 return (TRUE);
194 }
195 }
196
197 VOID KeRemoveAllWaitsThread(PETHREAD Thread, NTSTATUS WaitStatus, BOOL Unblock)
198 {
199 PKWAIT_BLOCK WaitBlock, PrevWaitBlock;
200 BOOLEAN WasWaiting = FALSE;
201 KIRQL OldIrql;
202
203 OldIrql = KeAcquireDispatcherDatabaseLock ();
204
205 WaitBlock = (PKWAIT_BLOCK)Thread->Tcb.WaitBlockList;
206 if (WaitBlock != NULL)
207 {
208 WasWaiting = TRUE;
209 }
210 while (WaitBlock != NULL)
211 {
212 if (WaitBlock->WaitListEntry.Flink != NULL && WaitBlock->WaitListEntry.Blink != NULL)
213 {
214 RemoveEntryList (&WaitBlock->WaitListEntry);
215 WaitBlock->WaitListEntry.Flink = WaitBlock->WaitListEntry.Blink = NULL;
216 }
217 PrevWaitBlock = WaitBlock;
218 WaitBlock = WaitBlock->NextWaitBlock;
219 PrevWaitBlock->NextWaitBlock = NULL;
220 }
221 Thread->Tcb.WaitBlockList = NULL;
222
223 if (WasWaiting && Unblock)
224 {
225 PsUnblockThread(Thread, &WaitStatus);
226 }
227
228 KeReleaseDispatcherDatabaseLock (OldIrql);
229 }
230
231 static BOOLEAN
232 KeDispatcherObjectWakeOneOrAll(DISPATCHER_HEADER * hdr,
233 BOOLEAN WakeAll)
234 {
235 PKWAIT_BLOCK Waiter;
236 PKWAIT_BLOCK WaiterHead;
237 PLIST_ENTRY EnumEntry;
238 NTSTATUS Status;
239 BOOLEAN Abandoned;
240 BOOLEAN AllSignaled;
241 BOOLEAN WakedAny = FALSE;
242
243 DPRINT("KeDispatcherObjectWakeOnOrAll(hdr %x)\n", hdr);
244 DPRINT ("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
245 hdr->WaitListHead.Flink, hdr->WaitListHead.Blink);
246
247 if (IsListEmpty(&hdr->WaitListHead))
248 {
249 return (FALSE);
250 }
251
252 //enum waiters for this dispatcher object
253 EnumEntry = hdr->WaitListHead.Flink;
254 while (EnumEntry != &hdr->WaitListHead && (WakeAll || !WakedAny))
255 {
256 WaiterHead = CONTAINING_RECORD(EnumEntry, KWAIT_BLOCK, WaitListEntry);
257 DPRINT("current_entry %x current %x\n", EnumEntry, WaiterHead);
258 EnumEntry = EnumEntry->Flink;
259 assert(WaiterHead->Thread != NULL);
260 assert(WaiterHead->Thread->WaitBlockList != NULL);
261
262 Abandoned = FALSE;
263
264 if (WaiterHead->WaitType == WaitAny)
265 {
266 DPRINT("WaitAny: Remove all wait blocks.\n");
267 for (Waiter = WaiterHead->Thread->WaitBlockList; Waiter; Waiter = Waiter->NextWaitBlock)
268 {
269 if (Waiter->WaitListEntry.Flink != NULL && Waiter->WaitListEntry.Blink != NULL)
270 {
271 RemoveEntryList(&Waiter->WaitListEntry);
272 Waiter->WaitListEntry.Flink = Waiter->WaitListEntry.Blink = NULL;
273 }
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) ? TRUE : Abandoned;
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 if (Waiter->WaitListEntry.Flink != NULL && Waiter->WaitListEntry.Blink != NULL)
309 {
310 RemoveEntryList(&Waiter->WaitListEntry);
311 Waiter->WaitListEntry.Flink = Waiter->WaitListEntry.Blink = NULL;
312 }
313
314 if (Waiter->WaitType == WaitAll)
315 {
316 Abandoned = KiSideEffectsBeforeWake(Waiter->Object, Waiter->Thread)
317 ? TRUE : Abandoned;
318 }
319
320 //no WaitAny objects can possibly be signaled since we are here
321 assert(!(Waiter->WaitType == WaitAny
322 && KiIsObjectSignalled(Waiter->Object, Waiter->Thread)));
323 }
324
325 WaiterHead->Thread->WaitBlockList = NULL;
326 }
327 }
328
329 if (WaiterHead->Thread->WaitBlockList == NULL)
330 {
331 Status = WaiterHead->WaitKey;
332 if (Abandoned)
333 {
334 DPRINT("Abandoned mutex among objects");
335 Status += STATUS_ABANDONED_WAIT_0;
336 }
337
338 WakedAny = TRUE;
339 DPRINT("Waking %x status = %x\n", WaiterHead->Thread, Status);
340 PsUnblockThread(CONTAINING_RECORD(WaiterHead->Thread, ETHREAD, Tcb), &Status);
341 }
342 }
343
344 return WakedAny;
345 }
346
347
348 BOOLEAN KeDispatcherObjectWake(DISPATCHER_HEADER* hdr)
349 /*
350 * FUNCTION: Wake threads waiting on a dispatcher object
351 * NOTE: The exact semantics of waking are dependant on the type of object
352 */
353 {
354 BOOL Ret;
355
356 DPRINT("Entering KeDispatcherObjectWake(hdr %x)\n",hdr);
357 // DPRINT("hdr->WaitListHead %x hdr->WaitListHead.Flink %x\n",
358 // &hdr->WaitListHead,hdr->WaitListHead.Flink);
359 DPRINT("hdr->Type %x\n",hdr->Type);
360 switch (hdr->Type)
361 {
362 case InternalNotificationEvent:
363 return(KeDispatcherObjectWakeAll(hdr));
364
365 case InternalNotificationTimer:
366 return(KeDispatcherObjectWakeAll(hdr));
367
368 case InternalSynchronizationEvent:
369 return(KeDispatcherObjectWakeOne(hdr));
370
371 case InternalSynchronizationTimer:
372 return(KeDispatcherObjectWakeOne(hdr));
373
374 case InternalQueueType:
375 case InternalSemaphoreType:
376 DPRINT("hdr->SignalState %d\n", hdr->SignalState);
377 if(hdr->SignalState>0)
378 {
379 do
380 {
381 DPRINT("Waking one semaphore waiter\n");
382 Ret = KeDispatcherObjectWakeOne(hdr);
383 } while(hdr->SignalState > 0 && Ret) ;
384 return(Ret);
385 }
386 else return FALSE;
387
388 case InternalProcessType:
389 return(KeDispatcherObjectWakeAll(hdr));
390
391 case InternalThreadType:
392 return(KeDispatcherObjectWakeAll(hdr));
393
394 case InternalMutexType:
395 return(KeDispatcherObjectWakeOne(hdr));
396 }
397 DbgPrint("Dispatcher object %x has unknown type %d\n", hdr, hdr->Type);
398 KEBUGCHECK(0);
399 return(FALSE);
400 }
401
402
403 /*
404 * @implemented
405 */
406 NTSTATUS STDCALL
407 KeWaitForSingleObject(PVOID Object,
408 KWAIT_REASON WaitReason,
409 KPROCESSOR_MODE WaitMode,
410 BOOLEAN Alertable,
411 PLARGE_INTEGER Timeout)
412 /*
413 * FUNCTION: Puts the current thread into a wait state until the
414 * given dispatcher object is set to signalled
415 * ARGUMENTS:
416 * Object = Object to wait on
417 * WaitReason = Reason for the wait (debugging aid)
418 * WaitMode = Can be KernelMode or UserMode, if UserMode then
419 * user-mode APCs can be delivered and the thread's
420 * stack can be paged out
421 * Altertable = Specifies if the wait is a alertable
422 * Timeout = Optional timeout value
423 * RETURNS: Status
424 */
425 {
426 return KeWaitForMultipleObjects(1,
427 &Object,
428 WaitAny,
429 WaitReason,
430 WaitMode,
431 Alertable,
432 Timeout,
433 NULL);
434 }
435
436
437 inline
438 PVOID
439 KiGetWaitableObjectFromObject(PVOID Object)
440 {
441 //special case when waiting on file objects
442 if ( ((PDISPATCHER_HEADER)Object)->Type == InternalFileType)
443 {
444 return &((PFILE_OBJECT)Object)->Event;
445 }
446
447 return Object;
448 }
449
450
451 /*
452 * @implemented
453 */
454 NTSTATUS STDCALL
455 KeWaitForMultipleObjects(ULONG Count,
456 PVOID Object[],
457 WAIT_TYPE WaitType,
458 KWAIT_REASON WaitReason,
459 KPROCESSOR_MODE WaitMode,
460 BOOLEAN Alertable,
461 PLARGE_INTEGER Timeout,
462 PKWAIT_BLOCK WaitBlockArray)
463 {
464 DISPATCHER_HEADER *hdr;
465 PKWAIT_BLOCK blk;
466 PKTHREAD CurrentThread;
467 ULONG CountSignaled;
468 ULONG i;
469 NTSTATUS Status;
470 KIRQL WaitIrql;
471 KIRQL OldIrql;
472 BOOLEAN Abandoned;
473
474 DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
475 "PsGetCurrentThread() %x\n", Count, Object, PsGetCurrentThread());
476
477 CurrentThread = KeGetCurrentThread();
478 WaitIrql = KeGetCurrentIrql();
479
480 /*
481 * Work out where we are going to put the wait blocks
482 */
483 if (WaitBlockArray == NULL)
484 {
485 if (Count > THREAD_WAIT_OBJECTS)
486 {
487 DPRINT("(%s:%d) Too many objects!\n", __FILE__, __LINE__);
488 return (STATUS_UNSUCCESSFUL);
489 }
490 WaitBlockArray = &CurrentThread->WaitBlock[0];
491 }
492 else
493 {
494 if (Count > EX_MAXIMUM_WAIT_OBJECTS)
495 {
496 DPRINT("(%s:%d) Too many objects!\n", __FILE__, __LINE__);
497 return (STATUS_UNSUCCESSFUL);
498 }
499 }
500
501 /*
502 * Set up the timeout if required
503 */
504 if (Timeout != NULL && Timeout->QuadPart != 0)
505 {
506 KeInitializeTimer(&CurrentThread->Timer);
507 KeSetTimer(&CurrentThread->Timer, *Timeout, NULL);
508 }
509
510 do
511 {
512 if (CurrentThread->WaitNext)
513 {
514 OldIrql = CurrentThread->WaitIrql;
515 CurrentThread->WaitNext = 0;
516 CurrentThread->WaitIrql = PASSIVE_LEVEL;
517 }
518 else
519 {
520 OldIrql = KeAcquireDispatcherDatabaseLock ();
521 }
522
523 /*
524 * If we are going to wait alertably and a user apc is pending
525 * then return
526 */
527 if (Alertable && KiTestAlert())
528 {
529 KeReleaseDispatcherDatabaseLock(OldIrql);
530 return (STATUS_USER_APC);
531 }
532
533 /*
534 * Check if the wait is (already) satisfied
535 */
536 CountSignaled = 0;
537 Abandoned = FALSE;
538 for (i = 0; i < Count; i++)
539 {
540 hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
541
542 if (KiIsObjectSignalled(hdr, CurrentThread))
543 {
544 CountSignaled++;
545
546 if (WaitType == WaitAny)
547 {
548 Abandoned = KiSideEffectsBeforeWake(hdr, CurrentThread) ? TRUE : Abandoned;
549
550 if (Timeout != NULL && Timeout->QuadPart != 0)
551 {
552 KeCancelTimer(&CurrentThread->Timer);
553 }
554
555 KeReleaseDispatcherDatabaseLock(OldIrql);
556
557 DPRINT("One object is (already) signaled!\n");
558 if (Abandoned == TRUE)
559 {
560 return (STATUS_ABANDONED_WAIT_0 + i);
561 }
562
563 return (STATUS_WAIT_0 + i);
564 }
565 }
566 }
567
568 Abandoned = FALSE;
569 if ((WaitType == WaitAll) && (CountSignaled == Count))
570 {
571 for (i = 0; i < Count; i++)
572 {
573 hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
574 Abandoned = KiSideEffectsBeforeWake(hdr, CurrentThread) ? TRUE : Abandoned;
575 }
576
577 if (Timeout != NULL && Timeout->QuadPart != 0)
578 {
579 KeCancelTimer(&CurrentThread->Timer);
580 }
581
582 KeReleaseDispatcherDatabaseLock(OldIrql);
583 DPRINT("All objects are (already) signaled!\n");
584
585 if (Abandoned == TRUE)
586 {
587 return (STATUS_ABANDONED_WAIT_0);
588 }
589
590 return (STATUS_WAIT_0);
591 }
592
593 //zero timeout is used for testing if the object(s) can be immediately acquired
594 if (Timeout != NULL && Timeout->QuadPart == 0)
595 {
596 KeReleaseDispatcherDatabaseLock(OldIrql);
597 return STATUS_TIMEOUT;
598 }
599
600 /*
601 * Check if we have already timed out
602 */
603 if (Timeout != NULL && KiIsObjectSignalled(&CurrentThread->Timer.Header, CurrentThread))
604 {
605 KiSideEffectsBeforeWake(&CurrentThread->Timer.Header, CurrentThread);
606 KeCancelTimer(&CurrentThread->Timer);
607 KeReleaseDispatcherDatabaseLock(OldIrql);
608 return (STATUS_TIMEOUT);
609 }
610
611 /* Append wait block to the KTHREAD wait block list */
612 CurrentThread->WaitBlockList = blk = WaitBlockArray;
613
614 /*
615 * Set up the wait
616 */
617 CurrentThread->WaitStatus = STATUS_UNSUCCESSFUL;
618
619 for (i = 0; i < Count; i++)
620 {
621 hdr = (DISPATCHER_HEADER *) KiGetWaitableObjectFromObject(Object[i]);
622
623 blk->Object = KiGetWaitableObjectFromObject(Object[i]);
624 blk->Thread = CurrentThread;
625 blk->WaitKey = STATUS_WAIT_0 + i;
626 blk->WaitType = WaitType;
627
628 if (i == (Count - 1))
629 {
630 if (Timeout != NULL)
631 {
632 blk->NextWaitBlock = &CurrentThread->WaitBlock[3];
633 }
634 else
635 {
636 blk->NextWaitBlock = NULL;
637 }
638 }
639 else
640 {
641 blk->NextWaitBlock = blk + 1;
642 }
643
644 /*
645 * add wait block to disp. obj. wait list
646 * Use FIFO for all waits except for queues which use LIFO
647 */
648 if (WaitReason == WrQueue)
649 {
650 InsertHeadList(&hdr->WaitListHead, &blk->WaitListEntry);
651 }
652 else
653 {
654 InsertTailList(&hdr->WaitListHead, &blk->WaitListEntry);
655 }
656
657 blk = blk->NextWaitBlock;
658 }
659
660 if (Timeout != NULL)
661 {
662 CurrentThread->WaitBlock[3].Object = (PVOID) & CurrentThread->Timer;
663 CurrentThread->WaitBlock[3].Thread = CurrentThread;
664 CurrentThread->WaitBlock[3].WaitKey = STATUS_TIMEOUT;
665 CurrentThread->WaitBlock[3].WaitType = WaitAny;
666 CurrentThread->WaitBlock[3].NextWaitBlock = NULL;
667
668 InsertTailList(&CurrentThread->Timer.Header.WaitListHead,
669 &CurrentThread->WaitBlock[3].WaitListEntry);
670 }
671
672 //io completion
673 if (CurrentThread->Queue)
674 {
675 CurrentThread->Queue->CurrentCount--;
676 if (WaitReason != WrQueue && CurrentThread->Queue->CurrentCount < CurrentThread->Queue->MaximumCount &&
677 !IsListEmpty(&CurrentThread->Queue->EntryListHead))
678 {
679 KeDispatcherObjectWake(&CurrentThread->Queue->Header);
680 }
681 }
682
683 PsBlockThread(&Status, Alertable, WaitMode, TRUE, WaitIrql, WaitReason);
684
685 //io completion
686 if (CurrentThread->Queue)
687 {
688 CurrentThread->Queue->CurrentCount++;
689 }
690
691
692 }
693 while (Status == STATUS_KERNEL_APC);
694
695 if (Timeout != NULL)
696 {
697 KeCancelTimer(&CurrentThread->Timer);
698 }
699
700 DPRINT("Returning from KeWaitForMultipleObjects()\n");
701 return (Status);
702 }
703
704 VOID KeInitializeDispatcher(VOID)
705 {
706 KeInitializeSpinLock(&DispatcherDatabaseLock);
707 }
708
709 NTSTATUS STDCALL
710 NtWaitForMultipleObjects(IN ULONG Count,
711 IN HANDLE Object [],
712 IN WAIT_TYPE WaitType,
713 IN BOOLEAN Alertable,
714 IN PLARGE_INTEGER Time)
715 {
716 KWAIT_BLOCK WaitBlockArray[EX_MAXIMUM_WAIT_OBJECTS];
717 PVOID ObjectPtrArray[EX_MAXIMUM_WAIT_OBJECTS];
718 NTSTATUS Status;
719 ULONG i, j;
720 KPROCESSOR_MODE WaitMode;
721
722 DPRINT("NtWaitForMultipleObjects(Count %lu Object[] %x, Alertable %d, "
723 "Time %x)\n", Count,Object,Alertable,Time);
724
725 if (Count > EX_MAXIMUM_WAIT_OBJECTS)
726 return STATUS_UNSUCCESSFUL;
727
728 WaitMode = ExGetPreviousMode();
729
730 /* reference all objects */
731 for (i = 0; i < Count; i++)
732 {
733 Status = ObReferenceObjectByHandle(Object[i],
734 SYNCHRONIZE,
735 NULL,
736 WaitMode,
737 &ObjectPtrArray[i],
738 NULL);
739 if (Status != STATUS_SUCCESS)
740 {
741 /* dereference all referenced objects */
742 for (j = 0; j < i; j++)
743 {
744 ObDereferenceObject(ObjectPtrArray[j]);
745 }
746
747 return(Status);
748 }
749 }
750
751 Status = KeWaitForMultipleObjects(Count,
752 ObjectPtrArray,
753 WaitType,
754 UserRequest,
755 WaitMode,
756 Alertable,
757 Time,
758 WaitBlockArray);
759
760 /* dereference all objects */
761 for (i = 0; i < Count; i++)
762 {
763 ObDereferenceObject(ObjectPtrArray[i]);
764 }
765
766 return(Status);
767 }
768
769
770 /*
771 * @implemented
772 */
773 NTSTATUS STDCALL
774 NtWaitForSingleObject(IN HANDLE Object,
775 IN BOOLEAN Alertable,
776 IN PLARGE_INTEGER Time)
777 {
778 PVOID ObjectPtr;
779 NTSTATUS Status;
780 KPROCESSOR_MODE WaitMode;
781
782 DPRINT("NtWaitForSingleObject(Object %x, Alertable %d, Time %x)\n",
783 Object,Alertable,Time);
784
785 WaitMode = ExGetPreviousMode();
786
787 Status = ObReferenceObjectByHandle(Object,
788 SYNCHRONIZE,
789 NULL,
790 WaitMode,
791 &ObjectPtr,
792 NULL);
793 if (!NT_SUCCESS(Status))
794 {
795 return(Status);
796 }
797
798 Status = KeWaitForSingleObject(ObjectPtr,
799 UserRequest,
800 WaitMode,
801 Alertable,
802 Time);
803
804 ObDereferenceObject(ObjectPtr);
805
806 return(Status);
807 }
808
809
810 NTSTATUS STDCALL
811 NtSignalAndWaitForSingleObject(IN HANDLE SignalObject,
812 IN HANDLE WaitObject,
813 IN BOOLEAN Alertable,
814 IN PLARGE_INTEGER Time)
815 {
816 KPROCESSOR_MODE WaitMode;
817 DISPATCHER_HEADER* hdr;
818 PVOID SignalObj;
819 PVOID WaitObj;
820 NTSTATUS Status;
821
822 WaitMode = ExGetPreviousMode();
823 Status = ObReferenceObjectByHandle(SignalObject,
824 0,
825 NULL,
826 WaitMode,
827 &SignalObj,
828 NULL);
829 if (!NT_SUCCESS(Status))
830 {
831 return Status;
832 }
833
834 Status = ObReferenceObjectByHandle(WaitObject,
835 SYNCHRONIZE,
836 NULL,
837 WaitMode,
838 &WaitObj,
839 NULL);
840 if (!NT_SUCCESS(Status))
841 {
842 ObDereferenceObject(SignalObj);
843 return Status;
844 }
845
846 hdr = (DISPATCHER_HEADER *)SignalObj;
847 switch (hdr->Type)
848 {
849 case InternalNotificationEvent:
850 case InternalSynchronizationEvent:
851 KeSetEvent(SignalObj,
852 EVENT_INCREMENT,
853 TRUE);
854 break;
855
856 case InternalMutexType:
857 KeReleaseMutex(SignalObj,
858 TRUE);
859 break;
860
861 case InternalSemaphoreType:
862 KeReleaseSemaphore(SignalObj,
863 SEMAPHORE_INCREMENT,
864 1,
865 TRUE);
866 break;
867
868 default:
869 ObDereferenceObject(SignalObj);
870 ObDereferenceObject(WaitObj);
871 return STATUS_OBJECT_TYPE_MISMATCH;
872 }
873
874 Status = KeWaitForSingleObject(WaitObj,
875 UserRequest,
876 WaitMode,
877 Alertable,
878 Time);
879
880 ObDereferenceObject(SignalObj);
881 ObDereferenceObject(WaitObj);
882
883 return Status;
884 }