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