diff options
| author | 2017-10-09 23:56:20 -0400 | |
|---|---|---|
| committer | 2017-10-09 23:56:20 -0400 | |
| commit | b1d5db1cf60344b6b081c9d03cb6ccc3264326cd (patch) | |
| tree | fde377c4ba3c0f92c032e6f5ec8627aae37270ef /src/core/hle/kernel/thread.cpp | |
| parent | loader: Various improvements for NSO/NRO loaders. (diff) | |
| parent | Merge pull request #2996 from MerryMage/split-travis (diff) | |
| download | yuzu-b1d5db1cf60344b6b081c9d03cb6ccc3264326cd.tar.gz yuzu-b1d5db1cf60344b6b081c9d03cb6ccc3264326cd.tar.xz yuzu-b1d5db1cf60344b6b081c9d03cb6ccc3264326cd.zip | |
Merge remote-tracking branch 'upstream/master' into nx
# Conflicts:
# src/core/CMakeLists.txt
# src/core/arm/dynarmic/arm_dynarmic.cpp
# src/core/arm/dyncom/arm_dyncom.cpp
# src/core/hle/kernel/process.cpp
# src/core/hle/kernel/thread.cpp
# src/core/hle/kernel/thread.h
# src/core/hle/kernel/vm_manager.cpp
# src/core/loader/3dsx.cpp
# src/core/loader/elf.cpp
# src/core/loader/ncch.cpp
# src/core/memory.cpp
# src/core/memory.h
# src/core/memory_setup.h
Diffstat (limited to 'src/core/hle/kernel/thread.cpp')
| -rw-r--r-- | src/core/hle/kernel/thread.cpp | 68 |
1 files changed, 45 insertions, 23 deletions
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index c01d08ebb..75df49ac2 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -111,7 +111,7 @@ void Thread::Stop() { | |||
| 111 | 111 | ||
| 112 | Thread* ArbitrateHighestPriorityThread(u32 address) { | 112 | Thread* ArbitrateHighestPriorityThread(u32 address) { |
| 113 | Thread* highest_priority_thread = nullptr; | 113 | Thread* highest_priority_thread = nullptr; |
| 114 | s32 priority = THREADPRIO_LOWEST; | 114 | u32 priority = THREADPRIO_LOWEST; |
| 115 | 115 | ||
| 116 | // Iterate through threads, find highest priority thread that is waiting to be arbitrated... | 116 | // Iterate through threads, find highest priority thread that is waiting to be arbitrated... |
| 117 | for (auto& thread : thread_list) { | 117 | for (auto& thread : thread_list) { |
| @@ -171,15 +171,24 @@ static void SwitchContext(Thread* new_thread) { | |||
| 171 | // Cancel any outstanding wakeup events for this thread | 171 | // Cancel any outstanding wakeup events for this thread |
| 172 | CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle); | 172 | CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle); |
| 173 | 173 | ||
| 174 | auto previous_process = Kernel::g_current_process; | ||
| 175 | |||
| 174 | current_thread = new_thread; | 176 | current_thread = new_thread; |
| 175 | 177 | ||
| 176 | ready_queue.remove(new_thread->current_priority, new_thread); | 178 | ready_queue.remove(new_thread->current_priority, new_thread); |
| 177 | new_thread->status = THREADSTATUS_RUNNING; | 179 | new_thread->status = THREADSTATUS_RUNNING; |
| 178 | 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 | |||
| 179 | Core::CPU().LoadContext(new_thread->context); | 186 | Core::CPU().LoadContext(new_thread->context); |
| 180 | Core::CPU().SetCP15Register(CP15_THREAD_URO, new_thread->GetTLSAddress()); | 187 | Core::CPU().SetCP15Register(CP15_THREAD_URO, new_thread->GetTLSAddress()); |
| 181 | } else { | 188 | } else { |
| 182 | current_thread = nullptr; | 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. | ||
| 183 | } | 192 | } |
| 184 | } | 193 | } |
| 185 | 194 | ||
| @@ -238,12 +247,15 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { | |||
| 238 | 247 | ||
| 239 | if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY || | 248 | if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY || |
| 240 | thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB) { | 249 | thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB) { |
| 241 | thread->wait_set_output = false; | 250 | |
| 251 | // Invoke the wakeup callback before clearing the wait objects | ||
| 252 | if (thread->wakeup_callback) | ||
| 253 | thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr); | ||
| 254 | |||
| 242 | // Remove the thread from each of its waiting objects' waitlists | 255 | // Remove the thread from each of its waiting objects' waitlists |
| 243 | for (auto& object : thread->wait_objects) | 256 | for (auto& object : thread->wait_objects) |
| 244 | object->RemoveWaitingThread(thread.get()); | 257 | object->RemoveWaitingThread(thread.get()); |
| 245 | thread->wait_objects.clear(); | 258 | thread->wait_objects.clear(); |
| 246 | thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); | ||
| 247 | } | 259 | } |
| 248 | 260 | ||
| 249 | thread->ResumeFromWait(); | 261 | thread->ResumeFromWait(); |
| @@ -269,6 +281,9 @@ void Thread::ResumeFromWait() { | |||
| 269 | break; | 281 | break; |
| 270 | 282 | ||
| 271 | case THREADSTATUS_READY: | 283 | case THREADSTATUS_READY: |
| 284 | // The thread's wakeup callback must have already been cleared when the thread was first | ||
| 285 | // awoken. | ||
| 286 | ASSERT(wakeup_callback == nullptr); | ||
| 272 | // If the thread is waiting on multiple wait objects, it might be awoken more than once | 287 | // If the thread is waiting on multiple wait objects, it might be awoken more than once |
| 273 | // before actually resuming. We can ignore subsequent wakeups if the thread status has | 288 | // before actually resuming. We can ignore subsequent wakeups if the thread status has |
| 274 | // already been set to THREADSTATUS_READY. | 289 | // already been set to THREADSTATUS_READY. |
| @@ -284,6 +299,8 @@ void Thread::ResumeFromWait() { | |||
| 284 | return; | 299 | return; |
| 285 | } | 300 | } |
| 286 | 301 | ||
| 302 | wakeup_callback = nullptr; | ||
| 303 | |||
| 287 | ready_queue.push_back(current_priority, this); | 304 | ready_queue.push_back(current_priority, this); |
| 288 | status = THREADSTATUS_READY; | 305 | status = THREADSTATUS_READY; |
| 289 | Core::System::GetInstance().PrepareReschedule(); | 306 | Core::System::GetInstance().PrepareReschedule(); |
| @@ -302,7 +319,7 @@ static void DebugThreadQueue() { | |||
| 302 | } | 319 | } |
| 303 | 320 | ||
| 304 | for (auto& t : thread_list) { | 321 | for (auto& t : thread_list) { |
| 305 | s32 priority = ready_queue.contains(t.get()); | 322 | u32 priority = ready_queue.contains(t.get()); |
| 306 | if (priority != -1) { | 323 | if (priority != -1) { |
| 307 | LOG_DEBUG(Kernel, "0x%02X %u", priority, t->GetObjectId()); | 324 | LOG_DEBUG(Kernel, "0x%02X %u", priority, t->GetObjectId()); |
| 308 | } | 325 | } |
| @@ -352,7 +369,8 @@ static void ResetThreadContext(ARM_Interface::ThreadContext& context, VAddr stac | |||
| 352 | } | 369 | } |
| 353 | 370 | ||
| 354 | ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, u32 priority, | 371 | ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, u32 priority, |
| 355 | u32 arg, s32 processor_id, VAddr stack_top) { | 372 | u32 arg, s32 processor_id, VAddr stack_top, |
| 373 | SharedPtr<Process> owner_process) { | ||
| 356 | // Check if priority is in ranged. Lowest priority -> highest priority id. | 374 | // Check if priority is in ranged. Lowest priority -> highest priority id. |
| 357 | if (priority > THREADPRIO_LOWEST) { | 375 | if (priority > THREADPRIO_LOWEST) { |
| 358 | LOG_ERROR(Kernel_SVC, "Invalid thread priority: %d", priority); | 376 | LOG_ERROR(Kernel_SVC, "Invalid thread priority: %d", priority); |
| @@ -366,7 +384,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, | |||
| 366 | 384 | ||
| 367 | // TODO(yuriks): Other checks, returning 0xD9001BEA | 385 | // TODO(yuriks): Other checks, returning 0xD9001BEA |
| 368 | 386 | ||
| 369 | if (!Memory::IsValidVirtualAddress(entry_point)) { | 387 | if (!Memory::IsValidVirtualAddress(*owner_process, entry_point)) { |
| 370 | LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name.c_str(), entry_point); | 388 | LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name.c_str(), entry_point); |
| 371 | // TODO: Verify error | 389 | // TODO: Verify error |
| 372 | return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, | 390 | return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, |
| @@ -385,15 +403,14 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, | |||
| 385 | thread->nominal_priority = thread->current_priority = priority; | 403 | thread->nominal_priority = thread->current_priority = priority; |
| 386 | thread->last_running_ticks = CoreTiming::GetTicks(); | 404 | thread->last_running_ticks = CoreTiming::GetTicks(); |
| 387 | thread->processor_id = processor_id; | 405 | thread->processor_id = processor_id; |
| 388 | thread->wait_set_output = false; | ||
| 389 | thread->wait_objects.clear(); | 406 | thread->wait_objects.clear(); |
| 390 | thread->wait_address = 0; | 407 | thread->wait_address = 0; |
| 391 | thread->name = std::move(name); | 408 | thread->name = std::move(name); |
| 392 | thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap(); | 409 | thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap(); |
| 393 | thread->owner_process = g_current_process; | 410 | thread->owner_process = owner_process; |
| 394 | 411 | ||
| 395 | // Find the next available TLS index, and mark it as used | 412 | // Find the next available TLS index, and mark it as used |
| 396 | auto& tls_slots = Kernel::g_current_process->tls_slots; | 413 | auto& tls_slots = owner_process->tls_slots; |
| 397 | bool needs_allocation = true; | 414 | bool needs_allocation = true; |
| 398 | u32 available_page; // Which allocated page has free space | 415 | u32 available_page; // Which allocated page has free space |
| 399 | u32 available_slot; // Which slot within the page is free | 416 | u32 available_slot; // Which slot within the page is free |
| @@ -412,18 +429,18 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, | |||
| 412 | return ERR_OUT_OF_MEMORY; | 429 | return ERR_OUT_OF_MEMORY; |
| 413 | } | 430 | } |
| 414 | 431 | ||
| 415 | u32 offset = linheap_memory->size(); | 432 | size_t offset = linheap_memory->size(); |
| 416 | 433 | ||
| 417 | // Allocate some memory from the end of the linear heap for this region. | 434 | // Allocate some memory from the end of the linear heap for this region. |
| 418 | linheap_memory->insert(linheap_memory->end(), Memory::PAGE_SIZE, 0); | 435 | linheap_memory->insert(linheap_memory->end(), Memory::PAGE_SIZE, 0); |
| 419 | memory_region->used += Memory::PAGE_SIZE; | 436 | memory_region->used += Memory::PAGE_SIZE; |
| 420 | Kernel::g_current_process->linear_heap_used += Memory::PAGE_SIZE; | 437 | owner_process->linear_heap_used += Memory::PAGE_SIZE; |
| 421 | 438 | ||
| 422 | tls_slots.emplace_back(0); // The page is completely available at the start | 439 | tls_slots.emplace_back(0); // The page is completely available at the start |
| 423 | available_page = tls_slots.size() - 1; | 440 | available_page = static_cast<u32>(tls_slots.size() - 1); |
| 424 | available_slot = 0; // Use the first slot in the new page | 441 | available_slot = 0; // Use the first slot in the new page |
| 425 | 442 | ||
| 426 | auto& vm_manager = Kernel::g_current_process->vm_manager; | 443 | auto& vm_manager = owner_process->vm_manager; |
| 427 | vm_manager.RefreshMemoryBlockMappings(linheap_memory.get()); | 444 | vm_manager.RefreshMemoryBlockMappings(linheap_memory.get()); |
| 428 | 445 | ||
| 429 | // Map the page to the current process' address space. | 446 | // Map the page to the current process' address space. |
| @@ -447,7 +464,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, | |||
| 447 | return MakeResult<SharedPtr<Thread>>(std::move(thread)); | 464 | return MakeResult<SharedPtr<Thread>>(std::move(thread)); |
| 448 | } | 465 | } |
| 449 | 466 | ||
| 450 | void Thread::SetPriority(s32 priority) { | 467 | void Thread::SetPriority(u32 priority) { |
| 451 | ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST, | 468 | ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST, |
| 452 | "Invalid priority value."); | 469 | "Invalid priority value."); |
| 453 | // If thread was ready, adjust queues | 470 | // If thread was ready, adjust queues |
| @@ -460,7 +477,7 @@ void Thread::SetPriority(s32 priority) { | |||
| 460 | } | 477 | } |
| 461 | 478 | ||
| 462 | void Thread::UpdatePriority() { | 479 | void Thread::UpdatePriority() { |
| 463 | s32 best_priority = nominal_priority; | 480 | u32 best_priority = nominal_priority; |
| 464 | for (auto& mutex : held_mutexes) { | 481 | for (auto& mutex : held_mutexes) { |
| 465 | if (mutex->priority < best_priority) | 482 | if (mutex->priority < best_priority) |
| 466 | best_priority = mutex->priority; | 483 | best_priority = mutex->priority; |
| @@ -468,7 +485,7 @@ void Thread::UpdatePriority() { | |||
| 468 | BoostPriority(best_priority); | 485 | BoostPriority(best_priority); |
| 469 | } | 486 | } |
| 470 | 487 | ||
| 471 | void Thread::BoostPriority(s32 priority) { | 488 | void Thread::BoostPriority(u32 priority) { |
| 472 | // If thread was ready, adjust queues | 489 | // If thread was ready, adjust queues |
| 473 | if (status == THREADSTATUS_READY) | 490 | if (status == THREADSTATUS_READY) |
| 474 | ready_queue.move(this, current_priority, priority); | 491 | ready_queue.move(this, current_priority, priority); |
| @@ -477,21 +494,20 @@ void Thread::BoostPriority(s32 priority) { | |||
| 477 | current_priority = priority; | 494 | current_priority = priority; |
| 478 | } | 495 | } |
| 479 | 496 | ||
| 480 | SharedPtr<Thread> SetupMainThread(VAddr entry_point, s32 priority) { | 497 | SharedPtr<Thread> SetupMainThread(u32 entry_point, u32 priority, SharedPtr<Process> owner_process) { |
| 481 | DEBUG_ASSERT(!GetCurrentThread()); | 498 | // Setup page table so we can write to memory |
| 499 | SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table); | ||
| 482 | 500 | ||
| 483 | // Initialize new "main" thread | 501 | // Initialize new "main" thread |
| 484 | auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0, | 502 | auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0, |
| 485 | Memory::HEAP_VADDR_END); | 503 | Memory::HEAP_VADDR_END, owner_process); |
| 486 | 504 | ||
| 487 | SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); | 505 | SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); |
| 488 | 506 | ||
| 489 | thread->context.fpscr = | 507 | thread->context.fpscr = |
| 490 | FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO | FPSCR_IXC; // 0x03C00010 | 508 | FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO | FPSCR_IXC; // 0x03C00010 |
| 491 | 509 | ||
| 492 | // Run new "main" thread | 510 | // Note: The newly created thread will be run when the scheduler fires. |
| 493 | SwitchContext(thread.get()); | ||
| 494 | |||
| 495 | return thread; | 511 | return thread; |
| 496 | } | 512 | } |
| 497 | 513 | ||
| @@ -525,7 +541,13 @@ void Thread::SetWaitSynchronizationOutput(s32 output) { | |||
| 525 | s32 Thread::GetWaitObjectIndex(WaitObject* object) const { | 541 | s32 Thread::GetWaitObjectIndex(WaitObject* object) const { |
| 526 | ASSERT_MSG(!wait_objects.empty(), "Thread is not waiting for anything"); | 542 | ASSERT_MSG(!wait_objects.empty(), "Thread is not waiting for anything"); |
| 527 | auto match = std::find(wait_objects.rbegin(), wait_objects.rend(), object); | 543 | auto match = std::find(wait_objects.rbegin(), wait_objects.rend(), object); |
| 528 | return std::distance(match, wait_objects.rend()) - 1; | 544 | return static_cast<s32>(std::distance(match, wait_objects.rend()) - 1); |
| 545 | } | ||
| 546 | |||
| 547 | VAddr Thread::GetCommandBufferAddress() const { | ||
| 548 | // Offset from the start of TLS at which the IPC command buffer begins. | ||
| 549 | static constexpr int CommandHeaderOffset = 0x80; | ||
| 550 | return GetTLSAddress() + CommandHeaderOffset; | ||
| 529 | } | 551 | } |
| 530 | 552 | ||
| 531 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 553 | //////////////////////////////////////////////////////////////////////////////////////////////////// |