(Finally) implemented Philip Susi's suggestion for non-counting block
[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 VOID KiSideEffectsBeforeWake(DISPATCHER_HEADER* hdr,
89 PKTHREAD Thread)
90 /*
91 * FUNCTION: Perform side effects on object before a wait for a thread is
92 * satisfied
93 */
94 {
95 switch (hdr->Type)
96 {
97 case InternalSynchronizationEvent:
98 hdr->SignalState = 0;
99 break;
100
101 case InternalSemaphoreType:
102 hdr->SignalState--;
103 break;
104
105 case InternalProcessType:
106 break;
107
108 case InternalThreadType:
109 break;
110
111 case InternalNotificationEvent:
112 break;
113
114 case InternalSynchronizationTimer:
115 hdr->SignalState = FALSE;
116 break;
117
118 case InternalNotificationTimer:
119 break;
120
121 case InternalMutexType:
122 {
123 PKMUTEX Mutex;
124
125 Mutex = CONTAINING_RECORD(hdr, KMUTEX, Header);
126 hdr->SignalState--;
127 assert(hdr->SignalState <= 1);
128 Mutex->OwnerThread = Thread;
129 }
130 break;
131
132 default:
133 DbgPrint("(%s:%d) Dispatcher object %x has unknown type\n",
134 __FILE__,__LINE__,hdr);
135 KeBugCheck(0);
136 }
137
138 }
139
140 static BOOLEAN KiIsObjectSignalled(DISPATCHER_HEADER* hdr,
141 PKTHREAD Thread)
142 {
143 if (hdr->Type == InternalMutexType)
144 {
145 PKMUTEX Mutex;
146
147 Mutex = CONTAINING_RECORD(hdr, KMUTEX, Header);
148
149 assert(hdr->SignalState <= 1);
150 if ((hdr->SignalState < 1 && Mutex->OwnerThread == Thread) ||
151 hdr->SignalState == 1)
152 {
153 KiSideEffectsBeforeWake(hdr,
154 Thread);
155 return(TRUE);
156 }
157 else
158 {
159 return(FALSE);
160 }
161 }
162 if (hdr->SignalState <= 0)
163 {
164 return(FALSE);
165 }
166 else
167 {
168 KiSideEffectsBeforeWake(hdr, Thread);
169 return(TRUE);
170 }
171 }
172
173 VOID KeRemoveAllWaitsThread(PETHREAD Thread, NTSTATUS WaitStatus)
174 {
175 PKWAIT_BLOCK WaitBlock;
176 BOOLEAN WasWaiting = FALSE;
177
178 KeAcquireDispatcherDatabaseLock(FALSE);
179
180 WaitBlock = (PKWAIT_BLOCK)Thread->Tcb.WaitBlockList;
181 if (WaitBlock != NULL)
182 {
183 WasWaiting = TRUE;
184 }
185 while (WaitBlock != NULL)
186 {
187 RemoveEntryList(&WaitBlock->WaitListEntry);
188 WaitBlock = WaitBlock->NextWaitBlock;
189 }
190 Thread->Tcb.WaitBlockList = NULL;
191
192 if (WasWaiting)
193 {
194 PsUnblockThread(Thread, &WaitStatus);
195 }
196
197 KeReleaseDispatcherDatabaseLock(FALSE);
198 }
199
200 static BOOLEAN KeDispatcherObjectWakeAll(DISPATCHER_HEADER* hdr)
201 {
202 PKWAIT_BLOCK current;
203 PLIST_ENTRY current_entry;
204 PKWAIT_BLOCK PrevBlock;
205 NTSTATUS Status;
206
207 DPRINT("KeDispatcherObjectWakeAll(hdr %x)\n",hdr);
208
209 if (IsListEmpty(&hdr->WaitListHead))
210 {
211 return(FALSE);
212 }
213
214 while (!IsListEmpty(&(hdr->WaitListHead)))
215 {
216 current_entry = RemoveHeadList(&hdr->WaitListHead);
217 current = CONTAINING_RECORD(current_entry,
218 KWAIT_BLOCK,
219 WaitListEntry);
220 DPRINT("Waking %x\n",current->Thread);
221 if (current->WaitType == WaitAny)
222 {
223 DPRINT("WaitAny: Remove all wait blocks.\n");
224 for( PrevBlock = current->Thread->WaitBlockList; PrevBlock; PrevBlock = PrevBlock->NextWaitBlock )
225 if( PrevBlock != current )
226 RemoveEntryList( &(PrevBlock->WaitListEntry) );
227 current->Thread->WaitBlockList = 0;
228 }
229 else
230 {
231 DPRINT("WaitAll: Remove the current wait block only.\n");
232
233 PrevBlock = current->Thread->WaitBlockList;
234 if (PrevBlock == current)
235 {
236 DPRINT( "WaitAll: Current block is list head.\n" );
237 current->Thread->WaitBlockList = current->NextWaitBlock;
238 }
239 else
240 {
241 DPRINT( "WaitAll: Current block is not list head.\n" );
242 while ( PrevBlock && PrevBlock->NextWaitBlock != current)
243 {
244 PrevBlock = PrevBlock->NextWaitBlock;
245 }
246 if (PrevBlock)
247 {
248 PrevBlock->NextWaitBlock = current->NextWaitBlock;
249 }
250 }
251 }
252 KiSideEffectsBeforeWake(hdr, current->Thread);
253 Status = current->WaitKey;
254 if (current->Thread->WaitBlockList == NULL)
255 {
256 PsUnblockThread(CONTAINING_RECORD(current->Thread,ETHREAD,Tcb),
257 &Status);
258 }
259 }
260 return(TRUE);
261 }
262
263 static BOOLEAN KeDispatcherObjectWakeOne(DISPATCHER_HEADER* hdr)
264 {
265 PKWAIT_BLOCK current;
266 PLIST_ENTRY current_entry;
267 PKWAIT_BLOCK PrevBlock;
268 NTSTATUS Status;
269
270 DPRINT("KeDispatcherObjectWakeOn(hdr %x)\n",hdr);
271 DPRINT("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
272 hdr->WaitListHead.Flink,hdr->WaitListHead.Blink);
273 if (IsListEmpty(&(hdr->WaitListHead)))
274 {
275 return(FALSE);
276 }
277 current_entry = RemoveHeadList(&(hdr->WaitListHead));
278 current = CONTAINING_RECORD(current_entry,KWAIT_BLOCK,
279 WaitListEntry);
280 DPRINT("current_entry %x current %x\n",current_entry,current);
281
282 if (current->WaitType == WaitAny)
283 {
284 DPRINT("WaitAny: Remove all wait blocks.\n");
285 for( PrevBlock = current->Thread->WaitBlockList; PrevBlock; PrevBlock = PrevBlock->NextWaitBlock )
286 if( PrevBlock != current )
287 RemoveEntryList( &(PrevBlock->WaitListEntry) );
288 current->Thread->WaitBlockList = 0;
289 }
290 else
291 {
292 DPRINT("WaitAll: Remove the current wait block only.\n");
293
294 PrevBlock = current->Thread->WaitBlockList;
295 if (PrevBlock == current)
296 {
297 DPRINT( "WaitAll: Current block is list head.\n" );
298 current->Thread->WaitBlockList = current->NextWaitBlock;
299 }
300 else
301 {
302 DPRINT( "WaitAll: Current block is not list head.\n" );
303 while ( PrevBlock && PrevBlock->NextWaitBlock != current)
304 {
305 PrevBlock = PrevBlock->NextWaitBlock;
306 }
307 if (PrevBlock)
308 {
309 PrevBlock->NextWaitBlock = current->NextWaitBlock;
310 }
311 }
312 }
313
314 DPRINT("Waking %x\n",current->Thread);
315 KiSideEffectsBeforeWake(hdr, current->Thread);
316 Status = current->WaitKey;
317 PsUnblockThread(CONTAINING_RECORD(current->Thread, ETHREAD, Tcb),
318 &Status);
319 return(TRUE);
320 }
321
322 BOOLEAN KeDispatcherObjectWake(DISPATCHER_HEADER* hdr)
323 /*
324 * FUNCTION: Wake threads waiting on a dispatcher object
325 * NOTE: The exact semantics of waking are dependant on the type of object
326 */
327 {
328 BOOL Ret;
329
330 DPRINT("Entering KeDispatcherObjectWake(hdr %x)\n",hdr);
331 // DPRINT("hdr->WaitListHead %x hdr->WaitListHead.Flink %x\n",
332 // &hdr->WaitListHead,hdr->WaitListHead.Flink);
333 DPRINT("hdr->Type %x\n",hdr->Type);
334 switch (hdr->Type)
335 {
336 case InternalNotificationEvent:
337 return(KeDispatcherObjectWakeAll(hdr));
338
339 case InternalNotificationTimer:
340 return(KeDispatcherObjectWakeAll(hdr));
341
342 case InternalSynchronizationEvent:
343 return(KeDispatcherObjectWakeOne(hdr));
344
345
346 case InternalSynchronizationTimer:
347 return(KeDispatcherObjectWakeOne(hdr));
348
349 case InternalSemaphoreType:
350 DPRINT("hdr->SignalState %d\n", hdr->SignalState);
351 if(hdr->SignalState>0)
352 {
353 do
354 {
355 DPRINT("Waking one semaphore waiter\n");
356 Ret = KeDispatcherObjectWakeOne(hdr);
357 } while(hdr->SignalState > 0 && Ret) ;
358 return(Ret);
359 }
360 else return FALSE;
361
362 case InternalProcessType:
363 return(KeDispatcherObjectWakeAll(hdr));
364
365 case InternalThreadType:
366 return(KeDispatcherObjectWakeAll(hdr));
367
368 case InternalMutexType:
369 return(KeDispatcherObjectWakeOne(hdr));
370 }
371 DbgPrint("Dispatcher object %x has unknown type %d\n", hdr, hdr->Type);
372 KeBugCheck(0);
373 return(FALSE);
374 }
375
376
377 NTSTATUS STDCALL KeWaitForSingleObject (PVOID Object,
378 KWAIT_REASON WaitReason,
379 KPROCESSOR_MODE WaitMode,
380 BOOLEAN Alertable,
381 PLARGE_INTEGER Timeout)
382 /*
383 * FUNCTION: Puts the current thread into a wait state until the
384 * given dispatcher object is set to signalled
385 * ARGUMENTS:
386 * Object = Object to wait on
387 * WaitReason = Reason for the wait (debugging aid)
388 * WaitMode = Can be KernelMode or UserMode, if UserMode then
389 * user-mode APCs can be delivered and the thread's
390 * stack can be paged out
391 * Altertable = Specifies if the wait is a alertable
392 * Timeout = Optional timeout value
393 * RETURNS: Status
394 */
395 {
396 DISPATCHER_HEADER* hdr = (DISPATCHER_HEADER *)Object;
397 PKTHREAD CurrentThread;
398 NTSTATUS Status;
399 KIRQL WaitIrql;
400
401 DPRINT("Entering KeWaitForSingleObject(Object %x) "
402 "PsGetCurrentThread() %x\n",Object,PsGetCurrentThread());
403
404 CurrentThread = KeGetCurrentThread();
405 WaitIrql = KeGetCurrentIrql();
406
407 if (Timeout != NULL)
408 {
409 KeAddThreadTimeout(CurrentThread, Timeout);
410 }
411
412 do
413 {
414 KeAcquireDispatcherDatabaseLock(FALSE);
415
416 DPRINT("hdr->SignalState %d\n", hdr->SignalState);
417
418 if (Alertable && KiTestAlert())
419 {
420 KeReleaseDispatcherDatabaseLock(FALSE);
421 return(STATUS_USER_APC);
422 }
423
424 if (KiIsObjectSignalled(hdr, CurrentThread))
425 {
426 KeReleaseDispatcherDatabaseLock(FALSE);
427 if (Timeout != NULL)
428 {
429 KeCancelTimer(&KeGetCurrentThread()->Timer);
430 }
431 return(STATUS_WAIT_0);
432 }
433
434 CurrentThread->WaitStatus = STATUS_UNSUCCESSFUL;
435 /* Append wait block to the KTHREAD wait block list */
436 CurrentThread->WaitBlockList = &CurrentThread->WaitBlock[0];
437 CurrentThread->WaitBlock[0].Object = Object;
438 CurrentThread->WaitBlock[0].Thread = CurrentThread;
439 CurrentThread->WaitBlock[0].WaitKey = 0;
440 CurrentThread->WaitBlock[0].WaitType = WaitAny;
441 CurrentThread->WaitBlock[0].NextWaitBlock = NULL;
442 InsertTailList(&hdr->WaitListHead,
443 &CurrentThread->WaitBlock[0].WaitListEntry);
444 PsBlockThread(&Status, (UCHAR)Alertable, WaitMode, TRUE, WaitIrql);
445 } while (Status == STATUS_KERNEL_APC);
446
447 if (Timeout != NULL)
448 {
449 KeCancelTimer(&KeGetCurrentThread()->Timer);
450 }
451
452 DPRINT("Returning from KeWaitForSingleObject()\n");
453 return(Status);
454 }
455
456
457 NTSTATUS STDCALL
458 KeWaitForMultipleObjects (ULONG Count,
459 PVOID Object[],
460 WAIT_TYPE WaitType,
461 KWAIT_REASON WaitReason,
462 KPROCESSOR_MODE WaitMode,
463 BOOLEAN Alertable,
464 PLARGE_INTEGER Timeout,
465 PKWAIT_BLOCK WaitBlockArray)
466 {
467 DISPATCHER_HEADER* hdr;
468 PKWAIT_BLOCK blk;
469 PKTHREAD CurrentThread;
470 ULONG CountSignaled;
471 ULONG i;
472 NTSTATUS Status;
473 KIRQL WaitIrql;
474
475 DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
476 "PsGetCurrentThread() %x\n",Count,Object,PsGetCurrentThread());
477
478 CountSignaled = 0;
479 CurrentThread = KeGetCurrentThread();
480 WaitIrql = KeGetCurrentIrql();
481
482 if (WaitBlockArray == NULL)
483 {
484 if (Count > 4)
485 {
486 DbgPrint("(%s:%d) Too many objects!\n",
487 __FILE__,__LINE__);
488 return STATUS_UNSUCCESSFUL;
489 }
490 blk = &CurrentThread->WaitBlock[0];
491 }
492 else
493 {
494 if (Count > 64)
495 {
496 DbgPrint("(%s:%d) Too many objects!\n",
497 __FILE__,__LINE__);
498 return STATUS_UNSUCCESSFUL;
499 }
500 blk = WaitBlockArray;
501 }
502 if (Timeout != NULL)
503 {
504 KeAddThreadTimeout(CurrentThread,Timeout);
505 }
506
507 do {
508 KeAcquireDispatcherDatabaseLock(FALSE);
509
510 for (i = 0; i < Count; i++)
511 {
512 hdr = (DISPATCHER_HEADER *)Object[i];
513
514 DPRINT("hdr->SignalState %d\n", hdr->SignalState);
515
516 if (KiIsObjectSignalled(hdr, CurrentThread))
517 {
518 CountSignaled++;
519
520 if (WaitType == WaitAny)
521 {
522 KeReleaseDispatcherDatabaseLock(FALSE);
523 DPRINT("One object is already signaled!\n");
524 return(STATUS_WAIT_0 + i);
525 }
526 }
527 }
528
529 if ((WaitType == WaitAll) && (CountSignaled == Count))
530 {
531 KeReleaseDispatcherDatabaseLock(FALSE);
532 DPRINT("All objects are already signaled!\n");
533 return(STATUS_WAIT_0);
534 }
535
536 /* Append wait block to the KTHREAD wait block list */
537 CurrentThread->WaitBlockList = blk;
538
539 for (i = 0; i < Count; i++)
540 {
541 hdr = (DISPATCHER_HEADER *)Object[i];
542
543 DPRINT("hdr->SignalState %d\n", hdr->SignalState);
544
545 blk->Object = Object[i];
546 blk->Thread = CurrentThread;
547 blk->WaitKey = i;
548 blk->WaitType = WaitType;
549 if (i == Count - 1)
550 blk->NextWaitBlock = NULL;
551 else
552 blk->NextWaitBlock = blk + 1;
553
554 InsertTailList(&(hdr->WaitListHead),&(blk->WaitListEntry));
555
556 blk = blk->NextWaitBlock;
557 }
558
559 PsBlockThread(&Status, Alertable, WaitMode, TRUE, WaitIrql);
560 } while( Status == STATUS_KERNEL_APC );
561 if (Timeout != NULL)
562 KeCancelTimer(&KeGetCurrentThread()->Timer);
563 DPRINT("Returning from KeWaitForMultipleObjects()\n");
564 return(Status);
565 }
566
567 VOID KeInitializeDispatcher(VOID)
568 {
569 KeInitializeSpinLock(&DispatcherDatabaseLock);
570 }
571
572 NTSTATUS STDCALL NtWaitForMultipleObjects(IN ULONG Count,
573 IN HANDLE Object [],
574 IN CINT WaitType,
575 IN BOOLEAN Alertable,
576 IN PLARGE_INTEGER Time)
577 {
578 KWAIT_BLOCK WaitBlockArray[EX_MAXIMUM_WAIT_OBJECTS];
579 PVOID ObjectPtrArray[EX_MAXIMUM_WAIT_OBJECTS];
580 NTSTATUS Status;
581 ULONG i, j;
582
583 DPRINT("NtWaitForMultipleObjects(Count %lu Object[] %x, Alertable %d, "
584 "Time %x)\n", Count,Object,Alertable,Time);
585
586 if (Count > EX_MAXIMUM_WAIT_OBJECTS)
587 return STATUS_UNSUCCESSFUL;
588
589 /* reference all objects */
590 for (i = 0; i < Count; i++)
591 {
592 Status = ObReferenceObjectByHandle(Object[i],
593 SYNCHRONIZE,
594 NULL,
595 UserMode,
596 &ObjectPtrArray[i],
597 NULL);
598 if (Status != STATUS_SUCCESS)
599 {
600 /* dereference all referenced objects */
601 for (j = 0; j < i; i++)
602 {
603 ObDereferenceObject(ObjectPtrArray[j]);
604 }
605
606 return(Status);
607 }
608 }
609
610 Status = KeWaitForMultipleObjects(Count,
611 ObjectPtrArray,
612 WaitType,
613 UserRequest,
614 UserMode,
615 Alertable,
616 Time,
617 WaitBlockArray);
618
619 /* dereference all objects */
620 for (i = 0; i < Count; i++)
621 {
622 ObDereferenceObject(ObjectPtrArray[i]);
623 }
624
625 return(Status);
626 }
627
628
629 NTSTATUS STDCALL NtWaitForSingleObject (IN HANDLE Object,
630 IN BOOLEAN Alertable,
631 IN PLARGE_INTEGER Time)
632 {
633 PVOID ObjectPtr;
634 NTSTATUS Status;
635
636 DPRINT("NtWaitForSingleObject(Object %x, Alertable %d, Time %x)\n",
637 Object,Alertable,Time);
638
639 Status = ObReferenceObjectByHandle(Object,
640 SYNCHRONIZE,
641 NULL,
642 UserMode,
643 &ObjectPtr,
644 NULL);
645 if (!NT_SUCCESS(Status))
646 {
647 return(Status);
648 }
649
650 Status = KeWaitForSingleObject(ObjectPtr,
651 UserMode,
652 UserMode,
653 Alertable,
654 Time);
655
656 ObDereferenceObject(ObjectPtr);
657
658 return(Status);
659 }
660
661
662 NTSTATUS STDCALL
663 NtSignalAndWaitForSingleObject(IN HANDLE SignalObject,
664 IN HANDLE WaitObject,
665 IN BOOLEAN Alertable,
666 IN PLARGE_INTEGER Time)
667 {
668 KPROCESSOR_MODE ProcessorMode;
669 DISPATCHER_HEADER* hdr;
670 PVOID SignalObj;
671 PVOID WaitObj;
672 NTSTATUS Status;
673
674 ProcessorMode = ExGetPreviousMode();
675 Status = ObReferenceObjectByHandle(SignalObject,
676 0,
677 NULL,
678 ProcessorMode,
679 &SignalObj,
680 NULL);
681 if (!NT_SUCCESS(Status))
682 {
683 return Status;
684 }
685
686 Status = ObReferenceObjectByHandle(WaitObject,
687 SYNCHRONIZE,
688 NULL,
689 ProcessorMode,
690 &WaitObj,
691 NULL);
692 if (!NT_SUCCESS(Status))
693 {
694 ObDereferenceObject(SignalObj);
695 return Status;
696 }
697
698 hdr = (DISPATCHER_HEADER *)SignalObj;
699 switch (hdr->Type)
700 {
701 case InternalNotificationEvent:
702 case InternalSynchronizationEvent:
703 KeSetEvent(SignalObj,
704 EVENT_INCREMENT,
705 TRUE);
706 break;
707
708 case InternalMutexType:
709 KeReleaseMutex(SignalObj,
710 TRUE);
711 break;
712
713 case InternalSemaphoreType:
714 KeReleaseSemaphore(SignalObj,
715 SEMAPHORE_INCREMENT,
716 1,
717 TRUE);
718 break;
719
720 default:
721 ObDereferenceObject(SignalObj);
722 ObDereferenceObject(WaitObj);
723 return STATUS_OBJECT_TYPE_MISMATCH;
724 }
725
726 Status = KeWaitForSingleObject(WaitObj,
727 UserRequest,
728 ProcessorMode,
729 Alertable,
730 Time);
731
732 ObDereferenceObject(SignalObj);
733 ObDereferenceObject(WaitObj);
734
735 return Status;
736 }