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