Thread/Process Termination/Repeaing Rewrite + Fixes
[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 *
7 * PROGRAMMERS: Alex Ionescu - Fixes and optimization.
8 * Gunnar Dalsnes - Implementation
9 */
10
11 /* INCLUDES ******************************************************************/
12
13 #include <ntoskrnl.h>
14
15 #define NDEBUG
16 #include <internal/debug.h>
17
18 /* GLOBALS ******************************************************************/
19
20 static KSPIN_LOCK DispatcherDatabaseLock;
21
22 /* Tells us if the Timer or Event is a Syncronization or Notification Object */
23 #define TIMER_OR_EVENT_TYPE 0x7L
24
25 /* One of the Reserved Wait Blocks, this one is for the Thread's Timer */
26 #define TIMER_WAIT_BLOCK 0x3L
27
28 /* FUNCTIONS *****************************************************************/
29
30 VOID
31 inline
32 FASTCALL
33 KiCheckAlertability(BOOLEAN Alertable,
34 PKTHREAD CurrentThread,
35 KPROCESSOR_MODE WaitMode,
36 PNTSTATUS Status)
37 {
38 /* At this point, we have to do a wait, so make sure we can make the thread Alertable if requested */
39 if (Alertable) {
40
41 /* If the Thread is Alerted, set the Wait Status accordingly */
42 if (CurrentThread->Alerted[(int)WaitMode]) {
43
44 CurrentThread->Alerted[(int)WaitMode] = FALSE;
45 DPRINT("Thread was Alerted\n");
46 *Status = STATUS_ALERTED;
47
48 /* If there are User APCs Pending, then we can't really be alertable */
49 } else if ((!IsListEmpty(&CurrentThread->ApcState.ApcListHead[UserMode])) &&
50 (WaitMode == UserMode)) {
51
52 DPRINT("APCs are Pending\n");
53 CurrentThread->ApcState.UserApcPending = TRUE;
54 *Status = STATUS_USER_APC;
55 }
56
57 /* If there are User APCs Pending and we are waiting in usermode, then we must notify the caller */
58 } else if ((CurrentThread->ApcState.UserApcPending) && (WaitMode == UserMode)) {
59 DPRINT("APCs are Pending\n");
60 *Status = STATUS_USER_APC;
61 }
62 }
63
64 /*
65 * @implemented
66 *
67 * FUNCTION: Puts the current thread into an alertable or nonalertable
68 * wait state for a given internal
69 * ARGUMENTS:
70 * WaitMode = Processor mode in which the caller is waiting
71 * Altertable = Specifies if the wait is alertable
72 * Interval = Specifies the interval to wait
73 * RETURNS: Status
74 */
75 NTSTATUS
76 STDCALL
77 KeDelayExecutionThread(KPROCESSOR_MODE WaitMode,
78 BOOLEAN Alertable,
79 PLARGE_INTEGER Interval)
80 {
81 PKWAIT_BLOCK TimerWaitBlock;
82 PKTIMER ThreadTimer;
83 PKTHREAD CurrentThread = KeGetCurrentThread();
84 NTSTATUS Status;
85
86 DPRINT("Entering KeDelayExecutionThread\n");
87
88 /* Check if the lock is already held */
89 if (CurrentThread->WaitNext) {
90
91 /* Lock is held, disable Wait Next */
92 DPRINT("Lock is held\n");
93 CurrentThread->WaitNext = FALSE;
94
95 } else {
96
97 /* Lock not held, acquire it */
98 DPRINT("Lock is not held, acquiring\n");
99 CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
100 }
101
102 /* Use built-in Wait block */
103 TimerWaitBlock = &CurrentThread->WaitBlock[TIMER_WAIT_BLOCK];
104
105 /* Start Wait Loop */
106 do {
107
108 /* We are going to wait no matter what (that's the point), so test Alertability */
109 KiCheckAlertability(Alertable, CurrentThread, KernelMode, &Status);
110
111 /* Set Timer */
112 ThreadTimer = &CurrentThread->Timer;
113
114 /* Setup the Wait Block */
115 CurrentThread->WaitBlockList = TimerWaitBlock;
116 TimerWaitBlock->Object = (PVOID)ThreadTimer;
117 TimerWaitBlock->Thread = CurrentThread;
118 TimerWaitBlock->WaitKey = (USHORT)STATUS_TIMEOUT;
119 TimerWaitBlock->WaitType = WaitAny;
120 TimerWaitBlock->NextWaitBlock = NULL;
121
122 /* Link the timer to this Wait Block */
123 InitializeListHead(&ThreadTimer->Header.WaitListHead);
124 InsertTailList(&ThreadTimer->Header.WaitListHead, &TimerWaitBlock->WaitListEntry);
125
126 /* Insert the Timer into the Timer Lists and enable it */
127 if (!KiInsertTimer(ThreadTimer, *Interval)) {
128
129 /* FIXME: Unhandled case...what should we do? */
130 DPRINT1("Could not create timer for KeDelayExecutionThread\n");
131 }
132
133 /* Handle Kernel Queues */
134 if (CurrentThread->Queue) {
135
136 DPRINT("Waking Queue\n");
137 KiWakeQueue(CurrentThread->Queue);
138 }
139
140 /* Block the Thread */
141 DPRINT("Blocking the Thread: %d, %d, %x\n", Alertable, WaitMode, KeGetCurrentThread());
142 KiBlockThread(&Status,
143 Alertable,
144 WaitMode,
145 DelayExecution);
146
147 /* Check if we were executing an APC or if we timed out */
148 if (Status != STATUS_KERNEL_APC) {
149
150 /* This is a good thing */
151 if (Status == STATUS_TIMEOUT) Status = STATUS_SUCCESS;
152
153 /* Return Status */
154 return Status;
155 }
156
157 DPRINT("Looping Again\n");
158 CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
159
160 } while (TRUE);
161
162 /* Release the Lock, we are done */
163 DPRINT("Returning from KeDelayExecutionThread(), %x. Status: %d\n", KeGetCurrentThread(), Status);
164 KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
165 return Status;
166 }
167
168 /*
169 * @implemented
170 *
171 * FUNCTION: Puts the current thread into a wait state until the
172 * given dispatcher object is set to signalled
173 * ARGUMENTS:
174 * Object = Object to wait on
175 * WaitReason = Reason for the wait (debugging aid)
176 * WaitMode = Can be KernelMode or UserMode, if UserMode then
177 * user-mode APCs can be delivered and the thread's
178 * stack can be paged out
179 * Altertable = Specifies if the wait is a alertable
180 * Timeout = Optional timeout value
181 * RETURNS: Status
182 */
183 NTSTATUS
184 STDCALL
185 KeWaitForSingleObject(PVOID Object,
186 KWAIT_REASON WaitReason,
187 KPROCESSOR_MODE WaitMode,
188 BOOLEAN Alertable,
189 PLARGE_INTEGER Timeout)
190 {
191 PDISPATCHER_HEADER CurrentObject;
192 PKWAIT_BLOCK WaitBlock;
193 PKWAIT_BLOCK TimerWaitBlock;
194 PKTIMER ThreadTimer;
195 PKTHREAD CurrentThread = KeGetCurrentThread();
196 NTSTATUS Status;
197 NTSTATUS WaitStatus;
198
199 DPRINT("Entering KeWaitForSingleObject\n");
200
201 /* Check if the lock is already held */
202 if (CurrentThread->WaitNext) {
203
204 /* Lock is held, disable Wait Next */
205 DPRINT("Lock is held\n");
206 CurrentThread->WaitNext = FALSE;
207
208 } else {
209
210 /* Lock not held, acquire it */
211 DPRINT("Lock is not held, acquiring\n");
212 CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
213 }
214
215 /* Start the actual Loop */
216 do {
217
218 /* Get the current Wait Status */
219 WaitStatus = CurrentThread->WaitStatus;
220
221 /* Append wait block to the KTHREAD wait block list */
222 CurrentThread->WaitBlockList = WaitBlock = &CurrentThread->WaitBlock[0];
223
224 /* Get the Current Object */
225 CurrentObject = (PDISPATCHER_HEADER)Object;
226
227 /* FIXME:
228 * Temporary hack until my Object Manager re-write. Basically some objects, like
229 * the File Object, but also LPCs and others, are actually waitable on their event.
230 * The Object Manager sets this up in The ObjectTypeInformation->DefaultObject member,
231 * by using pretty much the same kind of hack as us. Normal objects point to themselves
232 * in that pointer. Then, NtWaitForXXX will populate the WaitList that gets sent to us by
233 * using ->DefaultObject, so the proper actual objects will be sent to us. Until then however,
234 * I will keep this hack here, since there's no need to make an interim hack until the rewrite
235 * -- Alex Ionescu 24/02/05
236 */
237 if (CurrentObject->Type == IO_TYPE_FILE) {
238
239 DPRINT1("Hack used: %x\n", &((PFILE_OBJECT)CurrentObject)->Event);
240 CurrentObject = (PDISPATCHER_HEADER)(&((PFILE_OBJECT)CurrentObject)->Event);
241 }
242
243 /* Check if the Object is Signaled */
244 if (KiIsObjectSignaled(CurrentObject, CurrentThread)) {
245
246 /* Just unwait this guy and exit */
247 if (CurrentObject->SignalState != MINLONG) {
248
249 /* It has a normal signal state, so unwait it and return */
250 KiSatisfyObjectWait(CurrentObject, CurrentThread);
251 Status = STATUS_WAIT_0;
252 goto WaitDone;
253
254 } else {
255
256 /* Is this a Mutant? */
257 if (CurrentObject->Type == MutantObject) {
258
259 /* According to wasm.ru, we must raise this exception (tested and true) */
260 KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
261 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
262 }
263 }
264 }
265
266 /* Set up the Wait Block */
267 WaitBlock->Object = CurrentObject;
268 WaitBlock->Thread = CurrentThread;
269 WaitBlock->WaitKey = (USHORT)(STATUS_WAIT_0);
270 WaitBlock->WaitType = WaitAny;
271 WaitBlock->NextWaitBlock = NULL;
272
273 /* Make sure we can satisfy the Alertable request */
274 KiCheckAlertability(Alertable, CurrentThread, WaitMode, &Status);
275
276 /* Set the Wait Status */
277 CurrentThread->WaitStatus = Status;
278
279 /* Enable the Timeout Timer if there was any specified */
280 if (Timeout != NULL) {
281
282 /* However if 0 timeout was specified, then we must fail since we need to peform a wait */
283 if (!Timeout->QuadPart) {
284
285 /* Return a timeout */
286 Status = STATUS_TIMEOUT;
287 goto WaitDone;
288 }
289
290 /* Point to Timer Wait Block and Thread Timer */
291 TimerWaitBlock = &CurrentThread->WaitBlock[TIMER_WAIT_BLOCK];
292 ThreadTimer = &CurrentThread->Timer;
293
294 /* Connect the Timer Wait Block */
295 WaitBlock->NextWaitBlock = TimerWaitBlock;
296
297 /* Set up the Timer Wait Block */
298 TimerWaitBlock->Object = (PVOID)ThreadTimer;
299 TimerWaitBlock->Thread = CurrentThread;
300 TimerWaitBlock->WaitKey = STATUS_TIMEOUT;
301 TimerWaitBlock->WaitType = WaitAny;
302 TimerWaitBlock->NextWaitBlock = NULL;
303
304 /* Link the timer to this Wait Block */
305 InitializeListHead(&ThreadTimer->Header.WaitListHead);
306 InsertTailList(&ThreadTimer->Header.WaitListHead, &TimerWaitBlock->WaitListEntry);
307
308 /* Insert the Timer into the Timer Lists and enable it */
309 if (!KiInsertTimer(ThreadTimer, *Timeout)) {
310
311 /* Return a timeout if we couldn't insert the timer for some reason */
312 Status = STATUS_TIMEOUT;
313 goto WaitDone;
314 }
315 }
316
317 /* Link the Object to this Wait Block */
318 InsertTailList(&CurrentObject->WaitListHead, &WaitBlock->WaitListEntry);
319
320 /* Handle Kernel Queues */
321 if (CurrentThread->Queue) {
322
323 DPRINT("Waking Queue\n");
324 KiWakeQueue(CurrentThread->Queue);
325 }
326
327 /* Block the Thread */
328 DPRINT("Blocking the Thread: %d, %d, %d, %x\n", Alertable, WaitMode, WaitReason, KeGetCurrentThread());
329 KiBlockThread(&Status,
330 Alertable,
331 WaitMode,
332 (UCHAR)WaitReason);
333
334 /* Check if we were executing an APC */
335 if (Status != STATUS_KERNEL_APC) {
336
337 /* Return Status */
338 return Status;
339 }
340
341 DPRINT("Looping Again\n");
342 CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
343
344 } while (TRUE);
345
346 WaitDone:
347 /* Release the Lock, we are done */
348 DPRINT("Returning from KeWaitForMultipleObjects(), %x. Status: %d\n", KeGetCurrentThread(), Status);
349 KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
350 return Status;
351 }
352
353 /*
354 * @implemented
355 */
356 NTSTATUS STDCALL
357 KeWaitForMultipleObjects(ULONG Count,
358 PVOID Object[],
359 WAIT_TYPE WaitType,
360 KWAIT_REASON WaitReason,
361 KPROCESSOR_MODE WaitMode,
362 BOOLEAN Alertable,
363 PLARGE_INTEGER Timeout,
364 PKWAIT_BLOCK WaitBlockArray)
365 {
366 PDISPATCHER_HEADER CurrentObject;
367 PKWAIT_BLOCK WaitBlock;
368 PKWAIT_BLOCK TimerWaitBlock;
369 PKTIMER ThreadTimer;
370 PKTHREAD CurrentThread = KeGetCurrentThread();
371 ULONG AllObjectsSignaled;
372 ULONG WaitIndex;
373 NTSTATUS Status;
374 NTSTATUS WaitStatus;
375
376 DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
377 "PsGetCurrentThread() %x, Timeout %x\n", Count, Object, PsGetCurrentThread(), Timeout);
378
379 /* Set the Current Thread */
380 CurrentThread = KeGetCurrentThread();
381
382 /* Check if the lock is already held */
383 if (CurrentThread->WaitNext) {
384
385 /* Lock is held, disable Wait Next */
386 DPRINT("Lock is held\n");
387 CurrentThread->WaitNext = FALSE;
388
389 } else {
390
391 /* Lock not held, acquire it */
392 DPRINT("Lock is not held, acquiring\n");
393 CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
394 }
395
396 /* Make sure the Wait Count is valid for the Thread and Maximum Wait Objects */
397 if (!WaitBlockArray) {
398
399 /* Check in regards to the Thread Object Limit */
400 if (Count > THREAD_WAIT_OBJECTS) {
401
402 DPRINT1("(%s:%d) Too many objects!\n", __FILE__, __LINE__);
403 KEBUGCHECK(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
404 }
405
406 /* Use the Thread's Wait Block */
407 WaitBlockArray = &CurrentThread->WaitBlock[0];
408
409 } else {
410
411 /* Using our own Block Array. Check in regards to System Object Limit */
412 if (Count > MAXIMUM_WAIT_OBJECTS) {
413
414 DPRINT1("(%s:%d) Too many objects!\n", __FILE__, __LINE__);
415 KEBUGCHECK(MAXIMUM_WAIT_OBJECTS_EXCEEDED);
416 }
417 }
418
419 /* Start the actual Loop */
420 do {
421
422 /* Get the current Wait Status */
423 WaitStatus = CurrentThread->WaitStatus;
424
425 /* Append wait block to the KTHREAD wait block list */
426 CurrentThread->WaitBlockList = WaitBlock = WaitBlockArray;
427
428 /* Check if the wait is (already) satisfied */
429 AllObjectsSignaled = TRUE;
430
431 /* First, we'll try to satisfy the wait directly */
432 for (WaitIndex = 0; WaitIndex < Count; WaitIndex++) {
433
434 /* Get the Current Object */
435 CurrentObject = (PDISPATCHER_HEADER)Object[WaitIndex];
436
437 /* FIXME:
438 * Temporary hack until my Object Manager re-write. Basically some objects, like
439 * the File Object, but also LPCs and others, are actually waitable on their event.
440 * The Object Manager sets this up in The ObjectTypeInformation->DefaultObject member,
441 * by using pretty much the same kind of hack as us. Normal objects point to themselves
442 * in that pointer. Then, NtWaitForXXX will populate the WaitList that gets sent to us by
443 * using ->DefaultObject, so the proper actual objects will be sent to us. Until then however,
444 * I will keep this hack here, since there's no need to make an interim hack until the rewrite
445 * -- Alex Ionescu 24/02/05
446 */
447 if (CurrentObject->Type == IO_TYPE_FILE) {
448
449 DPRINT1("Hack used: %x\n", &((PFILE_OBJECT)CurrentObject)->Event);
450 CurrentObject = (PDISPATCHER_HEADER)(&((PFILE_OBJECT)CurrentObject)->Event);
451 }
452
453 /* Check if the Object is Signaled */
454 if (KiIsObjectSignaled(CurrentObject, CurrentThread)) {
455
456 /* Check what kind of wait this is */
457 if (WaitType == WaitAny) {
458
459 /* This is a Wait Any, so just unwait this guy and exit */
460 if (CurrentObject->SignalState != MINLONG) {
461
462 /* It has a normal signal state, so unwait it and return */
463 KiSatisfyObjectWait(CurrentObject, CurrentThread);
464 Status = STATUS_WAIT_0 | WaitIndex;
465 goto WaitDone;
466
467 } else {
468
469 /* Is this a Mutant? */
470 if (CurrentObject->Type == MutantObject) {
471
472 /* According to wasm.ru, we must raise this exception (tested and true) */
473 KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
474 ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
475 }
476 }
477 }
478
479 } else {
480
481 /* One of the objects isn't signaled... if this is a WaitAll, we will fail later */
482 AllObjectsSignaled = FALSE;
483 }
484
485 /* Set up a Wait Block for this Object */
486 WaitBlock->Object = CurrentObject;
487 WaitBlock->Thread = CurrentThread;
488 WaitBlock->WaitKey = (USHORT)(STATUS_WAIT_0 + WaitIndex);
489 WaitBlock->WaitType = (USHORT)WaitType;
490 WaitBlock->NextWaitBlock = WaitBlock + 1;
491
492 /* Move to the next Wait Block */
493 WaitBlock = WaitBlock->NextWaitBlock;
494 }
495
496 /* Return to the Root Wait Block */
497 WaitBlock--;
498 WaitBlock->NextWaitBlock = NULL;
499
500 /* Check if this is a Wait All and all the objects are signaled */
501 if ((WaitType == WaitAll) && (AllObjectsSignaled)) {
502
503 /* Return to the Root Wait Block */
504 WaitBlock = CurrentThread->WaitBlockList;
505
506 /* Satisfy their Waits and return to the caller */
507 KiSatisifyMultipleObjectWaits(WaitBlock);
508 Status = STATUS_WAIT_0;
509 goto WaitDone;
510 }
511
512 /* Make sure we can satisfy the Alertable request */
513 KiCheckAlertability(Alertable, CurrentThread, WaitMode, &Status);
514
515 /* Set the Wait Status */
516 CurrentThread->WaitStatus = Status;
517
518 /* Enable the Timeout Timer if there was any specified */
519 if (Timeout != NULL) {
520
521 /* However if 0 timeout was specified, then we must fail since we need to peform a wait */
522 if (!Timeout->QuadPart) {
523
524 /* Return a timeout */
525 Status = STATUS_TIMEOUT;
526 goto WaitDone;
527 }
528
529 /* Point to Timer Wait Block and Thread Timer */
530 TimerWaitBlock = &CurrentThread->WaitBlock[TIMER_WAIT_BLOCK];
531 ThreadTimer = &CurrentThread->Timer;
532
533 /* Connect the Timer Wait Block */
534 WaitBlock->NextWaitBlock = TimerWaitBlock;
535
536 /* Set up the Timer Wait Block */
537 TimerWaitBlock->Object = (PVOID)ThreadTimer;
538 TimerWaitBlock->Thread = CurrentThread;
539 TimerWaitBlock->WaitKey = STATUS_TIMEOUT;
540 TimerWaitBlock->WaitType = WaitAny;
541 TimerWaitBlock->NextWaitBlock = NULL;
542
543 /* Link the timer to this Wait Block */
544 InitializeListHead(&ThreadTimer->Header.WaitListHead);
545
546 /* Insert the Timer into the Timer Lists and enable it */
547 if (!KiInsertTimer(ThreadTimer, *Timeout)) {
548
549 /* Return a timeout if we couldn't insert the timer for some reason */
550 Status = STATUS_TIMEOUT;
551 goto WaitDone;
552 }
553 }
554
555 /* Insert into Object's Wait List*/
556 WaitBlock = CurrentThread->WaitBlockList;
557 while (WaitBlock) {
558
559 /* Get the Current Object */
560 CurrentObject = WaitBlock->Object;
561
562 /* Link the Object to this Wait Block */
563 InsertTailList(&CurrentObject->WaitListHead, &WaitBlock->WaitListEntry);
564
565 /* Move to the next Wait Block */
566 WaitBlock = WaitBlock->NextWaitBlock;
567 }
568
569 /* Handle Kernel Queues */
570 if (CurrentThread->Queue) {
571
572 DPRINT("Waking Queue\n");
573 KiWakeQueue(CurrentThread->Queue);
574 }
575
576 /* Block the Thread */
577 DPRINT("Blocking the Thread: %d, %d, %d, %x\n", Alertable, WaitMode, WaitReason, KeGetCurrentThread());
578 KiBlockThread(&Status,
579 Alertable,
580 WaitMode,
581 (UCHAR)WaitReason);
582
583 /* Check if we were executing an APC */
584 if (Status != STATUS_KERNEL_APC) {
585
586 /* Return Status */
587 return Status;
588 }
589
590 DPRINT("Looping Again\n");
591 CurrentThread->WaitIrql = KeAcquireDispatcherDatabaseLock();
592
593 } while (TRUE);
594
595 WaitDone:
596 /* Release the Lock, we are done */
597 DPRINT("Returning from KeWaitForMultipleObjects(), %x. Status: %d\n", KeGetCurrentThread(), Status);
598 KeReleaseDispatcherDatabaseLock(CurrentThread->WaitIrql);
599 return Status;
600 }
601
602 VOID
603 FASTCALL
604 KiSatisfyObjectWait(PDISPATCHER_HEADER Object,
605 PKTHREAD Thread)
606
607 {
608 /* Special case for Mutants */
609 if (Object->Type == MutantObject) {
610
611 /* Decrease the Signal State */
612 Object->SignalState--;
613
614 /* Check if it's now non-signaled */
615 if (Object->SignalState == 0) {
616
617 /* Set the Owner Thread */
618 ((PKMUTANT)Object)->OwnerThread = Thread;
619
620 /* Disable APCs if needed */
621 Thread->KernelApcDisable -= ((PKMUTANT)Object)->ApcDisable;
622
623 /* Check if it's abandoned */
624 if (((PKMUTANT)Object)->Abandoned) {
625
626 /* Unabandon it */
627 ((PKMUTANT)Object)->Abandoned = FALSE;
628
629 /* Return Status */
630 Thread->WaitStatus = STATUS_ABANDONED;
631 }
632
633 /* Insert it into the Mutant List */
634 InsertHeadList(&Thread->MutantListHead, &((PKMUTANT)Object)->MutantListEntry);
635 }
636
637 } else if ((Object->Type & TIMER_OR_EVENT_TYPE) == EventSynchronizationObject) {
638
639 /* These guys (Syncronization Timers and Events) just get un-signaled */
640 Object->SignalState = 0;
641
642 } else if (Object->Type == SemaphoreObject) {
643
644 /* These ones can have multiple signalings, so we only decrease it */
645 Object->SignalState--;
646 }
647 }
648
649 VOID
650 FASTCALL
651 KiWaitTest(PDISPATCHER_HEADER Object,
652 KPRIORITY Increment)
653 {
654 PLIST_ENTRY WaitEntry;
655 PLIST_ENTRY WaitList;
656 PKWAIT_BLOCK CurrentWaitBlock;
657 PKWAIT_BLOCK NextWaitBlock;
658
659 /* Loop the Wait Entries */
660 DPRINT("KiWaitTest for Object: %x\n", Object);
661 WaitList = &Object->WaitListHead;
662 WaitEntry = WaitList->Flink;
663 while ((WaitEntry != WaitList) && (Object->SignalState > 0)) {
664
665 /* Get the current wait block */
666 CurrentWaitBlock = CONTAINING_RECORD(WaitEntry, KWAIT_BLOCK, WaitListEntry);
667
668 /* Check the current Wait Mode */
669 if (CurrentWaitBlock->WaitType == WaitAny) {
670
671 /* Easy case, satisfy only this wait */
672 DPRINT("Satisfiying a Wait any\n");
673 WaitEntry = WaitEntry->Blink;
674 KiSatisfyObjectWait(Object, CurrentWaitBlock->Thread);
675
676 } else {
677
678 /* Everything must be satisfied */
679 DPRINT("Checking for a Wait All\n");
680 NextWaitBlock = CurrentWaitBlock->NextWaitBlock;
681
682 /* Loop first to make sure they are valid */
683 while (NextWaitBlock) {
684
685 /* Check if the object is signaled */
686 if (!KiIsObjectSignaled(Object, CurrentWaitBlock->Thread)) {
687
688 /* It's not, move to the next one */
689 DPRINT1("One of the object is non-signaled, sorry.\n");
690 goto SkipUnwait;
691 }
692
693 /* Go to the next Wait block */
694 NextWaitBlock = NextWaitBlock->NextWaitBlock;
695 }
696
697 /* All the objects are signaled, we can satisfy */
698 DPRINT("Satisfiying a Wait All\n");
699 WaitEntry = WaitEntry->Blink;
700 KiSatisifyMultipleObjectWaits(CurrentWaitBlock);
701 }
702
703 /* All waits satisfied, unwait the thread */
704 DPRINT("Unwaiting the Thread\n");
705 KiAbortWaitThread(CurrentWaitBlock->Thread, CurrentWaitBlock->WaitKey, Increment);
706
707 SkipUnwait:
708 /* Next entry */
709 WaitEntry = WaitEntry->Flink;
710 }
711
712 DPRINT("Done\n");
713 }
714
715 /* Must be called with the dispatcher lock held */
716 VOID
717 FASTCALL
718 KiAbortWaitThread(PKTHREAD Thread,
719 NTSTATUS WaitStatus,
720 KPRIORITY Increment)
721 {
722 PKWAIT_BLOCK WaitBlock;
723
724 /* If we are blocked, we must be waiting on something also */
725 DPRINT("KiAbortWaitThread: %x, Status: %x, %x \n", Thread, WaitStatus, Thread->WaitBlockList);
726 ASSERT((Thread->State == THREAD_STATE_BLOCKED) == (Thread->WaitBlockList != NULL));
727
728 /* Remove the Wait Blocks from the list */
729 DPRINT("Removing waits\n");
730 WaitBlock = Thread->WaitBlockList;
731 while (WaitBlock) {
732
733 /* Remove it */
734 DPRINT("Removing Waitblock: %x, %x\n", WaitBlock, WaitBlock->NextWaitBlock);
735 RemoveEntryList(&WaitBlock->WaitListEntry);
736
737 /* Go to the next one */
738 WaitBlock = WaitBlock->NextWaitBlock;
739 };
740
741 /* Check if there's a Thread Timer */
742 if (Thread->Timer.Header.Inserted) {
743
744 /* Cancel the Thread Timer with the no-lock fastpath */
745 DPRINT("Removing the Thread's Timer\n");
746 Thread->Timer.Header.Inserted = FALSE;
747 RemoveEntryList(&Thread->Timer.TimerListEntry);
748 }
749
750 /* Increment the Queue's active threads */
751 if (Thread->Queue) {
752
753 DPRINT("Incrementing Queue's active threads\n");
754 Thread->Queue->CurrentCount++;
755 }
756
757 /* Reschedule the Thread */
758 DPRINT("Unblocking the Thread\n");
759 KiUnblockThread(Thread, &WaitStatus, 0);
760 }
761
762 BOOLEAN
763 inline
764 FASTCALL
765 KiIsObjectSignaled(PDISPATCHER_HEADER Object,
766 PKTHREAD Thread)
767 {
768 /* Mutants are...well...mutants! */
769 if (Object->Type == MutantObject) {
770
771 /*
772 * Because Cutler hates mutants, they are actually signaled if the Signal State is <= 0
773 * Well, only if they are recursivly acquired (i.e if we own it right now).
774 * Of course, they are also signaled if their signal state is 1.
775 */
776 if ((Object->SignalState <= 0 && ((PKMUTANT)Object)->OwnerThread == Thread) ||
777 (Object->SignalState == 1)) {
778
779 /* Signaled Mutant */
780 return (TRUE);
781
782 } else {
783
784 /* Unsignaled Mutant */
785 return (FALSE);
786 }
787 }
788
789 /* Any other object is not a mutated freak, so let's use logic */
790 return (!Object->SignalState <= 0);
791 }
792
793 BOOL
794 inline
795 FASTCALL
796 KiIsObjectWaitable(PVOID Object)
797 {
798 POBJECT_HEADER Header;
799 Header = BODY_TO_HEADER(Object);
800
801 if (Header->ObjectType == ExEventObjectType ||
802 Header->ObjectType == ExIoCompletionType ||
803 Header->ObjectType == ExMutantObjectType ||
804 Header->ObjectType == ExSemaphoreObjectType ||
805 Header->ObjectType == ExTimerType ||
806 Header->ObjectType == PsProcessType ||
807 Header->ObjectType == PsThreadType ||
808 Header->ObjectType == IoFileObjectType) {
809
810 return TRUE;
811
812 } else {
813
814 return FALSE;
815 }
816 }
817
818 VOID
819 inline
820 FASTCALL
821 KiSatisifyMultipleObjectWaits(PKWAIT_BLOCK WaitBlock)
822 {
823 PKTHREAD WaitThread = WaitBlock->Thread;
824
825 /* Loop through all the Wait Blocks, and wake each Object */
826 while (WaitBlock) {
827
828 /* Wake the Object */
829 KiSatisfyObjectWait(WaitBlock->Object, WaitThread);
830 WaitBlock = WaitBlock->NextWaitBlock;
831 }
832 }
833
834 VOID
835 inline
836 FASTCALL
837 KeInitializeDispatcherHeader(DISPATCHER_HEADER* Header,
838 ULONG Type,
839 ULONG Size,
840 ULONG SignalState)
841 {
842 Header->Type = (UCHAR)Type;
843 Header->Absolute = 0;
844 Header->Inserted = 0;
845 Header->Size = (UCHAR)Size;
846 Header->SignalState = SignalState;
847 InitializeListHead(&(Header->WaitListHead));
848 }
849
850 KIRQL
851 inline
852 FASTCALL
853 KeAcquireDispatcherDatabaseLock(VOID)
854 {
855 KIRQL OldIrql;
856
857 KeAcquireSpinLock (&DispatcherDatabaseLock, &OldIrql);
858 return OldIrql;
859 }
860
861 VOID
862 inline
863 FASTCALL
864 KeAcquireDispatcherDatabaseLockAtDpcLevel(VOID)
865 {
866 KeAcquireSpinLockAtDpcLevel (&DispatcherDatabaseLock);
867 }
868
869 VOID
870 inline
871 FASTCALL
872 KeInitializeDispatcher(VOID)
873 {
874 /* Initialize the Dispatcher Lock */
875 KeInitializeSpinLock(&DispatcherDatabaseLock);
876 }
877
878 VOID
879 inline
880 FASTCALL
881 KeReleaseDispatcherDatabaseLock(KIRQL OldIrql)
882 {
883 /* If it's the idle thread, dispatch */
884 if (!KeIsExecutingDpc() && OldIrql < DISPATCH_LEVEL && KeGetCurrentThread() != NULL &&
885 KeGetCurrentThread() == KeGetCurrentPrcb()->IdleThread) {
886
887 KiDispatchThreadNoLock(THREAD_STATE_READY);
888 KeLowerIrql(OldIrql);
889
890 } else {
891
892 /* Just release the spin lock */
893 KeReleaseSpinLock(&DispatcherDatabaseLock, OldIrql);
894 }
895 }
896
897 VOID
898 inline
899 FASTCALL
900 KeReleaseDispatcherDatabaseLockFromDpcLevel(VOID)
901 {
902 KeReleaseSpinLockFromDpcLevel(&DispatcherDatabaseLock);
903 }
904
905 /* EOF */