- Remove LDT initialization, LDT shouldn't be initialized at bootup.
[reactos.git] / reactos / ntoskrnl / ke / kthread.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ke/kthread.c
5 * PURPOSE: Microkernel thread support
6 *
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
8 * David Welch (welch@cwcom.net)
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <internal/debug.h>
16
17 extern EX_WORK_QUEUE ExWorkerQueue[MaximumWorkQueue];
18
19 /*
20 * PURPOSE: List of threads associated with each priority level
21 */
22 LIST_ENTRY PriorityListHead[MAXIMUM_PRIORITY];
23 static ULONG PriorityListMask = 0;
24 ULONG IdleProcessorMask = 0;
25 extern LIST_ENTRY PspReaperListHead;
26
27 ULONG KiMask32Array[MAXIMUM_PRIORITY] =
28 {
29 0x1, 0x2, 0x4, 0x8, 0x10, 0x20,
30 0x40, 0x80, 0x100, 0x200, 0x4000, 0x800,
31 0x1000, 0x2000, 0x4000, 0x8000, 0x10000, 0x20000,
32 0x40000, 0x80000, 0x100000, 0x200000, 0x400000, 0x800000,
33 0x1000000, 0x2000000, 0x4000000, 0x8000000, 0x10000000, 0x20000000,
34 0x40000000, 0x80000000
35 };
36
37 /* FUNCTIONS *****************************************************************/
38
39 UCHAR
40 NTAPI
41 KeFindNextRightSetAffinity(IN UCHAR Number,
42 IN ULONG Set)
43 {
44 ULONG Bit, Result;
45 ASSERT(Set != 0);
46
47 /* Calculate the mask */
48 Bit = (AFFINITY_MASK(Number) - 1) & Set;
49
50 /* If it's 0, use the one we got */
51 if (!Bit) Bit = Set;
52
53 /* Now find the right set and return it */
54 BitScanReverse(&Result, Bit);
55 return (UCHAR)Result;
56 }
57
58 STATIC
59 VOID
60 KiRequestReschedule(CCHAR Processor)
61 {
62 PKPCR Pcr;
63
64 Pcr = (PKPCR)(KPCR_BASE + Processor * PAGE_SIZE);
65 Pcr->Prcb->QuantumEnd = TRUE;
66 KiIpiSendRequest(1 << Processor, IPI_DPC);
67 }
68
69 STATIC
70 VOID
71 KiInsertIntoThreadList(KPRIORITY Priority,
72 PKTHREAD Thread)
73 {
74 ASSERT(Ready == Thread->State);
75 ASSERT(Thread->Priority == Priority);
76
77 if (Priority >= MAXIMUM_PRIORITY || Priority < LOW_PRIORITY) {
78
79 DPRINT1("Invalid thread priority (%d)\n", Priority);
80 KEBUGCHECK(0);
81 }
82
83 InsertTailList(&PriorityListHead[Priority], &Thread->WaitListEntry);
84 PriorityListMask |= (1 << Priority);
85 }
86
87 STATIC
88 VOID
89 KiRemoveFromThreadList(PKTHREAD Thread)
90 {
91 ASSERT(Ready == Thread->State);
92 RemoveEntryList(&Thread->WaitListEntry);
93 if (IsListEmpty(&PriorityListHead[(ULONG)Thread->Priority])) {
94
95 PriorityListMask &= ~(1 << Thread->Priority);
96 }
97 }
98
99 STATIC
100 PKTHREAD
101 KiScanThreadList(KPRIORITY Priority,
102 KAFFINITY Affinity)
103 {
104 PKTHREAD current;
105 ULONG Mask;
106
107 Mask = (1 << Priority);
108
109 if (PriorityListMask & Mask) {
110
111 LIST_FOR_EACH(current, &PriorityListHead[Priority], KTHREAD, WaitListEntry) {
112
113 if (current->State != Ready) {
114
115 DPRINT1("%p/%d\n", current, current->State);
116 }
117
118 ASSERT(current->State == Ready);
119
120 if (current->Affinity & Affinity) {
121
122 KiRemoveFromThreadList(current);
123 return(current);
124 }
125 }
126 }
127
128 return(NULL);
129 }
130
131 BOOLEAN
132 STDCALL
133 KiDispatchThreadNoLock(ULONG NewThreadStatus)
134 {
135 KPRIORITY CurrentPriority;
136 PKTHREAD Candidate;
137 ULONG Affinity;
138 PKTHREAD CurrentThread = KeGetCurrentThread();
139 BOOLEAN ApcState;
140
141 DPRINT("KiDispatchThreadNoLock() %d/%d/%d/%d\n", KeGetCurrentProcessorNumber(),
142 CurrentThread, NewThreadStatus, CurrentThread->State);
143
144 CurrentThread->State = (UCHAR)NewThreadStatus;
145
146 if (NewThreadStatus == Ready) {
147
148 KiInsertIntoThreadList(CurrentThread->Priority,
149 CurrentThread);
150 }
151
152 Affinity = 1 << KeGetCurrentProcessorNumber();
153
154 for (CurrentPriority = HIGH_PRIORITY; CurrentPriority >= LOW_PRIORITY; CurrentPriority--) {
155
156 Candidate = KiScanThreadList(CurrentPriority, Affinity);
157
158 if (Candidate == CurrentThread) {
159
160 Candidate->State = Running;
161 KeReleaseDispatcherDatabaseLockFromDpcLevel();
162 return FALSE;
163 }
164
165 if (Candidate != NULL) {
166
167 PKTHREAD OldThread;
168 PKTHREAD IdleThread;
169
170 DPRINT("Scheduling %x(%d)\n",Candidate, CurrentPriority);
171
172 Candidate->State = Running;
173
174 OldThread = CurrentThread;
175 CurrentThread = Candidate;
176 IdleThread = KeGetCurrentPrcb()->IdleThread;
177
178 if (OldThread == IdleThread) {
179
180 IdleProcessorMask &= ~Affinity;
181
182 } else if (CurrentThread == IdleThread) {
183
184 IdleProcessorMask |= Affinity;
185 }
186
187 MmUpdatePageDir((PEPROCESS)PsGetCurrentProcess(),((PETHREAD)CurrentThread)->ThreadsProcess, sizeof(EPROCESS));
188
189 /* Special note for Filip: This will release the Dispatcher DB Lock ;-) -- Alex */
190 DPRINT("You are : %x, swapping to: %x.\n", OldThread, CurrentThread);
191 ApcState = KiSwapContext(OldThread, CurrentThread);
192 DPRINT("You are : %x, swapped from: %x\n", OldThread, CurrentThread);
193 return ApcState;
194 }
195 }
196
197 DPRINT1("CRITICAL: No threads are ready (CPU%d)\n", KeGetCurrentProcessorNumber());
198 KEBUGCHECK(0);
199 return FALSE;
200 }
201
202 NTSTATUS
203 NTAPI
204 KiSwapThread(VOID)
205 {
206 PKTHREAD CurrentThread = KeGetCurrentThread();
207 BOOLEAN ApcState;
208
209 /* Find a new thread to run */
210 DPRINT("Dispatching Thread as blocked\n");
211 ApcState = KiDispatchThreadNoLock(Waiting);
212
213 /* Check if we need to deliver APCs */
214 if (ApcState)
215 {
216 /* Lower to APC_LEVEL */
217 KeLowerIrql(APC_LEVEL);
218
219 /* Deliver APCs */
220 KiDeliverApc(KernelMode, NULL, NULL);
221 ASSERT(CurrentThread->WaitIrql == 0);
222 }
223
224 /* Lower IRQL back to what it was */
225 KfLowerIrql(CurrentThread->WaitIrql);
226
227 /* Return the wait status */
228 return CurrentThread->WaitStatus;
229 }
230
231 VOID
232 STDCALL
233 KiDispatchThread(ULONG NewThreadStatus)
234 {
235 KIRQL OldIrql;
236
237 if (KeGetCurrentPrcb()->IdleThread == NULL) {
238 return;
239 }
240
241 OldIrql = KeAcquireDispatcherDatabaseLock();
242 KiDispatchThreadNoLock(NewThreadStatus);
243 KeLowerIrql(OldIrql);
244 }
245
246 VOID
247 NTAPI
248 KiReadyThread(IN PKTHREAD Thread)
249 {
250 /* Makes a thread ready */
251 Thread->State = Ready;
252 KiInsertIntoThreadList(Thread->Priority, Thread);
253 }
254
255 VOID
256 STDCALL
257 KiAdjustQuantumThread(IN PKTHREAD Thread)
258 {
259 KPRIORITY Priority;
260
261 /* Don't adjust for RT threads */
262 if ((Thread->Priority < LOW_REALTIME_PRIORITY) &&
263 Thread->BasePriority < LOW_REALTIME_PRIORITY - 2)
264 {
265 /* Decrease Quantum by one and see if we've ran out */
266 if (--Thread->Quantum <= 0)
267 {
268 /* Return quantum */
269 Thread->Quantum = Thread->QuantumReset;
270
271 /* Calculate new Priority */
272 Priority = Thread->Priority - (Thread->PriorityDecrement + 1);
273
274 /* Normalize it if we've gone too low */
275 if (Priority < Thread->BasePriority) Priority = Thread->BasePriority;
276
277 /* Reset the priority decrement, we've done it */
278 Thread->PriorityDecrement = 0;
279
280 /* Set the new priority, if needed */
281 if (Priority != Thread->Priority)
282 {
283 /*
284 * FIXME: This should be a call to KiSetPriorityThread but
285 * due to the current ""scheduler"" in ROS, it can't be done
286 * cleanly since it actualyl dispatches threads instead.
287 */
288 Thread->Priority = Priority;
289 }
290 else
291 {
292 /* FIXME: Priority hasn't changed, find a new thread */
293 }
294 }
295 }
296
297 /* Nothing to do... */
298 return;
299 }
300
301
302 VOID
303 STDCALL
304 KiSuspendThreadKernelRoutine(PKAPC Apc,
305 PKNORMAL_ROUTINE* NormalRoutine,
306 PVOID* NormalContext,
307 PVOID* SystemArgument1,
308 PVOID* SystemArguemnt2)
309 {
310 }
311
312 VOID
313 STDCALL
314 KiSuspendThreadNormalRoutine(PVOID NormalContext,
315 PVOID SystemArgument1,
316 PVOID SystemArgument2)
317 {
318 PKTHREAD CurrentThread = KeGetCurrentThread();
319
320 /* Non-alertable kernel-mode suspended wait */
321 DPRINT("Waiting...\n");
322 KeWaitForSingleObject(&CurrentThread->SuspendSemaphore,
323 Suspended,
324 KernelMode,
325 FALSE,
326 NULL);
327 DPRINT("Done Waiting\n");
328 }
329
330 #ifdef KeGetCurrentThread
331 #undef KeGetCurrentThread
332 #endif
333 /*
334 * @implemented
335 */
336 PKTHREAD
337 STDCALL
338 KeGetCurrentThread(VOID)
339 {
340 #ifdef CONFIG_SMP
341 ULONG Flags;
342 PKTHREAD Thread;
343 Ke386SaveFlags(Flags);
344 Ke386DisableInterrupts();
345 Thread = KeGetCurrentPrcb()->CurrentThread;
346 Ke386RestoreFlags(Flags);
347 return Thread;
348 #else
349 return(KeGetCurrentPrcb()->CurrentThread);
350 #endif
351 }
352
353 VOID
354 STDCALL
355 KeSetPreviousMode(ULONG Mode)
356 {
357 PsGetCurrentThread()->Tcb.PreviousMode = (UCHAR)Mode;
358 }
359
360 /*
361 * @implemented
362 */
363 KPROCESSOR_MODE
364 STDCALL
365 KeGetPreviousMode(VOID)
366 {
367 return (ULONG)PsGetCurrentThread()->Tcb.PreviousMode;
368 }
369
370 BOOLEAN
371 STDCALL
372 KeDisableThreadApcQueueing(IN PKTHREAD Thread)
373 {
374 KIRQL OldIrql;
375 BOOLEAN PreviousState;
376
377 /* Lock the Dispatcher Database */
378 OldIrql = KeAcquireDispatcherDatabaseLock();
379
380 /* Save old state */
381 PreviousState = Thread->ApcQueueable;
382
383 /* Disable it now */
384 Thread->ApcQueueable = FALSE;
385
386 /* Release the Lock */
387 KeReleaseDispatcherDatabaseLock(OldIrql);
388
389 /* Return old state */
390 return PreviousState;
391 }
392
393 VOID
394 STDCALL
395 KeRundownThread(VOID)
396 {
397 KIRQL OldIrql;
398 PKTHREAD Thread = KeGetCurrentThread();
399 PLIST_ENTRY NextEntry, ListHead;
400 PKMUTANT Mutant;
401 DPRINT("KeRundownThread: %x\n", Thread);
402
403 /* Optimized path if nothing is on the list at the moment */
404 if (IsListEmpty(&Thread->MutantListHead)) return;
405
406 /* Lock the Dispatcher Database */
407 OldIrql = KeAcquireDispatcherDatabaseLock();
408
409 /* Get the List Pointers */
410 ListHead = &Thread->MutantListHead;
411 NextEntry = ListHead->Flink;
412 while (NextEntry != ListHead)
413 {
414 /* Get the Mutant */
415 Mutant = CONTAINING_RECORD(NextEntry, KMUTANT, MutantListEntry);
416 DPRINT1("Mutant: %p. Type, Size %x %x\n",
417 Mutant,
418 Mutant->Header.Type,
419 Mutant->Header.Size);
420
421 /* Make sure it's not terminating with APCs off */
422 if (Mutant->ApcDisable)
423 {
424 /* Bugcheck the system */
425 KEBUGCHECKEX(0,//THREAD_TERMINATE_HELD_MUTEX,
426 (ULONG_PTR)Thread,
427 (ULONG_PTR)Mutant,
428 0,
429 0);
430 }
431
432 /* Now we can remove it */
433 RemoveEntryList(&Mutant->MutantListEntry);
434
435 /* Unconditionally abandon it */
436 DPRINT("Abandonning the Mutant\n");
437 Mutant->Header.SignalState = 1;
438 Mutant->Abandoned = TRUE;
439 Mutant->OwnerThread = NULL;
440
441 /* Check if the Wait List isn't empty */
442 DPRINT("Checking whether to wake the Mutant\n");
443 if (!IsListEmpty(&Mutant->Header.WaitListHead))
444 {
445 /* Wake the Mutant */
446 DPRINT("Waking the Mutant\n");
447 KiWaitTest(&Mutant->Header, MUTANT_INCREMENT);
448 }
449
450 /* Move on */
451 NextEntry = NextEntry->Flink;
452 }
453
454 /* Release the Lock */
455 KeReleaseDispatcherDatabaseLock(OldIrql);
456 }
457
458 ULONG
459 NTAPI
460 KeResumeThread(IN PKTHREAD Thread)
461 {
462 ULONG PreviousCount;
463 KIRQL OldIrql;
464
465 DPRINT("KeResumeThread (Thread %p called). %x, %x\n", Thread,
466 Thread->SuspendCount, Thread->FreezeCount);
467
468 /* Lock the Dispatcher */
469 OldIrql = KeAcquireDispatcherDatabaseLock();
470
471 /* Save the Old Count */
472 PreviousCount = Thread->SuspendCount;
473
474 /* Check if it existed */
475 if (PreviousCount) {
476
477 Thread->SuspendCount--;
478
479 /* Decrease the current Suspend Count and Check Freeze Count */
480 if ((!Thread->SuspendCount) && (!Thread->FreezeCount)) {
481
482 /* Signal the Suspend Semaphore */
483 Thread->SuspendSemaphore.Header.SignalState++;
484 KiWaitTest(&Thread->SuspendSemaphore.Header, IO_NO_INCREMENT);
485 }
486 }
487
488 /* Release Lock and return the Old State */
489 KeReleaseDispatcherDatabaseLock(OldIrql);
490 return PreviousCount;
491 }
492
493 VOID
494 FASTCALL
495 KiInsertQueueApc(PKAPC Apc,
496 KPRIORITY PriorityBoost);
497
498 /*
499 * Used by the debugging code to freeze all the process's threads
500 * while the debugger is examining their state.
501 */
502 VOID
503 STDCALL
504 KeFreezeAllThreads(PKPROCESS Process)
505 {
506 KIRQL OldIrql;
507 PKTHREAD Current;
508 PKTHREAD CurrentThread = KeGetCurrentThread();
509
510 /* Acquire Lock */
511 OldIrql = KeAcquireDispatcherDatabaseLock();
512
513 /* If someone is already trying to free us, try again */
514 while (CurrentThread->FreezeCount)
515 {
516 /* Release and re-acquire the lock so the APC will go through */
517 KeReleaseDispatcherDatabaseLock(OldIrql);
518 OldIrql = KeAcquireDispatcherDatabaseLock();
519 }
520
521 /* Enter a critical region */
522 KeEnterCriticalRegion();
523
524 /* Loop the Process's Threads */
525 LIST_FOR_EACH(Current, &Process->ThreadListHead, KTHREAD, ThreadListEntry)
526 {
527 /* Make sure it's not ours */
528 if (Current != CurrentThread)
529 {
530 /* Should be bother inserting the APC? */
531 if (Current->ApcQueueable)
532 {
533 /* Make sure it wasn't already frozen, and that it's not suspended */
534 if (!(++Current->FreezeCount) && !(Current->SuspendCount))
535 {
536 /* Did we already insert it? */
537 if (!Current->SuspendApc.Inserted)
538 {
539 /* Insert the APC */
540 Current->SuspendApc.Inserted = TRUE;
541 KiInsertQueueApc(&Current->SuspendApc, IO_NO_INCREMENT);
542 }
543 else
544 {
545 /* Unsignal the Semaphore, the APC already got inserted */
546 Current->SuspendSemaphore.Header.SignalState--;
547 }
548 }
549 }
550 }
551 }
552
553 /* Release the lock */
554 KeReleaseDispatcherDatabaseLock(OldIrql);
555 }
556
557 NTSTATUS
558 STDCALL
559 KeSuspendThread(PKTHREAD Thread)
560 {
561 ULONG PreviousCount;
562 KIRQL OldIrql;
563
564 DPRINT("KeSuspendThread (Thread %p called). %x, %x\n", Thread, Thread->SuspendCount, Thread->FreezeCount);
565
566 /* Lock the Dispatcher */
567 OldIrql = KeAcquireDispatcherDatabaseLock();
568
569 /* Save the Old Count */
570 PreviousCount = Thread->SuspendCount;
571
572 /* Handle the maximum */
573 if (PreviousCount == MAXIMUM_SUSPEND_COUNT)
574 {
575 /* Raise an exception */
576 KeReleaseDispatcherDatabaseLock(OldIrql);
577 RtlRaiseStatus(STATUS_SUSPEND_COUNT_EXCEEDED);
578 }
579
580 /* Should we bother to queue at all? */
581 if (Thread->ApcQueueable)
582 {
583 /* Increment the suspend count */
584 Thread->SuspendCount++;
585
586 /* Check if we should suspend it */
587 if (!PreviousCount && !Thread->FreezeCount)
588 {
589 /* Is the APC already inserted? */
590 if (!Thread->SuspendApc.Inserted)
591 {
592 /* Not inserted, insert it */
593 Thread->SuspendApc.Inserted = TRUE;
594 KiInsertQueueApc(&Thread->SuspendApc, IO_NO_INCREMENT);
595 }
596 else
597 {
598 /* Unsignal the Semaphore, the APC already got inserted */
599 Thread->SuspendSemaphore.Header.SignalState--;
600 }
601 }
602 }
603
604 /* Release Lock and return the Old State */
605 KeReleaseDispatcherDatabaseLock(OldIrql);
606 return PreviousCount;
607 }
608
609 ULONG
610 STDCALL
611 KeForceResumeThread(IN PKTHREAD Thread)
612 {
613 KIRQL OldIrql;
614 ULONG PreviousCount;
615
616 /* Lock the Dispatcher Database and the APC Queue */
617 OldIrql = KeAcquireDispatcherDatabaseLock();
618
619 /* Save the old Suspend Count */
620 PreviousCount = Thread->SuspendCount + Thread->FreezeCount;
621
622 /* If the thread is suspended, wake it up!!! */
623 if (PreviousCount) {
624
625 /* Unwait it completely */
626 Thread->SuspendCount = 0;
627 Thread->FreezeCount = 0;
628
629 /* Signal and satisfy */
630 Thread->SuspendSemaphore.Header.SignalState++;
631 KiWaitTest(&Thread->SuspendSemaphore.Header, IO_NO_INCREMENT);
632 }
633
634 /* Release Lock and return the Old State */
635 KeReleaseDispatcherDatabaseLock(OldIrql);
636 return PreviousCount;
637 }
638
639 ULONG
640 NTAPI
641 KeAlertResumeThread(IN PKTHREAD Thread)
642 {
643 ULONG PreviousCount;
644 KIRQL OldIrql;
645
646 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
647
648 /* Lock the Dispatcher Database and the APC Queue */
649 OldIrql = KeAcquireDispatcherDatabaseLock();
650 KiAcquireSpinLock(&Thread->ApcQueueLock);
651
652 /* Return if Thread is already alerted. */
653 if (Thread->Alerted[KernelMode] == FALSE) {
654
655 /* If it's Blocked, unblock if it we should */
656 if (Thread->State == Waiting && Thread->Alertable) {
657
658 DPRINT("Aborting Wait\n");
659 KiAbortWaitThread(Thread, STATUS_ALERTED, THREAD_ALERT_INCREMENT);
660
661 } else {
662
663 /* If not, simply Alert it */
664 Thread->Alerted[KernelMode] = TRUE;
665 }
666 }
667
668 /* Save the old Suspend Count */
669 PreviousCount = Thread->SuspendCount;
670
671 /* If the thread is suspended, decrease one of the suspend counts */
672 if (PreviousCount) {
673
674 /* Decrease count. If we are now zero, unwait it completely */
675 if (--Thread->SuspendCount) {
676
677 /* Signal and satisfy */
678 Thread->SuspendSemaphore.Header.SignalState++;
679 KiWaitTest(&Thread->SuspendSemaphore.Header, IO_NO_INCREMENT);
680 }
681 }
682
683 /* Release Locks and return the Old State */
684 KiReleaseSpinLock(&Thread->ApcQueueLock);
685 KeReleaseDispatcherDatabaseLock(OldIrql);
686 return PreviousCount;
687 }
688
689 BOOLEAN
690 NTAPI
691 KeAlertThread(IN PKTHREAD Thread,
692 IN KPROCESSOR_MODE AlertMode)
693 {
694 KIRQL OldIrql;
695 BOOLEAN PreviousState;
696
697 /* Acquire the Dispatcher Database Lock */
698 OldIrql = KeAcquireDispatcherDatabaseLock();
699
700 /* Save the Previous State */
701 PreviousState = Thread->Alerted[AlertMode];
702
703 /* Return if Thread is already alerted. */
704 if (PreviousState == FALSE) {
705
706 /* If it's Blocked, unblock if it we should */
707 if (Thread->State == Waiting &&
708 (AlertMode == KernelMode || Thread->WaitMode == AlertMode) &&
709 Thread->Alertable) {
710
711 DPRINT("Aborting Wait\n");
712 KiAbortWaitThread(Thread, STATUS_ALERTED, THREAD_ALERT_INCREMENT);
713
714 } else {
715
716 /* If not, simply Alert it */
717 Thread->Alerted[AlertMode] = TRUE;
718 }
719 }
720
721 /* Release the Dispatcher Lock */
722 KeReleaseDispatcherDatabaseLock(OldIrql);
723
724 /* Return the old state */
725 return PreviousState;
726 }
727
728 /*
729 * @unimplemented
730 */
731 VOID
732 STDCALL
733 KeCapturePersistentThreadState(IN PVOID CurrentThread,
734 IN ULONG Setting1,
735 IN ULONG Setting2,
736 IN ULONG Setting3,
737 IN ULONG Setting4,
738 IN ULONG Setting5,
739 IN PVOID ThreadState)
740 {
741 UNIMPLEMENTED;
742 }
743
744 VOID
745 NTAPI
746 KeUninitThread(IN PKTHREAD Thread)
747 {
748 /* Delete the stack */
749 MmDeleteKernelStack(Thread->StackBase, FALSE);
750 Thread->InitialStack = NULL;
751 }
752
753 NTSTATUS
754 NTAPI
755 KeInitThread(IN OUT PKTHREAD Thread,
756 IN PVOID KernelStack,
757 IN PKSYSTEM_ROUTINE SystemRoutine,
758 IN PKSTART_ROUTINE StartRoutine,
759 IN PVOID StartContext,
760 IN PCONTEXT Context,
761 IN PVOID Teb,
762 IN PKPROCESS Process)
763 {
764 BOOLEAN AllocatedStack = FALSE;
765 ULONG i;
766 PKWAIT_BLOCK TimerWaitBlock;
767 PKTIMER Timer;
768 NTSTATUS Status;
769
770 /* Initalize the Dispatcher Header */
771 KeInitializeDispatcherHeader(&Thread->DispatcherHeader,
772 ThreadObject,
773 sizeof(KTHREAD) / sizeof(LONG),
774 FALSE);
775
776 /* Initialize the Mutant List */
777 InitializeListHead(&Thread->MutantListHead);
778
779 /* Initialize the wait blocks */
780 for (i = 0; i< (THREAD_WAIT_OBJECTS + 1); i++)
781 {
782 /* Put our pointer */
783 Thread->WaitBlock[i].Thread = Thread;
784 }
785
786 /* Set swap settings */
787 Thread->EnableStackSwap = FALSE;//TRUE;
788 Thread->IdealProcessor = 1;
789 Thread->SwapBusy = FALSE;
790 Thread->AdjustReason = 0;
791
792 /* Initialize the lock */
793 KeInitializeSpinLock(&Thread->ThreadLock);
794
795 /* Setup the Service Descriptor Table for Native Calls */
796 Thread->ServiceTable = KeServiceDescriptorTable;
797
798 /* Setup APC Fields */
799 InitializeListHead(&Thread->ApcState.ApcListHead[0]);
800 InitializeListHead(&Thread->ApcState.ApcListHead[1]);
801 Thread->ApcState.Process = Process;
802 Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState;
803 Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState;
804 Thread->ApcStateIndex = OriginalApcEnvironment;
805 Thread->ApcQueueable = TRUE;
806 KeInitializeSpinLock(&Thread->ApcQueueLock);
807
808 /* Initialize the Suspend APC */
809 KeInitializeApc(&Thread->SuspendApc,
810 Thread,
811 OriginalApcEnvironment,
812 KiSuspendThreadKernelRoutine,
813 NULL,
814 KiSuspendThreadNormalRoutine,
815 KernelMode,
816 NULL);
817
818 /* Initialize the Suspend Semaphore */
819 KeInitializeSemaphore(&Thread->SuspendSemaphore, 0, 2);
820
821 /* Setup the timer */
822 Timer = &Thread->Timer;
823 KeInitializeTimer(Timer);
824 TimerWaitBlock = &Thread->WaitBlock[TIMER_WAIT_BLOCK];
825 TimerWaitBlock->Object = Timer;
826 TimerWaitBlock->WaitKey = STATUS_TIMEOUT;
827 TimerWaitBlock->WaitType = WaitAny;
828 TimerWaitBlock->NextWaitBlock = NULL;
829
830 /* Link the two wait lists together */
831 TimerWaitBlock->WaitListEntry.Flink = &Timer->Header.WaitListHead;
832 TimerWaitBlock->WaitListEntry.Blink = &Timer->Header.WaitListHead;
833
834 /* Set the TEB */
835 Thread->Teb = Teb;
836
837 /* Check if we have a kernel stack */
838 if (!KernelStack)
839 {
840 /* We don't, allocate one */
841 KernelStack = (PVOID)((ULONG_PTR)MmCreateKernelStack(FALSE) +
842 KERNEL_STACK_SIZE);
843 if (!KernelStack) return STATUS_INSUFFICIENT_RESOURCES;
844
845 /* Remember for later */
846 AllocatedStack = TRUE;
847 }
848
849 /* Set the Thread Stacks */
850 Thread->InitialStack = (PCHAR)KernelStack;
851 Thread->StackBase = (PCHAR)KernelStack;
852 Thread->StackLimit = (ULONG_PTR)KernelStack - KERNEL_STACK_SIZE;
853 Thread->KernelStackResident = TRUE;
854
855 /* ROS Mm HACK */
856 MmUpdatePageDir((PEPROCESS)Process,
857 (PVOID)Thread->StackLimit,
858 KERNEL_STACK_SIZE);
859 MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread, sizeof(ETHREAD));
860
861 /* Enter SEH to avoid crashes due to user mode */
862 Status = STATUS_SUCCESS;
863 _SEH_TRY
864 {
865 /* Initalize the Thread Context */
866 Ke386InitThreadWithContext(Thread,
867 SystemRoutine,
868 StartRoutine,
869 StartContext,
870 Context);
871 }
872 _SEH_HANDLE
873 {
874 /* Set failure status */
875 Status = STATUS_UNSUCCESSFUL;
876
877 /* Check if a stack was allocated */
878 if (AllocatedStack)
879 {
880 /* Delete the stack */
881 MmDeleteKernelStack(Thread->StackBase, FALSE);
882 Thread->InitialStack = NULL;
883 }
884 }
885 _SEH_END;
886
887 /* Set the Thread to initalized */
888 Thread->State = Initialized;
889 return Status;
890 }
891
892 VOID
893 NTAPI
894 KeStartThread(IN OUT PKTHREAD Thread)
895 {
896 KIRQL OldIrql;
897 PKPROCESS Process = Thread->ApcState.Process;
898 PKNODE Node;
899 PKPRCB NodePrcb;
900 ULONG Set, Mask;
901 UCHAR IdealProcessor;
902
903 /* Setup static fields from parent */
904 Thread->Iopl = Process->Iopl;
905 Thread->Quantum = Process->QuantumReset;
906 Thread->QuantumReset = Process->QuantumReset;
907 Thread->SystemAffinityActive = FALSE;
908
909 /* Lock the process */
910 KeAcquireSpinLock(&Process->ProcessLock, &OldIrql);
911
912 /* Setup volatile data */
913 Thread->Priority = Process->BasePriority;
914 Thread->BasePriority = Process->BasePriority;
915 Thread->Affinity = Process->Affinity;
916 Thread->UserAffinity = Process->Affinity;
917
918 /* Get the KNODE and its PRCB */
919 Node = KeNodeBlock[Process->IdealNode];
920 NodePrcb = (PKPRCB)(KPCR_BASE + (Process->ThreadSeed * PAGE_SIZE));
921
922 /* Calculate affinity mask */
923 Set = ~NodePrcb->MultiThreadProcessorSet;
924 Mask = (ULONG)(Node->ProcessorMask & Process->Affinity);
925 Set &= Mask;
926 if (Set) Mask = Set;
927
928 /* Get the new thread seed */
929 IdealProcessor = KeFindNextRightSetAffinity(Process->ThreadSeed, Mask);
930 Process->ThreadSeed = IdealProcessor;
931
932 /* Sanity check */
933 ASSERT((Thread->UserAffinity & AFFINITY_MASK(IdealProcessor)));
934
935 /* Set the Ideal Processor */
936 Thread->IdealProcessor = IdealProcessor;
937 Thread->UserIdealProcessor = IdealProcessor;
938
939 /* Lock the Dispatcher Database */
940 KeAcquireDispatcherDatabaseLockAtDpcLevel();
941
942 /* Insert the thread into the process list */
943 InsertTailList(&Process->ThreadListHead, &Thread->ThreadListEntry);
944
945 /* Increase the stack count */
946 ASSERT(Process->StackCount != MAXULONG_PTR);
947 Process->StackCount++;
948
949 /* Release locks and return */
950 KeReleaseDispatcherDatabaseLockFromDpcLevel();
951 KeReleaseSpinLock(&Process->ProcessLock, OldIrql);
952 }
953
954 VOID
955 NTAPI
956 KeInitializeThread(IN PKPROCESS Process,
957 IN OUT PKTHREAD Thread,
958 IN PKSYSTEM_ROUTINE SystemRoutine,
959 IN PKSTART_ROUTINE StartRoutine,
960 IN PVOID StartContext,
961 IN PCONTEXT Context,
962 IN PVOID Teb,
963 IN PVOID KernelStack)
964 {
965 /* Initailize and start the thread on success */
966 if (NT_SUCCESS(KeInitThread(Thread,
967 KernelStack,
968 SystemRoutine,
969 StartRoutine,
970 StartContext,
971 Context,
972 Teb,
973 Process)))
974 {
975 /* Start it */
976 KeStartThread(Thread);
977 }
978 }
979
980 /*
981 * @implemented
982 */
983 KPRIORITY
984 STDCALL
985 KeQueryPriorityThread (IN PKTHREAD Thread)
986 {
987 return Thread->Priority;
988 }
989
990 /*
991 * @implemented
992 */
993 ULONG
994 STDCALL
995 KeQueryRuntimeThread(IN PKTHREAD Thread,
996 OUT PULONG UserTime)
997 {
998 /* Return the User Time */
999 *UserTime = Thread->UserTime;
1000
1001 /* Return the Kernel Time */
1002 return Thread->KernelTime;
1003 }
1004
1005 /*
1006 * @implemented
1007 */
1008 BOOLEAN
1009 STDCALL
1010 KeSetKernelStackSwapEnable(IN BOOLEAN Enable)
1011 {
1012 PKTHREAD Thread = KeGetCurrentThread();
1013 BOOLEAN PreviousState;
1014 KIRQL OldIrql;
1015
1016 /* Lock the Dispatcher Database */
1017 OldIrql = KeAcquireDispatcherDatabaseLock();
1018
1019 /* Save Old State */
1020 PreviousState = Thread->EnableStackSwap;
1021
1022 /* Set New State */
1023 Thread->EnableStackSwap = Enable;
1024
1025 /* No, Release Lock */
1026 KeReleaseDispatcherDatabaseLock(OldIrql);
1027
1028 /* Return Old State */
1029 return PreviousState;
1030 }
1031
1032 /*
1033 * @implemented
1034 */
1035 VOID
1036 STDCALL
1037 KeRevertToUserAffinityThread(VOID)
1038 {
1039 PKTHREAD CurrentThread = KeGetCurrentThread();
1040 KIRQL OldIrql;
1041
1042 ASSERT(CurrentThread->SystemAffinityActive != FALSE);
1043
1044 /* Lock the Dispatcher Database */
1045 OldIrql = KeAcquireDispatcherDatabaseLock();
1046
1047 /* Return to User Affinity */
1048 CurrentThread->Affinity = CurrentThread->UserAffinity;
1049
1050 /* Disable System Affinity */
1051 CurrentThread->SystemAffinityActive = FALSE;
1052
1053 /* Check if we need to Dispatch a New thread */
1054 if (CurrentThread->Affinity & (1 << KeGetCurrentProcessorNumber())) {
1055
1056 /* No, just release */
1057 KeReleaseDispatcherDatabaseLock(OldIrql);
1058
1059 } else {
1060
1061 /* We need to dispatch a new thread */
1062 CurrentThread->WaitIrql = OldIrql;
1063 KiDispatchThreadNoLock(Ready);
1064 KeLowerIrql(OldIrql);
1065 }
1066 }
1067
1068 /*
1069 * @implemented
1070 */
1071 CCHAR
1072 STDCALL
1073 KeSetIdealProcessorThread(IN PKTHREAD Thread,
1074 IN CCHAR Processor)
1075 {
1076 CCHAR PreviousIdealProcessor;
1077 KIRQL OldIrql;
1078
1079 /* Lock the Dispatcher Database */
1080 OldIrql = KeAcquireDispatcherDatabaseLock();
1081
1082 /* Save Old Ideal Processor */
1083 PreviousIdealProcessor = Thread->IdealProcessor;
1084
1085 /* Set New Ideal Processor */
1086 Thread->IdealProcessor = Processor;
1087
1088 /* Release Lock */
1089 KeReleaseDispatcherDatabaseLock(OldIrql);
1090
1091 /* Return Old Ideal Processor */
1092 return PreviousIdealProcessor;
1093 }
1094
1095 /*
1096 * @implemented
1097 */
1098 VOID
1099 STDCALL
1100 KeSetSystemAffinityThread(IN KAFFINITY Affinity)
1101 {
1102 PKTHREAD CurrentThread = KeGetCurrentThread();
1103 KIRQL OldIrql;
1104
1105 ASSERT(Affinity & ((1 << KeNumberProcessors) - 1));
1106
1107 /* Lock the Dispatcher Database */
1108 OldIrql = KeAcquireDispatcherDatabaseLock();
1109
1110 /* Set the System Affinity Specified */
1111 CurrentThread->Affinity = Affinity;
1112
1113 /* Enable System Affinity */
1114 CurrentThread->SystemAffinityActive = TRUE;
1115
1116 /* Check if we need to Dispatch a New thread */
1117 if (Affinity & (1 << KeGetCurrentProcessorNumber())) {
1118
1119 /* No, just release */
1120 KeReleaseDispatcherDatabaseLock(OldIrql);
1121
1122 } else {
1123
1124 /* We need to dispatch a new thread */
1125 CurrentThread->WaitIrql = OldIrql;
1126 KiDispatchThreadNoLock(Ready);
1127 KeLowerIrql(OldIrql);
1128 }
1129 }
1130
1131 LONG
1132 STDCALL
1133 KeQueryBasePriorityThread(IN PKTHREAD Thread)
1134 {
1135 LONG BasePriorityIncrement;
1136 KIRQL OldIrql;
1137 PKPROCESS Process;
1138
1139 /* Lock the Dispatcher Database */
1140 OldIrql = KeAcquireDispatcherDatabaseLock();
1141
1142 /* Get the Process */
1143 Process = Thread->ApcStatePointer[0]->Process;
1144
1145 /* Calculate the BPI */
1146 BasePriorityIncrement = Thread->BasePriority - Process->BasePriority;
1147
1148 /* If saturation occured, return the SI instead */
1149 if (Thread->Saturation) BasePriorityIncrement = (HIGH_PRIORITY + 1) / 2 *
1150 Thread->Saturation;
1151
1152 /* Release Lock */
1153 KeReleaseDispatcherDatabaseLock(OldIrql);
1154
1155 /* Return Increment */
1156 return BasePriorityIncrement;
1157 }
1158
1159 VOID
1160 STDCALL
1161 KiSetPriorityThread(PKTHREAD Thread,
1162 KPRIORITY Priority,
1163 PBOOLEAN Released)
1164 {
1165 KPRIORITY OldPriority = Thread->Priority;
1166 ULONG Mask;
1167 int i;
1168 PKPCR Pcr;
1169 DPRINT("Changing prio to : %lx\n", Priority);
1170
1171 /* Check if priority changed */
1172 if (OldPriority != Priority)
1173 {
1174 /* Set it */
1175 Thread->Priority = Priority;
1176
1177 /* Choose action based on thread's state */
1178 if (Thread->State == Ready)
1179 {
1180 /* Remove it from the current queue */
1181 KiRemoveFromThreadList(Thread);
1182
1183 /* Re-insert it at its current priority */
1184 KiInsertIntoThreadList(Priority, Thread);
1185
1186 /* Check if the old priority was lower */
1187 if (KeGetCurrentThread()->Priority < Priority)
1188 {
1189 /* Dispatch it immediately */
1190 KiDispatchThreadNoLock(Ready);
1191 *Released = TRUE;
1192 return;
1193 }
1194 }
1195 else if (Thread->State == Running)
1196 {
1197 /* Check if the new priority is lower */
1198 if (Priority < OldPriority)
1199 {
1200 /* Check for threads with a higher priority */
1201 Mask = ~((1 << (Priority + 1)) - 1);
1202 if (PriorityListMask & Mask)
1203 {
1204 /* Found a thread, is it us? */
1205 if (Thread == KeGetCurrentThread())
1206 {
1207 /* Dispatch us */
1208 KiDispatchThreadNoLock(Ready);
1209 *Released = TRUE;
1210 return;
1211 }
1212 else
1213 {
1214 /* Loop every CPU */
1215 for (i = 0; i < KeNumberProcessors; i++)
1216 {
1217 /* Get the PCR for this CPU */
1218 Pcr = (PKPCR)(KPCR_BASE + i * PAGE_SIZE);
1219
1220 /* Reschedule if the new one is already on a CPU */
1221 if (Pcr->Prcb->CurrentThread == Thread)
1222 {
1223 KeReleaseDispatcherDatabaseLockFromDpcLevel();
1224 KiRequestReschedule(i);
1225 *Released = TRUE;
1226 return;
1227 }
1228 }
1229 }
1230 }
1231 }
1232 }
1233 }
1234
1235 /* Return to caller */
1236 return;
1237 }
1238
1239 /*
1240 * @implemented
1241 */
1242 LONG
1243 STDCALL
1244 KeSetBasePriorityThread(PKTHREAD Thread,
1245 LONG Increment)
1246 {
1247 KIRQL OldIrql;
1248 PKPROCESS Process;
1249 KPRIORITY Priority;
1250 KPRIORITY CurrentBasePriority;
1251 KPRIORITY BasePriority;
1252 BOOLEAN Released = FALSE;
1253 LONG CurrentIncrement;
1254
1255 /* Lock the Dispatcher Database */
1256 OldIrql = KeAcquireDispatcherDatabaseLock();
1257
1258 /* Get the process and calculate current BP and BPI */
1259 Process = Thread->ApcStatePointer[0]->Process;
1260 CurrentBasePriority = Thread->BasePriority;
1261 CurrentIncrement = CurrentBasePriority - Process->BasePriority;
1262
1263 /* Change to use the SI if Saturation was used */
1264 if (Thread->Saturation) CurrentIncrement = (HIGH_PRIORITY + 1) / 2 *
1265 Thread->Saturation;
1266
1267 /* Now check if saturation is being used for the new value */
1268 if (abs(Increment) >= ((HIGH_PRIORITY + 1) / 2))
1269 {
1270 /* Check if we need positive or negative saturation */
1271 Thread->Saturation = (Increment > 0) ? 1 : -1;
1272 }
1273
1274 /* Normalize the Base Priority */
1275 BasePriority = Process->BasePriority + Increment;
1276 if (Process->BasePriority >= LOW_REALTIME_PRIORITY)
1277 {
1278 /* Check if it's too low */
1279 if (BasePriority < LOW_REALTIME_PRIORITY)
1280 BasePriority = LOW_REALTIME_PRIORITY;
1281
1282 /* Check if it's too high */
1283 if (BasePriority > HIGH_PRIORITY) BasePriority = HIGH_PRIORITY;
1284
1285 /* We are at RTP, so use the raw BP */
1286 Priority = BasePriority;
1287 }
1288 else
1289 {
1290 /* Check if it's entering RTP */
1291 if (BasePriority >= LOW_REALTIME_PRIORITY)
1292 BasePriority = LOW_REALTIME_PRIORITY - 1;
1293
1294 /* Check if it's too low */
1295 if (BasePriority <= LOW_PRIORITY)
1296 BasePriority = 1;
1297
1298 /* If Saturation is used, then use the raw BP */
1299 if (Thread->Saturation)
1300 {
1301 Priority = BasePriority;
1302 }
1303 else
1304 {
1305 /* Calculate the new priority */
1306 Priority = Thread->Priority + (BasePriority - CurrentBasePriority)-
1307 Thread->PriorityDecrement;
1308
1309 /* Make sure it won't enter RTP ranges */
1310 if (Priority >= LOW_REALTIME_PRIORITY)
1311 Priority = LOW_REALTIME_PRIORITY - 1;
1312 }
1313 }
1314
1315 /* Finally set the new base priority */
1316 Thread->BasePriority = BasePriority;
1317
1318 /* Reset the decrements */
1319 Thread->PriorityDecrement = 0;
1320
1321 /* If the priority will change, reset quantum and change it for real */
1322 if (Priority != Thread->Priority)
1323 {
1324 Thread->Quantum = Thread->QuantumReset;
1325 KiSetPriorityThread(Thread, Priority, &Released);
1326 }
1327
1328 /* Release Lock if needed */
1329 if (!Released)
1330 {
1331 KeReleaseDispatcherDatabaseLock(OldIrql);
1332 }
1333 else
1334 {
1335 KeLowerIrql(OldIrql);
1336 }
1337
1338 /* Return the Old Increment */
1339 return CurrentIncrement;
1340 }
1341
1342 /*
1343 * @implemented
1344 */
1345 KPRIORITY
1346 STDCALL
1347 KeSetPriorityThread(PKTHREAD Thread,
1348 KPRIORITY Priority)
1349 {
1350 KPRIORITY OldPriority;
1351 BOOLEAN Released = FALSE;
1352 KIRQL OldIrql;
1353
1354 /* Lock the Dispatcher Database */
1355 OldIrql = KeAcquireDispatcherDatabaseLock();
1356
1357 /* Save the old Priority */
1358 OldPriority = Thread->Priority;
1359
1360 /* Reset the Quantum and Decrements */
1361 Thread->Quantum = Thread->QuantumReset;
1362 Thread->PriorityDecrement = 0;
1363
1364 /* Set the new Priority */
1365 KiSetPriorityThread(Thread, Priority, &Released);
1366
1367 /* Release Lock if needed */
1368 if (!Released)
1369 {
1370 KeReleaseDispatcherDatabaseLock(OldIrql);
1371 }
1372 else
1373 {
1374 KeLowerIrql(OldIrql);
1375 }
1376
1377 /* Return Old Priority */
1378 return OldPriority;
1379 }
1380
1381 /*
1382 * @implemented
1383 *
1384 * Sets thread's affinity
1385 */
1386 NTSTATUS
1387 STDCALL
1388 KeSetAffinityThread(PKTHREAD Thread,
1389 KAFFINITY Affinity)
1390 {
1391 KIRQL OldIrql;
1392 LONG i;
1393 PKPCR Pcr;
1394 KAFFINITY ProcessorMask;
1395
1396 DPRINT("KeSetAffinityThread(Thread %x, Affinity %x)\n", Thread, Affinity);
1397
1398 /* Verify correct affinity */
1399 if ((Affinity & Thread->ApcStatePointer[0]->Process->Affinity) !=
1400 Affinity || !Affinity)
1401 {
1402 KEBUGCHECK(INVALID_AFFINITY_SET);
1403 }
1404
1405 OldIrql = KeAcquireDispatcherDatabaseLock();
1406
1407 Thread->UserAffinity = Affinity;
1408
1409 if (Thread->SystemAffinityActive == FALSE) {
1410
1411 Thread->Affinity = Affinity;
1412
1413 if (Thread->State == Running) {
1414
1415 ProcessorMask = 1 << KeGetCurrentProcessorNumber();
1416 if (Thread == KeGetCurrentThread()) {
1417
1418 if (!(Affinity & ProcessorMask)) {
1419
1420 KiDispatchThreadNoLock(Ready);
1421 KeLowerIrql(OldIrql);
1422 return STATUS_SUCCESS;
1423 }
1424
1425 } else {
1426
1427 for (i = 0; i < KeNumberProcessors; i++) {
1428
1429 Pcr = (PKPCR)(KPCR_BASE + i * PAGE_SIZE);
1430 if (Pcr->Prcb->CurrentThread == Thread) {
1431
1432 if (!(Affinity & ProcessorMask)) {
1433
1434 KeReleaseDispatcherDatabaseLockFromDpcLevel();
1435 KiRequestReschedule(i);
1436 KeLowerIrql(OldIrql);
1437 return STATUS_SUCCESS;
1438 }
1439
1440 break;
1441 }
1442 }
1443
1444 ASSERT (i < KeNumberProcessors);
1445 }
1446 }
1447 }
1448
1449 KeReleaseDispatcherDatabaseLock(OldIrql);
1450 return STATUS_SUCCESS;
1451 }
1452
1453 /*
1454 * @implemented
1455 */
1456 /* The Increment Argument seems to be ignored by NT and always 0 when called */
1457 VOID
1458 STDCALL
1459 KeTerminateThread(IN KPRIORITY Increment)
1460 {
1461 KIRQL OldIrql;
1462 PKTHREAD Thread = KeGetCurrentThread();
1463 PKPROCESS Process = Thread->ApcState.Process;
1464 PLIST_ENTRY *ListHead;
1465 PETHREAD Entry, SavedEntry;
1466 PETHREAD *ThreadAddr;
1467 DPRINT("Terminating\n");
1468
1469 /* Lock the Dispatcher Database and the APC Queue */
1470 ASSERT_IRQL(DISPATCH_LEVEL);
1471 OldIrql = KeAcquireDispatcherDatabaseLock();
1472 ASSERT(Thread->SwapBusy == FALSE);
1473
1474 /* Make sure we won't get Swapped */
1475 Thread->SwapBusy = TRUE;
1476
1477 /* Save the Kernel and User Times */
1478 Process->KernelTime += Thread->KernelTime;
1479 Process->UserTime += Thread->UserTime;
1480
1481 /* Get the current entry and our Port */
1482 Entry = (PETHREAD)PspReaperListHead.Flink;
1483 ThreadAddr = &((PETHREAD)Thread)->ReaperLink;
1484
1485 /* Add it to the reaper's list */
1486 do
1487 {
1488 /* Get the list head */
1489 ListHead = &PspReaperListHead.Flink;
1490
1491 /* Link ourselves */
1492 *ThreadAddr = Entry;
1493 SavedEntry = Entry;
1494
1495 /* Now try to do the exchange */
1496 Entry = InterlockedCompareExchangePointer(ListHead, ThreadAddr, Entry);
1497
1498 /* Break out if the change was succesful */
1499 } while (Entry != SavedEntry);
1500
1501 /* Check if the reaper wasn't active */
1502 if (!Entry)
1503 {
1504 /* Activate it as a work item, directly through its Queue */
1505 KiInsertQueue(&ExWorkerQueue[HyperCriticalWorkQueue].WorkerQueue,
1506 &PspReaperWorkItem.List,
1507 FALSE);
1508 }
1509
1510 /* Handle Kernel Queues */
1511 if (Thread->Queue)
1512 {
1513 DPRINT("Waking Queue\n");
1514 RemoveEntryList(&Thread->QueueListEntry);
1515 KiWakeQueue(Thread->Queue);
1516 }
1517
1518 /* Signal the thread */
1519 Thread->DispatcherHeader.SignalState = TRUE;
1520 if (IsListEmpty(&Thread->DispatcherHeader.WaitListHead) != TRUE)
1521 {
1522 /* Satisfy waits */
1523 KiWaitTest((PVOID)Thread, Increment);
1524 }
1525
1526 /* Remove the thread from the list */
1527 RemoveEntryList(&Thread->ThreadListEntry);
1528
1529 /* Set us as terminated, decrease the Process's stack count */
1530 Thread->State = Terminated;
1531 Process->StackCount--;
1532
1533 /* Find a new Thread */
1534 KiDispatchThreadNoLock(Terminated);
1535 }
1536
1537 /*
1538 * FUNCTION: Tests whether there are any pending APCs for the current thread
1539 * and if so the APCs will be delivered on exit from kernel mode
1540 */
1541 BOOLEAN
1542 STDCALL
1543 KeTestAlertThread(IN KPROCESSOR_MODE AlertMode)
1544 {
1545 KIRQL OldIrql;
1546 PKTHREAD Thread = KeGetCurrentThread();
1547 BOOLEAN OldState;
1548
1549 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
1550
1551 /* Lock the Dispatcher Database and the APC Queue */
1552 OldIrql = KeAcquireDispatcherDatabaseLock();
1553 KiAcquireSpinLock(&Thread->ApcQueueLock);
1554
1555 /* Save the old State */
1556 OldState = Thread->Alerted[AlertMode];
1557
1558 /* If the Thread is Alerted, Clear it */
1559 if (OldState) {
1560
1561 Thread->Alerted[AlertMode] = FALSE;
1562
1563 } else if ((AlertMode != KernelMode) && (!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]))) {
1564
1565 /* If the mode is User and the Queue isn't empty, set Pending */
1566 Thread->ApcState.UserApcPending = TRUE;
1567 }
1568
1569 /* Release Locks and return the Old State */
1570 KiReleaseSpinLock(&Thread->ApcQueueLock);
1571 KeReleaseDispatcherDatabaseLock(OldIrql);
1572 return OldState;
1573 }
1574
1575 NTSTATUS
1576 STDCALL
1577 NtDelayExecution(IN BOOLEAN Alertable,
1578 IN PLARGE_INTEGER DelayInterval)
1579 {
1580 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1581 LARGE_INTEGER SafeInterval;
1582 NTSTATUS Status;
1583
1584 /* Check if parameters are valid */
1585 if(PreviousMode != KernelMode) {
1586
1587 Status = STATUS_SUCCESS;
1588
1589 _SEH_TRY {
1590
1591 /* make a copy on the kernel stack and let DelayInterval point to it so
1592 we don't need to wrap KeDelayExecutionThread in SEH! */
1593 SafeInterval = ProbeForReadLargeInteger(DelayInterval);
1594 DelayInterval = &SafeInterval;
1595
1596 } _SEH_HANDLE {
1597
1598 Status = _SEH_GetExceptionCode();
1599 } _SEH_END;
1600
1601 if (!NT_SUCCESS(Status))
1602 {
1603 return Status;
1604 }
1605 }
1606
1607 /* Call the Kernel Function */
1608 Status = KeDelayExecutionThread(PreviousMode,
1609 Alertable,
1610 DelayInterval);
1611
1612 /* Return Status */
1613 return Status;
1614 }