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