summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/core.cpp9
-rw-r--r--src/core/core.h10
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp91
-rw-r--r--src/core/hle/kernel/address_arbiter.h60
-rw-r--r--src/core/hle/kernel/scheduler.cpp134
-rw-r--r--src/core/hle/kernel/scheduler.h73
-rw-r--r--src/core/hle/kernel/svc.cpp4
-rw-r--r--src/core/hle/kernel/thread.cpp203
-rw-r--r--src/core/hle/kernel/thread.h27
-rw-r--r--src/yuzu/debugger/wait_tree.cpp3
11 files changed, 239 insertions, 379 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index ce68194c5..76f70a844 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -28,8 +28,6 @@ add_library(core STATIC
28 hle/config_mem.h 28 hle/config_mem.h
29 hle/ipc.h 29 hle/ipc.h
30 hle/ipc_helpers.h 30 hle/ipc_helpers.h
31 hle/kernel/address_arbiter.cpp
32 hle/kernel/address_arbiter.h
33 hle/kernel/client_port.cpp 31 hle/kernel/client_port.cpp
34 hle/kernel/client_port.h 32 hle/kernel/client_port.h
35 hle/kernel/client_session.cpp 33 hle/kernel/client_session.cpp
@@ -55,6 +53,8 @@ add_library(core STATIC
55 hle/kernel/process.h 53 hle/kernel/process.h
56 hle/kernel/resource_limit.cpp 54 hle/kernel/resource_limit.cpp
57 hle/kernel/resource_limit.h 55 hle/kernel/resource_limit.h
56 hle/kernel/scheduler.cpp
57 hle/kernel/scheduler.h
58 hle/kernel/server_port.cpp 58 hle/kernel/server_port.cpp
59 hle/kernel/server_port.h 59 hle/kernel/server_port.h
60 hle/kernel/server_session.cpp 60 hle/kernel/server_session.cpp
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 613a98b4c..8c5dd3761 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -133,7 +133,7 @@ void System::Reschedule() {
133 } 133 }
134 134
135 reschedule_pending = false; 135 reschedule_pending = false;
136 Kernel::Reschedule(); 136 Core::System::GetInstance().Scheduler().Reschedule();
137} 137}
138 138
139System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { 139System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
@@ -141,19 +141,20 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
141 141
142 switch (Settings::values.cpu_core) { 142 switch (Settings::values.cpu_core) {
143 case Settings::CpuCore::Unicorn: 143 case Settings::CpuCore::Unicorn:
144 cpu_core = std::make_unique<ARM_Unicorn>(); 144 cpu_core = std::make_shared<ARM_Unicorn>();
145 break; 145 break;
146 case Settings::CpuCore::Dynarmic: 146 case Settings::CpuCore::Dynarmic:
147 default: 147 default:
148#ifdef ARCHITECTURE_x86_64 148#ifdef ARCHITECTURE_x86_64
149 cpu_core = std::make_unique<ARM_Dynarmic>(); 149 cpu_core = std::make_shared<ARM_Dynarmic>();
150#else 150#else
151 cpu_core = std::make_unique<ARM_Unicorn>(); 151 cpu_core = std::make_shared<ARM_Unicorn>();
152 LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); 152 LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
153#endif 153#endif
154 break; 154 break;
155 } 155 }
156 156
157 scheduler = std::make_unique<Kernel::Scheduler>(cpu_core.get());
157 gpu_core = std::make_unique<Tegra::GPU>(); 158 gpu_core = std::make_unique<Tegra::GPU>();
158 159
159 telemetry_session = std::make_unique<Core::TelemetrySession>(); 160 telemetry_session = std::make_unique<Core::TelemetrySession>();
diff --git a/src/core/core.h b/src/core/core.h
index f63cc47cc..ada23b347 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -7,6 +7,7 @@
7#include <memory> 7#include <memory>
8#include <string> 8#include <string>
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/hle/kernel/scheduler.h"
10#include "core/loader/loader.h" 11#include "core/loader/loader.h"
11#include "core/memory.h" 12#include "core/memory.h"
12#include "core/perf_stats.h" 13#include "core/perf_stats.h"
@@ -107,6 +108,10 @@ public:
107 return *gpu_core; 108 return *gpu_core;
108 } 109 }
109 110
111 Kernel::Scheduler& Scheduler() {
112 return *scheduler;
113 }
114
110 PerfStats perf_stats; 115 PerfStats perf_stats;
111 FrameLimiter frame_limiter; 116 FrameLimiter frame_limiter;
112 117
@@ -140,9 +145,8 @@ private:
140 /// AppLoader used to load the current executing application 145 /// AppLoader used to load the current executing application
141 std::unique_ptr<Loader::AppLoader> app_loader; 146 std::unique_ptr<Loader::AppLoader> app_loader;
142 147
143 ///< ARM11 CPU core 148 std::shared_ptr<ARM_Interface> cpu_core;
144 std::unique_ptr<ARM_Interface> cpu_core; 149 std::unique_ptr<Kernel::Scheduler> scheduler;
145
146 std::unique_ptr<Tegra::GPU> gpu_core; 150 std::unique_ptr<Tegra::GPU> gpu_core;
147 151
148 /// When true, signals that a reschedule should happen 152 /// When true, signals that a reschedule should happen
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
deleted file mode 100644
index 776d342f0..000000000
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/common_types.h"
6#include "common/logging/log.h"
7#include "core/hle/kernel/address_arbiter.h"
8#include "core/hle/kernel/errors.h"
9#include "core/hle/kernel/thread.h"
10#include "core/memory.h"
11
12////////////////////////////////////////////////////////////////////////////////////////////////////
13// Kernel namespace
14
15namespace Kernel {
16
17AddressArbiter::AddressArbiter() {}
18AddressArbiter::~AddressArbiter() {}
19
20SharedPtr<AddressArbiter> AddressArbiter::Create(std::string name) {
21 SharedPtr<AddressArbiter> address_arbiter(new AddressArbiter);
22
23 address_arbiter->name = std::move(name);
24
25 return address_arbiter;
26}
27
28ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address, s32 value,
29 u64 nanoseconds) {
30 switch (type) {
31
32 // Signal thread(s) waiting for arbitrate address...
33 case ArbitrationType::Signal:
34 // Negative value means resume all threads
35 if (value < 0) {
36 ArbitrateAllThreads(address);
37 } else {
38 // Resume first N threads
39 for (int i = 0; i < value; i++)
40 ArbitrateHighestPriorityThread(address);
41 }
42 break;
43
44 // Wait current thread (acquire the arbiter)...
45 case ArbitrationType::WaitIfLessThan:
46 if ((s32)Memory::Read32(address) < value) {
47 Kernel::WaitCurrentThread_ArbitrateAddress(address);
48 }
49 break;
50 case ArbitrationType::WaitIfLessThanWithTimeout:
51 if ((s32)Memory::Read32(address) < value) {
52 Kernel::WaitCurrentThread_ArbitrateAddress(address);
53 GetCurrentThread()->WakeAfterDelay(nanoseconds);
54 }
55 break;
56 case ArbitrationType::DecrementAndWaitIfLessThan: {
57 s32 memory_value = Memory::Read32(address);
58 if (memory_value < value) {
59 // Only change the memory value if the thread should wait
60 Memory::Write32(address, (s32)memory_value - 1);
61 Kernel::WaitCurrentThread_ArbitrateAddress(address);
62 }
63 break;
64 }
65 case ArbitrationType::DecrementAndWaitIfLessThanWithTimeout: {
66 s32 memory_value = Memory::Read32(address);
67 if (memory_value < value) {
68 // Only change the memory value if the thread should wait
69 Memory::Write32(address, (s32)memory_value - 1);
70 Kernel::WaitCurrentThread_ArbitrateAddress(address);
71 GetCurrentThread()->WakeAfterDelay(nanoseconds);
72 }
73 break;
74 }
75
76 default:
77 LOG_ERROR(Kernel, "unknown type=%d", type);
78 return ERR_INVALID_ENUM_VALUE_FND;
79 }
80
81 // The calls that use a timeout seem to always return a Timeout error even if they did not put
82 // the thread to sleep
83 if (type == ArbitrationType::WaitIfLessThanWithTimeout ||
84 type == ArbitrationType::DecrementAndWaitIfLessThanWithTimeout) {
85
86 return RESULT_TIMEOUT;
87 }
88 return RESULT_SUCCESS;
89}
90
91} // namespace Kernel
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h
deleted file mode 100644
index f902ddf2d..000000000
--- a/src/core/hle/kernel/address_arbiter.h
+++ /dev/null
@@ -1,60 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "core/hle/kernel/kernel.h"
9#include "core/hle/result.h"
10
11// Address arbiters are an underlying kernel synchronization object that can be created/used via
12// supervisor calls (SVCs). They function as sort of a global lock. Typically, games/other CTR
13// applications use them as an underlying mechanism to implement thread-safe barriers, events, and
14// semphores.
15
16////////////////////////////////////////////////////////////////////////////////////////////////////
17// Kernel namespace
18
19namespace Kernel {
20
21enum class ArbitrationType : u32 {
22 Signal,
23 WaitIfLessThan,
24 DecrementAndWaitIfLessThan,
25 WaitIfLessThanWithTimeout,
26 DecrementAndWaitIfLessThanWithTimeout,
27};
28
29class AddressArbiter final : public Object {
30public:
31 /**
32 * Creates an address arbiter.
33 *
34 * @param name Optional name used for debugging.
35 * @returns The created AddressArbiter.
36 */
37 static SharedPtr<AddressArbiter> Create(std::string name = "Unknown");
38
39 std::string GetTypeName() const override {
40 return "Arbiter";
41 }
42 std::string GetName() const override {
43 return name;
44 }
45
46 static const HandleType HANDLE_TYPE = HandleType::AddressArbiter;
47 HandleType GetHandleType() const override {
48 return HANDLE_TYPE;
49 }
50
51 std::string name; ///< Name of address arbiter object (optional)
52
53 ResultCode ArbitrateAddress(ArbitrationType type, VAddr address, s32 value, u64 nanoseconds);
54
55private:
56 AddressArbiter();
57 ~AddressArbiter() override;
58};
59
60} // namespace Kernel
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
new file mode 100644
index 000000000..235068b22
--- /dev/null
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -0,0 +1,134 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/core_timing.h"
6#include "core/hle/kernel/process.h"
7#include "core/hle/kernel/scheduler.h"
8
9namespace Kernel {
10
11Scheduler::Scheduler(ARM_Interface* cpu_core) : cpu_core(cpu_core) {}
12
13Scheduler::~Scheduler() {
14 for (auto& thread : thread_list) {
15 thread->Stop();
16 }
17}
18
19bool Scheduler::HaveReadyThreads() {
20 return ready_queue.get_first() != nullptr;
21}
22
23Thread* Scheduler::GetCurrentThread() const {
24 return current_thread.get();
25}
26
27Thread* Scheduler::PopNextReadyThread() {
28 Thread* next = nullptr;
29 Thread* thread = GetCurrentThread();
30
31 if (thread && thread->status == THREADSTATUS_RUNNING) {
32 // We have to do better than the current thread.
33 // This call returns null when that's not possible.
34 next = ready_queue.pop_first_better(thread->current_priority);
35 if (!next) {
36 // Otherwise just keep going with the current thread
37 next = thread;
38 }
39 } else {
40 next = ready_queue.pop_first();
41 }
42
43 return next;
44}
45
46void Scheduler::SwitchContext(Thread* new_thread) {
47 Thread* previous_thread = GetCurrentThread();
48
49 // Save context for previous thread
50 if (previous_thread) {
51 previous_thread->last_running_ticks = CoreTiming::GetTicks();
52 cpu_core->SaveContext(previous_thread->context);
53
54 if (previous_thread->status == THREADSTATUS_RUNNING) {
55 // This is only the case when a reschedule is triggered without the current thread
56 // yielding execution (i.e. an event triggered, system core time-sliced, etc)
57 ready_queue.push_front(previous_thread->current_priority, previous_thread);
58 previous_thread->status = THREADSTATUS_READY;
59 }
60 }
61
62 // Load context of new thread
63 if (new_thread) {
64 ASSERT_MSG(new_thread->status == THREADSTATUS_READY,
65 "Thread must be ready to become running.");
66
67 // Cancel any outstanding wakeup events for this thread
68 new_thread->CancelWakeupTimer();
69
70 auto previous_process = Kernel::g_current_process;
71
72 current_thread = new_thread;
73
74 ready_queue.remove(new_thread->current_priority, new_thread);
75 new_thread->status = THREADSTATUS_RUNNING;
76
77 if (previous_process != current_thread->owner_process) {
78 Kernel::g_current_process = current_thread->owner_process;
79 SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table);
80 }
81
82 cpu_core->LoadContext(new_thread->context);
83 cpu_core->SetTlsAddress(new_thread->GetTLSAddress());
84 } else {
85 current_thread = nullptr;
86 // Note: We do not reset the current process and current page table when idling because
87 // technically we haven't changed processes, our threads are just paused.
88 }
89}
90
91void Scheduler::Reschedule() {
92 Thread* cur = GetCurrentThread();
93 Thread* next = PopNextReadyThread();
94
95 if (cur && next) {
96 LOG_TRACE(Kernel, "context switch %u -> %u", cur->GetObjectId(), next->GetObjectId());
97 } else if (cur) {
98 LOG_TRACE(Kernel, "context switch %u -> idle", cur->GetObjectId());
99 } else if (next) {
100 LOG_TRACE(Kernel, "context switch idle -> %u", next->GetObjectId());
101 }
102
103 SwitchContext(next);
104}
105
106void Scheduler::AddThread(SharedPtr<Thread> thread, u32 priority) {
107 thread_list.push_back(thread);
108 ready_queue.prepare(priority);
109}
110
111void Scheduler::RemoveThread(Thread* thread) {
112 thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
113 thread_list.end());
114}
115
116void Scheduler::ScheduleThread(Thread* thread, u32 priority) {
117 ASSERT(thread->status == THREADSTATUS_READY);
118 ready_queue.push_back(priority, thread);
119}
120
121void Scheduler::UnscheduleThread(Thread* thread, u32 priority) {
122 ASSERT(thread->status == THREADSTATUS_READY);
123 ready_queue.remove(priority, thread);
124}
125
126void Scheduler::SetThreadPriority(Thread* thread, u32 priority) {
127 // If thread was ready, adjust queues
128 if (thread->status == THREADSTATUS_READY)
129 ready_queue.move(thread, thread->current_priority, priority);
130 else
131 ready_queue.prepare(priority);
132}
133
134} // namespace Kernel
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h
new file mode 100644
index 000000000..27d0247d6
--- /dev/null
+++ b/src/core/hle/kernel/scheduler.h
@@ -0,0 +1,73 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <vector>
8#include "common/common_types.h"
9#include "common/thread_queue_list.h"
10#include "core/arm/arm_interface.h"
11#include "core/hle/kernel/thread.h"
12
13namespace Kernel {
14
15class Scheduler final {
16public:
17 explicit Scheduler(ARM_Interface* cpu_core);
18 ~Scheduler();
19
20 /// Returns whether there are any threads that are ready to run.
21 bool HaveReadyThreads();
22
23 /// Reschedules to the next available thread (call after current thread is suspended)
24 void Reschedule();
25
26 /// Gets the current running thread
27 Thread* GetCurrentThread() const;
28
29 /// Adds a new thread to the scheduler
30 void AddThread(SharedPtr<Thread> thread, u32 priority);
31
32 /// Removes a thread from the scheduler
33 void RemoveThread(Thread* thread);
34
35 /// Schedules a thread that has become "ready"
36 void ScheduleThread(Thread* thread, u32 priority);
37
38 /// Unschedules a thread that was already scheduled
39 void UnscheduleThread(Thread* thread, u32 priority);
40
41 /// Sets the priority of a thread in the scheduler
42 void SetThreadPriority(Thread* thread, u32 priority);
43
44 /// Returns a list of all threads managed by the scheduler
45 const std::vector<SharedPtr<Thread>>& GetThreadList() const {
46 return thread_list;
47 }
48
49private:
50 /**
51 * Pops and returns the next thread from the thread queue
52 * @return A pointer to the next ready thread
53 */
54 Thread* PopNextReadyThread();
55
56 /**
57 * Switches the CPU's active thread context to that of the specified thread
58 * @param new_thread The thread to switch to
59 */
60 void SwitchContext(Thread* new_thread);
61
62 /// Lists all thread ids that aren't deleted/etc.
63 std::vector<SharedPtr<Thread>> thread_list;
64
65 /// Lists only ready thread ids.
66 Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST + 1> ready_queue;
67
68 SharedPtr<Thread> current_thread = nullptr;
69
70 ARM_Interface* cpu_core;
71};
72
73} // namespace Kernel
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 4d20ef134..cbd5b69aa 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -483,7 +483,7 @@ static void ExitProcess() {
483 g_current_process->status = ProcessStatus::Exited; 483 g_current_process->status = ProcessStatus::Exited;
484 484
485 // Stop all the process threads that are currently waiting for objects. 485 // Stop all the process threads that are currently waiting for objects.
486 auto& thread_list = GetThreadList(); 486 auto& thread_list = Core::System::GetInstance().Scheduler().GetThreadList();
487 for (auto& thread : thread_list) { 487 for (auto& thread : thread_list) {
488 if (thread->owner_process != g_current_process) 488 if (thread->owner_process != g_current_process)
489 continue; 489 continue;
@@ -585,7 +585,7 @@ static void SleepThread(s64 nanoseconds) {
585 585
586 // Don't attempt to yield execution if there are no available threads to run, 586 // Don't attempt to yield execution if there are no available threads to run,
587 // this way we avoid a useless reschedule to the idle thread. 587 // this way we avoid a useless reschedule to the idle thread.
588 if (nanoseconds == 0 && !HaveReadyThreads()) 588 if (nanoseconds == 0 && !Core::System::GetInstance().Scheduler().HaveReadyThreads())
589 return; 589 return;
590 590
591 // Sleep current thread and check for next thread to schedule 591 // Sleep current thread and check for next thread to schedule
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 130b669a0..dd0a8ae48 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -41,14 +41,6 @@ void Thread::Acquire(Thread* thread) {
41// us to simply use a pool index or similar. 41// us to simply use a pool index or similar.
42static Kernel::HandleTable wakeup_callback_handle_table; 42static Kernel::HandleTable wakeup_callback_handle_table;
43 43
44// Lists all thread ids that aren't deleted/etc.
45static std::vector<SharedPtr<Thread>> thread_list;
46
47// Lists only ready thread ids.
48static Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST + 1> ready_queue;
49
50static SharedPtr<Thread> current_thread;
51
52// The first available thread id at startup 44// The first available thread id at startup
53static u32 next_thread_id; 45static u32 next_thread_id;
54 46
@@ -63,10 +55,6 @@ inline static u32 const NewThreadId() {
63Thread::Thread() {} 55Thread::Thread() {}
64Thread::~Thread() {} 56Thread::~Thread() {}
65 57
66Thread* GetCurrentThread() {
67 return current_thread.get();
68}
69
70/** 58/**
71 * Check if the specified thread is waiting on the specified address to be arbitrated 59 * Check if the specified thread is waiting on the specified address to be arbitrated
72 * @param thread The thread to test 60 * @param thread The thread to test
@@ -86,7 +74,7 @@ void Thread::Stop() {
86 // Clean up thread from ready queue 74 // Clean up thread from ready queue
87 // This is only needed when the thread is termintated forcefully (SVC TerminateProcess) 75 // This is only needed when the thread is termintated forcefully (SVC TerminateProcess)
88 if (status == THREADSTATUS_READY) { 76 if (status == THREADSTATUS_READY) {
89 ready_queue.remove(current_priority, this); 77 Core::System::GetInstance().Scheduler().UnscheduleThread(this, current_priority);
90 } 78 }
91 79
92 status = THREADSTATUS_DEAD; 80 status = THREADSTATUS_DEAD;
@@ -109,112 +97,6 @@ void Thread::Stop() {
109 Kernel::g_current_process->tls_slots[tls_page].reset(tls_slot); 97 Kernel::g_current_process->tls_slots[tls_page].reset(tls_slot);
110} 98}
111 99
112Thread* ArbitrateHighestPriorityThread(u32 address) {
113 Thread* highest_priority_thread = nullptr;
114 u32 priority = THREADPRIO_LOWEST;
115
116 // Iterate through threads, find highest priority thread that is waiting to be arbitrated...
117 for (auto& thread : thread_list) {
118 if (!CheckWait_AddressArbiter(thread.get(), address))
119 continue;
120
121 if (thread == nullptr)
122 continue;
123
124 if (thread->current_priority <= priority) {
125 highest_priority_thread = thread.get();
126 priority = thread->current_priority;
127 }
128 }
129
130 // If a thread was arbitrated, resume it
131 if (nullptr != highest_priority_thread) {
132 highest_priority_thread->ResumeFromWait();
133 }
134
135 return highest_priority_thread;
136}
137
138void ArbitrateAllThreads(u32 address) {
139 // Resume all threads found to be waiting on the address
140 for (auto& thread : thread_list) {
141 if (CheckWait_AddressArbiter(thread.get(), address))
142 thread->ResumeFromWait();
143 }
144}
145
146/**
147 * Switches the CPU's active thread context to that of the specified thread
148 * @param new_thread The thread to switch to
149 */
150static void SwitchContext(Thread* new_thread) {
151 Thread* previous_thread = GetCurrentThread();
152
153 // Save context for previous thread
154 if (previous_thread) {
155 previous_thread->last_running_ticks = CoreTiming::GetTicks();
156 Core::CPU().SaveContext(previous_thread->context);
157
158 if (previous_thread->status == THREADSTATUS_RUNNING) {
159 // This is only the case when a reschedule is triggered without the current thread
160 // yielding execution (i.e. an event triggered, system core time-sliced, etc)
161 ready_queue.push_front(previous_thread->current_priority, previous_thread);
162 previous_thread->status = THREADSTATUS_READY;
163 }
164 }
165
166 // Load context of new thread
167 if (new_thread) {
168 ASSERT_MSG(new_thread->status == THREADSTATUS_READY,
169 "Thread must be ready to become running.");
170
171 // Cancel any outstanding wakeup events for this thread
172 CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle);
173
174 auto previous_process = Kernel::g_current_process;
175
176 current_thread = new_thread;
177
178 ready_queue.remove(new_thread->current_priority, new_thread);
179 new_thread->status = THREADSTATUS_RUNNING;
180
181 if (previous_process != current_thread->owner_process) {
182 Kernel::g_current_process = current_thread->owner_process;
183 SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table);
184 }
185
186 Core::CPU().LoadContext(new_thread->context);
187 Core::CPU().SetTlsAddress(new_thread->GetTLSAddress());
188 } else {
189 current_thread = nullptr;
190 // Note: We do not reset the current process and current page table when idling because
191 // technically we haven't changed processes, our threads are just paused.
192 }
193}
194
195/**
196 * Pops and returns the next thread from the thread queue
197 * @return A pointer to the next ready thread
198 */
199static Thread* PopNextReadyThread() {
200 Thread* next;
201 Thread* thread = GetCurrentThread();
202
203 if (thread && thread->status == THREADSTATUS_RUNNING) {
204 // We have to do better than the current thread.
205 // This call returns null when that's not possible.
206 next = ready_queue.pop_first_better(thread->current_priority);
207 if (!next) {
208 // Otherwise just keep going with the current thread
209 next = thread;
210 }
211 } else {
212 next = ready_queue.pop_first();
213 }
214
215 return next;
216}
217
218void WaitCurrentThread_Sleep() { 100void WaitCurrentThread_Sleep() {
219 Thread* thread = GetCurrentThread(); 101 Thread* thread = GetCurrentThread();
220 thread->status = THREADSTATUS_WAIT_SLEEP; 102 thread->status = THREADSTATUS_WAIT_SLEEP;
@@ -229,8 +111,7 @@ void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) {
229void ExitCurrentThread() { 111void ExitCurrentThread() {
230 Thread* thread = GetCurrentThread(); 112 Thread* thread = GetCurrentThread();
231 thread->Stop(); 113 thread->Stop();
232 thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread), 114 Core::System::GetInstance().Scheduler().RemoveThread(thread);
233 thread_list.end());
234} 115}
235 116
236/** 117/**
@@ -308,32 +189,12 @@ void Thread::ResumeFromWait() {
308 189
309 wakeup_callback = nullptr; 190 wakeup_callback = nullptr;
310 191
311 ready_queue.push_back(current_priority, this);
312 status = THREADSTATUS_READY; 192 status = THREADSTATUS_READY;
193 Core::System::GetInstance().Scheduler().ScheduleThread(this, current_priority);
313 Core::System::GetInstance().PrepareReschedule(); 194 Core::System::GetInstance().PrepareReschedule();
314} 195}
315 196
316/** 197/**
317 * Prints the thread queue for debugging purposes
318 */
319static void DebugThreadQueue() {
320 Thread* thread = GetCurrentThread();
321 if (!thread) {
322 LOG_DEBUG(Kernel, "Current: NO CURRENT THREAD");
323 } else {
324 LOG_DEBUG(Kernel, "0x%02X %u (current)", thread->current_priority,
325 GetCurrentThread()->GetObjectId());
326 }
327
328 for (auto& t : thread_list) {
329 u32 priority = ready_queue.contains(t.get());
330 if (priority != -1) {
331 LOG_DEBUG(Kernel, "0x%02X %u", priority, t->GetObjectId());
332 }
333 }
334}
335
336/**
337 * Finds a free location for the TLS section of a thread. 198 * Finds a free location for the TLS section of a thread.
338 * @param tls_slots The TLS page array of the thread's owner process. 199 * @param tls_slots The TLS page array of the thread's owner process.
339 * Returns a tuple of (page, slot, alloc_needed) where: 200 * Returns a tuple of (page, slot, alloc_needed) where:
@@ -400,8 +261,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
400 261
401 SharedPtr<Thread> thread(new Thread); 262 SharedPtr<Thread> thread(new Thread);
402 263
403 thread_list.push_back(thread); 264 Core::System::GetInstance().Scheduler().AddThread(thread, priority);
404 ready_queue.prepare(priority);
405 265
406 thread->thread_id = NewThreadId(); 266 thread->thread_id = NewThreadId();
407 thread->status = THREADSTATUS_DORMANT; 267 thread->status = THREADSTATUS_DORMANT;
@@ -472,12 +332,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
472void Thread::SetPriority(u32 priority) { 332void Thread::SetPriority(u32 priority) {
473 ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST, 333 ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST,
474 "Invalid priority value."); 334 "Invalid priority value.");
475 // If thread was ready, adjust queues 335 Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority);
476 if (status == THREADSTATUS_READY)
477 ready_queue.move(this, current_priority, priority);
478 else
479 ready_queue.prepare(priority);
480
481 nominal_priority = current_priority = priority; 336 nominal_priority = current_priority = priority;
482} 337}
483 338
@@ -491,11 +346,7 @@ void Thread::UpdatePriority() {
491} 346}
492 347
493void Thread::BoostPriority(u32 priority) { 348void Thread::BoostPriority(u32 priority) {
494 // If thread was ready, adjust queues 349 Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority);
495 if (status == THREADSTATUS_READY)
496 ready_queue.move(this, current_priority, priority);
497 else
498 ready_queue.prepare(priority);
499 current_priority = priority; 350 current_priority = priority;
500} 351}
501 352
@@ -521,25 +372,6 @@ SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority,
521 return thread; 372 return thread;
522} 373}
523 374
524bool HaveReadyThreads() {
525 return ready_queue.get_first() != nullptr;
526}
527
528void Reschedule() {
529 Thread* cur = GetCurrentThread();
530 Thread* next = PopNextReadyThread();
531
532 if (cur && next) {
533 LOG_TRACE(Kernel, "context switch %u -> %u", cur->GetObjectId(), next->GetObjectId());
534 } else if (cur) {
535 LOG_TRACE(Kernel, "context switch %u -> idle", cur->GetObjectId());
536 } else if (next) {
537 LOG_TRACE(Kernel, "context switch idle -> %u", next->GetObjectId());
538 }
539
540 SwitchContext(next);
541}
542
543void Thread::SetWaitSynchronizationResult(ResultCode result) { 375void Thread::SetWaitSynchronizationResult(ResultCode result) {
544 context.cpu_registers[0] = result.raw; 376 context.cpu_registers[0] = result.raw;
545} 377}
@@ -562,25 +394,18 @@ VAddr Thread::GetCommandBufferAddress() const {
562 394
563//////////////////////////////////////////////////////////////////////////////////////////////////// 395////////////////////////////////////////////////////////////////////////////////////////////////////
564 396
397/**
398 * Gets the current thread
399 */
400Thread* GetCurrentThread() {
401 return Core::System::GetInstance().Scheduler().GetCurrentThread();
402}
403
565void ThreadingInit() { 404void ThreadingInit() {
566 ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback); 405 ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
567
568 current_thread = nullptr;
569 next_thread_id = 1; 406 next_thread_id = 1;
570} 407}
571 408
572void ThreadingShutdown() { 409void ThreadingShutdown() {}
573 current_thread = nullptr;
574
575 for (auto& t : thread_list) {
576 t->Stop();
577 }
578 thread_list.clear();
579 ready_queue.clear();
580}
581
582const std::vector<SharedPtr<Thread>>& GetThreadList() {
583 return thread_list;
584}
585 410
586} // namespace Kernel 411} // namespace Kernel
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index bbffaf4cf..4fd2fc2f8 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -250,28 +250,6 @@ SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority,
250 SharedPtr<Process> owner_process); 250 SharedPtr<Process> owner_process);
251 251
252/** 252/**
253 * Returns whether there are any threads that are ready to run.
254 */
255bool HaveReadyThreads();
256
257/**
258 * Reschedules to the next available thread (call after current thread is suspended)
259 */
260void Reschedule();
261
262/**
263 * Arbitrate the highest priority thread that is waiting
264 * @param address The address for which waiting threads should be arbitrated
265 */
266Thread* ArbitrateHighestPriorityThread(VAddr address);
267
268/**
269 * Arbitrate all threads currently waiting.
270 * @param address The address for which waiting threads should be arbitrated
271 */
272void ArbitrateAllThreads(VAddr address);
273
274/**
275 * Gets the current thread 253 * Gets the current thread
276 */ 254 */
277Thread* GetCurrentThread(); 255Thread* GetCurrentThread();
@@ -302,9 +280,4 @@ void ThreadingInit();
302 */ 280 */
303void ThreadingShutdown(); 281void ThreadingShutdown();
304 282
305/**
306 * Get a const reference to the thread list for debug use
307 */
308const std::vector<SharedPtr<Thread>>& GetThreadList();
309
310} // namespace Kernel 283} // namespace Kernel
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index e4a6d16ae..7a62f57b5 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -5,6 +5,7 @@
5#include "yuzu/debugger/wait_tree.h" 5#include "yuzu/debugger/wait_tree.h"
6#include "yuzu/util/util.h" 6#include "yuzu/util/util.h"
7 7
8#include "core/core.h"
8#include "core/hle/kernel/condition_variable.h" 9#include "core/hle/kernel/condition_variable.h"
9#include "core/hle/kernel/event.h" 10#include "core/hle/kernel/event.h"
10#include "core/hle/kernel/mutex.h" 11#include "core/hle/kernel/mutex.h"
@@ -50,7 +51,7 @@ std::size_t WaitTreeItem::Row() const {
50} 51}
51 52
52std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList() { 53std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList() {
53 const auto& threads = Kernel::GetThreadList(); 54 const auto& threads = Core::System::GetInstance().Scheduler().GetThreadList();
54 std::vector<std::unique_ptr<WaitTreeThread>> item_list; 55 std::vector<std::unique_ptr<WaitTreeThread>> item_list;
55 item_list.reserve(threads.size()); 56 item_list.reserve(threads.size());
56 for (std::size_t i = 0; i < threads.size(); ++i) { 57 for (std::size_t i = 0; i < threads.size(); ++i) {