- Fix NtSuspendThread: correct PreviousMode check, return error if thread is terminat...
[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 * HACK HACK This isn't nice, but it's the only way with our
352 * current codebase
353 */
354 Thread->Priority = Priority;
355 }
356 else
357 {
358 /* FIXME: Priority hasn't changed, find a new thread */
359 }
360 }
361 }
362
363 /* Nothing to do... */
364 return;
365 }
366
367
368 VOID
369 STDCALL
370 KiSuspendThreadKernelRoutine(PKAPC Apc,
371 PKNORMAL_ROUTINE* NormalRoutine,
372 PVOID* NormalContext,
373 PVOID* SystemArgument1,
374 PVOID* SystemArguemnt2)
375 {
376 }
377
378 VOID
379 STDCALL
380 KiSuspendThreadNormalRoutine(PVOID NormalContext,
381 PVOID SystemArgument1,
382 PVOID SystemArgument2)
383 {
384 PKTHREAD CurrentThread = KeGetCurrentThread();
385
386 /* Non-alertable kernel-mode suspended wait */
387 DPRINT("Waiting...\n");
388 KeWaitForSingleObject(&CurrentThread->SuspendSemaphore,
389 Suspended,
390 KernelMode,
391 FALSE,
392 NULL);
393 DPRINT("Done Waiting\n");
394 }
395
396 #ifdef KeGetCurrentThread
397 #undef KeGetCurrentThread
398 #endif
399 /*
400 * @implemented
401 */
402 PKTHREAD
403 STDCALL
404 KeGetCurrentThread(VOID)
405 {
406 #ifdef CONFIG_SMP
407 ULONG Flags;
408 PKTHREAD Thread;
409 Ke386SaveFlags(Flags);
410 Ke386DisableInterrupts();
411 Thread = KeGetCurrentPrcb()->CurrentThread;
412 Ke386RestoreFlags(Flags);
413 return Thread;
414 #else
415 return(KeGetCurrentPrcb()->CurrentThread);
416 #endif
417 }
418
419 VOID
420 STDCALL
421 KeSetPreviousMode(ULONG Mode)
422 {
423 PsGetCurrentThread()->Tcb.PreviousMode = (UCHAR)Mode;
424 }
425
426 /*
427 * @implemented
428 */
429 KPROCESSOR_MODE
430 STDCALL
431 KeGetPreviousMode(VOID)
432 {
433 return (ULONG)PsGetCurrentThread()->Tcb.PreviousMode;
434 }
435
436 BOOLEAN
437 STDCALL
438 KeDisableThreadApcQueueing(IN PKTHREAD Thread)
439 {
440 KIRQL OldIrql;
441 BOOLEAN PreviousState;
442
443 /* Lock the Dispatcher Database */
444 OldIrql = KeAcquireDispatcherDatabaseLock();
445
446 /* Save old state */
447 PreviousState = Thread->ApcQueueable;
448
449 /* Disable it now */
450 Thread->ApcQueueable = FALSE;
451
452 /* Release the Lock */
453 KeReleaseDispatcherDatabaseLock(OldIrql);
454
455 /* Return old state */
456 return PreviousState;
457 }
458
459 VOID
460 STDCALL
461 KeRundownThread(VOID)
462 {
463 KIRQL OldIrql;
464 PKTHREAD Thread = KeGetCurrentThread();
465 PLIST_ENTRY CurrentEntry;
466 PKMUTANT Mutant;
467
468 DPRINT("KeRundownThread: %x\n", Thread);
469
470 /* Lock the Dispatcher Database */
471 OldIrql = KeAcquireDispatcherDatabaseLock();
472
473 while (!IsListEmpty(&Thread->MutantListHead)) {
474
475 /* Get the Mutant */
476 CurrentEntry = RemoveHeadList(&Thread->MutantListHead);
477 Mutant = CONTAINING_RECORD(CurrentEntry, KMUTANT, MutantListEntry);
478 ASSERT(Mutant->ApcDisable == 0);
479
480 /* Uncondtionally abandon it */
481 DPRINT("Abandonning the Mutant\n");
482 Mutant->Header.SignalState = 1;
483 Mutant->Abandoned = TRUE;
484 Mutant->OwnerThread = NULL;
485 RemoveEntryList(&Mutant->MutantListEntry);
486
487 /* Check if the Wait List isn't empty */
488 DPRINT("Checking whether to wake the Mutant\n");
489 if (!IsListEmpty(&Mutant->Header.WaitListHead)) {
490
491 /* Wake the Mutant */
492 DPRINT("Waking the Mutant\n");
493 KiWaitTest(&Mutant->Header, MUTANT_INCREMENT);
494 }
495 }
496
497 /* Release the Lock */
498 KeReleaseDispatcherDatabaseLock(OldIrql);
499 }
500
501 ULONG
502 STDCALL
503 KeResumeThread(PKTHREAD Thread)
504 {
505 ULONG PreviousCount;
506 KIRQL OldIrql;
507
508 DPRINT("KeResumeThread (Thread %p called). %x, %x\n", Thread,
509 Thread->SuspendCount, Thread->FreezeCount);
510
511 /* Lock the Dispatcher */
512 OldIrql = KeAcquireDispatcherDatabaseLock();
513
514 /* Save the Old Count */
515 PreviousCount = Thread->SuspendCount;
516
517 /* Check if it existed */
518 if (PreviousCount) {
519
520 Thread->SuspendCount--;
521
522 /* Decrease the current Suspend Count and Check Freeze Count */
523 if ((!Thread->SuspendCount) && (!Thread->FreezeCount)) {
524
525 /* Signal the Suspend Semaphore */
526 Thread->SuspendSemaphore.Header.SignalState++;
527 KiWaitTest(&Thread->SuspendSemaphore.Header, IO_NO_INCREMENT);
528 }
529 }
530
531 /* Release Lock and return the Old State */
532 KeReleaseDispatcherDatabaseLock(OldIrql);
533 return PreviousCount;
534 }
535
536 BOOLEAN
537 STDCALL
538 KiInsertQueueApc(PKAPC Apc,
539 KPRIORITY PriorityBoost);
540
541 /*
542 * Used by the debugging code to freeze all the process's threads
543 * while the debugger is examining their state.
544 */
545 VOID
546 STDCALL
547 KeFreezeAllThreads(PKPROCESS Process)
548 {
549 KIRQL OldIrql;
550 PLIST_ENTRY CurrentEntry;
551 PKTHREAD Current;
552 PKTHREAD CurrentThread = KeGetCurrentThread();
553
554 /* Acquire Lock */
555 OldIrql = KeAcquireDispatcherDatabaseLock();
556
557 /* Loop the Process's Threads */
558 CurrentEntry = Process->ThreadListHead.Flink;
559 while (CurrentEntry != &Process->ThreadListHead)
560 {
561 /* Get the Thread */
562 Current = CONTAINING_RECORD(CurrentEntry, KTHREAD, ThreadListEntry);
563
564 /* Make sure it's not ours */
565 if (Current == CurrentThread) continue;
566
567 /* Make sure it wasn't already frozen, and that it's not suspended */
568 if (!(++Current->FreezeCount) && !(Current->SuspendCount))
569 {
570 /* Insert the APC */
571 if (!KiInsertQueueApc(&Current->SuspendApc, IO_NO_INCREMENT))
572 {
573 /* Unsignal the Semaphore, the APC already got inserted */
574 Current->SuspendSemaphore.Header.SignalState--;
575 }
576 }
577
578 CurrentEntry = CurrentEntry->Flink;
579 }
580
581 /* Release the lock */
582 KeReleaseDispatcherDatabaseLock(OldIrql);
583 }
584
585 NTSTATUS
586 STDCALL
587 KeSuspendThread(PKTHREAD Thread)
588 {
589 ULONG PreviousCount;
590 KIRQL OldIrql;
591
592 DPRINT("KeSuspendThread (Thread %p called). %x, %x\n", Thread, Thread->SuspendCount, Thread->FreezeCount);
593
594 /* Lock the Dispatcher */
595 OldIrql = KeAcquireDispatcherDatabaseLock();
596
597 /* Save the Old Count */
598 PreviousCount = Thread->SuspendCount;
599
600 /* Handle the maximum */
601 if (PreviousCount == MAXIMUM_SUSPEND_COUNT)
602 {
603 /* Raise an exception */
604 KeReleaseDispatcherDatabaseLock(OldIrql);
605 ExRaiseStatus(STATUS_SUSPEND_COUNT_EXCEEDED);
606 }
607
608 /* Increment it */
609 Thread->SuspendCount++;
610
611 /* Check if we should suspend it */
612 if (!PreviousCount && !Thread->FreezeCount) {
613
614 /* Insert the APC */
615 if (!KiInsertQueueApc(&Thread->SuspendApc, IO_NO_INCREMENT)) {
616
617 /* Unsignal the Semaphore, the APC already got inserted */
618 Thread->SuspendSemaphore.Header.SignalState--;
619 }
620 }
621
622 /* Release Lock and return the Old State */
623 KeReleaseDispatcherDatabaseLock(OldIrql);
624 return PreviousCount;
625 }
626
627 ULONG
628 STDCALL
629 KeForceResumeThread(IN PKTHREAD Thread)
630 {
631 KIRQL OldIrql;
632 ULONG PreviousCount;
633
634 /* Lock the Dispatcher Database and the APC Queue */
635 OldIrql = KeAcquireDispatcherDatabaseLock();
636
637 /* Save the old Suspend Count */
638 PreviousCount = Thread->SuspendCount + Thread->FreezeCount;
639
640 /* If the thread is suspended, wake it up!!! */
641 if (PreviousCount) {
642
643 /* Unwait it completely */
644 Thread->SuspendCount = 0;
645 Thread->FreezeCount = 0;
646
647 /* Signal and satisfy */
648 Thread->SuspendSemaphore.Header.SignalState++;
649 KiWaitTest(&Thread->SuspendSemaphore.Header, IO_NO_INCREMENT);
650 }
651
652 /* Release Lock and return the Old State */
653 KeReleaseDispatcherDatabaseLock(OldIrql);
654 return PreviousCount;
655 }
656
657 ULONG
658 STDCALL
659 KeAlertResumeThread(IN PKTHREAD Thread)
660 {
661 ULONG PreviousCount;
662 KIRQL OldIrql;
663
664 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
665
666 /* Lock the Dispatcher Database and the APC Queue */
667 OldIrql = KeAcquireDispatcherDatabaseLock();
668 KiAcquireSpinLock(&Thread->ApcQueueLock);
669
670 /* Return if Thread is already alerted. */
671 if (Thread->Alerted[KernelMode] == FALSE) {
672
673 /* If it's Blocked, unblock if it we should */
674 if (Thread->State == Waiting && Thread->Alertable) {
675
676 DPRINT("Aborting Wait\n");
677 KiAbortWaitThread(Thread, STATUS_ALERTED, THREAD_ALERT_INCREMENT);
678
679 } else {
680
681 /* If not, simply Alert it */
682 Thread->Alerted[KernelMode] = TRUE;
683 }
684 }
685
686 /* Save the old Suspend Count */
687 PreviousCount = Thread->SuspendCount;
688
689 /* If the thread is suspended, decrease one of the suspend counts */
690 if (PreviousCount) {
691
692 /* Decrease count. If we are now zero, unwait it completely */
693 if (--Thread->SuspendCount) {
694
695 /* Signal and satisfy */
696 Thread->SuspendSemaphore.Header.SignalState++;
697 KiWaitTest(&Thread->SuspendSemaphore.Header, IO_NO_INCREMENT);
698 }
699 }
700
701 /* Release Locks and return the Old State */
702 KiReleaseSpinLock(&Thread->ApcQueueLock);
703 KeReleaseDispatcherDatabaseLock(OldIrql);
704 return PreviousCount;
705 }
706
707 BOOLEAN
708 STDCALL
709 KeAlertThread(PKTHREAD Thread,
710 KPROCESSOR_MODE AlertMode)
711 {
712 KIRQL OldIrql;
713 BOOLEAN PreviousState;
714
715 /* Acquire the Dispatcher Database Lock */
716 OldIrql = KeAcquireDispatcherDatabaseLock();
717
718 /* Save the Previous State */
719 PreviousState = Thread->Alerted[AlertMode];
720
721 /* Return if Thread is already alerted. */
722 if (PreviousState == FALSE) {
723
724 /* If it's Blocked, unblock if it we should */
725 if (Thread->State == Waiting &&
726 (AlertMode == KernelMode || Thread->WaitMode == AlertMode) &&
727 Thread->Alertable) {
728
729 DPRINT("Aborting Wait\n");
730 KiAbortWaitThread(Thread, STATUS_ALERTED, THREAD_ALERT_INCREMENT);
731
732 } else {
733
734 /* If not, simply Alert it */
735 Thread->Alerted[AlertMode] = TRUE;
736 }
737 }
738
739 /* Release the Dispatcher Lock */
740 KeReleaseDispatcherDatabaseLock(OldIrql);
741
742 /* Return the old state */
743 return PreviousState;
744 }
745
746 /*
747 * @unimplemented
748 */
749 VOID
750 STDCALL
751 KeCapturePersistentThreadState(IN PVOID CurrentThread,
752 IN ULONG Setting1,
753 IN ULONG Setting2,
754 IN ULONG Setting3,
755 IN ULONG Setting4,
756 IN ULONG Setting5,
757 IN PVOID ThreadState)
758 {
759 UNIMPLEMENTED;
760 }
761
762 /*
763 * FUNCTION: Initialize the microkernel state of the thread
764 */
765 VOID
766 STDCALL
767 KeInitializeThread(PKPROCESS Process,
768 PKTHREAD Thread,
769 PKSYSTEM_ROUTINE SystemRoutine,
770 PKSTART_ROUTINE StartRoutine,
771 PVOID StartContext,
772 PCONTEXT Context,
773 PVOID Teb,
774 PVOID KernelStack)
775 {
776 /* Initalize the Dispatcher Header */
777 DPRINT("Initializing Dispatcher Header for New Thread: %x in Process: %x\n", Thread, Process);
778 KeInitializeDispatcherHeader(&Thread->DispatcherHeader,
779 ThreadObject,
780 sizeof(KTHREAD),
781 FALSE);
782
783 DPRINT("Thread Header Created. SystemRoutine: %x, StartRoutine: %x with Context: %x\n",
784 SystemRoutine, StartRoutine, StartContext);
785 DPRINT("UserMode Information. Context: %x, Teb: %x\n", Context, Teb);
786
787 /* Initialize the Mutant List */
788 InitializeListHead(&Thread->MutantListHead);
789
790 /* Setup the Service Descriptor Table for Native Calls */
791 Thread->ServiceTable = KeServiceDescriptorTable;
792
793 /* Setup APC Fields */
794 InitializeListHead(&Thread->ApcState.ApcListHead[0]);
795 InitializeListHead(&Thread->ApcState.ApcListHead[1]);
796 Thread->ApcState.Process = Process;
797 Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState;
798 Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState;
799 Thread->ApcStateIndex = OriginalApcEnvironment;
800 KeInitializeSpinLock(&Thread->ApcQueueLock);
801
802 /* Initialize the Suspend APC */
803 KeInitializeApc(&Thread->SuspendApc,
804 Thread,
805 OriginalApcEnvironment,
806 KiSuspendThreadKernelRoutine,
807 NULL,
808 KiSuspendThreadNormalRoutine,
809 KernelMode,
810 NULL);
811
812 /* Initialize the Suspend Semaphore */
813 KeInitializeSemaphore(&Thread->SuspendSemaphore, 0, 128);
814
815 /* FIXME OPTIMIZATION OF DOOM. DO NOT ENABLE FIXME */
816 #if 0
817 Thread->WaitBlock[3].Object = (PVOID)&Thread->Timer;
818 Thread->WaitBlock[3].Thread = Thread;
819 Thread->WaitBlock[3].WaitKey = STATUS_TIMEOUT;
820 Thread->WaitBlock[3].WaitType = WaitAny;
821 Thread->WaitBlock[3].NextWaitBlock = NULL;
822 InsertTailList(&Thread->Timer.Header.WaitListHead,
823 &Thread->WaitBlock[3].WaitListEntry);
824 #endif
825 KeInitializeTimer(&Thread->Timer);
826
827 /* Set the TEB */
828 Thread->Teb = Teb;
829
830 /* Set the Thread Stacks */
831 Thread->InitialStack = (PCHAR)KernelStack + MM_STACK_SIZE;
832 Thread->StackBase = (PCHAR)KernelStack + MM_STACK_SIZE;
833 Thread->StackLimit = (ULONG_PTR)KernelStack;
834 Thread->KernelStackResident = TRUE;
835
836 /*
837 * Establish the pde's for the new stack and the thread structure within the
838 * address space of the new process. They are accessed while taskswitching or
839 * while handling page faults. At this point it isn't possible to call the
840 * page fault handler for the missing pde's.
841 */
842 MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread->StackLimit, MM_STACK_SIZE);
843 MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread, sizeof(ETHREAD));
844
845 /* Initalize the Thread Context */
846 DPRINT("Initializing the Context for the thread: %x\n", Thread);
847 KiArchInitThreadWithContext(Thread,
848 SystemRoutine,
849 StartRoutine,
850 StartContext,
851 Context);
852
853 /* Setup scheduler Fields based on Parent */
854 DPRINT("Thread context created, setting Scheduler Data\n");
855 Thread->BasePriority = Process->BasePriority;
856 Thread->Quantum = Process->QuantumReset;
857 Thread->QuantumReset = Process->QuantumReset;
858 Thread->Affinity = Process->Affinity;
859 Thread->Priority = Process->BasePriority;
860 Thread->UserAffinity = Process->Affinity;
861 Thread->DisableBoost = Process->DisableBoost;
862 Thread->AutoAlignment = Process->AutoAlignment;
863 Thread->Iopl = Process->Iopl;
864
865 /* Set the Thread to initalized */
866 Thread->State = Initialized;
867
868 /*
869 * Insert the Thread into the Process's Thread List
870 * Note, this is the KTHREAD Thread List. It is removed in
871 * ke/kthread.c!KeTerminateThread.
872 */
873 InsertTailList(&Process->ThreadListHead, &Thread->ThreadListEntry);
874 DPRINT("Thread initalized\n");
875 }
876
877
878 /*
879 * @implemented
880 */
881 KPRIORITY
882 STDCALL
883 KeQueryPriorityThread (IN PKTHREAD Thread)
884 {
885 return Thread->Priority;
886 }
887
888 /*
889 * @implemented
890 */
891 ULONG
892 STDCALL
893 KeQueryRuntimeThread(IN PKTHREAD Thread,
894 OUT PULONG UserTime)
895 {
896 /* Return the User Time */
897 *UserTime = Thread->UserTime;
898
899 /* Return the Kernel Time */
900 return Thread->KernelTime;
901 }
902
903 /*
904 * @implemented
905 */
906 BOOLEAN
907 STDCALL
908 KeSetKernelStackSwapEnable(IN BOOLEAN Enable)
909 {
910 PKTHREAD Thread = KeGetCurrentThread();
911 BOOLEAN PreviousState;
912 KIRQL OldIrql;
913
914 /* Lock the Dispatcher Database */
915 OldIrql = KeAcquireDispatcherDatabaseLock();
916
917 /* Save Old State */
918 PreviousState = Thread->EnableStackSwap;
919
920 /* Set New State */
921 Thread->EnableStackSwap = Enable;
922
923 /* No, Release Lock */
924 KeReleaseDispatcherDatabaseLock(OldIrql);
925
926 /* Return Old State */
927 return PreviousState;
928 }
929
930 /*
931 * @implemented
932 */
933 VOID
934 STDCALL
935 KeRevertToUserAffinityThread(VOID)
936 {
937 PKTHREAD CurrentThread = KeGetCurrentThread();
938 KIRQL OldIrql;
939
940 ASSERT(CurrentThread->SystemAffinityActive != FALSE);
941
942 /* Lock the Dispatcher Database */
943 OldIrql = KeAcquireDispatcherDatabaseLock();
944
945 /* Return to User Affinity */
946 CurrentThread->Affinity = CurrentThread->UserAffinity;
947
948 /* Disable System Affinity */
949 CurrentThread->SystemAffinityActive = FALSE;
950
951 /* Check if we need to Dispatch a New thread */
952 if (CurrentThread->Affinity & (1 << KeGetCurrentProcessorNumber())) {
953
954 /* No, just release */
955 KeReleaseDispatcherDatabaseLock(OldIrql);
956
957 } else {
958
959 /* We need to dispatch a new thread */
960 CurrentThread->WaitIrql = OldIrql;
961 KiDispatchThreadNoLock(Ready);
962 KeLowerIrql(OldIrql);
963 }
964 }
965
966 /*
967 * @implemented
968 */
969 CCHAR
970 STDCALL
971 KeSetIdealProcessorThread(IN PKTHREAD Thread,
972 IN CCHAR Processor)
973 {
974 CCHAR PreviousIdealProcessor;
975 KIRQL OldIrql;
976
977 /* Lock the Dispatcher Database */
978 OldIrql = KeAcquireDispatcherDatabaseLock();
979
980 /* Save Old Ideal Processor */
981 PreviousIdealProcessor = Thread->IdealProcessor;
982
983 /* Set New Ideal Processor */
984 Thread->IdealProcessor = Processor;
985
986 /* Release Lock */
987 KeReleaseDispatcherDatabaseLock(OldIrql);
988
989 /* Return Old Ideal Processor */
990 return PreviousIdealProcessor;
991 }
992
993 /*
994 * @implemented
995 */
996 VOID
997 STDCALL
998 KeSetSystemAffinityThread(IN KAFFINITY Affinity)
999 {
1000 PKTHREAD CurrentThread = KeGetCurrentThread();
1001 KIRQL OldIrql;
1002
1003 ASSERT(Affinity & ((1 << KeNumberProcessors) - 1));
1004
1005 /* Lock the Dispatcher Database */
1006 OldIrql = KeAcquireDispatcherDatabaseLock();
1007
1008 /* Set the System Affinity Specified */
1009 CurrentThread->Affinity = Affinity;
1010
1011 /* Enable System Affinity */
1012 CurrentThread->SystemAffinityActive = TRUE;
1013
1014 /* Check if we need to Dispatch a New thread */
1015 if (Affinity & (1 << KeGetCurrentProcessorNumber())) {
1016
1017 /* No, just release */
1018 KeReleaseDispatcherDatabaseLock(OldIrql);
1019
1020 } else {
1021
1022 /* We need to dispatch a new thread */
1023 CurrentThread->WaitIrql = OldIrql;
1024 KiDispatchThreadNoLock(Ready);
1025 KeLowerIrql(OldIrql);
1026 }
1027 }
1028
1029 /*
1030 * @implemented
1031 */
1032 LONG STDCALL
1033 KeSetBasePriorityThread (PKTHREAD Thread,
1034 LONG Increment)
1035 /*
1036 * Sets thread's base priority relative to the process' base priority
1037 * Should only be passed in THREAD_PRIORITY_ constants in pstypes.h
1038 */
1039 {
1040 KPRIORITY Priority;
1041 if (Increment < -2)
1042 {
1043 Increment = -2;
1044 }
1045 else if (Increment > 2)
1046 {
1047 Increment = 2;
1048 }
1049 Priority = ((PETHREAD)Thread)->ThreadsProcess->Pcb.BasePriority + Increment;
1050 if (Priority < LOW_PRIORITY)
1051 {
1052 Priority = LOW_PRIORITY;
1053 }
1054 else if (Priority >= MAXIMUM_PRIORITY)
1055 {
1056 Thread->BasePriority = HIGH_PRIORITY;
1057 }
1058 KeSetPriorityThread(Thread, Priority);
1059 return 1;
1060 }
1061
1062 /*
1063 * @implemented
1064 */
1065 KPRIORITY
1066 STDCALL
1067 KeSetPriorityThread(PKTHREAD Thread,
1068 KPRIORITY Priority)
1069 {
1070 KPRIORITY OldPriority;
1071 KIRQL OldIrql;
1072 PKTHREAD CurrentThread;
1073 ULONG Mask;
1074 int i;
1075 PKPCR Pcr;
1076
1077 if (Priority < LOW_PRIORITY || Priority >= MAXIMUM_PRIORITY) {
1078
1079 KEBUGCHECK(0);
1080 }
1081
1082 OldIrql = KeAcquireDispatcherDatabaseLock();
1083
1084 OldPriority = Thread->Priority;
1085
1086 if (OldPriority != Priority) {
1087
1088 CurrentThread = KeGetCurrentThread();
1089
1090 if (Thread->State == Ready) {
1091
1092 KiRemoveFromThreadList(Thread);
1093 Thread->BasePriority = Thread->Priority = (CHAR)Priority;
1094 KiInsertIntoThreadList(Priority, Thread);
1095
1096 if (CurrentThread->Priority < Priority) {
1097
1098 KiDispatchThreadNoLock(Ready);
1099 KeLowerIrql(OldIrql);
1100 return (OldPriority);
1101 }
1102
1103 } else if (Thread->State == Running) {
1104
1105 Thread->BasePriority = Thread->Priority = (CHAR)Priority;
1106
1107 if (Priority < OldPriority) {
1108
1109 /* Check for threads with a higher priority */
1110 Mask = ~((1 << (Priority + 1)) - 1);
1111 if (PriorityListMask & Mask) {
1112
1113 if (Thread == CurrentThread) {
1114
1115 KiDispatchThreadNoLock(Ready);
1116 KeLowerIrql(OldIrql);
1117 return (OldPriority);
1118
1119 } else {
1120
1121 for (i = 0; i < KeNumberProcessors; i++) {
1122
1123 Pcr = (PKPCR)(KPCR_BASE + i * PAGE_SIZE);
1124
1125 if (Pcr->Prcb->CurrentThread == Thread) {
1126
1127 KeReleaseDispatcherDatabaseLockFromDpcLevel();
1128 KiRequestReschedule(i);
1129 KeLowerIrql(OldIrql);
1130 return (OldPriority);
1131 }
1132 }
1133 }
1134 }
1135 }
1136 } else {
1137
1138 Thread->BasePriority = Thread->Priority = (CHAR)Priority;
1139 }
1140 }
1141
1142 KeReleaseDispatcherDatabaseLock(OldIrql);
1143 return(OldPriority);
1144 }
1145
1146 /*
1147 * @implemented
1148 *
1149 * Sets thread's affinity
1150 */
1151 NTSTATUS
1152 STDCALL
1153 KeSetAffinityThread(PKTHREAD Thread,
1154 KAFFINITY Affinity)
1155 {
1156 KIRQL OldIrql;
1157 LONG i;
1158 PKPCR Pcr;
1159 KAFFINITY ProcessorMask;
1160
1161 DPRINT("KeSetAffinityThread(Thread %x, Affinity %x)\n", Thread, Affinity);
1162
1163 ASSERT(Affinity & ((1 << KeNumberProcessors) - 1));
1164
1165 OldIrql = KeAcquireDispatcherDatabaseLock();
1166
1167 Thread->UserAffinity = Affinity;
1168
1169 if (Thread->SystemAffinityActive == FALSE) {
1170
1171 Thread->Affinity = Affinity;
1172
1173 if (Thread->State == Running) {
1174
1175 ProcessorMask = 1 << KeGetCurrentKPCR()->Number;
1176 if (Thread == KeGetCurrentThread()) {
1177
1178 if (!(Affinity & ProcessorMask)) {
1179
1180 KiDispatchThreadNoLock(Ready);
1181 KeLowerIrql(OldIrql);
1182 return STATUS_SUCCESS;
1183 }
1184
1185 } else {
1186
1187 for (i = 0; i < KeNumberProcessors; i++) {
1188
1189 Pcr = (PKPCR)(KPCR_BASE + i * PAGE_SIZE);
1190 if (Pcr->Prcb->CurrentThread == Thread) {
1191
1192 if (!(Affinity & ProcessorMask)) {
1193
1194 KeReleaseDispatcherDatabaseLockFromDpcLevel();
1195 KiRequestReschedule(i);
1196 KeLowerIrql(OldIrql);
1197 return STATUS_SUCCESS;
1198 }
1199
1200 break;
1201 }
1202 }
1203
1204 ASSERT (i < KeNumberProcessors);
1205 }
1206 }
1207 }
1208
1209 KeReleaseDispatcherDatabaseLock(OldIrql);
1210 return STATUS_SUCCESS;
1211 }
1212
1213 /*
1214 * @implemented
1215 */
1216 /* The Increment Argument seems to be ignored by NT and always 0 when called */
1217 VOID
1218 STDCALL
1219 KeTerminateThread(IN KPRIORITY Increment)
1220 {
1221 KIRQL OldIrql;
1222 PKTHREAD Thread = KeGetCurrentThread();
1223
1224 /* Lock the Dispatcher Database and the APC Queue */
1225 DPRINT("Terminating\n");
1226 OldIrql = KeAcquireDispatcherDatabaseLock();
1227
1228 /* Remove the thread from the list */
1229 RemoveEntryList(&Thread->ThreadListEntry);
1230
1231 /* Insert into the Reaper List */
1232 DPRINT("List: %p\n", PspReaperList);
1233 ((PETHREAD)Thread)->ReaperLink = PspReaperList;
1234 PspReaperList = (PETHREAD)Thread;
1235 DPRINT("List: %p\n", PspReaperList);
1236
1237 /* Check if it's active */
1238 if (PspReaping == FALSE) {
1239
1240 /* Activate it. We use the internal function for speed, and use the Hyper Critical Queue */
1241 PspReaping = TRUE;
1242 DPRINT("Terminating\n");
1243 KiInsertQueue(&ExWorkerQueue[HyperCriticalWorkQueue].WorkerQueue,
1244 &PspReaperWorkItem.List,
1245 FALSE);
1246 }
1247
1248 /* Handle Kernel Queues */
1249 if (Thread->Queue) {
1250
1251 DPRINT("Waking Queue\n");
1252 RemoveEntryList(&Thread->QueueListEntry);
1253 KiWakeQueue(Thread->Queue);
1254 }
1255
1256 /* Signal the thread */
1257 Thread->DispatcherHeader.SignalState = TRUE;
1258 if (IsListEmpty(&Thread->DispatcherHeader.WaitListHead) != TRUE) {
1259
1260 /* Satisfy waits */
1261 KiWaitTest((PVOID)Thread, Increment);
1262 }
1263
1264 /* Find a new Thread */
1265 KiDispatchThreadNoLock(Terminated);
1266 }
1267
1268 /*
1269 * FUNCTION: Tests whether there are any pending APCs for the current thread
1270 * and if so the APCs will be delivered on exit from kernel mode
1271 */
1272 BOOLEAN
1273 STDCALL
1274 KeTestAlertThread(IN KPROCESSOR_MODE AlertMode)
1275 {
1276 KIRQL OldIrql;
1277 PKTHREAD Thread = KeGetCurrentThread();
1278 BOOLEAN OldState;
1279
1280 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
1281
1282 /* Lock the Dispatcher Database and the APC Queue */
1283 OldIrql = KeAcquireDispatcherDatabaseLock();
1284 KiAcquireSpinLock(&Thread->ApcQueueLock);
1285
1286 /* Save the old State */
1287 OldState = Thread->Alerted[AlertMode];
1288
1289 /* If the Thread is Alerted, Clear it */
1290 if (OldState) {
1291
1292 Thread->Alerted[AlertMode] = FALSE;
1293
1294 } else if ((AlertMode == UserMode) && (!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]))) {
1295
1296 /* If the mode is User and the Queue isn't empty, set Pending */
1297 Thread->ApcState.UserApcPending = TRUE;
1298 }
1299
1300 /* Release Locks and return the Old State */
1301 KiReleaseSpinLock(&Thread->ApcQueueLock);
1302 KeReleaseDispatcherDatabaseLock(OldIrql);
1303 return OldState;
1304 }
1305
1306 VOID
1307 KiServiceCheck (VOID)
1308 {
1309 PKTHREAD Thread = KeGetCurrentThread();
1310
1311 /* Check if we need to inialize Win32 for this Thread */
1312 if (Thread->ServiceTable != KeServiceDescriptorTableShadow) {
1313
1314 /* We do. Initialize it and save the new table */
1315 PsInitWin32Thread((PETHREAD)Thread);
1316 Thread->ServiceTable = KeServiceDescriptorTableShadow;
1317 }
1318 }
1319
1320 /*
1321 *
1322 * NOT EXPORTED
1323 */
1324 NTSTATUS
1325 STDCALL
1326 NtAlertResumeThread(IN HANDLE ThreadHandle,
1327 OUT PULONG SuspendCount)
1328 {
1329 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1330 PETHREAD Thread;
1331 NTSTATUS Status;
1332 ULONG PreviousState;
1333
1334 /* Check if parameters are valid */
1335 if(PreviousMode != KernelMode) {
1336
1337 _SEH_TRY {
1338
1339 ProbeForWrite(SuspendCount,
1340 sizeof(HANDLE),
1341 sizeof(ULONG));
1342
1343 } _SEH_HANDLE {
1344
1345 Status = _SEH_GetExceptionCode();
1346
1347 } _SEH_END;
1348 }
1349
1350 /* Reference the Object */
1351 Status = ObReferenceObjectByHandle(ThreadHandle,
1352 THREAD_SUSPEND_RESUME,
1353 PsThreadType,
1354 PreviousMode,
1355 (PVOID*)&Thread,
1356 NULL);
1357
1358 /* Check for Success */
1359 if (NT_SUCCESS(Status)) {
1360
1361 /* Call the Kernel Function */
1362 PreviousState = KeAlertResumeThread(&Thread->Tcb);
1363
1364 /* Dereference Object */
1365 ObDereferenceObject(Thread);
1366
1367 if (SuspendCount) {
1368
1369 _SEH_TRY {
1370
1371 *SuspendCount = PreviousState;
1372
1373 } _SEH_HANDLE {
1374
1375 Status = _SEH_GetExceptionCode();
1376
1377 } _SEH_END;
1378 }
1379 }
1380
1381 /* Return status */
1382 return Status;
1383 }
1384
1385 /*
1386 * @implemented
1387 *
1388 * EXPORTED
1389 */
1390 NTSTATUS
1391 STDCALL
1392 NtAlertThread (IN HANDLE ThreadHandle)
1393 {
1394 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1395 PETHREAD Thread;
1396 NTSTATUS Status;
1397
1398 /* Reference the Object */
1399 Status = ObReferenceObjectByHandle(ThreadHandle,
1400 THREAD_SUSPEND_RESUME,
1401 PsThreadType,
1402 PreviousMode,
1403 (PVOID*)&Thread,
1404 NULL);
1405
1406 /* Check for Success */
1407 if (NT_SUCCESS(Status)) {
1408
1409 /*
1410 * Do an alert depending on the processor mode. If some kmode code wants to
1411 * enforce a umode alert it should call KeAlertThread() directly. If kmode
1412 * code wants to do a kmode alert it's sufficient to call it with Zw or just
1413 * use KeAlertThread() directly
1414 */
1415 KeAlertThread(&Thread->Tcb, PreviousMode);
1416
1417 /* Dereference Object */
1418 ObDereferenceObject(Thread);
1419 }
1420
1421 /* Return status */
1422 return Status;
1423 }
1424
1425 NTSTATUS
1426 STDCALL
1427 NtDelayExecution(IN BOOLEAN Alertable,
1428 IN PLARGE_INTEGER DelayInterval)
1429 {
1430 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1431 LARGE_INTEGER SafeInterval;
1432 NTSTATUS Status;
1433
1434 /* Check if parameters are valid */
1435 if(PreviousMode != KernelMode) {
1436
1437 _SEH_TRY {
1438
1439 ProbeForRead(DelayInterval,
1440 sizeof(LARGE_INTEGER),
1441 sizeof(ULONG));
1442
1443 /* make a copy on the kernel stack and let DelayInterval point to it so
1444 we don't need to wrap KeDelayExecutionThread in SEH! */
1445 SafeInterval = *DelayInterval;
1446
1447 } _SEH_HANDLE {
1448
1449 Status = _SEH_GetExceptionCode();
1450 } _SEH_END;
1451 }
1452
1453 /* Call the Kernel Function */
1454 Status = KeDelayExecutionThread(PreviousMode,
1455 Alertable,
1456 &SafeInterval);
1457
1458 /* Return Status */
1459 return Status;
1460 }