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