- Update KeRevertToUserAffinityThread, KeSetSystemAffinityThread to use new lock...
[reactos.git] / reactos / ntoskrnl / ke / thrdobj.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ke/thrdobj.c
5 * PURPOSE: Implements routines to manage the Kernel Thread Object
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <internal/debug.h>
14
15 extern EX_WORK_QUEUE ExWorkerQueue[MaximumWorkQueue];
16 extern LIST_ENTRY PspReaperListHead;
17
18 ULONG KiMask32Array[MAXIMUM_PRIORITY] =
19 {
20 0x1, 0x2, 0x4, 0x8, 0x10, 0x20,
21 0x40, 0x80, 0x100, 0x200, 0x4000, 0x800,
22 0x1000, 0x2000, 0x4000, 0x8000, 0x10000, 0x20000,
23 0x40000, 0x80000, 0x100000, 0x200000, 0x400000, 0x800000,
24 0x1000000, 0x2000000, 0x4000000, 0x8000000, 0x10000000, 0x20000000,
25 0x40000000, 0x80000000
26 };
27
28 /* FUNCTIONS *****************************************************************/
29
30 UCHAR
31 NTAPI
32 KeFindNextRightSetAffinity(IN UCHAR Number,
33 IN ULONG Set)
34 {
35 ULONG Bit, Result;
36 ASSERT(Set != 0);
37
38 /* Calculate the mask */
39 Bit = (AFFINITY_MASK(Number) - 1) & Set;
40
41 /* If it's 0, use the one we got */
42 if (!Bit) Bit = Set;
43
44 /* Now find the right set and return it */
45 BitScanReverse(&Result, Bit);
46 return (UCHAR)Result;
47 }
48
49 ULONG
50 NTAPI
51 KeAlertResumeThread(IN PKTHREAD Thread)
52 {
53 ULONG PreviousCount;
54 KLOCK_QUEUE_HANDLE ApcLock;
55 ASSERT_THREAD(Thread);
56 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
57
58 /* Lock the Dispatcher Database and the APC Queue */
59 KiAcquireApcLock(Thread, &ApcLock);
60 KiAcquireDispatcherLockAtDpcLevel();
61
62 /* Return if Thread is already alerted. */
63 if (!Thread->Alerted[KernelMode])
64 {
65 /* If it's Blocked, unblock if it we should */
66 if ((Thread->State == Waiting) && (Thread->Alertable))
67 {
68 /* Abort the wait */
69 KiAbortWaitThread(Thread, STATUS_ALERTED, THREAD_ALERT_INCREMENT);
70 }
71 else
72 {
73 /* If not, simply Alert it */
74 Thread->Alerted[KernelMode] = TRUE;
75 }
76 }
77
78 /* Save the old Suspend Count */
79 PreviousCount = Thread->SuspendCount;
80
81 /* If the thread is suspended, decrease one of the suspend counts */
82 if (PreviousCount)
83 {
84 /* Decrease count. If we are now zero, unwait it completely */
85 if (--Thread->SuspendCount)
86 {
87 /* Signal and satisfy */
88 Thread->SuspendSemaphore.Header.SignalState++;
89 KiWaitTest(&Thread->SuspendSemaphore.Header, IO_NO_INCREMENT);
90 }
91 }
92
93 /* Release Locks and return the Old State */
94 KiReleaseDispatcherLockFromDpcLevel();
95 KiReleaseApcLockFromDpcLevel(&ApcLock);
96 KiExitDispatcher(ApcLock.OldIrql);
97 return PreviousCount;
98 }
99
100 BOOLEAN
101 NTAPI
102 KeAlertThread(IN PKTHREAD Thread,
103 IN KPROCESSOR_MODE AlertMode)
104 {
105 BOOLEAN PreviousState;
106 KLOCK_QUEUE_HANDLE ApcLock;
107 ASSERT_THREAD(Thread);
108 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
109
110 /* Lock the Dispatcher Database and the APC Queue */
111 KiAcquireApcLock(Thread, &ApcLock);
112 KiAcquireDispatcherLockAtDpcLevel();
113
114 /* Save the Previous State */
115 PreviousState = Thread->Alerted[AlertMode];
116
117 /* Check if it's already alerted */
118 if (!PreviousState)
119 {
120 /* Check if the thread is alertable, and blocked in the given mode */
121 if ((Thread->State == Waiting) &&
122 ((AlertMode == KernelMode) || (Thread->WaitMode == AlertMode)) &&
123 (Thread->Alertable))
124 {
125 /* Abort the wait to alert the thread */
126 KiAbortWaitThread(Thread, STATUS_ALERTED, THREAD_ALERT_INCREMENT);
127 }
128 else
129 {
130 /* Otherwise, merely set the alerted state */
131 Thread->Alerted[AlertMode] = TRUE;
132 }
133 }
134
135 /* Release the Dispatcher Lock */
136 KiReleaseDispatcherLockFromDpcLevel();
137 KiReleaseApcLockFromDpcLevel(&ApcLock);
138 KiExitDispatcher(ApcLock.OldIrql);
139
140 /* Return the old state */
141 return PreviousState;
142 }
143
144 ULONG
145 NTAPI
146 KeForceResumeThread(IN PKTHREAD Thread)
147 {
148 KLOCK_QUEUE_HANDLE ApcLock;
149 ULONG PreviousCount;
150 ASSERT_THREAD(Thread);
151 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
152
153 /* Lock the APC Queue */
154 KiAcquireApcLock(Thread, &ApcLock);
155
156 /* Save the old Suspend Count */
157 PreviousCount = Thread->SuspendCount + Thread->FreezeCount;
158
159 /* If the thread is suspended, wake it up!!! */
160 if (PreviousCount)
161 {
162 /* Unwait it completely */
163 Thread->SuspendCount = 0;
164 Thread->FreezeCount = 0;
165
166 /* Lock the dispatcher */
167 KiAcquireDispatcherLockAtDpcLevel();
168
169 /* Signal and satisfy */
170 Thread->SuspendSemaphore.Header.SignalState++;
171 KiWaitTest(&Thread->SuspendSemaphore.Header, IO_NO_INCREMENT);
172
173 /* Release the dispatcher */
174 KiReleaseDispatcherLockFromDpcLevel();
175 }
176
177 /* Release Lock and return the Old State */
178 KiReleaseApcLockFromDpcLevel(&ApcLock);
179 KiExitDispatcher(ApcLock.OldIrql);
180 return PreviousCount;
181 }
182
183 /*
184 * Used by the debugging code to freeze all the process's threads
185 * while the debugger is examining their state.
186 */
187 VOID
188 NTAPI
189 KeFreezeAllThreads(IN PKPROCESS Process)
190 {
191 KLOCK_QUEUE_HANDLE LockHandle, ApcLock;
192 PKTHREAD Current, CurrentThread = KeGetCurrentThread();
193 PLIST_ENTRY ListHead, NextEntry;
194 LONG OldCount;
195 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
196
197 /* Lock the process */
198 KiAcquireProcessLock(Process, &LockHandle);
199
200 /* If someone is already trying to free us, try again */
201 while (CurrentThread->FreezeCount)
202 {
203 /* Release and re-acquire the process lock so the APC will go through */
204 KiReleaseProcessLock(&LockHandle);
205 KiAcquireProcessLock(Process, &LockHandle);
206 }
207
208 /* Enter a critical region */
209 KeEnterCriticalRegion();
210
211 /* Loop the Process's Threads */
212 ListHead = &Process->ThreadListHead;
213 NextEntry = ListHead->Flink;
214 while (NextEntry != ListHead)
215 {
216 /* Get the current thread */
217 Current = CONTAINING_RECORD(NextEntry, KTHREAD, ThreadListEntry);
218
219 /* Lock it */
220 KiAcquireApcLockAtDpcLevel(Current, &ApcLock);
221
222 /* Make sure it's not ours, and check if APCs are enabled */
223 if ((Current != CurrentThread) && (Current->ApcQueueable))
224 {
225 /* Sanity check */
226 OldCount = Current->SuspendCount;
227 ASSERT(OldCount != MAXIMUM_SUSPEND_COUNT);
228
229 /* Increase the freeze count */
230 Current->FreezeCount++;
231
232 /* Make sure it wasn't already suspended */
233 if (!(OldCount) && !(Current->SuspendCount))
234 {
235 /* Did we already insert it? */
236 if (!Current->SuspendApc.Inserted)
237 {
238 /* Insert the APC */
239 Current->SuspendApc.Inserted = TRUE;
240 KiInsertQueueApc(&Current->SuspendApc, IO_NO_INCREMENT);
241 }
242 else
243 {
244 /* Lock the dispatcher */
245 KiAcquireDispatcherLockAtDpcLevel();
246
247 /* Unsignal the semaphore, the APC was already inserted */
248 Current->SuspendSemaphore.Header.SignalState--;
249
250 /* Release the dispatcher */
251 KiReleaseDispatcherLockFromDpcLevel();
252 }
253 }
254 }
255
256 /* Release the APC lock */
257 KiReleaseApcLockFromDpcLevel(&ApcLock);
258 }
259
260 /* Release the process lock and exit the dispatcher */
261 KiReleaseProcessLock(&LockHandle);
262 KiExitDispatcher(LockHandle.OldIrql);
263 }
264
265 ULONG
266 NTAPI
267 KeResumeThread(IN PKTHREAD Thread)
268 {
269 KLOCK_QUEUE_HANDLE ApcLock;
270 ULONG PreviousCount;
271 ASSERT_THREAD(Thread);
272 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
273
274 /* Lock the APC Queue */
275 KiAcquireApcLock(Thread, &ApcLock);
276
277 /* Save the Old Count */
278 PreviousCount = Thread->SuspendCount;
279
280 /* Check if it existed */
281 if (PreviousCount)
282 {
283 /* Decrease the suspend count */
284 Thread->SuspendCount--;
285
286 /* Check if the thrad is still suspended or not */
287 if ((!Thread->SuspendCount) && (!Thread->FreezeCount))
288 {
289 /* Acquire the dispatcher lock */
290 KiAcquireDispatcherLockAtDpcLevel();
291
292 /* Signal the Suspend Semaphore */
293 Thread->SuspendSemaphore.Header.SignalState++;
294 KiWaitTest(&Thread->SuspendSemaphore.Header, IO_NO_INCREMENT);
295
296 /* Release the dispatcher lock */
297 KiReleaseDispatcherLockFromDpcLevel();
298 }
299 }
300
301 /* Release APC Queue lock and return the Old State */
302 KiReleaseApcLockFromDpcLevel(&ApcLock);
303 KiExitDispatcher(ApcLock.OldIrql);
304 return PreviousCount;
305 }
306
307 VOID
308 NTAPI
309 KeRundownThread(VOID)
310 {
311 KIRQL OldIrql;
312 PKTHREAD Thread = KeGetCurrentThread();
313 PLIST_ENTRY NextEntry, ListHead;
314 PKMUTANT Mutant;
315
316 /* Optimized path if nothing is on the list at the moment */
317 if (IsListEmpty(&Thread->MutantListHead)) return;
318
319 /* Lock the Dispatcher Database */
320 OldIrql = KiAcquireDispatcherLock();
321
322 /* Get the List Pointers */
323 ListHead = &Thread->MutantListHead;
324 NextEntry = ListHead->Flink;
325 while (NextEntry != ListHead)
326 {
327 /* Get the Mutant */
328 Mutant = CONTAINING_RECORD(NextEntry, KMUTANT, MutantListEntry);
329
330 /* Make sure it's not terminating with APCs off */
331 if (Mutant->ApcDisable)
332 {
333 /* Bugcheck the system */
334 KEBUGCHECKEX(0, //THREAD_TERMINATE_HELD_MUTEX,
335 (ULONG_PTR)Thread,
336 (ULONG_PTR)Mutant,
337 0,
338 0);
339 }
340
341 /* Now we can remove it */
342 RemoveEntryList(&Mutant->MutantListEntry);
343
344 /* Unconditionally abandon it */
345 Mutant->Header.SignalState = 1;
346 Mutant->Abandoned = TRUE;
347 Mutant->OwnerThread = NULL;
348
349 /* Check if the Wait List isn't empty */
350 if (!IsListEmpty(&Mutant->Header.WaitListHead))
351 {
352 /* Wake the Mutant */
353 KiWaitTest(&Mutant->Header, MUTANT_INCREMENT);
354 }
355
356 /* Move on */
357 NextEntry = NextEntry->Flink;
358 }
359
360 /* Release the Lock */
361 KiReleaseDispatcherLock(OldIrql);
362 }
363
364 VOID
365 NTAPI
366 KeStartThread(IN OUT PKTHREAD Thread)
367 {
368 KLOCK_QUEUE_HANDLE LockHandle;
369 #ifdef CONFIG_SMP
370 PKNODE Node;
371 PKPRCB NodePrcb;
372 ULONG Set, Mask;
373 #endif
374 UCHAR IdealProcessor = 0;
375 PKPROCESS Process = Thread->ApcState.Process;
376
377 /* Setup static fields from parent */
378 Thread->Iopl = Process->Iopl;
379 Thread->Quantum = Process->QuantumReset;
380 Thread->QuantumReset = Process->QuantumReset;
381 Thread->SystemAffinityActive = FALSE;
382
383 /* Lock the process */
384 KiAcquireProcessLock(Process, &LockHandle);
385
386 /* Setup volatile data */
387 Thread->Priority = Process->BasePriority;
388 Thread->BasePriority = Process->BasePriority;
389 Thread->Affinity = Process->Affinity;
390 Thread->UserAffinity = Process->Affinity;
391
392 #ifdef CONFIG_SMP
393 /* Get the KNODE and its PRCB */
394 Node = KeNodeBlock[Process->IdealNode];
395 NodePrcb = (PKPRCB)(KPCR_BASE + (Process->ThreadSeed * PAGE_SIZE));
396
397 /* Calculate affinity mask */
398 Set = ~NodePrcb->MultiThreadProcessorSet;
399 Mask = (ULONG)(Node->ProcessorMask & Process->Affinity);
400 Set &= Mask;
401 if (Set) Mask = Set;
402
403 /* Get the new thread seed */
404 IdealProcessor = KeFindNextRightSetAffinity(Process->ThreadSeed, Mask);
405 Process->ThreadSeed = IdealProcessor;
406
407 /* Sanity check */
408 ASSERT((Thread->UserAffinity & AFFINITY_MASK(IdealProcessor)));
409 #endif
410
411 /* Set the Ideal Processor */
412 Thread->IdealProcessor = IdealProcessor;
413 Thread->UserIdealProcessor = IdealProcessor;
414
415 /* Lock the Dispatcher Database */
416 KiAcquireDispatcherLockAtDpcLevel();
417
418 /* Insert the thread into the process list */
419 InsertTailList(&Process->ThreadListHead, &Thread->ThreadListEntry);
420
421 /* Increase the stack count */
422 ASSERT(Process->StackCount != MAXULONG_PTR);
423 Process->StackCount++;
424
425 /* Release locks and return */
426 KiReleaseDispatcherLockFromDpcLevel();
427 KiReleaseProcessLock(&LockHandle);
428 }
429
430 VOID
431 NTAPI
432 KiSuspendRundown(IN PKAPC Apc)
433 {
434 /* Does nothing */
435 UNREFERENCED_PARAMETER(Apc);
436 }
437
438 VOID
439 NTAPI
440 KiSuspendNop(IN PKAPC Apc,
441 IN PKNORMAL_ROUTINE *NormalRoutine,
442 IN PVOID *NormalContext,
443 IN PVOID *SystemArgument1,
444 IN PVOID *SystemArgument2)
445 {
446 /* Does nothing */
447 UNREFERENCED_PARAMETER(Apc);
448 UNREFERENCED_PARAMETER(NormalRoutine);
449 UNREFERENCED_PARAMETER(NormalContext);
450 UNREFERENCED_PARAMETER(SystemArgument1);
451 UNREFERENCED_PARAMETER(SystemArgument2);
452 }
453
454 VOID
455 NTAPI
456 KiSuspendThread(IN PVOID NormalContext,
457 IN PVOID SystemArgument1,
458 IN PVOID SystemArgument2)
459 {
460 /* Non-alertable kernel-mode suspended wait */
461 KeWaitForSingleObject(&KeGetCurrentThread()->SuspendSemaphore,
462 Suspended,
463 KernelMode,
464 FALSE,
465 NULL);
466 }
467
468 NTSTATUS
469 NTAPI
470 KeSuspendThread(PKTHREAD Thread)
471 {
472 KLOCK_QUEUE_HANDLE ApcLock;
473 ULONG PreviousCount;
474 ASSERT_THREAD(Thread);
475 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
476
477 /* Lock the APC Queue */
478 KiAcquireApcLock(Thread, &ApcLock);
479
480 /* Save the Old Count */
481 PreviousCount = Thread->SuspendCount;
482
483 /* Handle the maximum */
484 if (PreviousCount == MAXIMUM_SUSPEND_COUNT)
485 {
486 /* Raise an exception */
487 KiReleaseApcLock(&ApcLock);
488 RtlRaiseStatus(STATUS_SUSPEND_COUNT_EXCEEDED);
489 }
490
491 /* Should we bother to queue at all? */
492 if (Thread->ApcQueueable)
493 {
494 /* Increment the suspend count */
495 Thread->SuspendCount++;
496
497 /* Check if we should suspend it */
498 if (!(PreviousCount) && !(Thread->FreezeCount))
499 {
500 /* Is the APC already inserted? */
501 if (!Thread->SuspendApc.Inserted)
502 {
503 /* Not inserted, insert it */
504 Thread->SuspendApc.Inserted = TRUE;
505 KiInsertQueueApc(&Thread->SuspendApc, IO_NO_INCREMENT);
506 }
507 else
508 {
509 /* Lock the dispatcher */
510 KiAcquireDispatcherLockAtDpcLevel();
511
512 /* Unsignal the semaphore, the APC was already inserted */
513 Thread->SuspendSemaphore.Header.SignalState--;
514
515 /* Release the dispatcher */
516 KiReleaseDispatcherLockFromDpcLevel();
517 }
518 }
519 }
520
521 /* Release Lock and return the Old State */
522 KiReleaseApcLockFromDpcLevel(&ApcLock);
523 KiExitDispatcher(ApcLock.OldIrql);
524 return PreviousCount;
525 }
526
527 BOOLEAN
528 NTAPI
529 KeTestAlertThread(IN KPROCESSOR_MODE AlertMode)
530 {
531 PKTHREAD Thread = KeGetCurrentThread();
532 BOOLEAN OldState;
533 KLOCK_QUEUE_HANDLE ApcLock;
534 ASSERT_THREAD(Thread);
535 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
536
537 /* Lock the Dispatcher Database and the APC Queue */
538 KiAcquireApcLock(Thread, &ApcLock);
539 KiAcquireDispatcherLockAtDpcLevel();
540
541 /* Save the old State */
542 OldState = Thread->Alerted[AlertMode];
543
544 /* Check the Thread is alerted */
545 if (OldState)
546 {
547 /* Disable alert for this mode */
548 Thread->Alerted[AlertMode] = FALSE;
549 }
550 else if ((AlertMode != KernelMode) &&
551 (!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])))
552 {
553 /* If the mode is User and the Queue isn't empty, set Pending */
554 Thread->ApcState.UserApcPending = TRUE;
555 }
556
557 /* Release Locks and return the Old State */
558 KiReleaseDispatcherLockFromDpcLevel();
559 KiReleaseApcLockFromDpcLevel(&ApcLock);
560 KiExitDispatcher(ApcLock.OldIrql);
561 return OldState;
562 }
563
564 NTSTATUS
565 NTAPI
566 KeInitThread(IN OUT PKTHREAD Thread,
567 IN PVOID KernelStack,
568 IN PKSYSTEM_ROUTINE SystemRoutine,
569 IN PKSTART_ROUTINE StartRoutine,
570 IN PVOID StartContext,
571 IN PCONTEXT Context,
572 IN PVOID Teb,
573 IN PKPROCESS Process)
574 {
575 BOOLEAN AllocatedStack = FALSE;
576 ULONG i;
577 PKWAIT_BLOCK TimerWaitBlock;
578 PKTIMER Timer;
579 NTSTATUS Status;
580
581 /* Initalize the Dispatcher Header */
582 KeInitializeDispatcherHeader(&Thread->DispatcherHeader,
583 ThreadObject,
584 sizeof(KTHREAD) / sizeof(LONG),
585 FALSE);
586
587 /* Initialize the Mutant List */
588 InitializeListHead(&Thread->MutantListHead);
589
590 /* Initialize the wait blocks */
591 for (i = 0; i< (THREAD_WAIT_OBJECTS + 1); i++)
592 {
593 /* Put our pointer */
594 Thread->WaitBlock[i].Thread = Thread;
595 }
596
597 /* Set swap settings */
598 Thread->EnableStackSwap = FALSE;//TRUE;
599 Thread->IdealProcessor = 1;
600 Thread->SwapBusy = FALSE;
601 Thread->AdjustReason = 0;
602
603 /* Initialize the lock */
604 KeInitializeSpinLock(&Thread->ThreadLock);
605
606 /* Setup the Service Descriptor Table for Native Calls */
607 Thread->ServiceTable = KeServiceDescriptorTable;
608
609 /* Setup APC Fields */
610 InitializeListHead(&Thread->ApcState.ApcListHead[0]);
611 InitializeListHead(&Thread->ApcState.ApcListHead[1]);
612 Thread->ApcState.Process = Process;
613 Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState;
614 Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState;
615 Thread->ApcStateIndex = OriginalApcEnvironment;
616 Thread->ApcQueueable = TRUE;
617 KeInitializeSpinLock(&Thread->ApcQueueLock);
618
619 /* Initialize the Suspend APC */
620 KeInitializeApc(&Thread->SuspendApc,
621 Thread,
622 OriginalApcEnvironment,
623 KiSuspendNop,
624 KiSuspendRundown,
625 KiSuspendThread,
626 KernelMode,
627 NULL);
628
629 /* Initialize the Suspend Semaphore */
630 KeInitializeSemaphore(&Thread->SuspendSemaphore, 0, 2);
631
632 /* Setup the timer */
633 Timer = &Thread->Timer;
634 KeInitializeTimer(Timer);
635 TimerWaitBlock = &Thread->WaitBlock[TIMER_WAIT_BLOCK];
636 TimerWaitBlock->Object = Timer;
637 TimerWaitBlock->WaitKey = STATUS_TIMEOUT;
638 TimerWaitBlock->WaitType = WaitAny;
639 TimerWaitBlock->NextWaitBlock = NULL;
640
641 /* Link the two wait lists together */
642 TimerWaitBlock->WaitListEntry.Flink = &Timer->Header.WaitListHead;
643 TimerWaitBlock->WaitListEntry.Blink = &Timer->Header.WaitListHead;
644
645 /* Set the TEB */
646 Thread->Teb = Teb;
647
648 /* Check if we have a kernel stack */
649 if (!KernelStack)
650 {
651 /* We don't, allocate one */
652 KernelStack = (PVOID)((ULONG_PTR)MmCreateKernelStack(FALSE) +
653 KERNEL_STACK_SIZE);
654 if (!KernelStack) return STATUS_INSUFFICIENT_RESOURCES;
655
656 /* Remember for later */
657 AllocatedStack = TRUE;
658 }
659
660 /* Set the Thread Stacks */
661 Thread->InitialStack = (PCHAR)KernelStack;
662 Thread->StackBase = (PCHAR)KernelStack;
663 Thread->StackLimit = (ULONG_PTR)KernelStack - KERNEL_STACK_SIZE;
664 Thread->KernelStackResident = TRUE;
665
666 /* ROS Mm HACK */
667 MmUpdatePageDir((PEPROCESS)Process,
668 (PVOID)Thread->StackLimit,
669 KERNEL_STACK_SIZE);
670 MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread, sizeof(ETHREAD));
671
672 /* Enter SEH to avoid crashes due to user mode */
673 Status = STATUS_SUCCESS;
674 _SEH_TRY
675 {
676 /* Initalize the Thread Context */
677 Ke386InitThreadWithContext(Thread,
678 SystemRoutine,
679 StartRoutine,
680 StartContext,
681 Context);
682 }
683 _SEH_HANDLE
684 {
685 /* Set failure status */
686 Status = STATUS_UNSUCCESSFUL;
687
688 /* Check if a stack was allocated */
689 if (AllocatedStack)
690 {
691 /* Delete the stack */
692 MmDeleteKernelStack(Thread->StackBase, FALSE);
693 Thread->InitialStack = NULL;
694 }
695 }
696 _SEH_END;
697
698 /* Set the Thread to initalized */
699 Thread->State = Initialized;
700 return Status;
701 }
702
703 VOID
704 NTAPI
705 KeInitializeThread(IN PKPROCESS Process,
706 IN OUT PKTHREAD Thread,
707 IN PKSYSTEM_ROUTINE SystemRoutine,
708 IN PKSTART_ROUTINE StartRoutine,
709 IN PVOID StartContext,
710 IN PCONTEXT Context,
711 IN PVOID Teb,
712 IN PVOID KernelStack)
713 {
714 /* Initailize and start the thread on success */
715 if (NT_SUCCESS(KeInitThread(Thread,
716 KernelStack,
717 SystemRoutine,
718 StartRoutine,
719 StartContext,
720 Context,
721 Teb,
722 Process)))
723 {
724 /* Start it */
725 KeStartThread(Thread);
726 }
727 }
728
729 VOID
730 NTAPI
731 KeUninitThread(IN PKTHREAD Thread)
732 {
733 /* Delete the stack */
734 MmDeleteKernelStack(Thread->StackBase, FALSE);
735 Thread->InitialStack = NULL;
736 }
737
738 /* PUBLIC FUNCTIONS **********************************************************/
739
740 /*
741 * @unimplemented
742 */
743 VOID
744 NTAPI
745 KeCapturePersistentThreadState(IN PVOID CurrentThread,
746 IN ULONG Setting1,
747 IN ULONG Setting2,
748 IN ULONG Setting3,
749 IN ULONG Setting4,
750 IN ULONG Setting5,
751 IN PVOID ThreadState)
752 {
753 UNIMPLEMENTED;
754 }
755
756 /*
757 * @implemented
758 */
759 #undef KeGetCurrentThread
760 PKTHREAD
761 NTAPI
762 KeGetCurrentThread(VOID)
763 {
764 /* Return the current thread on this PCR */
765 return ((PKIPCR)KeGetPcr())->PrcbData.CurrentThread;
766 }
767
768 /*
769 * @implemented
770 */
771 KPROCESSOR_MODE
772 NTAPI
773 KeGetPreviousMode(VOID)
774 {
775 /* Return the previous mode of this thread */
776 return KeGetCurrentThread()->PreviousMode;
777 }
778
779 /*
780 * @implemented
781 */
782 ULONG
783 NTAPI
784 KeQueryRuntimeThread(IN PKTHREAD Thread,
785 OUT PULONG UserTime)
786 {
787 ASSERT_THREAD(Thread);
788
789 /* Return the User Time */
790 *UserTime = Thread->UserTime;
791
792 /* Return the Kernel Time */
793 return Thread->KernelTime;
794 }
795
796 /*
797 * @implemented
798 */
799 BOOLEAN
800 NTAPI
801 KeSetKernelStackSwapEnable(IN BOOLEAN Enable)
802 {
803 BOOLEAN PreviousState;
804 PKTHREAD Thread = KeGetCurrentThread();
805
806 /* Save Old State */
807 PreviousState = Thread->EnableStackSwap;
808
809 /* Set New State */
810 Thread->EnableStackSwap = Enable;
811
812 /* Return Old State */
813 return PreviousState;
814 }
815
816 /*
817 * @implemented
818 */
819 KPRIORITY
820 NTAPI
821 KeQueryPriorityThread(IN PKTHREAD Thread)
822 {
823 ASSERT_THREAD(Thread);
824
825 /* Return the current priority */
826 return Thread->Priority;
827 }
828
829 /*
830 * @implemented
831 */
832 VOID
833 NTAPI
834 KeRevertToUserAffinityThread(VOID)
835 {
836 KIRQL OldIrql;
837 PKPRCB Prcb;
838 PKTHREAD NextThread, CurrentThread = KeGetCurrentThread();
839 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
840 ASSERT(CurrentThread->SystemAffinityActive != FALSE);
841
842 /* Lock the Dispatcher Database */
843 OldIrql = KiAcquireDispatcherLock();
844
845 /* Set the user affinity and processor and disable system affinity */
846 CurrentThread->Affinity = CurrentThread->UserAffinity;
847 CurrentThread->IdealProcessor = CurrentThread->UserIdealProcessor;
848 CurrentThread->SystemAffinityActive = FALSE;
849
850 /* Get the current PRCB and check if it doesn't match this affinity */
851 Prcb = KeGetCurrentPrcb();
852 if (!(Prcb->SetMember & CurrentThread->Affinity))
853 {
854 /* Lock the PRCB */
855 KiAcquirePrcbLock(Prcb);
856
857 #ifdef NEW_SCHEDULER
858 /* Check if there's no next thread scheduled */
859 if (!Prcb->NextThread)
860 {
861 /* Select a new thread and set it on standby */
862 NextThread = KiSelectNextThread(Prcb);
863 NextThread->State = Standby;
864 Prcb->NextThread = NextThread;
865 }
866 #else
867 /* We need to dispatch a new thread */
868 NextThread = NULL;
869 CurrentThread->WaitIrql = OldIrql;
870 KiDispatchThreadNoLock(Ready);
871 KeLowerIrql(OldIrql);
872 return;
873 #endif
874
875 /* Release the PRCB lock */
876 KiReleasePrcbLock(Prcb);
877 }
878
879 /* Unlock dispatcher database */
880 KiReleaseDispatcherLock(OldIrql);
881 }
882
883 /*
884 * @implemented
885 */
886 UCHAR
887 NTAPI
888 KeSetIdealProcessorThread(IN PKTHREAD Thread,
889 IN UCHAR Processor)
890 {
891 CCHAR OldIdealProcessor;
892 KIRQL OldIrql;
893 ASSERT(Processor <= MAXIMUM_PROCESSORS);
894
895 /* Lock the Dispatcher Database */
896 OldIrql = KiAcquireDispatcherLock();
897
898 /* Save Old Ideal Processor */
899 OldIdealProcessor = Thread->UserIdealProcessor;
900
901 /* Make sure a valid CPU was given */
902 if (Processor < MAXIMUM_PROCESSORS)
903 {
904 /* Check if the user ideal CPU is in the affinity */
905 if (Thread->UserIdealProcessor & AFFINITY_MASK(Processor))
906 {
907 /* Set the ideal processor */
908 Thread->IdealProcessor = Processor;
909
910 /* Check if system affinity is used */
911 if (!Thread->SystemAffinityActive)
912 {
913 /* It's not, so update the user CPU too */
914 Thread->UserIdealProcessor = Processor;
915 }
916 }
917 }
918
919 /* Release dispatcher lock and return the old ideal CPU */
920 KiReleaseDispatcherLock(OldIrql);
921 return OldIdealProcessor;
922 }
923
924 /*
925 * @implemented
926 */
927 VOID
928 NTAPI
929 KeSetSystemAffinityThread(IN KAFFINITY Affinity)
930 {
931 KIRQL OldIrql;
932 PKPRCB Prcb;
933 #ifdef CONFIG_SMP
934 ULONG AffinitySet, NodeMask;
935 #endif
936 PKTHREAD NextThread, CurrentThread = KeGetCurrentThread();
937 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
938 ASSERT((Affinity & KeActiveProcessors) != 0);
939
940 /* Lock the Dispatcher Database */
941 OldIrql = KiAcquireDispatcherLock();
942
943 /* Restore the affinity and enable system affinity */
944 CurrentThread->Affinity = Affinity;
945 CurrentThread->SystemAffinityActive = TRUE;
946
947 /* Check if the ideal processor is part of the affinity */
948 #ifdef CONFIG_SMP
949 if (!(Affinity & AFFINITY_MASK(CurrentThread->IdealProcessor)))
950 {
951 /* It's not! Get the PRCB */
952 Prcb = KiProcessorBlock[CurrentThread->IdealProcessor];
953
954 /* Calculate the affinity set */
955 AffinitySet = KeActiveProcessors & Affinity;
956 NodeMask = Prcb->ParentNode->ProcessorMask & AffinitySet;
957 if (NodeMask)
958 {
959 /* Use the Node set instead */
960 AffinitySet = NodeMask;
961 }
962
963 /* Calculate the ideal CPU from the affinity set */
964 BitScanReverse(&NodeMask, AffinitySet);
965 CurrentThread->IdealProcessor = (UCHAR)NodeMask;
966 }
967 #endif
968
969 /* Get the current PRCB and check if it doesn't match this affinity */
970 Prcb = KeGetCurrentPrcb();
971 if (!(Prcb->SetMember & CurrentThread->Affinity))
972 {
973 /* Lock the PRCB */
974 KiAcquirePrcbLock(Prcb);
975
976 #ifdef NEW_SCHEDULER
977 /* Check if there's no next thread scheduled */
978 if (!Prcb->NextThread)
979 {
980 /* Select a new thread and set it on standby */
981 NextThread = KiSelectNextThread(Prcb);
982 NextThread->State = Standby;
983 Prcb->NextThread = NextThread;
984 }
985 #else
986 /* We need to dispatch a new thread */
987 NextThread = NULL;
988 CurrentThread->WaitIrql = OldIrql;
989 KiDispatchThreadNoLock(Ready);
990 KeLowerIrql(OldIrql);
991 return;
992 #endif
993
994 /* Release the PRCB lock */
995 KiReleasePrcbLock(Prcb);
996 }
997
998 /* Unlock dispatcher database */
999 KiReleaseDispatcherLock(OldIrql);
1000 }
1001
1002 LONG
1003 STDCALL
1004 KeQueryBasePriorityThread(IN PKTHREAD Thread)
1005 {
1006 LONG BasePriorityIncrement;
1007 KIRQL OldIrql;
1008 PKPROCESS Process;
1009
1010 /* Lock the Dispatcher Database */
1011 OldIrql = KeAcquireDispatcherDatabaseLock();
1012
1013 /* Get the Process */
1014 Process = Thread->ApcStatePointer[0]->Process;
1015
1016 /* Calculate the BPI */
1017 BasePriorityIncrement = Thread->BasePriority - Process->BasePriority;
1018
1019 /* If saturation occured, return the SI instead */
1020 if (Thread->Saturation) BasePriorityIncrement = (HIGH_PRIORITY + 1) / 2 *
1021 Thread->Saturation;
1022
1023 /* Release Lock */
1024 KeReleaseDispatcherDatabaseLock(OldIrql);
1025
1026 /* Return Increment */
1027 return BasePriorityIncrement;
1028 }
1029
1030 /*
1031 * @implemented
1032 */
1033 LONG
1034 STDCALL
1035 KeSetBasePriorityThread(PKTHREAD Thread,
1036 LONG Increment)
1037 {
1038 KIRQL OldIrql;
1039 PKPROCESS Process;
1040 KPRIORITY Priority;
1041 KPRIORITY CurrentBasePriority;
1042 KPRIORITY BasePriority;
1043 BOOLEAN Released = FALSE;
1044 LONG CurrentIncrement;
1045
1046 /* Lock the Dispatcher Database */
1047 OldIrql = KeAcquireDispatcherDatabaseLock();
1048
1049 /* Get the process and calculate current BP and BPI */
1050 Process = Thread->ApcStatePointer[0]->Process;
1051 CurrentBasePriority = Thread->BasePriority;
1052 CurrentIncrement = CurrentBasePriority - Process->BasePriority;
1053
1054 /* Change to use the SI if Saturation was used */
1055 if (Thread->Saturation) CurrentIncrement = (HIGH_PRIORITY + 1) / 2 *
1056 Thread->Saturation;
1057
1058 /* Now check if saturation is being used for the new value */
1059 if (abs(Increment) >= ((HIGH_PRIORITY + 1) / 2))
1060 {
1061 /* Check if we need positive or negative saturation */
1062 Thread->Saturation = (Increment > 0) ? 1 : -1;
1063 }
1064
1065 /* Normalize the Base Priority */
1066 BasePriority = Process->BasePriority + Increment;
1067 if (Process->BasePriority >= LOW_REALTIME_PRIORITY)
1068 {
1069 /* Check if it's too low */
1070 if (BasePriority < LOW_REALTIME_PRIORITY)
1071 BasePriority = LOW_REALTIME_PRIORITY;
1072
1073 /* Check if it's too high */
1074 if (BasePriority > HIGH_PRIORITY) BasePriority = HIGH_PRIORITY;
1075
1076 /* We are at RTP, so use the raw BP */
1077 Priority = BasePriority;
1078 }
1079 else
1080 {
1081 /* Check if it's entering RTP */
1082 if (BasePriority >= LOW_REALTIME_PRIORITY)
1083 BasePriority = LOW_REALTIME_PRIORITY - 1;
1084
1085 /* Check if it's too low */
1086 if (BasePriority <= LOW_PRIORITY)
1087 BasePriority = 1;
1088
1089 /* If Saturation is used, then use the raw BP */
1090 if (Thread->Saturation)
1091 {
1092 Priority = BasePriority;
1093 }
1094 else
1095 {
1096 /* Calculate the new priority */
1097 Priority = Thread->Priority + (BasePriority - CurrentBasePriority)-
1098 Thread->PriorityDecrement;
1099
1100 /* Make sure it won't enter RTP ranges */
1101 if (Priority >= LOW_REALTIME_PRIORITY)
1102 Priority = LOW_REALTIME_PRIORITY - 1;
1103 }
1104 }
1105
1106 /* Finally set the new base priority */
1107 Thread->BasePriority = BasePriority;
1108
1109 /* Reset the decrements */
1110 Thread->PriorityDecrement = 0;
1111
1112 /* If the priority will change, reset quantum and change it for real */
1113 if (Priority != Thread->Priority)
1114 {
1115 Thread->Quantum = Thread->QuantumReset;
1116 KiSetPriorityThread(Thread, Priority, &Released);
1117 }
1118
1119 /* Release Lock if needed */
1120 if (!Released)
1121 {
1122 KeReleaseDispatcherDatabaseLock(OldIrql);
1123 }
1124 else
1125 {
1126 KeLowerIrql(OldIrql);
1127 }
1128
1129 /* Return the Old Increment */
1130 return CurrentIncrement;
1131 }
1132
1133 /*
1134 * @implemented
1135 */
1136 KPRIORITY
1137 STDCALL
1138 KeSetPriorityThread(PKTHREAD Thread,
1139 KPRIORITY Priority)
1140 {
1141 KPRIORITY OldPriority;
1142 BOOLEAN Released = FALSE;
1143 KIRQL OldIrql;
1144
1145 /* Lock the Dispatcher Database */
1146 OldIrql = KeAcquireDispatcherDatabaseLock();
1147
1148 /* Save the old Priority */
1149 OldPriority = Thread->Priority;
1150
1151 /* Reset the Quantum and Decrements */
1152 Thread->Quantum = Thread->QuantumReset;
1153 Thread->PriorityDecrement = 0;
1154
1155 /* Set the new Priority */
1156 KiSetPriorityThread(Thread, Priority, &Released);
1157
1158 /* Release Lock if needed */
1159 if (!Released)
1160 {
1161 KeReleaseDispatcherDatabaseLock(OldIrql);
1162 }
1163 else
1164 {
1165 KeLowerIrql(OldIrql);
1166 }
1167
1168 /* Return Old Priority */
1169 return OldPriority;
1170 }
1171
1172 /*
1173 * @implemented
1174 *
1175 * Sets thread's affinity
1176 */
1177 KAFFINITY
1178 STDCALL
1179 KeSetAffinityThread(PKTHREAD Thread,
1180 KAFFINITY Affinity)
1181 {
1182 KIRQL OldIrql;
1183 KAFFINITY OldAffinity;
1184 BOOLEAN Released;
1185
1186 DPRINT("KeSetAffinityThread(Thread %x, Affinity %x)\n", Thread, Affinity);
1187
1188 OldIrql = KeAcquireDispatcherDatabaseLock();
1189
1190 /* Call the internal function */
1191 OldAffinity = KiSetAffinityThread(Thread, Affinity, &Released);
1192
1193 /* Release Lock if needed */
1194 if (!Released)
1195 {
1196 KeReleaseDispatcherDatabaseLock(OldIrql);
1197 }
1198 else
1199 {
1200 KeLowerIrql(OldIrql);
1201 }
1202
1203 return OldAffinity;
1204 }
1205
1206 /*
1207 * @implemented
1208 */
1209 VOID
1210 NTAPI
1211 KeTerminateThread(IN KPRIORITY Increment)
1212 {
1213 PLIST_ENTRY *ListHead;
1214 PETHREAD Entry, SavedEntry;
1215 PETHREAD *ThreadAddr;
1216 KLOCK_QUEUE_HANDLE LockHandle;
1217 PKTHREAD Thread = KeGetCurrentThread();
1218 PKPROCESS Process = Thread->ApcState.Process;
1219 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
1220
1221 /* Lock the process */
1222 KiAcquireProcessLock(Process, &LockHandle);
1223
1224 /* Make sure we won't get Swapped */
1225 KiSetThreadSwapBusy(Thread);
1226
1227 /* Save the Kernel and User Times */
1228 Process->KernelTime += Thread->KernelTime;
1229 Process->UserTime += Thread->UserTime;
1230
1231 /* Get the current entry and our Port */
1232 Entry = (PETHREAD)PspReaperListHead.Flink;
1233 ThreadAddr = &((PETHREAD)Thread)->ReaperLink;
1234
1235 /* Add it to the reaper's list */
1236 do
1237 {
1238 /* Get the list head */
1239 ListHead = &PspReaperListHead.Flink;
1240
1241 /* Link ourselves */
1242 *ThreadAddr = Entry;
1243 SavedEntry = Entry;
1244
1245 /* Now try to do the exchange */
1246 Entry = InterlockedCompareExchangePointer(ListHead, ThreadAddr, Entry);
1247
1248 /* Break out if the change was succesful */
1249 } while (Entry != SavedEntry);
1250
1251 /* Acquire the dispatcher lock */
1252 KiAcquireDispatcherLockAtDpcLevel();
1253
1254 /* Check if the reaper wasn't active */
1255 if (!Entry)
1256 {
1257 /* Activate it as a work item, directly through its Queue */
1258 KiInsertQueue(&ExWorkerQueue[HyperCriticalWorkQueue].WorkerQueue,
1259 &PspReaperWorkItem.List,
1260 FALSE);
1261 }
1262
1263 /* Check the thread has an associated queue */
1264 if (Thread->Queue)
1265 {
1266 /* Remove it from the list, and handle the queue */
1267 RemoveEntryList(&Thread->QueueListEntry);
1268 KiWakeQueue(Thread->Queue);
1269 }
1270
1271 /* Signal the thread */
1272 Thread->DispatcherHeader.SignalState = TRUE;
1273 if (IsListEmpty(&Thread->DispatcherHeader.WaitListHead) != TRUE)
1274 {
1275 /* Satisfy waits */
1276 KiWaitTest((PVOID)Thread, Increment);
1277 }
1278
1279 /* Remove the thread from the list */
1280 RemoveEntryList(&Thread->ThreadListEntry);
1281
1282 /* Release the process lock */
1283 KiReleaseProcessLock(&LockHandle);
1284
1285 /* Set us as terminated, decrease the Process's stack count */
1286 Thread->State = Terminated;
1287
1288 /* Decrease stack count */
1289 ASSERT(Process->StackCount != 0);
1290 ASSERT(Process->State == ProcessInMemory);
1291 Process->StackCount--;
1292 if (!Process->StackCount)
1293 {
1294 /* FIXME: Swap stacks */
1295 }
1296
1297 /* Rundown arch-specific parts */
1298 KiRundownThread(Thread);
1299
1300 /* Swap to a new thread */
1301 KiReleaseDispatcherLockFromDpcLevel();
1302 KiSwapThread(Thread, KeGetCurrentPrcb());
1303 }