summaryrefslogtreecommitdiff
path: root/src/core/hle/kernel/thread.cpp
diff options
context:
space:
mode:
authorGravatar Subv2016-12-03 22:38:14 -0500
committerGravatar Subv2016-12-03 22:38:14 -0500
commit8634b8cb83755b6c6554faa11c0e488d2ad21f90 (patch)
tree93c2e91659ccd2925210dcffb559213edbd2a64a /src/core/hle/kernel/thread.cpp
parentMerge pull request #2251 from JayFoxRox/remove-version (diff)
downloadyuzu-8634b8cb83755b6c6554faa11c0e488d2ad21f90.tar.gz
yuzu-8634b8cb83755b6c6554faa11c0e488d2ad21f90.tar.xz
yuzu-8634b8cb83755b6c6554faa11c0e488d2ad21f90.zip
Threading: Reworked the way our scheduler works.
Threads will now be awakened when the objects they're waiting on are signaled, instead of repeating the WaitSynchronization call every now and then. The scheduler is now called once after every SVC call, and once after a thread is awakened from sleep by its timeout callback. This new implementation is based off reverse-engineering of the real kernel. See https://gist.github.com/Subv/02f29bd9f1e5deb7aceea1e8f019c8f4 for a more detailed description of how the real kernel handles rescheduling.
Diffstat (limited to 'src/core/hle/kernel/thread.cpp')
-rw-r--r--src/core/hle/kernel/thread.cpp97
1 files changed, 3 insertions, 94 deletions
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 84d6d24c6..49ed9d899 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -120,8 +120,6 @@ void Thread::Stop() {
120 u32 tls_slot = 120 u32 tls_slot =
121 ((tls_address - Memory::TLS_AREA_VADDR) % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE; 121 ((tls_address - Memory::TLS_AREA_VADDR) % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE;
122 Kernel::g_current_process->tls_slots[tls_page].reset(tls_slot); 122 Kernel::g_current_process->tls_slots[tls_page].reset(tls_slot);
123
124 HLE::Reschedule(__func__);
125} 123}
126 124
127Thread* ArbitrateHighestPriorityThread(u32 address) { 125Thread* ArbitrateHighestPriorityThread(u32 address) {
@@ -181,50 +179,6 @@ static void PriorityBoostStarvedThreads() {
181} 179}
182 180
183/** 181/**
184 * Gets the registers for timeout parameter of the next WaitSynchronization call.
185 * @param thread a pointer to the thread that is ready to call WaitSynchronization
186 * @returns a tuple of two register pointers to low and high part of the timeout parameter
187 */
188static std::tuple<u32*, u32*> GetWaitSynchTimeoutParameterRegister(Thread* thread) {
189 bool thumb_mode = (thread->context.cpsr & TBIT) != 0;
190 u16 thumb_inst = Memory::Read16(thread->context.pc & 0xFFFFFFFE);
191 u32 inst = Memory::Read32(thread->context.pc & 0xFFFFFFFC) & 0x0FFFFFFF;
192
193 if ((thumb_mode && thumb_inst == 0xDF24) || (!thumb_mode && inst == 0x0F000024)) {
194 // svc #0x24 (WaitSynchronization1)
195 return std::make_tuple(&thread->context.cpu_registers[2],
196 &thread->context.cpu_registers[3]);
197 } else if ((thumb_mode && thumb_inst == 0xDF25) || (!thumb_mode && inst == 0x0F000025)) {
198 // svc #0x25 (WaitSynchronizationN)
199 return std::make_tuple(&thread->context.cpu_registers[0],
200 &thread->context.cpu_registers[4]);
201 }
202
203 UNREACHABLE();
204}
205
206/**
207 * Updates the WaitSynchronization timeout parameter according to the difference
208 * between ticks of the last WaitSynchronization call and the incoming one.
209 * @param timeout_low a pointer to the register for the low part of the timeout parameter
210 * @param timeout_high a pointer to the register for the high part of the timeout parameter
211 * @param last_tick tick of the last WaitSynchronization call
212 */
213static void UpdateTimeoutParameter(u32* timeout_low, u32* timeout_high, u64 last_tick) {
214 s64 timeout = ((s64)*timeout_high << 32) | *timeout_low;
215
216 if (timeout != -1) {
217 timeout -= cyclesToUs(CoreTiming::GetTicks() - last_tick) * 1000; // in nanoseconds
218
219 if (timeout < 0)
220 timeout = 0;
221
222 *timeout_low = timeout & 0xFFFFFFFF;
223 *timeout_high = timeout >> 32;
224 }
225}
226
227/**
228 * Switches the CPU's active thread context to that of the specified thread 182 * Switches the CPU's active thread context to that of the specified thread
229 * @param new_thread The thread to switch to 183 * @param new_thread The thread to switch to
230 */ 184 */
@@ -254,32 +208,6 @@ static void SwitchContext(Thread* new_thread) {
254 208
255 current_thread = new_thread; 209 current_thread = new_thread;
256 210
257 // If the thread was waited by a svcWaitSynch call, step back PC by one instruction to rerun
258 // the SVC when the thread wakes up. This is necessary to ensure that the thread can acquire
259 // the requested wait object(s) before continuing.
260 if (new_thread->waitsynch_waited) {
261 // CPSR flag indicates CPU mode
262 bool thumb_mode = (new_thread->context.cpsr & TBIT) != 0;
263
264 // SVC instruction is 2 bytes for THUMB, 4 bytes for ARM
265 new_thread->context.pc -= thumb_mode ? 2 : 4;
266
267 // Get the register for timeout parameter
268 u32 *timeout_low, *timeout_high;
269 std::tie(timeout_low, timeout_high) = GetWaitSynchTimeoutParameterRegister(new_thread);
270
271 // Update the timeout parameter
272 UpdateTimeoutParameter(timeout_low, timeout_high, new_thread->last_running_ticks);
273 }
274
275 // Clean up the thread's wait_objects, they'll be restored if needed during
276 // the svcWaitSynchronization call
277 for (size_t i = 0; i < new_thread->wait_objects.size(); ++i) {
278 SharedPtr<WaitObject> object = new_thread->wait_objects[i];
279 object->RemoveWaitingThread(new_thread);
280 }
281 new_thread->wait_objects.clear();
282
283 ready_queue.remove(new_thread->current_priority, new_thread); 211 ready_queue.remove(new_thread->current_priority, new_thread);
284 new_thread->status = THREADSTATUS_RUNNING; 212 new_thread->status = THREADSTATUS_RUNNING;
285 213
@@ -319,17 +247,13 @@ static Thread* PopNextReadyThread() {
319void WaitCurrentThread_Sleep() { 247void WaitCurrentThread_Sleep() {
320 Thread* thread = GetCurrentThread(); 248 Thread* thread = GetCurrentThread();
321 thread->status = THREADSTATUS_WAIT_SLEEP; 249 thread->status = THREADSTATUS_WAIT_SLEEP;
322
323 HLE::Reschedule(__func__);
324} 250}
325 251
326void WaitCurrentThread_WaitSynchronization(std::vector<SharedPtr<WaitObject>> wait_objects, 252void WaitCurrentThread_WaitSynchronization(std::vector<SharedPtr<WaitObject>> wait_objects,
327 bool wait_set_output, bool wait_all) { 253 bool wait_set_output) {
328 Thread* thread = GetCurrentThread(); 254 Thread* thread = GetCurrentThread();
329 thread->wait_set_output = wait_set_output; 255 thread->wait_set_output = wait_set_output;
330 thread->wait_all = wait_all;
331 thread->wait_objects = std::move(wait_objects); 256 thread->wait_objects = std::move(wait_objects);
332 thread->waitsynch_waited = true;
333 thread->status = THREADSTATUS_WAIT_SYNCH; 257 thread->status = THREADSTATUS_WAIT_SYNCH;
334} 258}
335 259
@@ -351,15 +275,11 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
351 return; 275 return;
352 } 276 }
353 277
354 thread->waitsynch_waited = false;
355
356 if (thread->status == THREADSTATUS_WAIT_SYNCH || thread->status == THREADSTATUS_WAIT_ARB) { 278 if (thread->status == THREADSTATUS_WAIT_SYNCH || thread->status == THREADSTATUS_WAIT_ARB) {
279 thread->wait_set_output = false;
357 thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS, 280 thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS,
358 ErrorSummary::StatusChanged, 281 ErrorSummary::StatusChanged,
359 ErrorLevel::Info)); 282 ErrorLevel::Info));
360
361 if (thread->wait_set_output)
362 thread->SetWaitSynchronizationOutput(-1);
363 } 283 }
364 284
365 thread->ResumeFromWait(); 285 thread->ResumeFromWait();
@@ -399,6 +319,7 @@ void Thread::ResumeFromWait() {
399 319
400 ready_queue.push_back(current_priority, this); 320 ready_queue.push_back(current_priority, this);
401 status = THREADSTATUS_READY; 321 status = THREADSTATUS_READY;
322 HLE::Reschedule(__func__);
402} 323}
403 324
404/** 325/**
@@ -494,13 +415,11 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
494 thread->last_running_ticks = CoreTiming::GetTicks(); 415 thread->last_running_ticks = CoreTiming::GetTicks();
495 thread->processor_id = processor_id; 416 thread->processor_id = processor_id;
496 thread->wait_set_output = false; 417 thread->wait_set_output = false;
497 thread->wait_all = false;
498 thread->wait_objects.clear(); 418 thread->wait_objects.clear();
499 thread->wait_address = 0; 419 thread->wait_address = 0;
500 thread->name = std::move(name); 420 thread->name = std::move(name);
501 thread->callback_handle = wakeup_callback_handle_table.Create(thread).MoveFrom(); 421 thread->callback_handle = wakeup_callback_handle_table.Create(thread).MoveFrom();
502 thread->owner_process = g_current_process; 422 thread->owner_process = g_current_process;
503 thread->waitsynch_waited = false;
504 423
505 // Find the next available TLS index, and mark it as used 424 // Find the next available TLS index, and mark it as used
506 auto& tls_slots = Kernel::g_current_process->tls_slots; 425 auto& tls_slots = Kernel::g_current_process->tls_slots;
@@ -555,8 +474,6 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
555 ready_queue.push_back(thread->current_priority, thread.get()); 474 ready_queue.push_back(thread->current_priority, thread.get());
556 thread->status = THREADSTATUS_READY; 475 thread->status = THREADSTATUS_READY;
557 476
558 HLE::Reschedule(__func__);
559
560 return MakeResult<SharedPtr<Thread>>(std::move(thread)); 477 return MakeResult<SharedPtr<Thread>>(std::move(thread));
561} 478}
562 479
@@ -619,14 +536,6 @@ void Reschedule() {
619 536
620 HLE::DoneRescheduling(); 537 HLE::DoneRescheduling();
621 538
622 // Don't bother switching to the same thread.
623 // But if the thread was waiting on objects, we still need to switch it
624 // to perform PC modification, change state to RUNNING, etc.
625 // This occurs in the case when an object the thread is waiting on immediately wakes up
626 // the current thread before Reschedule() is called.
627 if (next == cur && (next == nullptr || next->waitsynch_waited == false))
628 return;
629
630 if (cur && next) { 539 if (cur && next) {
631 LOG_TRACE(Kernel, "context switch %u -> %u", cur->GetObjectId(), next->GetObjectId()); 540 LOG_TRACE(Kernel, "context switch %u -> %u", cur->GetObjectId(), next->GetObjectId());
632 } else if (cur) { 541 } else if (cur) {