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