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)
9 /* INCLUDES ******************************************************************/
15 extern EX_WORK_QUEUE ExWorkerQueue
[MaximumWorkQueue
];
16 extern LIST_ENTRY PspReaperListHead
;
18 ULONG KiMask32Array
[MAXIMUM_PRIORITY
] =
20 0x1, 0x2, 0x4, 0x8, 0x10, 0x20,
21 0x40, 0x80, 0x100, 0x200, 0x400, 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
28 /* FUNCTIONS *****************************************************************/
32 KeFindNextRightSetAffinity(IN UCHAR Number
,
38 /* Calculate the mask */
39 Bit
= (AFFINITY_MASK(Number
) - 1) & Set
;
41 /* If it's 0, use the one we got */
44 /* Now find the right set and return it */
45 BitScanReverse(&Result
, Bit
);
51 KeReadStateThread(IN PKTHREAD Thread
)
53 ASSERT_THREAD(Thread
);
55 /* Return signal state */
56 return (BOOLEAN
)Thread
->Header
.SignalState
;
61 KeQueryBasePriorityThread(IN PKTHREAD Thread
)
66 ASSERT_THREAD(Thread
);
67 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
69 /* Raise IRQL to synch level */
70 OldIrql
= KeRaiseIrqlToSynchLevel();
73 KiAcquireThreadLock(Thread
);
76 Process
= Thread
->ApcStatePointer
[0]->Process
;
78 /* Calculate the base increment */
79 BaseIncrement
= Thread
->BasePriority
- Process
->BasePriority
;
81 /* If saturation occured, return the saturation increment instead */
82 if (Thread
->Saturation
) BaseIncrement
= (HIGH_PRIORITY
+ 1) / 2 *
85 /* Release thread lock */
86 KiReleaseThreadLock(Thread
);
88 /* Lower IRQl and return Increment */
95 KeSetDisableBoostThread(IN OUT PKTHREAD Thread
,
98 ASSERT_THREAD(Thread
);
100 /* Check if we're enabling or disabling */
104 return InterlockedBitTestAndSet(&Thread
->ThreadFlags
, 1);
109 return InterlockedBitTestAndReset(&Thread
->ThreadFlags
, 1);
115 KeReadyThread(IN PKTHREAD Thread
)
118 ASSERT_THREAD(Thread
);
119 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
121 /* Lock the Dispatcher Database */
122 OldIrql
= KiAcquireDispatcherLock();
124 /* Make the thread ready */
125 KiReadyThread(Thread
);
127 /* Unlock dispatcher database */
128 KiReleaseDispatcherLock(OldIrql
);
133 KeAlertResumeThread(IN PKTHREAD Thread
)
136 KLOCK_QUEUE_HANDLE ApcLock
;
137 ASSERT_THREAD(Thread
);
138 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
140 /* Lock the Dispatcher Database and the APC Queue */
141 KiAcquireApcLock(Thread
, &ApcLock
);
142 KiAcquireDispatcherLockAtDpcLevel();
144 /* Return if Thread is already alerted. */
145 if (!Thread
->Alerted
[KernelMode
])
147 /* If it's Blocked, unblock if it we should */
148 if ((Thread
->State
== Waiting
) && (Thread
->Alertable
))
151 KiUnwaitThread(Thread
, STATUS_ALERTED
, THREAD_ALERT_INCREMENT
);
155 /* If not, simply Alert it */
156 Thread
->Alerted
[KernelMode
] = TRUE
;
160 /* Save the old Suspend Count */
161 PreviousCount
= Thread
->SuspendCount
;
163 /* If the thread is suspended, decrease one of the suspend counts */
166 /* Decrease count. If we are now zero, unwait it completely */
167 Thread
->SuspendCount
--;
168 if (!(Thread
->SuspendCount
) && !(Thread
->FreezeCount
))
170 /* Signal and satisfy */
171 Thread
->SuspendSemaphore
.Header
.SignalState
++;
172 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
176 /* Release Locks and return the Old State */
177 KiReleaseDispatcherLockFromDpcLevel();
178 KiReleaseApcLockFromDpcLevel(&ApcLock
);
179 KiExitDispatcher(ApcLock
.OldIrql
);
180 return PreviousCount
;
185 KeAlertThread(IN PKTHREAD Thread
,
186 IN KPROCESSOR_MODE AlertMode
)
188 BOOLEAN PreviousState
;
189 KLOCK_QUEUE_HANDLE ApcLock
;
190 ASSERT_THREAD(Thread
);
191 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
193 /* Lock the Dispatcher Database and the APC Queue */
194 KiAcquireApcLock(Thread
, &ApcLock
);
195 KiAcquireDispatcherLockAtDpcLevel();
197 /* Save the Previous State */
198 PreviousState
= Thread
->Alerted
[AlertMode
];
200 /* Check if it's already alerted */
203 /* Check if the thread is alertable, and blocked in the given mode */
204 if ((Thread
->State
== Waiting
) &&
205 (Thread
->Alertable
) &&
206 (AlertMode
<= Thread
->WaitMode
))
208 /* Abort the wait to alert the thread */
209 KiUnwaitThread(Thread
, STATUS_ALERTED
, THREAD_ALERT_INCREMENT
);
213 /* Otherwise, merely set the alerted state */
214 Thread
->Alerted
[AlertMode
] = TRUE
;
218 /* Release the Dispatcher Lock */
219 KiReleaseDispatcherLockFromDpcLevel();
220 KiReleaseApcLockFromDpcLevel(&ApcLock
);
221 KiExitDispatcher(ApcLock
.OldIrql
);
223 /* Return the old state */
224 return PreviousState
;
229 KeForceResumeThread(IN PKTHREAD Thread
)
231 KLOCK_QUEUE_HANDLE ApcLock
;
233 ASSERT_THREAD(Thread
);
234 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
236 /* Lock the APC Queue */
237 KiAcquireApcLock(Thread
, &ApcLock
);
239 /* Save the old Suspend Count */
240 PreviousCount
= Thread
->SuspendCount
+ Thread
->FreezeCount
;
242 /* If the thread is suspended, wake it up!!! */
245 /* Unwait it completely */
246 Thread
->SuspendCount
= 0;
247 Thread
->FreezeCount
= 0;
249 /* Lock the dispatcher */
250 KiAcquireDispatcherLockAtDpcLevel();
252 /* Signal and satisfy */
253 Thread
->SuspendSemaphore
.Header
.SignalState
++;
254 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
256 /* Release the dispatcher */
257 KiReleaseDispatcherLockFromDpcLevel();
260 /* Release Lock and return the Old State */
261 KiReleaseApcLockFromDpcLevel(&ApcLock
);
262 KiExitDispatcher(ApcLock
.OldIrql
);
263 return PreviousCount
;
268 KeFreezeAllThreads(VOID
)
270 KLOCK_QUEUE_HANDLE LockHandle
, ApcLock
;
271 PKTHREAD Current
, CurrentThread
= KeGetCurrentThread();
272 PKPROCESS Process
= CurrentThread
->ApcState
.Process
;
273 PLIST_ENTRY ListHead
, NextEntry
;
275 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
277 /* Lock the process */
278 KiAcquireProcessLock(Process
, &LockHandle
);
280 /* If someone is already trying to free us, try again */
281 while (CurrentThread
->FreezeCount
)
283 /* Release and re-acquire the process lock so the APC will go through */
284 KiReleaseProcessLock(&LockHandle
);
285 KiAcquireProcessLock(Process
, &LockHandle
);
288 /* Enter a critical region */
289 KeEnterCriticalRegion();
291 /* Loop the Process's Threads */
292 ListHead
= &Process
->ThreadListHead
;
293 NextEntry
= ListHead
->Flink
;
296 /* Get the current thread */
297 Current
= CONTAINING_RECORD(NextEntry
, KTHREAD
, ThreadListEntry
);
300 KiAcquireApcLockAtDpcLevel(Current
, &ApcLock
);
302 /* Make sure it's not ours, and check if APCs are enabled */
303 if ((Current
!= CurrentThread
) && (Current
->ApcQueueable
))
306 OldCount
= Current
->SuspendCount
;
307 ASSERT(OldCount
!= MAXIMUM_SUSPEND_COUNT
);
309 /* Increase the freeze count */
310 Current
->FreezeCount
++;
312 /* Make sure it wasn't already suspended */
313 if (!(OldCount
) && !(Current
->SuspendCount
))
315 /* Did we already insert it? */
316 if (!Current
->SuspendApc
.Inserted
)
319 Current
->SuspendApc
.Inserted
= TRUE
;
320 KiInsertQueueApc(&Current
->SuspendApc
, IO_NO_INCREMENT
);
324 /* Lock the dispatcher */
325 KiAcquireDispatcherLockAtDpcLevel();
327 /* Unsignal the semaphore, the APC was already inserted */
328 Current
->SuspendSemaphore
.Header
.SignalState
--;
330 /* Release the dispatcher */
331 KiReleaseDispatcherLockFromDpcLevel();
336 /* Release the APC lock */
337 KiReleaseApcLockFromDpcLevel(&ApcLock
);
339 /* Move to the next thread */
340 NextEntry
= NextEntry
->Flink
;
341 } while (NextEntry
!= ListHead
);
343 /* Release the process lock and exit the dispatcher */
344 KiReleaseProcessLockFromDpcLevel(&LockHandle
);
345 KiExitDispatcher(LockHandle
.OldIrql
);
350 KeResumeThread(IN PKTHREAD Thread
)
352 KLOCK_QUEUE_HANDLE ApcLock
;
354 ASSERT_THREAD(Thread
);
355 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
357 /* Lock the APC Queue */
358 KiAcquireApcLock(Thread
, &ApcLock
);
360 /* Save the Old Count */
361 PreviousCount
= Thread
->SuspendCount
;
363 /* Check if it existed */
366 /* Decrease the suspend count */
367 Thread
->SuspendCount
--;
369 /* Check if the thrad is still suspended or not */
370 if ((!Thread
->SuspendCount
) && (!Thread
->FreezeCount
))
372 /* Acquire the dispatcher lock */
373 KiAcquireDispatcherLockAtDpcLevel();
375 /* Signal the Suspend Semaphore */
376 Thread
->SuspendSemaphore
.Header
.SignalState
++;
377 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
379 /* Release the dispatcher lock */
380 KiReleaseDispatcherLockFromDpcLevel();
384 /* Release APC Queue lock and return the Old State */
385 KiReleaseApcLockFromDpcLevel(&ApcLock
);
386 KiExitDispatcher(ApcLock
.OldIrql
);
387 return PreviousCount
;
392 KeRundownThread(VOID
)
395 PKTHREAD Thread
= KeGetCurrentThread();
396 PLIST_ENTRY NextEntry
, ListHead
;
398 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
400 /* Optimized path if nothing is on the list at the moment */
401 if (IsListEmpty(&Thread
->MutantListHead
)) return;
403 /* Lock the Dispatcher Database */
404 OldIrql
= KiAcquireDispatcherLock();
406 /* Get the List Pointers */
407 ListHead
= &Thread
->MutantListHead
;
408 NextEntry
= ListHead
->Flink
;
409 while (NextEntry
!= ListHead
)
412 Mutant
= CONTAINING_RECORD(NextEntry
, KMUTANT
, MutantListEntry
);
414 /* Make sure it's not terminating with APCs off */
415 if (Mutant
->ApcDisable
)
417 /* Bugcheck the system */
418 KeBugCheckEx(THREAD_TERMINATE_HELD_MUTEX
,
425 /* Now we can remove it */
426 RemoveEntryList(&Mutant
->MutantListEntry
);
428 /* Unconditionally abandon it */
429 Mutant
->Header
.SignalState
= 1;
430 Mutant
->Abandoned
= TRUE
;
431 Mutant
->OwnerThread
= NULL
;
433 /* Check if the Wait List isn't empty */
434 if (!IsListEmpty(&Mutant
->Header
.WaitListHead
))
436 /* Wake the Mutant */
437 KiWaitTest(&Mutant
->Header
, MUTANT_INCREMENT
);
441 NextEntry
= NextEntry
->Flink
;
444 /* Release the Lock */
445 KiReleaseDispatcherLock(OldIrql
);
450 KeStartThread(IN OUT PKTHREAD Thread
)
452 KLOCK_QUEUE_HANDLE LockHandle
;
458 UCHAR IdealProcessor
= 0;
459 PKPROCESS Process
= Thread
->ApcState
.Process
;
461 /* Setup static fields from parent */
462 Thread
->DisableBoost
= Process
->DisableBoost
;
464 Thread
->Iopl
= Process
->Iopl
;
466 Thread
->Quantum
= Process
->QuantumReset
;
467 Thread
->QuantumReset
= Process
->QuantumReset
;
468 Thread
->SystemAffinityActive
= FALSE
;
470 /* Lock the process */
471 KiAcquireProcessLock(Process
, &LockHandle
);
473 /* Setup volatile data */
474 Thread
->Priority
= Process
->BasePriority
;
475 Thread
->BasePriority
= Process
->BasePriority
;
476 Thread
->Affinity
= Process
->Affinity
;
477 Thread
->UserAffinity
= Process
->Affinity
;
480 /* Get the KNODE and its PRCB */
481 Node
= KeNodeBlock
[Process
->IdealNode
];
482 NodePrcb
= KiProcessorBlock
[Process
->ThreadSeed
];
484 /* Calculate affinity mask */
485 Set
= ~NodePrcb
->MultiThreadProcessorSet
;
486 Mask
= (ULONG
)(Node
->ProcessorMask
& Process
->Affinity
);
490 /* Get the new thread seed */
491 IdealProcessor
= KeFindNextRightSetAffinity(Process
->ThreadSeed
, Mask
);
492 Process
->ThreadSeed
= IdealProcessor
;
495 ASSERT((Thread
->UserAffinity
& AFFINITY_MASK(IdealProcessor
)));
498 /* Set the Ideal Processor */
499 Thread
->IdealProcessor
= IdealProcessor
;
500 Thread
->UserIdealProcessor
= IdealProcessor
;
502 /* Lock the Dispatcher Database */
503 KiAcquireDispatcherLockAtDpcLevel();
505 /* Insert the thread into the process list */
506 InsertTailList(&Process
->ThreadListHead
, &Thread
->ThreadListEntry
);
508 /* Increase the stack count */
509 ASSERT(Process
->StackCount
!= MAXULONG_PTR
);
510 Process
->StackCount
++;
512 /* Release locks and return */
513 KiReleaseDispatcherLockFromDpcLevel();
514 KiReleaseProcessLock(&LockHandle
);
519 KiSuspendRundown(IN PKAPC Apc
)
522 UNREFERENCED_PARAMETER(Apc
);
527 KiSuspendNop(IN PKAPC Apc
,
528 IN PKNORMAL_ROUTINE
*NormalRoutine
,
529 IN PVOID
*NormalContext
,
530 IN PVOID
*SystemArgument1
,
531 IN PVOID
*SystemArgument2
)
534 UNREFERENCED_PARAMETER(Apc
);
535 UNREFERENCED_PARAMETER(NormalRoutine
);
536 UNREFERENCED_PARAMETER(NormalContext
);
537 UNREFERENCED_PARAMETER(SystemArgument1
);
538 UNREFERENCED_PARAMETER(SystemArgument2
);
543 KiSuspendThread(IN PVOID NormalContext
,
544 IN PVOID SystemArgument1
,
545 IN PVOID SystemArgument2
)
547 /* Non-alertable kernel-mode suspended wait */
548 KeWaitForSingleObject(&KeGetCurrentThread()->SuspendSemaphore
,
557 KeSuspendThread(PKTHREAD Thread
)
559 KLOCK_QUEUE_HANDLE ApcLock
;
561 ASSERT_THREAD(Thread
);
562 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
564 /* Lock the APC Queue */
565 KiAcquireApcLock(Thread
, &ApcLock
);
567 /* Save the Old Count */
568 PreviousCount
= Thread
->SuspendCount
;
570 /* Handle the maximum */
571 if (PreviousCount
== MAXIMUM_SUSPEND_COUNT
)
573 /* Raise an exception */
574 KiReleaseApcLock(&ApcLock
);
575 RtlRaiseStatus(STATUS_SUSPEND_COUNT_EXCEEDED
);
578 /* Should we bother to queue at all? */
579 if (Thread
->ApcQueueable
)
581 /* Increment the suspend count */
582 Thread
->SuspendCount
++;
584 /* Check if we should suspend it */
585 if (!(PreviousCount
) && !(Thread
->FreezeCount
))
587 /* Is the APC already inserted? */
588 if (!Thread
->SuspendApc
.Inserted
)
590 /* Not inserted, insert it */
591 Thread
->SuspendApc
.Inserted
= TRUE
;
592 KiInsertQueueApc(&Thread
->SuspendApc
, IO_NO_INCREMENT
);
596 /* Lock the dispatcher */
597 KiAcquireDispatcherLockAtDpcLevel();
599 /* Unsignal the semaphore, the APC was already inserted */
600 Thread
->SuspendSemaphore
.Header
.SignalState
--;
602 /* Release the dispatcher */
603 KiReleaseDispatcherLockFromDpcLevel();
608 /* Release Lock and return the Old State */
609 KiReleaseApcLockFromDpcLevel(&ApcLock
);
610 KiExitDispatcher(ApcLock
.OldIrql
);
611 return PreviousCount
;
616 KeThawAllThreads(VOID
)
618 KLOCK_QUEUE_HANDLE LockHandle
, ApcLock
;
619 PKTHREAD Current
, CurrentThread
= KeGetCurrentThread();
620 PKPROCESS Process
= CurrentThread
->ApcState
.Process
;
621 PLIST_ENTRY ListHead
, NextEntry
;
623 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
625 /* Lock the process */
626 KiAcquireProcessLock(Process
, &LockHandle
);
628 /* Loop the Process's Threads */
629 ListHead
= &Process
->ThreadListHead
;
630 NextEntry
= ListHead
->Flink
;
633 /* Get the current thread */
634 Current
= CONTAINING_RECORD(NextEntry
, KTHREAD
, ThreadListEntry
);
637 KiAcquireApcLockAtDpcLevel(Current
, &ApcLock
);
639 /* Make sure we are frozen */
640 OldCount
= Current
->FreezeCount
;
643 /* Decrease the freeze count */
644 Current
->FreezeCount
--;
646 /* Check if both counts are zero now */
647 if (!(Current
->SuspendCount
) && (!Current
->FreezeCount
))
649 /* Lock the dispatcher */
650 KiAcquireDispatcherLockAtDpcLevel();
652 /* Signal the suspend semaphore and wake it */
653 Current
->SuspendSemaphore
.Header
.SignalState
++;
654 KiWaitTest(&Current
->SuspendSemaphore
, 0);
656 /* Unlock the dispatcher */
657 KiReleaseDispatcherLockFromDpcLevel();
661 /* Release the APC lock */
662 KiReleaseApcLockFromDpcLevel(&ApcLock
);
664 /* Go to the next one */
665 NextEntry
= NextEntry
->Flink
;
666 } while (NextEntry
!= ListHead
);
668 /* Release the process lock and exit the dispatcher */
669 KiReleaseProcessLockFromDpcLevel(&LockHandle
);
670 KiExitDispatcher(LockHandle
.OldIrql
);
672 /* Leave the critical region */
673 KeLeaveCriticalRegion();
678 KeTestAlertThread(IN KPROCESSOR_MODE AlertMode
)
680 PKTHREAD Thread
= KeGetCurrentThread();
682 KLOCK_QUEUE_HANDLE ApcLock
;
683 ASSERT_THREAD(Thread
);
684 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
686 /* Lock the Dispatcher Database and the APC Queue */
687 KiAcquireApcLock(Thread
, &ApcLock
);
689 /* Save the old State */
690 OldState
= Thread
->Alerted
[AlertMode
];
692 /* Check the Thread is alerted */
695 /* Disable alert for this mode */
696 Thread
->Alerted
[AlertMode
] = FALSE
;
698 else if ((AlertMode
!= KernelMode
) &&
699 (!IsListEmpty(&Thread
->ApcState
.ApcListHead
[UserMode
])))
701 /* If the mode is User and the Queue isn't empty, set Pending */
702 Thread
->ApcState
.UserApcPending
= TRUE
;
705 /* Release Locks and return the Old State */
706 KiReleaseApcLock(&ApcLock
);
712 KeInitThread(IN OUT PKTHREAD Thread
,
713 IN PVOID KernelStack
,
714 IN PKSYSTEM_ROUTINE SystemRoutine
,
715 IN PKSTART_ROUTINE StartRoutine
,
716 IN PVOID StartContext
,
719 IN PKPROCESS Process
)
721 BOOLEAN AllocatedStack
= FALSE
;
723 PKWAIT_BLOCK TimerWaitBlock
;
727 /* Initalize the Dispatcher Header */
728 Thread
->Header
.Type
= ThreadObject
;
729 Thread
->Header
.ThreadControlFlags
= 0;
730 Thread
->Header
.DebugActive
= FALSE
;
731 Thread
->Header
.SignalState
= 0;
732 InitializeListHead(&(Thread
->Header
.WaitListHead
));
734 /* Initialize the Mutant List */
735 InitializeListHead(&Thread
->MutantListHead
);
737 /* Initialize the wait blocks */
738 for (i
= 0; i
< (THREAD_WAIT_OBJECTS
+ 1); i
++)
740 /* Put our pointer */
741 Thread
->WaitBlock
[i
].Thread
= Thread
;
744 /* Set swap settings */
745 Thread
->EnableStackSwap
= TRUE
;
746 Thread
->IdealProcessor
= 1;
747 Thread
->SwapBusy
= FALSE
;
748 Thread
->KernelStackResident
= TRUE
;
749 Thread
->AdjustReason
= AdjustNone
;
751 /* Initialize the lock */
752 KeInitializeSpinLock(&Thread
->ThreadLock
);
754 /* Setup the Service Descriptor Table for Native Calls */
755 Thread
->ServiceTable
= KeServiceDescriptorTable
;
757 /* Setup APC Fields */
758 InitializeListHead(&Thread
->ApcState
.ApcListHead
[0]);
759 InitializeListHead(&Thread
->ApcState
.ApcListHead
[1]);
760 Thread
->ApcState
.Process
= Process
;
761 Thread
->ApcStatePointer
[OriginalApcEnvironment
] = &Thread
->ApcState
;
762 Thread
->ApcStatePointer
[AttachedApcEnvironment
] = &Thread
->SavedApcState
;
763 Thread
->ApcStateIndex
= OriginalApcEnvironment
;
764 Thread
->ApcQueueable
= TRUE
;
765 KeInitializeSpinLock(&Thread
->ApcQueueLock
);
767 /* Initialize the Suspend APC */
768 KeInitializeApc(&Thread
->SuspendApc
,
770 OriginalApcEnvironment
,
777 /* Initialize the Suspend Semaphore */
778 KeInitializeSemaphore(&Thread
->SuspendSemaphore
, 0, 2);
780 /* Setup the timer */
781 Timer
= &Thread
->Timer
;
782 KeInitializeTimer(Timer
);
783 TimerWaitBlock
= &Thread
->WaitBlock
[TIMER_WAIT_BLOCK
];
784 TimerWaitBlock
->Object
= Timer
;
785 TimerWaitBlock
->WaitKey
= STATUS_TIMEOUT
;
786 TimerWaitBlock
->WaitType
= WaitAny
;
787 TimerWaitBlock
->NextWaitBlock
= NULL
;
789 /* Link the two wait lists together */
790 TimerWaitBlock
->WaitListEntry
.Flink
= &Timer
->Header
.WaitListHead
;
791 TimerWaitBlock
->WaitListEntry
.Blink
= &Timer
->Header
.WaitListHead
;
796 /* Check if we have a kernel stack */
799 /* We don't, allocate one */
800 KernelStack
= MmCreateKernelStack(FALSE
, 0);
801 if (!KernelStack
) return STATUS_INSUFFICIENT_RESOURCES
;
803 /* Remember for later */
804 AllocatedStack
= TRUE
;
807 /* Set the Thread Stacks */
808 Thread
->InitialStack
= KernelStack
;
809 Thread
->StackBase
= KernelStack
;
810 Thread
->StackLimit
= (ULONG_PTR
)KernelStack
- KERNEL_STACK_SIZE
;
811 Thread
->KernelStackResident
= TRUE
;
813 /* Enter SEH to avoid crashes due to user mode */
814 Status
= STATUS_SUCCESS
;
817 /* Initalize the Thread Context */
818 KiInitializeContextThread(Thread
,
824 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
826 /* Set failure status */
827 Status
= STATUS_UNSUCCESSFUL
;
829 /* Check if a stack was allocated */
832 /* Delete the stack */
833 MmDeleteKernelStack((PVOID
)Thread
->StackBase
, FALSE
);
834 Thread
->InitialStack
= NULL
;
839 /* Set the Thread to initalized */
840 Thread
->State
= Initialized
;
846 KeInitializeThread(IN PKPROCESS Process
,
847 IN OUT PKTHREAD Thread
,
848 IN PKSYSTEM_ROUTINE SystemRoutine
,
849 IN PKSTART_ROUTINE StartRoutine
,
850 IN PVOID StartContext
,
853 IN PVOID KernelStack
)
855 /* Initialize and start the thread on success */
856 if (NT_SUCCESS(KeInitThread(Thread
,
866 KeStartThread(Thread
);
872 KeUninitThread(IN PKTHREAD Thread
)
874 /* Delete the stack */
875 MmDeleteKernelStack((PVOID
)Thread
->StackBase
, FALSE
);
876 Thread
->InitialStack
= NULL
;
879 /* PUBLIC FUNCTIONS **********************************************************/
886 KeCapturePersistentThreadState(IN PVOID CurrentThread
,
892 IN PVOID ThreadState
)
900 #undef KeGetCurrentThread
903 KeGetCurrentThread(VOID
)
905 /* Return the current thread on this PCR */
906 return _KeGetCurrentThread();
912 #undef KeGetPreviousMode
915 KeGetPreviousMode(VOID
)
917 /* Return the previous mode of this thread */
918 return _KeGetPreviousMode();
926 KeQueryRuntimeThread(IN PKTHREAD Thread
,
929 ASSERT_THREAD(Thread
);
931 /* Return the User Time */
932 *UserTime
= Thread
->UserTime
;
934 /* Return the Kernel Time */
935 return Thread
->KernelTime
;
943 KeSetKernelStackSwapEnable(IN BOOLEAN Enable
)
945 BOOLEAN PreviousState
;
946 PKTHREAD Thread
= KeGetCurrentThread();
949 PreviousState
= Thread
->EnableStackSwap
;
952 Thread
->EnableStackSwap
= Enable
;
954 /* Return Old State */
955 return PreviousState
;
963 KeQueryPriorityThread(IN PKTHREAD Thread
)
965 ASSERT_THREAD(Thread
);
967 /* Return the current priority */
968 return Thread
->Priority
;
976 KeRevertToUserAffinityThread(VOID
)
980 PKTHREAD NextThread
, CurrentThread
= KeGetCurrentThread();
981 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
982 ASSERT(CurrentThread
->SystemAffinityActive
!= FALSE
);
984 /* Lock the Dispatcher Database */
985 OldIrql
= KiAcquireDispatcherLock();
987 /* Set the user affinity and processor and disable system affinity */
988 CurrentThread
->Affinity
= CurrentThread
->UserAffinity
;
989 CurrentThread
->IdealProcessor
= CurrentThread
->UserIdealProcessor
;
990 CurrentThread
->SystemAffinityActive
= FALSE
;
992 /* Get the current PRCB and check if it doesn't match this affinity */
993 Prcb
= KeGetCurrentPrcb();
994 if (!(Prcb
->SetMember
& CurrentThread
->Affinity
))
997 KiAcquirePrcbLock(Prcb
);
999 /* Check if there's no next thread scheduled */
1000 if (!Prcb
->NextThread
)
1002 /* Select a new thread and set it on standby */
1003 NextThread
= KiSelectNextThread(Prcb
);
1004 NextThread
->State
= Standby
;
1005 Prcb
->NextThread
= NextThread
;
1008 /* Release the PRCB lock */
1009 KiReleasePrcbLock(Prcb
);
1012 /* Unlock dispatcher database */
1013 KiReleaseDispatcherLock(OldIrql
);
1021 KeSetIdealProcessorThread(IN PKTHREAD Thread
,
1024 CCHAR OldIdealProcessor
;
1026 ASSERT(Processor
<= MAXIMUM_PROCESSORS
);
1028 /* Lock the Dispatcher Database */
1029 OldIrql
= KiAcquireDispatcherLock();
1031 /* Save Old Ideal Processor */
1032 OldIdealProcessor
= Thread
->UserIdealProcessor
;
1034 /* Make sure a valid CPU was given */
1035 if (Processor
< KeNumberProcessors
)
1037 /* Check if the user ideal CPU is in the affinity */
1038 if (Thread
->Affinity
& AFFINITY_MASK(Processor
))
1040 /* Set the ideal processor */
1041 Thread
->IdealProcessor
= Processor
;
1043 /* Check if system affinity is used */
1044 if (!Thread
->SystemAffinityActive
)
1046 /* It's not, so update the user CPU too */
1047 Thread
->UserIdealProcessor
= Processor
;
1052 /* Release dispatcher lock and return the old ideal CPU */
1053 KiReleaseDispatcherLock(OldIrql
);
1054 return OldIdealProcessor
;
1062 KeSetSystemAffinityThread(IN KAFFINITY Affinity
)
1066 PKTHREAD NextThread
, CurrentThread
= KeGetCurrentThread();
1067 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
1068 ASSERT((Affinity
& KeActiveProcessors
) != 0);
1070 /* Lock the Dispatcher Database */
1071 OldIrql
= KiAcquireDispatcherLock();
1073 /* Restore the affinity and enable system affinity */
1074 CurrentThread
->Affinity
= Affinity
;
1075 CurrentThread
->SystemAffinityActive
= TRUE
;
1077 /* Check if the ideal processor is part of the affinity */
1079 if (!(Affinity
& AFFINITY_MASK(CurrentThread
->IdealProcessor
)))
1081 ULONG AffinitySet
, NodeMask
;
1083 /* It's not! Get the PRCB */
1084 Prcb
= KiProcessorBlock
[CurrentThread
->IdealProcessor
];
1086 /* Calculate the affinity set */
1087 AffinitySet
= KeActiveProcessors
& Affinity
;
1088 NodeMask
= Prcb
->ParentNode
->ProcessorMask
& AffinitySet
;
1091 /* Use the Node set instead */
1092 AffinitySet
= NodeMask
;
1095 /* Calculate the ideal CPU from the affinity set */
1096 BitScanReverse(&NodeMask
, AffinitySet
);
1097 CurrentThread
->IdealProcessor
= (UCHAR
)NodeMask
;
1101 /* Get the current PRCB and check if it doesn't match this affinity */
1102 Prcb
= KeGetCurrentPrcb();
1103 if (!(Prcb
->SetMember
& CurrentThread
->Affinity
))
1106 KiAcquirePrcbLock(Prcb
);
1108 /* Check if there's no next thread scheduled */
1109 if (!Prcb
->NextThread
)
1111 /* Select a new thread and set it on standby */
1112 NextThread
= KiSelectNextThread(Prcb
);
1113 NextThread
->State
= Standby
;
1114 Prcb
->NextThread
= NextThread
;
1117 /* Release the PRCB lock */
1118 KiReleasePrcbLock(Prcb
);
1121 /* Unlock dispatcher database */
1122 KiReleaseDispatcherLock(OldIrql
);
1130 KeSetBasePriorityThread(IN PKTHREAD Thread
,
1134 KPRIORITY OldBasePriority
, Priority
, BasePriority
;
1137 ASSERT_THREAD(Thread
);
1138 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
1140 /* Get the process */
1141 Process
= Thread
->ApcState
.Process
;
1143 /* Lock the Dispatcher Database */
1144 OldIrql
= KiAcquireDispatcherLock();
1146 /* Lock the thread */
1147 KiAcquireThreadLock(Thread
);
1149 /* Save the old base priority and increment */
1150 OldBasePriority
= Thread
->BasePriority
;
1151 OldIncrement
= OldBasePriority
- Process
->BasePriority
;
1153 /* If priority saturation happened, use the saturated increment */
1154 if (Thread
->Saturation
) OldIncrement
= (HIGH_PRIORITY
+ 1) / 2 *
1157 /* Reset the saturation value */
1158 Thread
->Saturation
= 0;
1160 /* Now check if saturation is being used for the new value */
1161 if (abs(Increment
) >= ((HIGH_PRIORITY
+ 1) / 2))
1163 /* Check if we need positive or negative saturation */
1164 Thread
->Saturation
= (Increment
> 0) ? 1 : -1;
1167 /* Normalize the Base Priority */
1168 BasePriority
= Process
->BasePriority
+ Increment
;
1169 if (Process
->BasePriority
>= LOW_REALTIME_PRIORITY
)
1171 /* Check if it's too low */
1172 if (BasePriority
< LOW_REALTIME_PRIORITY
)
1174 /* Set it to the lowest real time level */
1175 BasePriority
= LOW_REALTIME_PRIORITY
;
1178 /* Check if it's too high */
1179 if (BasePriority
> HIGH_PRIORITY
) BasePriority
= HIGH_PRIORITY
;
1181 /* We are at real time, so use the raw base priority */
1182 Priority
= BasePriority
;
1186 /* Check if it's entering the real time range */
1187 if (BasePriority
>= LOW_REALTIME_PRIORITY
)
1189 /* Set it to the highest dynamic level */
1190 BasePriority
= LOW_REALTIME_PRIORITY
- 1;
1193 /* Check if it's too low and normalize it */
1194 if (BasePriority
<= LOW_PRIORITY
) BasePriority
= 1;
1196 /* Check if Saturation is used */
1197 if (Thread
->Saturation
)
1199 /* Use the raw base priority */
1200 Priority
= BasePriority
;
1204 /* Otherwise, calculate the new priority */
1205 Priority
= KiComputeNewPriority(Thread
, 0);
1206 Priority
+= (BasePriority
- OldBasePriority
);
1208 /* Check if it entered the real-time range */
1209 if (Priority
>= LOW_REALTIME_PRIORITY
)
1211 /* Normalize it down to the highest dynamic priority */
1212 Priority
= LOW_REALTIME_PRIORITY
- 1;
1214 else if (Priority
<= LOW_PRIORITY
)
1216 /* It went too low, normalize it */
1222 /* Finally set the new base priority */
1223 Thread
->BasePriority
= (SCHAR
)BasePriority
;
1225 /* Reset the decrements */
1226 Thread
->PriorityDecrement
= 0;
1228 /* Check if we're changing priority after all */
1229 if (Priority
!= Thread
->Priority
)
1231 /* Reset the quantum and do the actual priority modification */
1232 Thread
->Quantum
= Thread
->QuantumReset
;
1233 KiSetPriorityThread(Thread
, Priority
);
1236 /* Release thread lock */
1237 KiReleaseThreadLock(Thread
);
1239 /* Release the dispatcher database and return old increment */
1240 KiReleaseDispatcherLock(OldIrql
);
1241 return OldIncrement
;
1249 KeSetAffinityThread(IN PKTHREAD Thread
,
1250 IN KAFFINITY Affinity
)
1253 KAFFINITY OldAffinity
;
1254 ASSERT_THREAD(Thread
);
1255 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
1257 /* Lock the dispatcher database */
1258 OldIrql
= KiAcquireDispatcherLock();
1260 /* Call the internal function */
1261 OldAffinity
= KiSetAffinityThread(Thread
, Affinity
);
1263 /* Release the dispatcher database and return old affinity */
1264 KiReleaseDispatcherLock(OldIrql
);
1273 KeSetPriorityThread(IN PKTHREAD Thread
,
1274 IN KPRIORITY Priority
)
1277 KPRIORITY OldPriority
;
1278 ASSERT_THREAD(Thread
);
1279 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
1280 ASSERT((Priority
<= HIGH_PRIORITY
) && (Priority
>= LOW_PRIORITY
));
1281 ASSERT(KeIsExecutingDpc() == FALSE
);
1283 /* Lock the Dispatcher Database */
1284 OldIrql
= KiAcquireDispatcherLock();
1286 /* Lock the thread */
1287 KiAcquireThreadLock(Thread
);
1289 /* Save the old Priority and reset decrement */
1290 OldPriority
= Thread
->Priority
;
1291 Thread
->PriorityDecrement
= 0;
1293 /* Make sure that an actual change is being done */
1294 if (Priority
!= Thread
->Priority
)
1296 /* Reset the quantum */
1297 Thread
->Quantum
= Thread
->QuantumReset
;
1299 /* Check if priority is being set too low and normalize if so */
1300 if ((Thread
->BasePriority
!= 0) && !(Priority
)) Priority
= 1;
1302 /* Set the new Priority */
1303 KiSetPriorityThread(Thread
, Priority
);
1306 /* Release thread lock */
1307 KiReleaseThreadLock(Thread
);
1309 /* Release the dispatcher database */
1310 KiReleaseDispatcherLock(OldIrql
);
1312 /* Return Old Priority */
1321 KeTerminateThread(IN KPRIORITY Increment
)
1323 PLIST_ENTRY
*ListHead
;
1324 PETHREAD Entry
, SavedEntry
;
1325 PETHREAD
*ThreadAddr
;
1326 KLOCK_QUEUE_HANDLE LockHandle
;
1327 PKTHREAD Thread
= KeGetCurrentThread();
1328 PKPROCESS Process
= Thread
->ApcState
.Process
;
1329 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
1331 /* Lock the process */
1332 KiAcquireProcessLock(Process
, &LockHandle
);
1334 /* Make sure we won't get Swapped */
1335 KiSetThreadSwapBusy(Thread
);
1337 /* Save the Kernel and User Times */
1338 Process
->KernelTime
+= Thread
->KernelTime
;
1339 Process
->UserTime
+= Thread
->UserTime
;
1341 /* Get the current entry and our Port */
1342 Entry
= (PETHREAD
)PspReaperListHead
.Flink
;
1343 ThreadAddr
= &((PETHREAD
)Thread
)->ReaperLink
;
1345 /* Add it to the reaper's list */
1348 /* Get the list head */
1349 ListHead
= &PspReaperListHead
.Flink
;
1351 /* Link ourselves */
1352 *ThreadAddr
= Entry
;
1355 /* Now try to do the exchange */
1356 Entry
= InterlockedCompareExchangePointer((PVOID
*)ListHead
,
1360 /* Break out if the change was succesful */
1361 } while (Entry
!= SavedEntry
);
1363 /* Acquire the dispatcher lock */
1364 KiAcquireDispatcherLockAtDpcLevel();
1366 /* Check if the reaper wasn't active */
1369 /* Activate it as a work item, directly through its Queue */
1370 KiInsertQueue(&ExWorkerQueue
[HyperCriticalWorkQueue
].WorkerQueue
,
1371 &PspReaperWorkItem
.List
,
1375 /* Check the thread has an associated queue */
1378 /* Remove it from the list, and handle the queue */
1379 RemoveEntryList(&Thread
->QueueListEntry
);
1380 KiActivateWaiterQueue(Thread
->Queue
);
1383 /* Signal the thread */
1384 Thread
->Header
.SignalState
= TRUE
;
1385 if (!IsListEmpty(&Thread
->Header
.WaitListHead
))
1387 /* Unwait the threads */
1388 KxUnwaitThread(&Thread
->Header
, Increment
);
1391 /* Remove the thread from the list */
1392 RemoveEntryList(&Thread
->ThreadListEntry
);
1394 /* Release the process lock */
1395 KiReleaseProcessLockFromDpcLevel(&LockHandle
);
1397 /* Set us as terminated, decrease the Process's stack count */
1398 Thread
->State
= Terminated
;
1400 /* Decrease stack count */
1401 ASSERT(Process
->StackCount
!= 0);
1402 ASSERT(Process
->State
== ProcessInMemory
);
1403 Process
->StackCount
--;
1404 if (!(Process
->StackCount
) && !(IsListEmpty(&Process
->ThreadListHead
)))
1406 /* FIXME: Swap stacks */
1409 /* Rundown arch-specific parts */
1410 KiRundownThread(Thread
);
1412 /* Swap to a new thread */
1413 KiReleaseDispatcherLockFromDpcLevel();
1414 KiSwapThread(Thread
, KeGetCurrentPrcb());