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