2006-11-08 11:47:44 +00:00
|
|
|
/*
|
|
|
|
* PROJECT: ReactOS Kernel
|
|
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
|
|
* FILE: ntoskrnl/ke/thrdschd.c
|
|
|
|
* PURPOSE: Kernel Thread Scheduler (Affinity, Priority, Scheduling)
|
|
|
|
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* INCLUDES ******************************************************************/
|
|
|
|
|
|
|
|
#include <ntoskrnl.h>
|
|
|
|
#define NDEBUG
|
|
|
|
#include <debug.h>
|
|
|
|
|
[HAL/NDK]
- Make Vector parameter in HalEnableSystemInterrupt, HalDisableSystemInterrupt and HalBeginSystemInterrupt an ULONG, not an UCHAR
[NDK]
- 64bit fixes for HANDLE_TABLE, KPROCESS, SECTION_IMAGE_INFORMATION, MMADDRESS_LIST, MMVAD_FLAGS, MMVAD, MMVAD_LONG, MMVAD_SHORT, MEMORY_DESCRIPTOR, MEMORY_ALLOCATION_DESCRIPTOR, LdrVerifyMappedImageMatchesChecksum
- KDPC_DATA::DpcQueueDepth is signed on amd64, unsigned on x86
[NTOSKRNL]
- Fix hundreds of MSVC and amd64 warnings
- add a pragma message to FstubFixupEfiPartition, since it looks broken
- Move portable Ke constants from <arch>/cpu.c to krnlinit.c
- Fixed a bug in amd64 KiGeneralProtectionFaultHandler
svn path=/trunk/; revision=53734
2011-09-18 13:11:45 +00:00
|
|
|
#ifdef _WIN64
|
|
|
|
# define InterlockedOrSetMember(Destination, SetMember) \
|
|
|
|
InterlockedOr64((PLONG64)Destination, SetMember);
|
|
|
|
#else
|
|
|
|
# define InterlockedOrSetMember(Destination, SetMember) \
|
|
|
|
InterlockedOr((PLONG)Destination, SetMember);
|
|
|
|
#endif
|
|
|
|
|
2006-11-08 11:47:44 +00:00
|
|
|
/* GLOBALS *******************************************************************/
|
|
|
|
|
[HAL/NDK]
- Make Vector parameter in HalEnableSystemInterrupt, HalDisableSystemInterrupt and HalBeginSystemInterrupt an ULONG, not an UCHAR
[NDK]
- 64bit fixes for HANDLE_TABLE, KPROCESS, SECTION_IMAGE_INFORMATION, MMADDRESS_LIST, MMVAD_FLAGS, MMVAD, MMVAD_LONG, MMVAD_SHORT, MEMORY_DESCRIPTOR, MEMORY_ALLOCATION_DESCRIPTOR, LdrVerifyMappedImageMatchesChecksum
- KDPC_DATA::DpcQueueDepth is signed on amd64, unsigned on x86
[NTOSKRNL]
- Fix hundreds of MSVC and amd64 warnings
- add a pragma message to FstubFixupEfiPartition, since it looks broken
- Move portable Ke constants from <arch>/cpu.c to krnlinit.c
- Fixed a bug in amd64 KiGeneralProtectionFaultHandler
svn path=/trunk/; revision=53734
2011-09-18 13:11:45 +00:00
|
|
|
ULONG_PTR KiIdleSummary;
|
|
|
|
ULONG_PTR KiIdleSMTSummary;
|
2006-11-08 11:47:44 +00:00
|
|
|
|
|
|
|
/* FUNCTIONS *****************************************************************/
|
|
|
|
|
2008-10-07 17:55:32 +00:00
|
|
|
PKTHREAD
|
|
|
|
FASTCALL
|
|
|
|
KiIdleSchedule(IN PKPRCB Prcb)
|
|
|
|
{
|
|
|
|
/* FIXME: TODO */
|
2009-09-30 20:30:57 +00:00
|
|
|
ASSERTMSG("SMP: Not yet implemented\n", FALSE);
|
2008-10-07 17:55:32 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
FASTCALL
|
|
|
|
KiProcessDeferredReadyList(IN PKPRCB Prcb)
|
|
|
|
{
|
2009-09-30 20:30:57 +00:00
|
|
|
PSINGLE_LIST_ENTRY ListEntry;
|
|
|
|
PKTHREAD Thread;
|
|
|
|
|
|
|
|
/* Make sure there is something on the ready list */
|
|
|
|
ASSERT(Prcb->DeferredReadyListHead.Next != NULL);
|
|
|
|
|
|
|
|
/* Get the first entry and clear the list */
|
|
|
|
ListEntry = Prcb->DeferredReadyListHead.Next;
|
|
|
|
Prcb->DeferredReadyListHead.Next = NULL;
|
|
|
|
|
|
|
|
/* Start processing loop */
|
|
|
|
do
|
|
|
|
{
|
|
|
|
/* Get the thread and advance to the next entry */
|
|
|
|
Thread = CONTAINING_RECORD(ListEntry, KTHREAD, SwapListEntry);
|
|
|
|
ListEntry = ListEntry->Next;
|
|
|
|
|
|
|
|
/* Make the thread ready */
|
|
|
|
KiDeferredReadyThread(Thread);
|
|
|
|
} while (ListEntry != NULL);
|
|
|
|
|
|
|
|
/* Make sure the ready list is still empty */
|
|
|
|
ASSERT(Prcb->DeferredReadyListHead.Next == NULL);
|
2008-10-07 17:55:32 +00:00
|
|
|
}
|
|
|
|
|
2007-01-17 20:44:37 +00:00
|
|
|
VOID
|
|
|
|
FASTCALL
|
|
|
|
KiQueueReadyThread(IN PKTHREAD Thread,
|
|
|
|
IN PKPRCB Prcb)
|
|
|
|
{
|
|
|
|
/* Call the macro. We keep the API for compatibility with ASM code */
|
|
|
|
KxQueueReadyThread(Thread, Prcb);
|
|
|
|
}
|
|
|
|
|
2007-01-17 21:53:45 +00:00
|
|
|
VOID
|
2009-09-30 20:30:57 +00:00
|
|
|
FASTCALL
|
2007-01-17 21:53:45 +00:00
|
|
|
KiDeferredReadyThread(IN PKTHREAD Thread)
|
|
|
|
{
|
|
|
|
PKPRCB Prcb;
|
|
|
|
BOOLEAN Preempted;
|
|
|
|
ULONG Processor = 0;
|
|
|
|
KPRIORITY OldPriority;
|
|
|
|
PKTHREAD NextThread;
|
|
|
|
|
|
|
|
/* Sanity checks */
|
|
|
|
ASSERT(Thread->State == DeferredReady);
|
|
|
|
ASSERT((Thread->Priority >= 0) && (Thread->Priority <= HIGH_PRIORITY));
|
|
|
|
|
|
|
|
/* Check if we have any adjusts to do */
|
|
|
|
if (Thread->AdjustReason == AdjustBoost)
|
|
|
|
{
|
|
|
|
/* Lock the thread */
|
|
|
|
KiAcquireThreadLock(Thread);
|
|
|
|
|
|
|
|
/* Check if the priority is low enough to qualify for boosting */
|
|
|
|
if ((Thread->Priority <= Thread->AdjustIncrement) &&
|
|
|
|
(Thread->Priority < (LOW_REALTIME_PRIORITY - 3)) &&
|
|
|
|
!(Thread->DisableBoost))
|
|
|
|
{
|
|
|
|
/* Calculate the new priority based on the adjust increment */
|
|
|
|
OldPriority = min(Thread->AdjustIncrement + 1,
|
2008-09-12 11:13:15 +00:00
|
|
|
LOW_REALTIME_PRIORITY - 3);
|
2007-01-17 21:53:45 +00:00
|
|
|
|
|
|
|
/* Make sure we're not decreasing outside of the priority range */
|
|
|
|
ASSERT((Thread->PriorityDecrement >= 0) &&
|
|
|
|
(Thread->PriorityDecrement <= Thread->Priority));
|
|
|
|
|
|
|
|
/* Calculate the new priority decrement based on the boost */
|
|
|
|
Thread->PriorityDecrement += ((SCHAR)OldPriority - Thread->Priority);
|
|
|
|
|
|
|
|
/* Again verify that this decrement is valid */
|
|
|
|
ASSERT((Thread->PriorityDecrement >= 0) &&
|
|
|
|
(Thread->PriorityDecrement <= OldPriority));
|
|
|
|
|
|
|
|
/* Set the new priority */
|
|
|
|
Thread->Priority = (SCHAR)OldPriority;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We need 4 quanta, make sure we have them, then decrease by one */
|
|
|
|
if (Thread->Quantum < 4) Thread->Quantum = 4;
|
|
|
|
Thread->Quantum--;
|
|
|
|
|
|
|
|
/* Make sure the priority is still valid */
|
|
|
|
ASSERT((Thread->Priority >= 0) && (Thread->Priority <= HIGH_PRIORITY));
|
|
|
|
|
|
|
|
/* Release the lock and clear the adjust reason */
|
|
|
|
KiReleaseThreadLock(Thread);
|
|
|
|
Thread->AdjustReason = AdjustNone;
|
|
|
|
}
|
|
|
|
else if (Thread->AdjustReason == AdjustUnwait)
|
|
|
|
{
|
|
|
|
/* Acquire the thread lock and check if this is a real-time thread */
|
|
|
|
KiAcquireThreadLock(Thread);
|
|
|
|
if (Thread->Priority < LOW_REALTIME_PRIORITY)
|
|
|
|
{
|
|
|
|
/* It's not real time, but is it time critical? */
|
|
|
|
if (Thread->BasePriority >= (LOW_REALTIME_PRIORITY - 2))
|
|
|
|
{
|
|
|
|
/* It is, so simply reset its quantum */
|
|
|
|
Thread->Quantum = Thread->QuantumReset;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Has the priority been adjusted previously? */
|
|
|
|
if (!(Thread->PriorityDecrement) && (Thread->AdjustIncrement))
|
|
|
|
{
|
|
|
|
/* Yes, reset its quantum */
|
|
|
|
Thread->Quantum = Thread->QuantumReset;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Wait code already handles quantum adjustment during APCs */
|
|
|
|
if (Thread->WaitStatus != STATUS_KERNEL_APC)
|
|
|
|
{
|
|
|
|
/* Decrease the quantum by one and check if we're out */
|
|
|
|
if (--Thread->Quantum <= 0)
|
|
|
|
{
|
|
|
|
/* We are, reset the quantum and get a new priority */
|
|
|
|
Thread->Quantum = Thread->QuantumReset;
|
|
|
|
Thread->Priority = KiComputeNewPriority(Thread, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now check if we have no decrement and boosts are enabled */
|
|
|
|
if (!(Thread->PriorityDecrement) && !(Thread->DisableBoost))
|
|
|
|
{
|
|
|
|
/* Make sure we have an increment */
|
|
|
|
ASSERT(Thread->AdjustIncrement >= 0);
|
|
|
|
|
|
|
|
/* Calculate the new priority after the increment */
|
|
|
|
OldPriority = Thread->BasePriority + Thread->AdjustIncrement;
|
|
|
|
|
2015-09-23 05:10:58 +00:00
|
|
|
/* Check if this is a foreground process */
|
|
|
|
if (CONTAINING_RECORD(Thread->ApcState.Process, EPROCESS, Pcb)->
|
|
|
|
Vm.Flags.MemoryPriority == MEMORY_PRIORITY_FOREGROUND)
|
|
|
|
{
|
|
|
|
/* Apply the foreground boost */
|
|
|
|
OldPriority += PsPrioritySeparation;
|
|
|
|
}
|
|
|
|
|
2007-01-17 21:53:45 +00:00
|
|
|
/* Check if this new priority is higher */
|
|
|
|
if (OldPriority > Thread->Priority)
|
|
|
|
{
|
|
|
|
/* Make sure we don't go into the real time range */
|
|
|
|
if (OldPriority >= LOW_REALTIME_PRIORITY)
|
|
|
|
{
|
|
|
|
/* Normalize it back down one notch */
|
|
|
|
OldPriority = LOW_REALTIME_PRIORITY - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if the priority is higher then the boosted base */
|
|
|
|
if (OldPriority > (Thread->BasePriority +
|
|
|
|
Thread->AdjustIncrement))
|
|
|
|
{
|
|
|
|
/* Setup a priority decrement to nullify the boost */
|
|
|
|
Thread->PriorityDecrement = ((SCHAR)OldPriority -
|
|
|
|
Thread->BasePriority -
|
|
|
|
Thread->AdjustIncrement);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Make sure that the priority decrement is valid */
|
|
|
|
ASSERT((Thread->PriorityDecrement >= 0) &&
|
|
|
|
(Thread->PriorityDecrement <= OldPriority));
|
|
|
|
|
|
|
|
/* Set this new priority */
|
|
|
|
Thread->Priority = (SCHAR)OldPriority;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* It's a real-time thread, so just reset its quantum */
|
|
|
|
Thread->Quantum = Thread->QuantumReset;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Make sure the priority makes sense */
|
|
|
|
ASSERT((Thread->Priority >= 0) && (Thread->Priority <= HIGH_PRIORITY));
|
|
|
|
|
|
|
|
/* Release the thread lock and reset the adjust reason */
|
|
|
|
KiReleaseThreadLock(Thread);
|
|
|
|
Thread->AdjustReason = AdjustNone;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear thread preemption status and save current values */
|
|
|
|
Preempted = Thread->Preempted;
|
|
|
|
OldPriority = Thread->Priority;
|
|
|
|
Thread->Preempted = FALSE;
|
|
|
|
|
2009-09-30 20:30:57 +00:00
|
|
|
/* Queue the thread on CPU 0 and get the PRCB and lock it */
|
2007-01-17 21:53:45 +00:00
|
|
|
Thread->NextProcessor = 0;
|
|
|
|
Prcb = KiProcessorBlock[0];
|
2009-09-30 20:30:57 +00:00
|
|
|
KiAcquirePrcbLock(Prcb);
|
2007-01-17 21:53:45 +00:00
|
|
|
|
|
|
|
/* Check if we have an idle summary */
|
|
|
|
if (KiIdleSummary)
|
|
|
|
{
|
|
|
|
/* Clear it and set this thread as the next one */
|
|
|
|
KiIdleSummary = 0;
|
|
|
|
Thread->State = Standby;
|
|
|
|
Prcb->NextThread = Thread;
|
2009-09-30 20:30:57 +00:00
|
|
|
|
|
|
|
/* Unlock the PRCB and return */
|
|
|
|
KiReleasePrcbLock(Prcb);
|
2007-01-17 21:53:45 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set the CPU number */
|
|
|
|
Thread->NextProcessor = (UCHAR)Processor;
|
|
|
|
|
|
|
|
/* Get the next scheduled thread */
|
|
|
|
NextThread = Prcb->NextThread;
|
|
|
|
if (NextThread)
|
|
|
|
{
|
|
|
|
/* Sanity check */
|
|
|
|
ASSERT(NextThread->State == Standby);
|
|
|
|
|
|
|
|
/* Check if priority changed */
|
|
|
|
if (OldPriority > NextThread->Priority)
|
|
|
|
{
|
|
|
|
/* Preempt the thread */
|
|
|
|
NextThread->Preempted = TRUE;
|
|
|
|
|
|
|
|
/* Put this one as the next one */
|
|
|
|
Thread->State = Standby;
|
|
|
|
Prcb->NextThread = Thread;
|
|
|
|
|
|
|
|
/* Set it in deferred ready mode */
|
|
|
|
NextThread->State = DeferredReady;
|
|
|
|
NextThread->DeferredProcessor = Prcb->Number;
|
|
|
|
KiReleasePrcbLock(Prcb);
|
|
|
|
KiDeferredReadyThread(NextThread);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Set the next thread as the current thread */
|
|
|
|
NextThread = Prcb->CurrentThread;
|
|
|
|
if (OldPriority > NextThread->Priority)
|
|
|
|
{
|
|
|
|
/* Preempt it if it's already running */
|
|
|
|
if (NextThread->State == Running) NextThread->Preempted = TRUE;
|
|
|
|
|
|
|
|
/* Set the thread on standby and as the next thread */
|
|
|
|
Thread->State = Standby;
|
|
|
|
Prcb->NextThread = Thread;
|
|
|
|
|
|
|
|
/* Release the lock */
|
|
|
|
KiReleasePrcbLock(Prcb);
|
|
|
|
|
|
|
|
/* Check if we're running on another CPU */
|
|
|
|
if (KeGetCurrentProcessorNumber() != Thread->NextProcessor)
|
|
|
|
{
|
|
|
|
/* We are, send an IPI */
|
2008-11-01 11:44:04 +00:00
|
|
|
KiIpiSend(AFFINITY_MASK(Thread->NextProcessor), IPI_DPC);
|
2007-01-17 21:53:45 +00:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Sanity check */
|
|
|
|
ASSERT((OldPriority >= 0) && (OldPriority <= HIGH_PRIORITY));
|
|
|
|
|
|
|
|
/* Set this thread as ready */
|
|
|
|
Thread->State = Ready;
|
|
|
|
Thread->WaitTime = KeTickCount.LowPart;
|
|
|
|
|
|
|
|
/* Insert this thread in the appropriate order */
|
|
|
|
Preempted ? InsertHeadList(&Prcb->DispatcherReadyListHead[OldPriority],
|
|
|
|
&Thread->WaitListEntry) :
|
|
|
|
InsertTailList(&Prcb->DispatcherReadyListHead[OldPriority],
|
|
|
|
&Thread->WaitListEntry);
|
|
|
|
|
|
|
|
/* Update the ready summary */
|
|
|
|
Prcb->ReadySummary |= PRIORITY_MASK(OldPriority);
|
|
|
|
|
|
|
|
/* Sanity check */
|
|
|
|
ASSERT(OldPriority == Thread->Priority);
|
|
|
|
|
|
|
|
/* Release the lock */
|
|
|
|
KiReleasePrcbLock(Prcb);
|
|
|
|
}
|
|
|
|
|
2007-01-17 01:56:15 +00:00
|
|
|
PKTHREAD
|
|
|
|
FASTCALL
|
|
|
|
KiSelectNextThread(IN PKPRCB Prcb)
|
|
|
|
{
|
|
|
|
PKTHREAD Thread;
|
|
|
|
|
|
|
|
/* Select a ready thread */
|
|
|
|
Thread = KiSelectReadyThread(0, Prcb);
|
|
|
|
if (!Thread)
|
|
|
|
{
|
|
|
|
/* Didn't find any, get the current idle thread */
|
|
|
|
Thread = Prcb->IdleThread;
|
|
|
|
|
|
|
|
/* Enable idle scheduling */
|
[HAL/NDK]
- Make Vector parameter in HalEnableSystemInterrupt, HalDisableSystemInterrupt and HalBeginSystemInterrupt an ULONG, not an UCHAR
[NDK]
- 64bit fixes for HANDLE_TABLE, KPROCESS, SECTION_IMAGE_INFORMATION, MMADDRESS_LIST, MMVAD_FLAGS, MMVAD, MMVAD_LONG, MMVAD_SHORT, MEMORY_DESCRIPTOR, MEMORY_ALLOCATION_DESCRIPTOR, LdrVerifyMappedImageMatchesChecksum
- KDPC_DATA::DpcQueueDepth is signed on amd64, unsigned on x86
[NTOSKRNL]
- Fix hundreds of MSVC and amd64 warnings
- add a pragma message to FstubFixupEfiPartition, since it looks broken
- Move portable Ke constants from <arch>/cpu.c to krnlinit.c
- Fixed a bug in amd64 KiGeneralProtectionFaultHandler
svn path=/trunk/; revision=53734
2011-09-18 13:11:45 +00:00
|
|
|
InterlockedOrSetMember(&KiIdleSummary, Prcb->SetMember);
|
2007-01-17 01:56:15 +00:00
|
|
|
Prcb->IdleSchedule = TRUE;
|
|
|
|
|
|
|
|
/* FIXME: SMT support */
|
2009-09-30 20:30:57 +00:00
|
|
|
ASSERTMSG("SMP: Not yet implemented\n", FALSE);
|
2007-01-17 01:56:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Sanity checks and return the thread */
|
|
|
|
ASSERT(Thread != NULL);
|
|
|
|
ASSERT((Thread->BasePriority == 0) || (Thread->Priority != 0));
|
|
|
|
return Thread;
|
|
|
|
}
|
|
|
|
|
2010-01-13 22:35:43 +00:00
|
|
|
LONG_PTR
|
2006-11-08 11:47:44 +00:00
|
|
|
FASTCALL
|
|
|
|
KiSwapThread(IN PKTHREAD CurrentThread,
|
|
|
|
IN PKPRCB Prcb)
|
|
|
|
{
|
2007-01-17 21:53:45 +00:00
|
|
|
BOOLEAN ApcState = FALSE;
|
|
|
|
KIRQL WaitIrql;
|
|
|
|
LONG_PTR WaitStatus;
|
|
|
|
PKTHREAD NextThread;
|
2006-11-08 11:47:44 +00:00
|
|
|
ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL);
|
|
|
|
|
2007-01-17 21:53:45 +00:00
|
|
|
/* Acquire the PRCB lock */
|
|
|
|
KiAcquirePrcbLock(Prcb);
|
|
|
|
|
|
|
|
/* Get the next thread */
|
|
|
|
NextThread = Prcb->NextThread;
|
|
|
|
if (NextThread)
|
|
|
|
{
|
|
|
|
/* Already got a thread, set it up */
|
|
|
|
Prcb->NextThread = NULL;
|
|
|
|
Prcb->CurrentThread = NextThread;
|
|
|
|
NextThread->State = Running;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Try to find a ready thread */
|
|
|
|
NextThread = KiSelectReadyThread(0, Prcb);
|
|
|
|
if (NextThread)
|
|
|
|
{
|
|
|
|
/* Switch to it */
|
|
|
|
Prcb->CurrentThread = NextThread;
|
|
|
|
NextThread->State = Running;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Set the idle summary */
|
[HAL/NDK]
- Make Vector parameter in HalEnableSystemInterrupt, HalDisableSystemInterrupt and HalBeginSystemInterrupt an ULONG, not an UCHAR
[NDK]
- 64bit fixes for HANDLE_TABLE, KPROCESS, SECTION_IMAGE_INFORMATION, MMADDRESS_LIST, MMVAD_FLAGS, MMVAD, MMVAD_LONG, MMVAD_SHORT, MEMORY_DESCRIPTOR, MEMORY_ALLOCATION_DESCRIPTOR, LdrVerifyMappedImageMatchesChecksum
- KDPC_DATA::DpcQueueDepth is signed on amd64, unsigned on x86
[NTOSKRNL]
- Fix hundreds of MSVC and amd64 warnings
- add a pragma message to FstubFixupEfiPartition, since it looks broken
- Move portable Ke constants from <arch>/cpu.c to krnlinit.c
- Fixed a bug in amd64 KiGeneralProtectionFaultHandler
svn path=/trunk/; revision=53734
2011-09-18 13:11:45 +00:00
|
|
|
InterlockedOrSetMember(&KiIdleSummary, Prcb->SetMember);
|
2007-01-17 01:56:15 +00:00
|
|
|
|
2007-01-17 21:53:45 +00:00
|
|
|
/* Schedule the idle thread */
|
|
|
|
NextThread = Prcb->IdleThread;
|
|
|
|
Prcb->CurrentThread = NextThread;
|
|
|
|
NextThread->State = Running;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Sanity check and release the PRCB */
|
|
|
|
ASSERT(CurrentThread != Prcb->IdleThread);
|
|
|
|
KiReleasePrcbLock(Prcb);
|
|
|
|
|
|
|
|
/* Save the wait IRQL */
|
|
|
|
WaitIrql = CurrentThread->WaitIrql;
|
|
|
|
|
|
|
|
/* Swap contexts */
|
2010-11-08 11:56:22 +00:00
|
|
|
ApcState = KiSwapContext(WaitIrql, CurrentThread);
|
2007-01-17 21:53:45 +00:00
|
|
|
|
|
|
|
/* Get the wait status */
|
|
|
|
WaitStatus = CurrentThread->WaitStatus;
|
2006-11-08 11:47:44 +00:00
|
|
|
|
|
|
|
/* Check if we need to deliver APCs */
|
|
|
|
if (ApcState)
|
|
|
|
{
|
|
|
|
/* Lower to APC_LEVEL */
|
|
|
|
KeLowerIrql(APC_LEVEL);
|
|
|
|
|
|
|
|
/* Deliver APCs */
|
|
|
|
KiDeliverApc(KernelMode, NULL, NULL);
|
2007-01-17 21:53:45 +00:00
|
|
|
ASSERT(WaitIrql == 0);
|
2006-11-08 11:47:44 +00:00
|
|
|
}
|
|
|
|
|
2007-01-17 21:53:45 +00:00
|
|
|
/* Lower IRQL back to what it was and return the wait status */
|
|
|
|
KeLowerIrql(WaitIrql);
|
|
|
|
return WaitStatus;
|
2006-11-08 11:47:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
NTAPI
|
|
|
|
KiReadyThread(IN PKTHREAD Thread)
|
|
|
|
{
|
|
|
|
IN PKPROCESS Process = Thread->ApcState.Process;
|
|
|
|
|
|
|
|
/* Check if the process is paged out */
|
|
|
|
if (Process->State != ProcessInMemory)
|
|
|
|
{
|
|
|
|
/* We don't page out processes in ROS */
|
2008-08-24 15:48:05 +00:00
|
|
|
ASSERT(FALSE);
|
2006-11-08 11:47:44 +00:00
|
|
|
}
|
|
|
|
else if (!Thread->KernelStackResident)
|
|
|
|
{
|
|
|
|
/* Increase the stack count */
|
|
|
|
ASSERT(Process->StackCount != MAXULONG_PTR);
|
|
|
|
Process->StackCount++;
|
|
|
|
|
|
|
|
/* Set the thread to transition */
|
|
|
|
ASSERT(Thread->State != Transition);
|
|
|
|
Thread->State = Transition;
|
|
|
|
|
|
|
|
/* The stack is always resident in ROS */
|
2008-08-24 15:48:05 +00:00
|
|
|
ASSERT(FALSE);
|
2006-11-08 11:47:44 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Insert the thread on the deferred ready list */
|
|
|
|
KiInsertDeferredReadyList(Thread);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
2007-01-17 01:56:15 +00:00
|
|
|
NTAPI
|
2006-11-08 11:47:44 +00:00
|
|
|
KiAdjustQuantumThread(IN PKTHREAD Thread)
|
|
|
|
{
|
2007-01-16 20:49:40 +00:00
|
|
|
PKPRCB Prcb = KeGetCurrentPrcb();
|
2007-01-17 21:53:45 +00:00
|
|
|
PKTHREAD NextThread;
|
2007-01-16 20:49:40 +00:00
|
|
|
|
|
|
|
/* Acquire thread and PRCB lock */
|
|
|
|
KiAcquireThreadLock(Thread);
|
|
|
|
KiAcquirePrcbLock(Prcb);
|
2006-11-08 11:47:44 +00:00
|
|
|
|
|
|
|
/* Don't adjust for RT threads */
|
|
|
|
if ((Thread->Priority < LOW_REALTIME_PRIORITY) &&
|
2007-01-16 20:49:40 +00:00
|
|
|
(Thread->BasePriority < (LOW_REALTIME_PRIORITY - 2)))
|
2006-11-08 11:47:44 +00:00
|
|
|
{
|
|
|
|
/* Decrease Quantum by one and see if we've ran out */
|
|
|
|
if (--Thread->Quantum <= 0)
|
|
|
|
{
|
|
|
|
/* Return quantum */
|
|
|
|
Thread->Quantum = Thread->QuantumReset;
|
|
|
|
|
|
|
|
/* Calculate new Priority */
|
2007-01-16 20:49:40 +00:00
|
|
|
Thread->Priority = KiComputeNewPriority(Thread, 1);
|
2006-11-08 11:47:44 +00:00
|
|
|
|
2007-01-16 20:49:40 +00:00
|
|
|
/* Check if there's no next thread scheduled */
|
|
|
|
if (!Prcb->NextThread)
|
2006-11-08 11:47:44 +00:00
|
|
|
{
|
2007-01-17 01:56:15 +00:00
|
|
|
/* Select a ready thread and check if we found one */
|
|
|
|
NextThread = KiSelectReadyThread(Thread->Priority, Prcb);
|
|
|
|
if (NextThread)
|
|
|
|
{
|
|
|
|
/* Set it on standby and switch to it */
|
|
|
|
NextThread->State = Standby;
|
|
|
|
Prcb->NextThread = NextThread;
|
|
|
|
}
|
2006-11-08 11:47:44 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2007-01-16 20:49:40 +00:00
|
|
|
/* This thread can be preempted again */
|
|
|
|
Thread->Preempted = FALSE;
|
2006-11-08 11:47:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-01-16 20:49:40 +00:00
|
|
|
/* Release locks */
|
|
|
|
KiReleasePrcbLock(Prcb);
|
|
|
|
KiReleaseThreadLock(Thread);
|
2007-01-17 20:44:37 +00:00
|
|
|
KiExitDispatcher(Thread->WaitIrql);
|
2006-11-08 11:47:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
2007-01-17 21:53:45 +00:00
|
|
|
FASTCALL
|
2007-01-16 20:49:40 +00:00
|
|
|
KiSetPriorityThread(IN PKTHREAD Thread,
|
2007-01-17 21:53:45 +00:00
|
|
|
IN KPRIORITY Priority)
|
2006-11-08 11:47:44 +00:00
|
|
|
{
|
2007-01-17 01:56:15 +00:00
|
|
|
PKPRCB Prcb;
|
|
|
|
ULONG Processor;
|
|
|
|
BOOLEAN RequestInterrupt = FALSE;
|
|
|
|
KPRIORITY OldPriority;
|
|
|
|
PKTHREAD NewThread;
|
2007-01-16 20:49:40 +00:00
|
|
|
ASSERT((Priority >= 0) && (Priority <= HIGH_PRIORITY));
|
2006-11-08 11:47:44 +00:00
|
|
|
|
|
|
|
/* Check if priority changed */
|
2007-01-17 01:56:15 +00:00
|
|
|
if (Thread->Priority != Priority)
|
2006-11-08 11:47:44 +00:00
|
|
|
{
|
2007-01-17 01:56:15 +00:00
|
|
|
/* Loop priority setting in case we need to start over */
|
|
|
|
for (;;)
|
2006-11-08 11:47:44 +00:00
|
|
|
{
|
2007-01-17 01:56:15 +00:00
|
|
|
/* Choose action based on thread's state */
|
|
|
|
if (Thread->State == Ready)
|
2006-11-08 11:47:44 +00:00
|
|
|
{
|
2007-01-17 01:56:15 +00:00
|
|
|
/* Make sure we're not on the ready queue */
|
2007-01-17 20:44:37 +00:00
|
|
|
if (!Thread->ProcessReadyQueue)
|
2006-11-08 11:47:44 +00:00
|
|
|
{
|
2007-01-17 01:56:15 +00:00
|
|
|
/* Get the PRCB for the thread and lock it */
|
|
|
|
Processor = Thread->NextProcessor;
|
|
|
|
Prcb = KiProcessorBlock[Processor];
|
|
|
|
KiAcquirePrcbLock(Prcb);
|
|
|
|
|
|
|
|
/* Make sure the thread is still ready and on this CPU */
|
|
|
|
if ((Thread->State == Ready) &&
|
|
|
|
(Thread->NextProcessor == Prcb->Number))
|
2006-11-08 11:47:44 +00:00
|
|
|
{
|
2007-01-17 01:56:15 +00:00
|
|
|
/* Sanity check */
|
|
|
|
ASSERT((Prcb->ReadySummary &
|
|
|
|
PRIORITY_MASK(Thread->Priority)));
|
|
|
|
|
|
|
|
/* Remove it from the current queue */
|
|
|
|
if (RemoveEntryList(&Thread->WaitListEntry))
|
|
|
|
{
|
|
|
|
/* Update the ready summary */
|
2007-01-17 21:53:45 +00:00
|
|
|
Prcb->ReadySummary ^= PRIORITY_MASK(Thread->
|
|
|
|
Priority);
|
2007-01-17 01:56:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Update priority */
|
|
|
|
Thread->Priority = (SCHAR)Priority;
|
|
|
|
|
|
|
|
/* Re-insert it at its current priority */
|
|
|
|
KiInsertDeferredReadyList(Thread);
|
|
|
|
|
|
|
|
/* Release the PRCB Lock */
|
|
|
|
KiReleasePrcbLock(Prcb);
|
|
|
|
}
|
2006-11-08 11:47:44 +00:00
|
|
|
else
|
|
|
|
{
|
2007-01-17 01:56:15 +00:00
|
|
|
/* Release the lock and loop again */
|
|
|
|
KiReleasePrcbLock(Prcb);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* It's already on the ready queue, just update priority */
|
|
|
|
Thread->Priority = (SCHAR)Priority;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (Thread->State == Standby)
|
|
|
|
{
|
|
|
|
/* Get the PRCB for the thread and lock it */
|
|
|
|
Processor = Thread->NextProcessor;
|
|
|
|
Prcb = KiProcessorBlock[Processor];
|
|
|
|
KiAcquirePrcbLock(Prcb);
|
|
|
|
|
|
|
|
/* Check if we're still the next thread to run */
|
|
|
|
if (Thread == Prcb->NextThread)
|
|
|
|
{
|
|
|
|
/* Get the old priority and update ours */
|
|
|
|
OldPriority = Thread->Priority;
|
|
|
|
Thread->Priority = (SCHAR)Priority;
|
|
|
|
|
|
|
|
/* Check if there was a change */
|
|
|
|
if (Priority < OldPriority)
|
|
|
|
{
|
|
|
|
/* Find a new thread */
|
|
|
|
NewThread = KiSelectReadyThread(Priority + 1, Prcb);
|
|
|
|
if (NewThread)
|
2006-11-08 11:47:44 +00:00
|
|
|
{
|
2007-01-17 01:56:15 +00:00
|
|
|
/* Found a new one, set it on standby */
|
|
|
|
NewThread->State = Standby;
|
|
|
|
Prcb->NextThread = NewThread;
|
2006-11-08 11:47:44 +00:00
|
|
|
|
2007-01-17 01:56:15 +00:00
|
|
|
/* Dispatch our thread */
|
|
|
|
KiInsertDeferredReadyList(Thread);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Release the PRCB lock */
|
|
|
|
KiReleasePrcbLock(Prcb);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Release the lock and try again */
|
|
|
|
KiReleasePrcbLock(Prcb);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (Thread->State == Running)
|
|
|
|
{
|
|
|
|
/* Get the PRCB for the thread and lock it */
|
|
|
|
Processor = Thread->NextProcessor;
|
|
|
|
Prcb = KiProcessorBlock[Processor];
|
|
|
|
KiAcquirePrcbLock(Prcb);
|
|
|
|
|
|
|
|
/* Check if we're still the current thread running */
|
|
|
|
if (Thread == Prcb->CurrentThread)
|
|
|
|
{
|
|
|
|
/* Get the old priority and update ours */
|
|
|
|
OldPriority = Thread->Priority;
|
|
|
|
Thread->Priority = (SCHAR)Priority;
|
|
|
|
|
|
|
|
/* Check if there was a change and there's no new thread */
|
|
|
|
if ((Priority < OldPriority) && !(Prcb->NextThread))
|
|
|
|
{
|
|
|
|
/* Find a new thread */
|
|
|
|
NewThread = KiSelectReadyThread(Priority + 1, Prcb);
|
|
|
|
if (NewThread)
|
|
|
|
{
|
|
|
|
/* Found a new one, set it on standby */
|
|
|
|
NewThread->State = Standby;
|
|
|
|
Prcb->NextThread = NewThread;
|
|
|
|
|
|
|
|
/* Request an interrupt */
|
|
|
|
RequestInterrupt = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Release the lock and check if we need an interrupt */
|
|
|
|
KiReleasePrcbLock(Prcb);
|
|
|
|
if (RequestInterrupt)
|
|
|
|
{
|
|
|
|
/* Check if we're running on another CPU */
|
|
|
|
if (KeGetCurrentProcessorNumber() != Processor)
|
|
|
|
{
|
|
|
|
/* We are, send an IPI */
|
2008-11-01 11:44:04 +00:00
|
|
|
KiIpiSend(AFFINITY_MASK(Processor), IPI_DPC);
|
2007-01-17 01:56:15 +00:00
|
|
|
}
|
2006-11-08 11:47:44 +00:00
|
|
|
}
|
|
|
|
}
|
2007-01-17 01:56:15 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Thread changed, release lock and restart */
|
|
|
|
KiReleasePrcbLock(Prcb);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (Thread->State == DeferredReady)
|
|
|
|
{
|
|
|
|
/* FIXME: TODO */
|
|
|
|
DPRINT1("Deferred state not yet supported\n");
|
2008-08-24 15:48:05 +00:00
|
|
|
ASSERT(FALSE);
|
2007-01-17 01:56:15 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Any other state, just change priority */
|
|
|
|
Thread->Priority = (SCHAR)Priority;
|
2006-11-08 11:47:44 +00:00
|
|
|
}
|
2007-01-17 01:56:15 +00:00
|
|
|
|
|
|
|
/* If we got here, then thread state was consistent, so bail out */
|
|
|
|
break;
|
2006-11-08 11:47:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
KAFFINITY
|
2007-01-17 01:56:15 +00:00
|
|
|
FASTCALL
|
2006-11-08 11:47:44 +00:00
|
|
|
KiSetAffinityThread(IN PKTHREAD Thread,
|
2007-01-17 01:56:15 +00:00
|
|
|
IN KAFFINITY Affinity)
|
2006-11-08 11:47:44 +00:00
|
|
|
{
|
|
|
|
KAFFINITY OldAffinity;
|
2007-01-17 01:56:15 +00:00
|
|
|
|
|
|
|
/* Get the current affinity */
|
|
|
|
OldAffinity = Thread->UserAffinity;
|
2006-11-08 11:47:44 +00:00
|
|
|
|
|
|
|
/* Make sure that the affinity is valid */
|
|
|
|
if (((Affinity & Thread->ApcState.Process->Affinity) != (Affinity)) ||
|
|
|
|
(!Affinity))
|
|
|
|
{
|
|
|
|
/* Bugcheck the system */
|
|
|
|
KeBugCheck(INVALID_AFFINITY_SET);
|
|
|
|
}
|
|
|
|
|
2007-01-17 01:56:15 +00:00
|
|
|
/* Update the new affinity */
|
2006-11-08 11:47:44 +00:00
|
|
|
Thread->UserAffinity = Affinity;
|
|
|
|
|
2007-01-17 01:56:15 +00:00
|
|
|
/* Check if system affinity is disabled */
|
|
|
|
if (!Thread->SystemAffinityActive)
|
|
|
|
{
|
2014-11-04 20:47:18 +00:00
|
|
|
#ifdef CONFIG_SMP
|
2007-01-17 01:56:15 +00:00
|
|
|
/* FIXME: TODO */
|
|
|
|
DPRINT1("Affinity support disabled!\n");
|
2014-11-04 20:47:18 +00:00
|
|
|
#endif
|
2006-11-08 11:47:44 +00:00
|
|
|
}
|
|
|
|
|
2007-01-17 01:56:15 +00:00
|
|
|
/* Return the old affinity */
|
2006-11-08 11:47:44 +00:00
|
|
|
return OldAffinity;
|
|
|
|
}
|
|
|
|
|
[NTOSKRNL]: It seems NtYieldExecution in Windows heavily biases towards avoiding a yield as much as possible, and thus attempts to locklessly check the PRCB's ready summary. Problem being that the old code was running a bunch of instructions before checking for this ready summary, and doing a double-fs/gs dereference to get the ready summary. Upon reading some WRK documentation, it appears Windows uses a KiGetCurrentReadySummary macro, which WinDBG analysis shows to be an inline that reads the ReadySummary straight from PrcbData with only a single segment read, and no code preceeding it. Although it's "unlikely" to ever really be a problem, it's perhaps best to replicate this behavior since there could be dependencies on it. Comments in file point to PDF with this information (hosted on Microsoft Research's website, publically accessible).
[NTOSKRNL]: Also, fix locking order such that the current PRCB is only read after the IRQL is raised to SYNCH_LEVEL. I'm too tired to tell if this is a requirement, but the PDF shows it being done that way in Windows, so I've re-ordered the operations to match. Intuition would indicate that this shouldn't matter, as fs:1C shouldn't change from under us in the middle of execution, but why argue with public source code?
vicmarcal said it's okay if this breaks the build (I don't have any way to build ReactOS anymore).
svn path=/trunk/; revision=52412
2011-06-21 22:43:55 +00:00
|
|
|
//
|
|
|
|
// This macro exists because NtYieldExecution locklessly attempts to read from
|
|
|
|
// the KPRCB's ready summary, and the usual way of going through KeGetCurrentPrcb
|
|
|
|
// would require getting fs:1C first (or gs), and then doing another dereference.
|
|
|
|
// In an attempt to minimize the amount of instructions and potential race/tear
|
|
|
|
// that could happen, Windows seems to define this as a macro that directly acceses
|
|
|
|
// the ready summary through a single fs: read by going through the KPCR's PrcbData.
|
|
|
|
//
|
|
|
|
// See http://research.microsoft.com/en-us/collaboration/global/asia-pacific/
|
|
|
|
// programs/trk_case4_process-thread_management.pdf
|
|
|
|
//
|
|
|
|
// We need this per-arch because sometimes it's Prcb and sometimes PrcbData, and
|
|
|
|
// because on x86 it's FS, and on x64 it's GS (not sure what it is on ARM/PPC).
|
|
|
|
//
|
|
|
|
#ifdef _M_IX86
|
|
|
|
#define KiGetCurrentReadySummary() __readfsdword(FIELD_OFFSET(KIPCR, PrcbData.ReadySummary))
|
|
|
|
#elif _M_AMD64
|
|
|
|
#define KiGetCurrentReadySummary() __readgsdword(FIELD_OFFSET(KIPCR, Prcb.ReadySummary))
|
|
|
|
#else
|
2015-05-14 22:31:58 +00:00
|
|
|
#define KiGetCurrentReadySummary() KeGetCurrentPrcb()->ReadySummary
|
[NTOSKRNL]: It seems NtYieldExecution in Windows heavily biases towards avoiding a yield as much as possible, and thus attempts to locklessly check the PRCB's ready summary. Problem being that the old code was running a bunch of instructions before checking for this ready summary, and doing a double-fs/gs dereference to get the ready summary. Upon reading some WRK documentation, it appears Windows uses a KiGetCurrentReadySummary macro, which WinDBG analysis shows to be an inline that reads the ReadySummary straight from PrcbData with only a single segment read, and no code preceeding it. Although it's "unlikely" to ever really be a problem, it's perhaps best to replicate this behavior since there could be dependencies on it. Comments in file point to PDF with this information (hosted on Microsoft Research's website, publically accessible).
[NTOSKRNL]: Also, fix locking order such that the current PRCB is only read after the IRQL is raised to SYNCH_LEVEL. I'm too tired to tell if this is a requirement, but the PDF shows it being done that way in Windows, so I've re-ordered the operations to match. Intuition would indicate that this shouldn't matter, as fs:1C shouldn't change from under us in the middle of execution, but why argue with public source code?
vicmarcal said it's okay if this breaks the build (I don't have any way to build ReactOS anymore).
svn path=/trunk/; revision=52412
2011-06-21 22:43:55 +00:00
|
|
|
#endif
|
|
|
|
|
2006-11-08 11:47:44 +00:00
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
NTSTATUS
|
|
|
|
NTAPI
|
|
|
|
NtYieldExecution(VOID)
|
|
|
|
{
|
[NTOSKRNL]: It seems NtYieldExecution in Windows heavily biases towards avoiding a yield as much as possible, and thus attempts to locklessly check the PRCB's ready summary. Problem being that the old code was running a bunch of instructions before checking for this ready summary, and doing a double-fs/gs dereference to get the ready summary. Upon reading some WRK documentation, it appears Windows uses a KiGetCurrentReadySummary macro, which WinDBG analysis shows to be an inline that reads the ReadySummary straight from PrcbData with only a single segment read, and no code preceeding it. Although it's "unlikely" to ever really be a problem, it's perhaps best to replicate this behavior since there could be dependencies on it. Comments in file point to PDF with this information (hosted on Microsoft Research's website, publically accessible).
[NTOSKRNL]: Also, fix locking order such that the current PRCB is only read after the IRQL is raised to SYNCH_LEVEL. I'm too tired to tell if this is a requirement, but the PDF shows it being done that way in Windows, so I've re-ordered the operations to match. Intuition would indicate that this shouldn't matter, as fs:1C shouldn't change from under us in the middle of execution, but why argue with public source code?
vicmarcal said it's okay if this breaks the build (I don't have any way to build ReactOS anymore).
svn path=/trunk/; revision=52412
2011-06-21 22:43:55 +00:00
|
|
|
NTSTATUS Status;
|
2007-01-17 21:53:45 +00:00
|
|
|
KIRQL OldIrql;
|
[NTOSKRNL]: It seems NtYieldExecution in Windows heavily biases towards avoiding a yield as much as possible, and thus attempts to locklessly check the PRCB's ready summary. Problem being that the old code was running a bunch of instructions before checking for this ready summary, and doing a double-fs/gs dereference to get the ready summary. Upon reading some WRK documentation, it appears Windows uses a KiGetCurrentReadySummary macro, which WinDBG analysis shows to be an inline that reads the ReadySummary straight from PrcbData with only a single segment read, and no code preceeding it. Although it's "unlikely" to ever really be a problem, it's perhaps best to replicate this behavior since there could be dependencies on it. Comments in file point to PDF with this information (hosted on Microsoft Research's website, publically accessible).
[NTOSKRNL]: Also, fix locking order such that the current PRCB is only read after the IRQL is raised to SYNCH_LEVEL. I'm too tired to tell if this is a requirement, but the PDF shows it being done that way in Windows, so I've re-ordered the operations to match. Intuition would indicate that this shouldn't matter, as fs:1C shouldn't change from under us in the middle of execution, but why argue with public source code?
vicmarcal said it's okay if this breaks the build (I don't have any way to build ReactOS anymore).
svn path=/trunk/; revision=52412
2011-06-21 22:43:55 +00:00
|
|
|
PKPRCB Prcb;
|
2011-06-21 22:52:02 +00:00
|
|
|
PKTHREAD Thread, NextThread;
|
[NTOSKRNL]: It seems NtYieldExecution in Windows heavily biases towards avoiding a yield as much as possible, and thus attempts to locklessly check the PRCB's ready summary. Problem being that the old code was running a bunch of instructions before checking for this ready summary, and doing a double-fs/gs dereference to get the ready summary. Upon reading some WRK documentation, it appears Windows uses a KiGetCurrentReadySummary macro, which WinDBG analysis shows to be an inline that reads the ReadySummary straight from PrcbData with only a single segment read, and no code preceeding it. Although it's "unlikely" to ever really be a problem, it's perhaps best to replicate this behavior since there could be dependencies on it. Comments in file point to PDF with this information (hosted on Microsoft Research's website, publically accessible).
[NTOSKRNL]: Also, fix locking order such that the current PRCB is only read after the IRQL is raised to SYNCH_LEVEL. I'm too tired to tell if this is a requirement, but the PDF shows it being done that way in Windows, so I've re-ordered the operations to match. Intuition would indicate that this shouldn't matter, as fs:1C shouldn't change from under us in the middle of execution, but why argue with public source code?
vicmarcal said it's okay if this breaks the build (I don't have any way to build ReactOS anymore).
svn path=/trunk/; revision=52412
2011-06-21 22:43:55 +00:00
|
|
|
|
|
|
|
/* NB: No instructions (other than entry code) should preceed this line */
|
2007-01-17 21:53:45 +00:00
|
|
|
|
|
|
|
/* Fail if there's no ready summary */
|
[NTOSKRNL]: It seems NtYieldExecution in Windows heavily biases towards avoiding a yield as much as possible, and thus attempts to locklessly check the PRCB's ready summary. Problem being that the old code was running a bunch of instructions before checking for this ready summary, and doing a double-fs/gs dereference to get the ready summary. Upon reading some WRK documentation, it appears Windows uses a KiGetCurrentReadySummary macro, which WinDBG analysis shows to be an inline that reads the ReadySummary straight from PrcbData with only a single segment read, and no code preceeding it. Although it's "unlikely" to ever really be a problem, it's perhaps best to replicate this behavior since there could be dependencies on it. Comments in file point to PDF with this information (hosted on Microsoft Research's website, publically accessible).
[NTOSKRNL]: Also, fix locking order such that the current PRCB is only read after the IRQL is raised to SYNCH_LEVEL. I'm too tired to tell if this is a requirement, but the PDF shows it being done that way in Windows, so I've re-ordered the operations to match. Intuition would indicate that this shouldn't matter, as fs:1C shouldn't change from under us in the middle of execution, but why argue with public source code?
vicmarcal said it's okay if this breaks the build (I don't have any way to build ReactOS anymore).
svn path=/trunk/; revision=52412
2011-06-21 22:43:55 +00:00
|
|
|
if (!KiGetCurrentReadySummary()) return STATUS_NO_YIELD_PERFORMED;
|
|
|
|
|
|
|
|
/* Now get the current thread, set the status... */
|
|
|
|
Status = STATUS_NO_YIELD_PERFORMED;
|
|
|
|
Thread = KeGetCurrentThread();
|
2007-01-17 21:53:45 +00:00
|
|
|
|
[NTOSKRNL]: It seems NtYieldExecution in Windows heavily biases towards avoiding a yield as much as possible, and thus attempts to locklessly check the PRCB's ready summary. Problem being that the old code was running a bunch of instructions before checking for this ready summary, and doing a double-fs/gs dereference to get the ready summary. Upon reading some WRK documentation, it appears Windows uses a KiGetCurrentReadySummary macro, which WinDBG analysis shows to be an inline that reads the ReadySummary straight from PrcbData with only a single segment read, and no code preceeding it. Although it's "unlikely" to ever really be a problem, it's perhaps best to replicate this behavior since there could be dependencies on it. Comments in file point to PDF with this information (hosted on Microsoft Research's website, publically accessible).
[NTOSKRNL]: Also, fix locking order such that the current PRCB is only read after the IRQL is raised to SYNCH_LEVEL. I'm too tired to tell if this is a requirement, but the PDF shows it being done that way in Windows, so I've re-ordered the operations to match. Intuition would indicate that this shouldn't matter, as fs:1C shouldn't change from under us in the middle of execution, but why argue with public source code?
vicmarcal said it's okay if this breaks the build (I don't have any way to build ReactOS anymore).
svn path=/trunk/; revision=52412
2011-06-21 22:43:55 +00:00
|
|
|
/* Raise IRQL to synch and get the KPRCB now */
|
2007-01-17 21:53:45 +00:00
|
|
|
OldIrql = KeRaiseIrqlToSynchLevel();
|
[NTOSKRNL]: It seems NtYieldExecution in Windows heavily biases towards avoiding a yield as much as possible, and thus attempts to locklessly check the PRCB's ready summary. Problem being that the old code was running a bunch of instructions before checking for this ready summary, and doing a double-fs/gs dereference to get the ready summary. Upon reading some WRK documentation, it appears Windows uses a KiGetCurrentReadySummary macro, which WinDBG analysis shows to be an inline that reads the ReadySummary straight from PrcbData with only a single segment read, and no code preceeding it. Although it's "unlikely" to ever really be a problem, it's perhaps best to replicate this behavior since there could be dependencies on it. Comments in file point to PDF with this information (hosted on Microsoft Research's website, publically accessible).
[NTOSKRNL]: Also, fix locking order such that the current PRCB is only read after the IRQL is raised to SYNCH_LEVEL. I'm too tired to tell if this is a requirement, but the PDF shows it being done that way in Windows, so I've re-ordered the operations to match. Intuition would indicate that this shouldn't matter, as fs:1C shouldn't change from under us in the middle of execution, but why argue with public source code?
vicmarcal said it's okay if this breaks the build (I don't have any way to build ReactOS anymore).
svn path=/trunk/; revision=52412
2011-06-21 22:43:55 +00:00
|
|
|
Prcb = KeGetCurrentPrcb();
|
2007-01-17 21:53:45 +00:00
|
|
|
|
|
|
|
/* Now check if there's still a ready summary */
|
|
|
|
if (Prcb->ReadySummary)
|
|
|
|
{
|
|
|
|
/* Acquire thread and PRCB lock */
|
|
|
|
KiAcquireThreadLock(Thread);
|
|
|
|
KiAcquirePrcbLock(Prcb);
|
|
|
|
|
|
|
|
/* Find a new thread to run if none was selected */
|
|
|
|
if (!Prcb->NextThread) Prcb->NextThread = KiSelectReadyThread(1, Prcb);
|
|
|
|
|
|
|
|
/* Make sure we still have a next thread to schedule */
|
|
|
|
NextThread = Prcb->NextThread;
|
|
|
|
if (NextThread)
|
|
|
|
{
|
|
|
|
/* Reset quantum and recalculate priority */
|
|
|
|
Thread->Quantum = Thread->QuantumReset;
|
|
|
|
Thread->Priority = KiComputeNewPriority(Thread, 1);
|
|
|
|
|
|
|
|
/* Release the thread lock */
|
|
|
|
KiReleaseThreadLock(Thread);
|
|
|
|
|
|
|
|
/* Set context swap busy */
|
|
|
|
KiSetThreadSwapBusy(Thread);
|
|
|
|
|
|
|
|
/* Set the new thread as running */
|
|
|
|
Prcb->NextThread = NULL;
|
|
|
|
Prcb->CurrentThread = NextThread;
|
|
|
|
NextThread->State = Running;
|
|
|
|
|
|
|
|
/* Setup a yield wait and queue the thread */
|
|
|
|
Thread->WaitReason = WrYieldExecution;
|
|
|
|
KxQueueReadyThread(Thread, Prcb);
|
|
|
|
|
|
|
|
/* Make it wait at APC_LEVEL */
|
|
|
|
Thread->WaitIrql = APC_LEVEL;
|
|
|
|
|
|
|
|
/* Sanity check */
|
|
|
|
ASSERT(OldIrql <= DISPATCH_LEVEL);
|
|
|
|
|
|
|
|
/* Swap to new thread */
|
2010-11-08 11:56:22 +00:00
|
|
|
KiSwapContext(APC_LEVEL, Thread);
|
2007-01-17 21:53:45 +00:00
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Release the PRCB and thread lock */
|
|
|
|
KiReleasePrcbLock(Prcb);
|
|
|
|
KiReleaseThreadLock(Thread);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Lower IRQL and return */
|
|
|
|
KeLowerIrql(OldIrql);
|
|
|
|
return Status;
|
2006-11-08 11:47:44 +00:00
|
|
|
}
|