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 ******************************************************************/
13 #include <internal/debug.h>
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, 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
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 KeQueryBasePriorityThread(IN PKTHREAD Thread
)
56 ASSERT_THREAD(Thread
);
57 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
59 /* Raise IRQL to synch level */
60 OldIrql
= KeRaiseIrqlToSynchLevel();
63 KiAcquireThreadLock(Thread
);
66 Process
= Thread
->ApcStatePointer
[0]->Process
;
68 /* Calculate the base increment */
69 BaseIncrement
= Thread
->BasePriority
- Process
->BasePriority
;
71 /* If saturation occured, return the saturation increment instead */
72 if (Thread
->Saturation
) BaseIncrement
= (HIGH_PRIORITY
+ 1) / 2 *
75 /* Release thread lock */
76 KiReleaseThreadLock(Thread
);
78 /* Lower IRQl and return Increment */
85 KeReadyThread(IN PKTHREAD Thread
)
88 ASSERT_THREAD(Thread
);
89 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
91 /* Lock the Dispatcher Database */
92 OldIrql
= KiAcquireDispatcherLock();
94 /* Make the thread ready */
95 KiReadyThread(Thread
);
97 /* Unlock dispatcher database */
98 KiReleaseDispatcherLock(OldIrql
);
103 KeAlertResumeThread(IN PKTHREAD Thread
)
106 KLOCK_QUEUE_HANDLE ApcLock
;
107 ASSERT_THREAD(Thread
);
108 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
110 /* Lock the Dispatcher Database and the APC Queue */
111 KiAcquireApcLock(Thread
, &ApcLock
);
112 KiAcquireDispatcherLockAtDpcLevel();
114 /* Return if Thread is already alerted. */
115 if (!Thread
->Alerted
[KernelMode
])
117 /* If it's Blocked, unblock if it we should */
118 if ((Thread
->State
== Waiting
) && (Thread
->Alertable
))
121 KiUnwaitThread(Thread
, STATUS_ALERTED
, THREAD_ALERT_INCREMENT
);
125 /* If not, simply Alert it */
126 Thread
->Alerted
[KernelMode
] = TRUE
;
130 /* Save the old Suspend Count */
131 PreviousCount
= Thread
->SuspendCount
;
133 /* If the thread is suspended, decrease one of the suspend counts */
136 /* Decrease count. If we are now zero, unwait it completely */
137 if (--Thread
->SuspendCount
)
139 /* Signal and satisfy */
140 Thread
->SuspendSemaphore
.Header
.SignalState
++;
141 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
145 /* Release Locks and return the Old State */
146 KiReleaseDispatcherLockFromDpcLevel();
147 KiReleaseApcLockFromDpcLevel(&ApcLock
);
148 KiExitDispatcher(ApcLock
.OldIrql
);
149 return PreviousCount
;
154 KeAlertThread(IN PKTHREAD Thread
,
155 IN KPROCESSOR_MODE AlertMode
)
157 BOOLEAN PreviousState
;
158 KLOCK_QUEUE_HANDLE ApcLock
;
159 ASSERT_THREAD(Thread
);
160 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
162 /* Lock the Dispatcher Database and the APC Queue */
163 KiAcquireApcLock(Thread
, &ApcLock
);
164 KiAcquireDispatcherLockAtDpcLevel();
166 /* Save the Previous State */
167 PreviousState
= Thread
->Alerted
[AlertMode
];
169 /* Check if it's already alerted */
172 /* Check if the thread is alertable, and blocked in the given mode */
173 if ((Thread
->State
== Waiting
) &&
174 ((AlertMode
== KernelMode
) || (Thread
->WaitMode
== AlertMode
)) &&
177 /* Abort the wait to alert the thread */
178 KiUnwaitThread(Thread
, STATUS_ALERTED
, THREAD_ALERT_INCREMENT
);
182 /* Otherwise, merely set the alerted state */
183 Thread
->Alerted
[AlertMode
] = TRUE
;
187 /* Release the Dispatcher Lock */
188 KiReleaseDispatcherLockFromDpcLevel();
189 KiReleaseApcLockFromDpcLevel(&ApcLock
);
190 KiExitDispatcher(ApcLock
.OldIrql
);
192 /* Return the old state */
193 return PreviousState
;
198 KeForceResumeThread(IN PKTHREAD Thread
)
200 KLOCK_QUEUE_HANDLE ApcLock
;
202 ASSERT_THREAD(Thread
);
203 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
205 /* Lock the APC Queue */
206 KiAcquireApcLock(Thread
, &ApcLock
);
208 /* Save the old Suspend Count */
209 PreviousCount
= Thread
->SuspendCount
+ Thread
->FreezeCount
;
211 /* If the thread is suspended, wake it up!!! */
214 /* Unwait it completely */
215 Thread
->SuspendCount
= 0;
216 Thread
->FreezeCount
= 0;
218 /* Lock the dispatcher */
219 KiAcquireDispatcherLockAtDpcLevel();
221 /* Signal and satisfy */
222 Thread
->SuspendSemaphore
.Header
.SignalState
++;
223 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
225 /* Release the dispatcher */
226 KiReleaseDispatcherLockFromDpcLevel();
229 /* Release Lock and return the Old State */
230 KiReleaseApcLockFromDpcLevel(&ApcLock
);
231 KiExitDispatcher(ApcLock
.OldIrql
);
232 return PreviousCount
;
237 KeFreezeAllThreads(VOID
)
239 KLOCK_QUEUE_HANDLE LockHandle
, ApcLock
;
240 PKTHREAD Current
, CurrentThread
= KeGetCurrentThread();
241 PKPROCESS Process
= CurrentThread
->ApcState
.Process
;
242 PLIST_ENTRY ListHead
, NextEntry
;
244 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
246 /* Lock the process */
247 KiAcquireProcessLock(Process
, &LockHandle
);
249 /* If someone is already trying to free us, try again */
250 while (CurrentThread
->FreezeCount
)
252 /* Release and re-acquire the process lock so the APC will go through */
253 KiReleaseProcessLock(&LockHandle
);
254 KiAcquireProcessLock(Process
, &LockHandle
);
257 /* Enter a critical region */
258 KeEnterCriticalRegion();
260 /* Loop the Process's Threads */
261 ListHead
= &Process
->ThreadListHead
;
262 NextEntry
= ListHead
->Flink
;
263 while (NextEntry
!= ListHead
)
265 /* Get the current thread */
266 Current
= CONTAINING_RECORD(NextEntry
, KTHREAD
, ThreadListEntry
);
269 KiAcquireApcLockAtDpcLevel(Current
, &ApcLock
);
271 /* Make sure it's not ours, and check if APCs are enabled */
272 if ((Current
!= CurrentThread
) && (Current
->ApcQueueable
))
275 OldCount
= Current
->SuspendCount
;
276 ASSERT(OldCount
!= MAXIMUM_SUSPEND_COUNT
);
278 /* Increase the freeze count */
279 Current
->FreezeCount
++;
281 /* Make sure it wasn't already suspended */
282 if (!(OldCount
) && !(Current
->SuspendCount
))
284 /* Did we already insert it? */
285 if (!Current
->SuspendApc
.Inserted
)
288 Current
->SuspendApc
.Inserted
= TRUE
;
289 KiInsertQueueApc(&Current
->SuspendApc
, IO_NO_INCREMENT
);
293 /* Lock the dispatcher */
294 KiAcquireDispatcherLockAtDpcLevel();
296 /* Unsignal the semaphore, the APC was already inserted */
297 Current
->SuspendSemaphore
.Header
.SignalState
--;
299 /* Release the dispatcher */
300 KiReleaseDispatcherLockFromDpcLevel();
305 /* Release the APC lock */
306 KiReleaseApcLockFromDpcLevel(&ApcLock
);
308 /* Move to the next thread */
309 NextEntry
= NextEntry
->Flink
;
312 /* Release the process lock and exit the dispatcher */
313 KiReleaseProcessLock(&LockHandle
);
314 KiExitDispatcher(LockHandle
.OldIrql
);
316 /* Leave the critical region */
317 KeLeaveCriticalRegion();
322 KeResumeThread(IN PKTHREAD Thread
)
324 KLOCK_QUEUE_HANDLE ApcLock
;
326 ASSERT_THREAD(Thread
);
327 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
329 /* Lock the APC Queue */
330 KiAcquireApcLock(Thread
, &ApcLock
);
332 /* Save the Old Count */
333 PreviousCount
= Thread
->SuspendCount
;
335 /* Check if it existed */
338 /* Decrease the suspend count */
339 Thread
->SuspendCount
--;
341 /* Check if the thrad is still suspended or not */
342 if ((!Thread
->SuspendCount
) && (!Thread
->FreezeCount
))
344 /* Acquire the dispatcher lock */
345 KiAcquireDispatcherLockAtDpcLevel();
347 /* Signal the Suspend Semaphore */
348 Thread
->SuspendSemaphore
.Header
.SignalState
++;
349 KiWaitTest(&Thread
->SuspendSemaphore
.Header
, IO_NO_INCREMENT
);
351 /* Release the dispatcher lock */
352 KiReleaseDispatcherLockFromDpcLevel();
356 /* Release APC Queue lock and return the Old State */
357 KiReleaseApcLockFromDpcLevel(&ApcLock
);
358 KiExitDispatcher(ApcLock
.OldIrql
);
359 return PreviousCount
;
364 KeRundownThread(VOID
)
367 PKTHREAD Thread
= KeGetCurrentThread();
368 PLIST_ENTRY NextEntry
, ListHead
;
371 /* Optimized path if nothing is on the list at the moment */
372 if (IsListEmpty(&Thread
->MutantListHead
)) return;
374 /* Lock the Dispatcher Database */
375 OldIrql
= KiAcquireDispatcherLock();
377 /* Get the List Pointers */
378 ListHead
= &Thread
->MutantListHead
;
379 NextEntry
= ListHead
->Flink
;
380 while (NextEntry
!= ListHead
)
383 Mutant
= CONTAINING_RECORD(NextEntry
, KMUTANT
, MutantListEntry
);
385 /* Make sure it's not terminating with APCs off */
386 if (Mutant
->ApcDisable
)
388 /* Bugcheck the system */
389 KEBUGCHECKEX(0, //THREAD_TERMINATE_HELD_MUTEX,
396 /* Now we can remove it */
397 RemoveEntryList(&Mutant
->MutantListEntry
);
399 /* Unconditionally abandon it */
400 Mutant
->Header
.SignalState
= 1;
401 Mutant
->Abandoned
= TRUE
;
402 Mutant
->OwnerThread
= NULL
;
404 /* Check if the Wait List isn't empty */
405 if (!IsListEmpty(&Mutant
->Header
.WaitListHead
))
407 /* Wake the Mutant */
408 KiWaitTest(&Mutant
->Header
, MUTANT_INCREMENT
);
412 NextEntry
= NextEntry
->Flink
;
415 /* Release the Lock */
416 KiReleaseDispatcherLock(OldIrql
);
421 KeStartThread(IN OUT PKTHREAD Thread
)
423 KLOCK_QUEUE_HANDLE LockHandle
;
429 UCHAR IdealProcessor
= 0;
430 PKPROCESS Process
= Thread
->ApcState
.Process
;
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
;
438 /* Lock the process */
439 KiAcquireProcessLock(Process
, &LockHandle
);
441 /* Setup volatile data */
442 Thread
->Priority
= Process
->BasePriority
;
443 Thread
->BasePriority
= Process
->BasePriority
;
444 Thread
->Affinity
= Process
->Affinity
;
445 Thread
->UserAffinity
= Process
->Affinity
;
448 /* Get the KNODE and its PRCB */
449 Node
= KeNodeBlock
[Process
->IdealNode
];
450 NodePrcb
= (PKPRCB
)(KPCR_BASE
+ (Process
->ThreadSeed
* PAGE_SIZE
));
452 /* Calculate affinity mask */
453 Set
= ~NodePrcb
->MultiThreadProcessorSet
;
454 Mask
= (ULONG
)(Node
->ProcessorMask
& Process
->Affinity
);
458 /* Get the new thread seed */
459 IdealProcessor
= KeFindNextRightSetAffinity(Process
->ThreadSeed
, Mask
);
460 Process
->ThreadSeed
= IdealProcessor
;
463 ASSERT((Thread
->UserAffinity
& AFFINITY_MASK(IdealProcessor
)));
466 /* Set the Ideal Processor */
467 Thread
->IdealProcessor
= IdealProcessor
;
468 Thread
->UserIdealProcessor
= IdealProcessor
;
470 /* Lock the Dispatcher Database */
471 KiAcquireDispatcherLockAtDpcLevel();
473 /* Insert the thread into the process list */
474 InsertTailList(&Process
->ThreadListHead
, &Thread
->ThreadListEntry
);
476 /* Increase the stack count */
477 ASSERT(Process
->StackCount
!= MAXULONG_PTR
);
478 Process
->StackCount
++;
480 /* Release locks and return */
481 KiReleaseDispatcherLockFromDpcLevel();
482 KiReleaseProcessLock(&LockHandle
);
487 KiSuspendRundown(IN PKAPC Apc
)
490 UNREFERENCED_PARAMETER(Apc
);
495 KiSuspendNop(IN PKAPC Apc
,
496 IN PKNORMAL_ROUTINE
*NormalRoutine
,
497 IN PVOID
*NormalContext
,
498 IN PVOID
*SystemArgument1
,
499 IN PVOID
*SystemArgument2
)
502 UNREFERENCED_PARAMETER(Apc
);
503 UNREFERENCED_PARAMETER(NormalRoutine
);
504 UNREFERENCED_PARAMETER(NormalContext
);
505 UNREFERENCED_PARAMETER(SystemArgument1
);
506 UNREFERENCED_PARAMETER(SystemArgument2
);
511 KiSuspendThread(IN PVOID NormalContext
,
512 IN PVOID SystemArgument1
,
513 IN PVOID SystemArgument2
)
515 /* Non-alertable kernel-mode suspended wait */
516 KeWaitForSingleObject(&KeGetCurrentThread()->SuspendSemaphore
,
525 KeSuspendThread(PKTHREAD Thread
)
527 KLOCK_QUEUE_HANDLE ApcLock
;
529 ASSERT_THREAD(Thread
);
530 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
532 /* Lock the APC Queue */
533 KiAcquireApcLock(Thread
, &ApcLock
);
535 /* Save the Old Count */
536 PreviousCount
= Thread
->SuspendCount
;
538 /* Handle the maximum */
539 if (PreviousCount
== MAXIMUM_SUSPEND_COUNT
)
541 /* Raise an exception */
542 KiReleaseApcLock(&ApcLock
);
543 RtlRaiseStatus(STATUS_SUSPEND_COUNT_EXCEEDED
);
546 /* Should we bother to queue at all? */
547 if (Thread
->ApcQueueable
)
549 /* Increment the suspend count */
550 Thread
->SuspendCount
++;
552 /* Check if we should suspend it */
553 if (!(PreviousCount
) && !(Thread
->FreezeCount
))
555 /* Is the APC already inserted? */
556 if (!Thread
->SuspendApc
.Inserted
)
558 /* Not inserted, insert it */
559 Thread
->SuspendApc
.Inserted
= TRUE
;
560 KiInsertQueueApc(&Thread
->SuspendApc
, IO_NO_INCREMENT
);
564 /* Lock the dispatcher */
565 KiAcquireDispatcherLockAtDpcLevel();
567 /* Unsignal the semaphore, the APC was already inserted */
568 Thread
->SuspendSemaphore
.Header
.SignalState
--;
570 /* Release the dispatcher */
571 KiReleaseDispatcherLockFromDpcLevel();
576 /* Release Lock and return the Old State */
577 KiReleaseApcLockFromDpcLevel(&ApcLock
);
578 KiExitDispatcher(ApcLock
.OldIrql
);
579 return PreviousCount
;
584 KeThawAllThreads(VOID
)
586 KLOCK_QUEUE_HANDLE LockHandle
, ApcLock
;
587 PKTHREAD Current
, CurrentThread
= KeGetCurrentThread();
588 PKPROCESS Process
= CurrentThread
->ApcState
.Process
;
589 PLIST_ENTRY ListHead
, NextEntry
;
591 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
593 /* Lock the process */
594 KiAcquireProcessLock(Process
, &LockHandle
);
596 /* Enter a critical region */
597 KeEnterCriticalRegion();
599 /* Loop the Process's Threads */
600 ListHead
= &Process
->ThreadListHead
;
601 NextEntry
= ListHead
->Flink
;
602 while (NextEntry
!= ListHead
)
604 /* Get the current thread */
605 Current
= CONTAINING_RECORD(NextEntry
, KTHREAD
, ThreadListEntry
);
608 KiAcquireApcLockAtDpcLevel(Current
, &ApcLock
);
610 /* Make sure we are frozen */
611 OldCount
= Current
->FreezeCount
;
614 /* Decrease the freeze count */
615 Current
->FreezeCount
--;
617 /* Check if both counts are zero now */
618 if (!(Current
->SuspendCount
) && (!Current
->FreezeCount
))
620 /* Lock the dispatcher */
621 KiAcquireDispatcherLockAtDpcLevel();
623 /* Signal the suspend semaphore and wake it */
624 Current
->SuspendSemaphore
.Header
.SignalState
++;
625 KiWaitTest(&Current
->SuspendSemaphore
, 1);
627 /* Unlock the dispatcher */
628 KiReleaseDispatcherLockFromDpcLevel();
632 /* Release the APC lock */
633 KiReleaseApcLockFromDpcLevel(&ApcLock
);
635 /* Go to the next one */
636 NextEntry
= NextEntry
->Flink
;
639 /* Release the process lock and exit the dispatcher */
640 KiReleaseProcessLock(&LockHandle
);
641 KiExitDispatcher(LockHandle
.OldIrql
);
643 /* Leave the critical region */
644 KeLeaveCriticalRegion();
649 KeTestAlertThread(IN KPROCESSOR_MODE AlertMode
)
651 PKTHREAD Thread
= KeGetCurrentThread();
653 KLOCK_QUEUE_HANDLE ApcLock
;
654 ASSERT_THREAD(Thread
);
655 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
657 /* Lock the Dispatcher Database and the APC Queue */
658 KiAcquireApcLock(Thread
, &ApcLock
);
659 KiAcquireDispatcherLockAtDpcLevel();
661 /* Save the old State */
662 OldState
= Thread
->Alerted
[AlertMode
];
664 /* Check the Thread is alerted */
667 /* Disable alert for this mode */
668 Thread
->Alerted
[AlertMode
] = FALSE
;
670 else if ((AlertMode
!= KernelMode
) &&
671 (!IsListEmpty(&Thread
->ApcState
.ApcListHead
[UserMode
])))
673 /* If the mode is User and the Queue isn't empty, set Pending */
674 Thread
->ApcState
.UserApcPending
= TRUE
;
677 /* Release Locks and return the Old State */
678 KiReleaseDispatcherLockFromDpcLevel();
679 KiReleaseApcLockFromDpcLevel(&ApcLock
);
680 KiExitDispatcher(ApcLock
.OldIrql
);
686 KeInitThread(IN OUT PKTHREAD Thread
,
687 IN PVOID KernelStack
,
688 IN PKSYSTEM_ROUTINE SystemRoutine
,
689 IN PKSTART_ROUTINE StartRoutine
,
690 IN PVOID StartContext
,
693 IN PKPROCESS Process
)
695 BOOLEAN AllocatedStack
= FALSE
;
697 PKWAIT_BLOCK TimerWaitBlock
;
701 /* Initalize the Dispatcher Header */
702 KeInitializeDispatcherHeader(&Thread
->DispatcherHeader
,
704 sizeof(KTHREAD
) / sizeof(LONG
),
707 /* Initialize the Mutant List */
708 InitializeListHead(&Thread
->MutantListHead
);
710 /* Initialize the wait blocks */
711 for (i
= 0; i
< (THREAD_WAIT_OBJECTS
+ 1); i
++)
713 /* Put our pointer */
714 Thread
->WaitBlock
[i
].Thread
= Thread
;
717 /* Set swap settings */
718 Thread
->EnableStackSwap
= FALSE
;//TRUE;
719 Thread
->IdealProcessor
= 1;
720 Thread
->SwapBusy
= FALSE
;
721 Thread
->AdjustReason
= 0;
723 /* Initialize the lock */
724 KeInitializeSpinLock(&Thread
->ThreadLock
);
726 /* Setup the Service Descriptor Table for Native Calls */
727 Thread
->ServiceTable
= KeServiceDescriptorTable
;
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
);
739 /* Initialize the Suspend APC */
740 KeInitializeApc(&Thread
->SuspendApc
,
742 OriginalApcEnvironment
,
749 /* Initialize the Suspend Semaphore */
750 KeInitializeSemaphore(&Thread
->SuspendSemaphore
, 0, 2);
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
;
761 /* Link the two wait lists together */
762 TimerWaitBlock
->WaitListEntry
.Flink
= &Timer
->Header
.WaitListHead
;
763 TimerWaitBlock
->WaitListEntry
.Blink
= &Timer
->Header
.WaitListHead
;
768 /* Check if we have a kernel stack */
771 /* We don't, allocate one */
772 KernelStack
= (PVOID
)((ULONG_PTR
)MmCreateKernelStack(FALSE
) +
774 if (!KernelStack
) return STATUS_INSUFFICIENT_RESOURCES
;
776 /* Remember for later */
777 AllocatedStack
= TRUE
;
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
;
787 MmUpdatePageDir((PEPROCESS
)Process
,
788 (PVOID
)Thread
->StackLimit
,
790 MmUpdatePageDir((PEPROCESS
)Process
, (PVOID
)Thread
, sizeof(ETHREAD
));
792 /* Enter SEH to avoid crashes due to user mode */
793 Status
= STATUS_SUCCESS
;
796 /* Initalize the Thread Context */
797 Ke386InitThreadWithContext(Thread
,
805 /* Set failure status */
806 Status
= STATUS_UNSUCCESSFUL
;
808 /* Check if a stack was allocated */
811 /* Delete the stack */
812 MmDeleteKernelStack(Thread
->StackBase
, FALSE
);
813 Thread
->InitialStack
= NULL
;
818 /* Set the Thread to initalized */
819 Thread
->State
= Initialized
;
825 KeInitializeThread(IN PKPROCESS Process
,
826 IN OUT PKTHREAD Thread
,
827 IN PKSYSTEM_ROUTINE SystemRoutine
,
828 IN PKSTART_ROUTINE StartRoutine
,
829 IN PVOID StartContext
,
832 IN PVOID KernelStack
)
834 /* Initailize and start the thread on success */
835 if (NT_SUCCESS(KeInitThread(Thread
,
845 KeStartThread(Thread
);
851 KeUninitThread(IN PKTHREAD Thread
)
853 /* Delete the stack */
854 MmDeleteKernelStack(Thread
->StackBase
, FALSE
);
855 Thread
->InitialStack
= NULL
;
858 /* PUBLIC FUNCTIONS **********************************************************/
865 KeCapturePersistentThreadState(IN PVOID CurrentThread
,
871 IN PVOID ThreadState
)
879 #undef KeGetCurrentThread
882 KeGetCurrentThread(VOID
)
884 /* Return the current thread on this PCR */
885 return _KeGetCurrentThread();
891 #undef KeGetPreviousMode
894 KeGetPreviousMode(VOID
)
896 /* Return the previous mode of this thread */
897 return _KeGetPreviousMode();
905 KeQueryRuntimeThread(IN PKTHREAD Thread
,
908 ASSERT_THREAD(Thread
);
910 /* Return the User Time */
911 *UserTime
= Thread
->UserTime
;
913 /* Return the Kernel Time */
914 return Thread
->KernelTime
;
922 KeSetKernelStackSwapEnable(IN BOOLEAN Enable
)
924 BOOLEAN PreviousState
;
925 PKTHREAD Thread
= KeGetCurrentThread();
928 PreviousState
= Thread
->EnableStackSwap
;
931 Thread
->EnableStackSwap
= Enable
;
933 /* Return Old State */
934 return PreviousState
;
942 KeQueryPriorityThread(IN PKTHREAD Thread
)
944 ASSERT_THREAD(Thread
);
946 /* Return the current priority */
947 return Thread
->Priority
;
955 KeRevertToUserAffinityThread(VOID
)
959 PKTHREAD NextThread
, CurrentThread
= KeGetCurrentThread();
960 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
961 ASSERT(CurrentThread
->SystemAffinityActive
!= FALSE
);
963 /* Lock the Dispatcher Database */
964 OldIrql
= KiAcquireDispatcherLock();
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
;
971 /* Get the current PRCB and check if it doesn't match this affinity */
972 Prcb
= KeGetCurrentPrcb();
973 if (!(Prcb
->SetMember
& CurrentThread
->Affinity
))
976 KiAcquirePrcbLock(Prcb
);
979 /* Check if there's no next thread scheduled */
980 if (!Prcb
->NextThread
)
982 /* Select a new thread and set it on standby */
983 NextThread
= KiSelectNextThread(Prcb
);
984 NextThread
->State
= Standby
;
985 Prcb
->NextThread
= NextThread
;
988 /* We need to dispatch a new thread */
990 CurrentThread
->WaitIrql
= OldIrql
;
991 KiDispatchThreadNoLock(Ready
);
992 KeLowerIrql(OldIrql
);
996 /* Release the PRCB lock */
997 KiReleasePrcbLock(Prcb
);
1000 /* Unlock dispatcher database */
1001 KiReleaseDispatcherLock(OldIrql
);
1009 KeSetIdealProcessorThread(IN PKTHREAD Thread
,
1012 CCHAR OldIdealProcessor
;
1014 ASSERT(Processor
<= MAXIMUM_PROCESSORS
);
1016 /* Lock the Dispatcher Database */
1017 OldIrql
= KiAcquireDispatcherLock();
1019 /* Save Old Ideal Processor */
1020 OldIdealProcessor
= Thread
->UserIdealProcessor
;
1022 /* Make sure a valid CPU was given */
1023 if (Processor
< MAXIMUM_PROCESSORS
)
1025 /* Check if the user ideal CPU is in the affinity */
1026 if (Thread
->UserIdealProcessor
& AFFINITY_MASK(Processor
))
1028 /* Set the ideal processor */
1029 Thread
->IdealProcessor
= Processor
;
1031 /* Check if system affinity is used */
1032 if (!Thread
->SystemAffinityActive
)
1034 /* It's not, so update the user CPU too */
1035 Thread
->UserIdealProcessor
= Processor
;
1040 /* Release dispatcher lock and return the old ideal CPU */
1041 KiReleaseDispatcherLock(OldIrql
);
1042 return OldIdealProcessor
;
1050 KeSetSystemAffinityThread(IN KAFFINITY Affinity
)
1054 PKTHREAD NextThread
, CurrentThread
= KeGetCurrentThread();
1055 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
1056 ASSERT((Affinity
& KeActiveProcessors
) != 0);
1058 /* Lock the Dispatcher Database */
1059 OldIrql
= KiAcquireDispatcherLock();
1061 /* Restore the affinity and enable system affinity */
1062 CurrentThread
->Affinity
= Affinity
;
1063 CurrentThread
->SystemAffinityActive
= TRUE
;
1065 /* Check if the ideal processor is part of the affinity */
1067 if (!(Affinity
& AFFINITY_MASK(CurrentThread
->IdealProcessor
)))
1069 ULONG AffinitySet
, NodeMask
;
1071 /* It's not! Get the PRCB */
1072 Prcb
= KiProcessorBlock
[CurrentThread
->IdealProcessor
];
1074 /* Calculate the affinity set */
1075 AffinitySet
= KeActiveProcessors
& Affinity
;
1076 NodeMask
= Prcb
->ParentNode
->ProcessorMask
& AffinitySet
;
1079 /* Use the Node set instead */
1080 AffinitySet
= NodeMask
;
1083 /* Calculate the ideal CPU from the affinity set */
1084 BitScanReverse(&NodeMask
, AffinitySet
);
1085 CurrentThread
->IdealProcessor
= (UCHAR
)NodeMask
;
1089 /* Get the current PRCB and check if it doesn't match this affinity */
1090 Prcb
= KeGetCurrentPrcb();
1091 if (!(Prcb
->SetMember
& CurrentThread
->Affinity
))
1094 KiAcquirePrcbLock(Prcb
);
1096 #ifdef NEW_SCHEDULER
1097 /* Check if there's no next thread scheduled */
1098 if (!Prcb
->NextThread
)
1100 /* Select a new thread and set it on standby */
1101 NextThread
= KiSelectNextThread(Prcb
);
1102 NextThread
->State
= Standby
;
1103 Prcb
->NextThread
= NextThread
;
1106 /* We need to dispatch a new thread */
1108 CurrentThread
->WaitIrql
= OldIrql
;
1109 KiDispatchThreadNoLock(Ready
);
1110 KeLowerIrql(OldIrql
);
1114 /* Release the PRCB lock */
1115 KiReleasePrcbLock(Prcb
);
1118 /* Unlock dispatcher database */
1119 KiReleaseDispatcherLock(OldIrql
);
1127 KeSetBasePriorityThread(IN PKTHREAD Thread
,
1131 KPRIORITY OldBasePriority
, Priority
, BasePriority
;
1134 BOOLEAN Released
= FALSE
;
1135 ASSERT_THREAD(Thread
);
1136 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
1138 /* Get the process */
1139 Process
= Thread
->ApcState
.Process
;
1141 /* Lock the Dispatcher Database */
1142 OldIrql
= KiAcquireDispatcherLock();
1144 /* Lock the thread */
1145 KiAcquireThreadLock(Thread
);
1147 /* Save the old base priority and increment */
1148 OldBasePriority
= Thread
->BasePriority
;
1149 OldIncrement
= OldBasePriority
- Process
->BasePriority
;
1151 /* If priority saturation happened, use the saturated increment */
1152 if (Thread
->Saturation
) OldIncrement
= (HIGH_PRIORITY
+ 1) / 2 *
1155 /* Now check if saturation is being used for the new value */
1156 if (abs(Increment
) >= ((HIGH_PRIORITY
+ 1) / 2))
1158 /* Check if we need positive or negative saturation */
1159 Thread
->Saturation
= (Increment
> 0) ? 1 : -1;
1162 /* Normalize the Base Priority */
1163 BasePriority
= Process
->BasePriority
+ Increment
;
1164 if (Process
->BasePriority
>= LOW_REALTIME_PRIORITY
)
1166 /* Check if it's too low */
1167 if (BasePriority
< LOW_REALTIME_PRIORITY
)
1169 /* Set it to the lowest real time level */
1170 BasePriority
= LOW_REALTIME_PRIORITY
;
1173 /* Check if it's too high */
1174 if (BasePriority
> HIGH_PRIORITY
) BasePriority
= HIGH_PRIORITY
;
1176 /* We are at real time, so use the raw base priority */
1177 Priority
= BasePriority
;
1181 /* Check if it's entering the real time range */
1182 if (BasePriority
>= LOW_REALTIME_PRIORITY
)
1184 /* Set it to the highest dynamic level */
1185 BasePriority
= LOW_REALTIME_PRIORITY
- 1;
1188 /* Check if it's too low and normalize it */
1189 if (BasePriority
<= LOW_PRIORITY
) BasePriority
= 1;
1191 /* Check if Saturation is used */
1192 if (Thread
->Saturation
)
1194 /* Use the raw base priority */
1195 Priority
= BasePriority
;
1199 /* Otherwise, calculate the new priority */
1200 Priority
= KiComputeNewPriority(Thread
);
1202 /* Check if it entered the real-time range */
1203 if (Priority
>= LOW_REALTIME_PRIORITY
)
1205 /* Normalize it down to the highest dynamic priority */
1206 Priority
= LOW_REALTIME_PRIORITY
- 1;
1211 /* Finally set the new base priority */
1212 Thread
->BasePriority
= (SCHAR
)BasePriority
;
1214 /* Reset the decrements */
1215 Thread
->PriorityDecrement
= 0;
1217 /* Check if we're changing priority after all */
1218 if (Priority
!= Thread
->Priority
)
1220 /* Reset the quantum and do the actual priority modification */
1221 Thread
->Quantum
= Thread
->QuantumReset
;
1222 KiSetPriorityThread(Thread
, Priority
, &Released
);
1225 /* Release thread lock */
1226 KiReleaseThreadLock(Thread
);
1228 /* Check if lock was released */
1231 /* Release the dispatcher database */
1232 KiReleaseDispatcherLock(OldIrql
);
1236 /* Lower IRQL only */
1237 KeLowerIrql(OldIrql
);
1240 /* Return old increment */
1241 return OldIncrement
;
1249 KeSetAffinityThread(IN PKTHREAD Thread
,
1250 IN KAFFINITY Affinity
)
1253 KAFFINITY OldAffinity
;
1255 ASSERT_THREAD(Thread
);
1256 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
1258 /* Lock the dispatcher database */
1259 OldIrql
= KiAcquireDispatcherLock();
1261 /* Call the internal function */
1262 OldAffinity
= KiSetAffinityThread(Thread
, Affinity
, &Released
);
1264 /* Check if lock was released */
1267 /* Release the dispatcher database */
1268 KiReleaseDispatcherLock(OldIrql
);
1272 /* Lower IRQL only */
1273 KeLowerIrql(OldIrql
);
1276 /* Return old affinity */
1285 KeSetPriorityThread(IN PKTHREAD Thread
,
1286 IN KPRIORITY Priority
)
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
));
1295 /* Lock the Dispatcher Database */
1296 OldIrql
= KiAcquireDispatcherLock();
1298 /* Lock the thread */
1299 KiAcquireThreadLock(Thread
);
1301 /* Save the old Priority */
1302 OldPriority
= Thread
->Priority
;
1304 /* Make sure that an actual change is being done */
1305 if (OldPriority
!= Priority
)
1307 /* Reset the Quantum and Decrements */
1308 Thread
->Quantum
= Thread
->QuantumReset
;
1309 Thread
->PriorityDecrement
= 0;
1311 /* Set the new Priority */
1312 KiSetPriorityThread(Thread
, Priority
, &Released
);
1315 /* Release thread lock */
1316 KiReleaseThreadLock(Thread
);
1318 /* Check if lock was released */
1321 /* Release the dispatcher database */
1322 KiReleaseDispatcherLock(OldIrql
);
1326 /* Lower IRQL only */
1327 KeLowerIrql(OldIrql
);
1330 /* Return Old Priority */
1339 KeTerminateThread(IN KPRIORITY Increment
)
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
);
1349 /* Lock the process */
1350 KiAcquireProcessLock(Process
, &LockHandle
);
1352 /* Make sure we won't get Swapped */
1353 KiSetThreadSwapBusy(Thread
);
1355 /* Save the Kernel and User Times */
1356 Process
->KernelTime
+= Thread
->KernelTime
;
1357 Process
->UserTime
+= Thread
->UserTime
;
1359 /* Get the current entry and our Port */
1360 Entry
= (PETHREAD
)PspReaperListHead
.Flink
;
1361 ThreadAddr
= &((PETHREAD
)Thread
)->ReaperLink
;
1363 /* Add it to the reaper's list */
1366 /* Get the list head */
1367 ListHead
= &PspReaperListHead
.Flink
;
1369 /* Link ourselves */
1370 *ThreadAddr
= Entry
;
1373 /* Now try to do the exchange */
1374 Entry
= InterlockedCompareExchangePointer(ListHead
, ThreadAddr
, Entry
);
1376 /* Break out if the change was succesful */
1377 } while (Entry
!= SavedEntry
);
1379 /* Acquire the dispatcher lock */
1380 KiAcquireDispatcherLockAtDpcLevel();
1382 /* Check if the reaper wasn't active */
1385 /* Activate it as a work item, directly through its Queue */
1386 KiInsertQueue(&ExWorkerQueue
[HyperCriticalWorkQueue
].WorkerQueue
,
1387 &PspReaperWorkItem
.List
,
1391 /* Check the thread has an associated queue */
1394 /* Remove it from the list, and handle the queue */
1395 RemoveEntryList(&Thread
->QueueListEntry
);
1396 KiActivateWaiterQueue(Thread
->Queue
);
1399 /* Signal the thread */
1400 Thread
->DispatcherHeader
.SignalState
= TRUE
;
1401 if (!IsListEmpty(&Thread
->DispatcherHeader
.WaitListHead
))
1403 /* Unwait the threads */
1404 KxUnwaitThread(&Thread
->DispatcherHeader
, Increment
);
1407 /* Remove the thread from the list */
1408 RemoveEntryList(&Thread
->ThreadListEntry
);
1410 /* Release the process lock */
1411 KiReleaseProcessLockFromDpcLevel(&LockHandle
);
1413 /* Set us as terminated, decrease the Process's stack count */
1414 Thread
->State
= Terminated
;
1416 /* Decrease stack count */
1417 ASSERT(Process
->StackCount
!= 0);
1418 ASSERT(Process
->State
== ProcessInMemory
);
1419 Process
->StackCount
--;
1420 if (!Process
->StackCount
)
1422 /* FIXME: Swap stacks */
1425 /* Rundown arch-specific parts */
1426 KiRundownThread(Thread
);
1428 /* Swap to a new thread */
1429 KiReleaseDispatcherLockFromDpcLevel();
1430 KiSwapThread(Thread
, KeGetCurrentPrcb());