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