Skip to content
Snippets Groups Projects
Commit 5fcbfc06 authored by Kevin Hartman's avatar Kevin Hartman
Browse files

Scheduler refactor Pt. 1

* Simplifies scheduling logic, specifically regarding thread status. It should be much clearer which statuses are valid
for a thread at any given point in the system.
* Removes dead code from thread.cpp.
* Moves the implementation of resetting a ThreadContext to the corresponding core's implementation.

Other changes:
* Fixed comments in arm interfaces.
* Updated comments in thread.cpp
* Removed confusing, useless, functions like MakeReady() and ChangeStatus() from thread.cpp.
* Removed stack_size from Thread. In the CTR kernel, the thread's stack would be allocated before thread creation.
parent 848795f3
No related branches found
No related tags found
No related merge requests found
...@@ -85,6 +85,15 @@ public: ...@@ -85,6 +85,15 @@ public:
*/ */
virtual void AddTicks(u64 ticks) = 0; virtual void AddTicks(u64 ticks) = 0;
/**
* Initializes a CPU context for use on this CPU
* @param context Thread context to reset
* @param stack_top Pointer to the top of the stack
* @param entry_point Entry point for execution
* @param arg User argument for thread
*/
virtual void ResetContext(Core::ThreadContext& context, u32 stack_top, u32 entry_point, u32 arg) = 0;
/** /**
* Saves the current CPU context * Saves the current CPU context
* @param ctx Thread context to save * @param ctx Thread context to save
......
...@@ -93,6 +93,16 @@ void ARM_DynCom::ExecuteInstructions(int num_instructions) { ...@@ -93,6 +93,16 @@ void ARM_DynCom::ExecuteInstructions(int num_instructions) {
AddTicks(ticks_executed); AddTicks(ticks_executed);
} }
void ARM_DynCom::ResetContext(Core::ThreadContext& context, u32 stack_top, u32 entry_point, u32 arg) {
memset(&context, 0, sizeof(Core::ThreadContext));
context.cpu_registers[0] = arg;
context.pc = entry_point;
context.sp = stack_top;
context.cpsr = 0x1F; // Usermode
context.mode = 8; // Instructs dyncom CPU core to start execution as if it's "resuming" a thread.
}
void ARM_DynCom::SaveContext(Core::ThreadContext& ctx) { void ARM_DynCom::SaveContext(Core::ThreadContext& ctx) {
memcpy(ctx.cpu_registers, state->Reg, sizeof(ctx.cpu_registers)); memcpy(ctx.cpu_registers, state->Reg, sizeof(ctx.cpu_registers));
memcpy(ctx.fpu_registers, state->ExtReg, sizeof(ctx.fpu_registers)); memcpy(ctx.fpu_registers, state->ExtReg, sizeof(ctx.fpu_registers));
......
...@@ -13,79 +13,24 @@ ...@@ -13,79 +13,24 @@
class ARM_DynCom final : virtual public ARM_Interface { class ARM_DynCom final : virtual public ARM_Interface {
public: public:
ARM_DynCom(); ARM_DynCom();
~ARM_DynCom(); ~ARM_DynCom();
/**
* Set the Program Counter to an address
* @param pc Address to set PC to
*/
void SetPC(u32 pc) override; void SetPC(u32 pc) override;
/*
* Get the current Program Counter
* @return Returns current PC
*/
u32 GetPC() const override; u32 GetPC() const override;
/**
* Get an ARM register
* @param index Register index (0-15)
* @return Returns the value in the register
*/
u32 GetReg(int index) const override; u32 GetReg(int index) const override;
/**
* Set an ARM register
* @param index Register index (0-15)
* @param value Value to set register to
*/
void SetReg(int index, u32 value) override; void SetReg(int index, u32 value) override;
/**
* Get the current CPSR register
* @return Returns the value of the CPSR register
*/
u32 GetCPSR() const override; u32 GetCPSR() const override;
/**
* Set the current CPSR register
* @param cpsr Value to set CPSR to
*/
void SetCPSR(u32 cpsr) override; void SetCPSR(u32 cpsr) override;
/**
* Returns the number of clock ticks since the last reset
* @return Returns number of clock ticks
*/
u64 GetTicks() const override; u64 GetTicks() const override;
/**
* Advance the CPU core by the specified number of ticks (e.g. to simulate CPU execution time)
* @param ticks Number of ticks to advance the CPU core
*/
void AddTicks(u64 ticks) override; void AddTicks(u64 ticks) override;
/** void ResetContext(Core::ThreadContext& context, u32 stack_top, u32 entry_point, u32 arg);
* Saves the current CPU context
* @param ctx Thread context to save
*/
void SaveContext(Core::ThreadContext& ctx) override; void SaveContext(Core::ThreadContext& ctx) override;
/**
* Loads a CPU context
* @param ctx Thread context to load
*/
void LoadContext(const Core::ThreadContext& ctx) override; void LoadContext(const Core::ThreadContext& ctx) override;
/// Prepare core for thread reschedule (if needed to correctly handle state)
void PrepareReschedule() override; void PrepareReschedule() override;
/**
* Executes the given number of instructions
* @param num_instructions Number of instructions to executes
*/
void ExecuteInstructions(int num_instructions) override; void ExecuteInstructions(int num_instructions) override;
private: private:
......
...@@ -153,12 +153,8 @@ void Shutdown() { ...@@ -153,12 +153,8 @@ void Shutdown() {
* @return True on success, otherwise false * @return True on success, otherwise false
*/ */
bool LoadExec(u32 entry_point) { bool LoadExec(u32 entry_point) {
Core::g_app_core->SetPC(entry_point);
// 0x30 is the typical main thread priority I've seen used so far // 0x30 is the typical main thread priority I've seen used so far
g_main_thread = Kernel::SetupMainThread(0x30, Kernel::DEFAULT_STACK_SIZE); g_main_thread = Kernel::SetupMainThread(Kernel::DEFAULT_STACK_SIZE, entry_point, 0x30);
// Setup the idle thread
Kernel::SetupIdleThread();
return true; return true;
} }
......
This diff is collapsed.
...@@ -31,13 +31,13 @@ enum ThreadProcessorId { ...@@ -31,13 +31,13 @@ enum ThreadProcessorId {
}; };
enum ThreadStatus { enum ThreadStatus {
THREADSTATUS_RUNNING = 1, THREADSTATUS_RUNNING, ///< Currently running
THREADSTATUS_READY = 2, THREADSTATUS_READY, ///< Ready to run
THREADSTATUS_WAIT = 4, THREADSTATUS_WAIT_ARB, ///< Waiting on an address arbiter
THREADSTATUS_SUSPEND = 8, THREADSTATUS_WAIT_SLEEP, ///< Waiting due to a SleepThread SVC
THREADSTATUS_DORMANT = 16, THREADSTATUS_WAIT_SYNCH, ///< Waiting due to a WaitSynchronization SVC
THREADSTATUS_DEAD = 32, THREADSTATUS_DORMANT, ///< Created but not yet made ready
THREADSTATUS_WAITSUSPEND = THREADSTATUS_WAIT | THREADSTATUS_SUSPEND THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated
}; };
namespace Kernel { namespace Kernel {
...@@ -46,8 +46,19 @@ class Mutex; ...@@ -46,8 +46,19 @@ class Mutex;
class Thread final : public WaitObject { class Thread final : public WaitObject {
public: public:
/**
* Creates and returns a new thread. The new thread is immediately scheduled
* @param name The friendly name desired for the thread
* @param entry_point The address at which the thread should start execution
* @param priority The thread's priority
* @param arg User data to pass to the thread
* @param processor_id The ID(s) of the processors on which the thread is desired to be run
* @param stack_top The address of the thread's stack top
* @param stack_size The size of the thread's stack
* @return A shared pointer to the newly created thread
*/
static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, s32 priority, static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, s32 priority,
u32 arg, s32 processor_id, VAddr stack_top, u32 stack_size); u32 arg, s32 processor_id, VAddr stack_top);
std::string GetName() const override { return name; } std::string GetName() const override { return name; }
std::string GetTypeName() const override { return "Thread"; } std::string GetTypeName() const override { return "Thread"; }
...@@ -55,22 +66,32 @@ public: ...@@ -55,22 +66,32 @@ public:
static const HandleType HANDLE_TYPE = HandleType::Thread; static const HandleType HANDLE_TYPE = HandleType::Thread;
HandleType GetHandleType() const override { return HANDLE_TYPE; } HandleType GetHandleType() const override { return HANDLE_TYPE; }
inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; }
inline bool IsStopped() const { return (status & THREADSTATUS_DORMANT) != 0; }
inline bool IsReady() const { return (status & THREADSTATUS_READY) != 0; }
inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; }
inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; }
inline bool IsIdle() const { return idle; }
bool ShouldWait() override; bool ShouldWait() override;
void Acquire() override; void Acquire() override;
/**
* Checks if the thread is an idle (stub) thread
* @return True if the thread is an idle (stub) thread, false otherwise
*/
inline bool IsIdle() const { return idle; }
/**
* Gets the thread's current priority
* @return The current thread's priority
*/
s32 GetPriority() const { return current_priority; } s32 GetPriority() const { return current_priority; }
/**
* Sets the thread's current priority
* @param priority The new priority
*/
void SetPriority(s32 priority); void SetPriority(s32 priority);
/**
* Gets the thread's thread ID
* @return The thread's ID
*/
u32 GetThreadId() const { return thread_id; } u32 GetThreadId() const { return thread_id; }
void Stop(const char* reason);
/** /**
* Release an acquired wait object * Release an acquired wait object
...@@ -78,12 +99,14 @@ public: ...@@ -78,12 +99,14 @@ public:
*/ */
void ReleaseWaitObject(WaitObject* wait_object); void ReleaseWaitObject(WaitObject* wait_object);
/// Resumes a thread from waiting by marking it as "ready" /**
* Resumes a thread from waiting
*/
void ResumeFromWait(); void ResumeFromWait();
/** /**
* Schedules an event to wake up the specified thread after the specified delay. * Schedules an event to wake up the specified thread after the specified delay
* @param nanoseconds The time this thread will be allowed to sleep for. * @param nanoseconds The time this thread will be allowed to sleep for
*/ */
void WakeAfterDelay(s64 nanoseconds); void WakeAfterDelay(s64 nanoseconds);
...@@ -99,6 +122,11 @@ public: ...@@ -99,6 +122,11 @@ public:
*/ */
void SetWaitSynchronizationOutput(s32 output); void SetWaitSynchronizationOutput(s32 output);
/**
* Stops a thread, invalidating it from further use
*/
void Stop();
Core::ThreadContext context; Core::ThreadContext context;
u32 thread_id; u32 thread_id;
...@@ -106,7 +134,6 @@ public: ...@@ -106,7 +134,6 @@ public:
u32 status; u32 status;
u32 entry_point; u32 entry_point;
u32 stack_top; u32 stack_top;
u32 stack_size;
s32 initial_priority; s32 initial_priority;
s32 current_priority; s32 current_priority;
...@@ -136,31 +163,49 @@ private: ...@@ -136,31 +163,49 @@ private:
extern SharedPtr<Thread> g_main_thread; extern SharedPtr<Thread> g_main_thread;
/// Sets up the primary application thread /**
SharedPtr<Thread> SetupMainThread(s32 priority, u32 stack_size); * Sets up the primary application thread
* @param stack_size The size of the thread's stack
* @param entry_point The address at which the thread should start execution
* @param priority The priority to give the main thread
* @return A shared pointer to the main thread
*/
SharedPtr<Thread> SetupMainThread(u32 stack_size, u32 entry_point, s32 priority);
/// Reschedules to the next available thread (call after current thread is suspended) /**
* Reschedules to the next available thread (call after current thread is suspended)
*/
void Reschedule(); void Reschedule();
/// Arbitrate the highest priority thread that is waiting /**
* Arbitrate the highest priority thread that is waiting
* @param address The address for which waiting threads should be arbitrated
*/
Thread* ArbitrateHighestPriorityThread(u32 address); Thread* ArbitrateHighestPriorityThread(u32 address);
/// Arbitrate all threads currently waiting... /**
* Arbitrate all threads currently waiting.
* @param address The address for which waiting threads should be arbitrated
*/
void ArbitrateAllThreads(u32 address); void ArbitrateAllThreads(u32 address);
/// Gets the current thread /**
* Gets the current thread
*/
Thread* GetCurrentThread(); Thread* GetCurrentThread();
/// Waits the current thread on a sleep /**
* Waits the current thread on a sleep
*/
void WaitCurrentThread_Sleep(); void WaitCurrentThread_Sleep();
/** /**
* Waits the current thread from a WaitSynchronization call * Waits the current thread from a WaitSynchronization call
* @param wait_object Kernel object that we are waiting on * @param wait_objects Kernel objects that we are waiting on
* @param wait_set_output If true, set the output parameter on thread wakeup (for WaitSynchronizationN only) * @param wait_set_output If true, set the output parameter on thread wakeup (for WaitSynchronizationN only)
* @param wait_all If true, wait on all objects before resuming (for WaitSynchronizationN only) * @param wait_all If true, wait on all objects before resuming (for WaitSynchronizationN only)
*/ */
void WaitCurrentThread_WaitSynchronization(SharedPtr<WaitObject> wait_object, bool wait_set_output, bool wait_all); void WaitCurrentThread_WaitSynchronization(std::vector<SharedPtr<WaitObject>> wait_objects, bool wait_set_output, bool wait_all);
/** /**
* Waits the current thread from an ArbitrateAddress call * Waits the current thread from an ArbitrateAddress call
...@@ -172,14 +217,18 @@ void WaitCurrentThread_ArbitrateAddress(VAddr wait_address); ...@@ -172,14 +217,18 @@ void WaitCurrentThread_ArbitrateAddress(VAddr wait_address);
* Sets up the idle thread, this is a thread that is intended to never execute instructions, * Sets up the idle thread, this is a thread that is intended to never execute instructions,
* only to advance the timing. It is scheduled when there are no other ready threads in the thread queue * only to advance the timing. It is scheduled when there are no other ready threads in the thread queue
* and will try to yield on every call. * and will try to yield on every call.
* @returns The handle of the idle thread * @return The handle of the idle thread
*/ */
SharedPtr<Thread> SetupIdleThread(); SharedPtr<Thread> SetupIdleThread();
/// Initialize threading /**
* Initialize threading
*/
void ThreadingInit(); void ThreadingInit();
/// Shutdown threading /**
* Shutdown threading
*/
void ThreadingShutdown(); void ThreadingShutdown();
} // namespace } // namespace
...@@ -150,7 +150,7 @@ static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) { ...@@ -150,7 +150,7 @@ static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) {
if (object->ShouldWait()) { if (object->ShouldWait()) {
object->AddWaitingThread(Kernel::GetCurrentThread()); object->AddWaitingThread(Kernel::GetCurrentThread());
Kernel::WaitCurrentThread_WaitSynchronization(object, false, false); Kernel::WaitCurrentThread_WaitSynchronization({ object }, false, false);
// Create an event to wake the thread up after the specified nanosecond delay has passed // Create an event to wake the thread up after the specified nanosecond delay has passed
Kernel::GetCurrentThread()->WakeAfterDelay(nano_seconds); Kernel::GetCurrentThread()->WakeAfterDelay(nano_seconds);
...@@ -212,7 +212,6 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou ...@@ -212,7 +212,6 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou
// NOTE: This should deadlock the current thread if no timeout was specified // NOTE: This should deadlock the current thread if no timeout was specified
if (!wait_all) { if (!wait_all) {
wait_thread = true; wait_thread = true;
Kernel::WaitCurrentThread_WaitSynchronization(nullptr, true, wait_all);
} }
} }
...@@ -222,12 +221,17 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou ...@@ -222,12 +221,17 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou
if (wait_thread) { if (wait_thread) {
// Actually wait the current thread on each object if we decided to wait... // Actually wait the current thread on each object if we decided to wait...
std::vector<SharedPtr<Kernel::WaitObject>> wait_objects;
wait_objects.reserve(handle_count);
for (int i = 0; i < handle_count; ++i) { for (int i = 0; i < handle_count; ++i) {
auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); auto object = Kernel::g_handle_table.GetWaitObject(handles[i]);
object->AddWaitingThread(Kernel::GetCurrentThread()); object->AddWaitingThread(Kernel::GetCurrentThread());
Kernel::WaitCurrentThread_WaitSynchronization(object, true, wait_all); wait_objects.push_back(object);
} }
Kernel::WaitCurrentThread_WaitSynchronization(std::move(wait_objects), true, wait_all);
// Create an event to wake the thread up after the specified nanosecond delay has passed // Create an event to wake the thread up after the specified nanosecond delay has passed
Kernel::GetCurrentThread()->WakeAfterDelay(nano_seconds); Kernel::GetCurrentThread()->WakeAfterDelay(nano_seconds);
...@@ -319,7 +323,7 @@ static ResultCode CreateThread(u32* out_handle, u32 priority, u32 entry_point, u ...@@ -319,7 +323,7 @@ static ResultCode CreateThread(u32* out_handle, u32 priority, u32 entry_point, u
} }
CASCADE_RESULT(SharedPtr<Thread> thread, Kernel::Thread::Create( CASCADE_RESULT(SharedPtr<Thread> thread, Kernel::Thread::Create(
name, entry_point, priority, arg, processor_id, stack_top, Kernel::DEFAULT_STACK_SIZE)); name, entry_point, priority, arg, processor_id, stack_top));
CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(thread))); CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(thread)));
LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, "
...@@ -338,7 +342,7 @@ static ResultCode CreateThread(u32* out_handle, u32 priority, u32 entry_point, u ...@@ -338,7 +342,7 @@ static ResultCode CreateThread(u32* out_handle, u32 priority, u32 entry_point, u
static void ExitThread() { static void ExitThread() {
LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::g_app_core->GetPC());
Kernel::GetCurrentThread()->Stop(__func__); Kernel::GetCurrentThread()->Stop();
HLE::Reschedule(__func__); HLE::Reschedule(__func__);
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment