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