aboutsummaryrefslogtreecommitdiff
path: root/src/core/hle/kernel/thread.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle/kernel/thread.cpp')
-rw-r--r--src/core/hle/kernel/thread.cpp224
1 files changed, 168 insertions, 56 deletions
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index dd0a8ae48..cffa7ca83 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -55,16 +55,6 @@ inline static u32 const NewThreadId() {
Thread::Thread() {}
Thread::~Thread() {}
-/**
- * Check if the specified thread is waiting on the specified address to be arbitrated
- * @param thread The thread to test
- * @param wait_address The address to test against
- * @return True if the thread is waiting, false otherwise
- */
-static bool CheckWait_AddressArbiter(const Thread* thread, VAddr wait_address) {
- return thread->status == THREADSTATUS_WAIT_ARB && wait_address == thread->wait_address;
-}
-
void Thread::Stop() {
// Cancel any outstanding wakeup events for this thread
CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle);
@@ -74,7 +64,7 @@ void Thread::Stop() {
// Clean up thread from ready queue
// This is only needed when the thread is termintated forcefully (SVC TerminateProcess)
if (status == THREADSTATUS_READY) {
- Core::System::GetInstance().Scheduler().UnscheduleThread(this, current_priority);
+ scheduler->UnscheduleThread(this, current_priority);
}
status = THREADSTATUS_DEAD;
@@ -87,14 +77,11 @@ void Thread::Stop() {
}
wait_objects.clear();
- // Release all the mutexes that this thread holds
- ReleaseThreadMutexes(this);
-
// Mark the TLS slot in the thread's page as free.
u64 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE;
u64 tls_slot =
((tls_address - Memory::TLS_AREA_VADDR) % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE;
- Kernel::g_current_process->tls_slots[tls_page].reset(tls_slot);
+ Core::CurrentProcess()->tls_slots[tls_page].reset(tls_slot);
}
void WaitCurrentThread_Sleep() {
@@ -102,16 +89,10 @@ void WaitCurrentThread_Sleep() {
thread->status = THREADSTATUS_WAIT_SLEEP;
}
-void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) {
- Thread* thread = GetCurrentThread();
- thread->wait_address = wait_address;
- thread->status = THREADSTATUS_WAIT_ARB;
-}
-
void ExitCurrentThread() {
Thread* thread = GetCurrentThread();
thread->Stop();
- Core::System::GetInstance().Scheduler().RemoveThread(thread);
+ Core::System::GetInstance().CurrentScheduler().RemoveThread(thread);
}
/**
@@ -120,16 +101,18 @@ void ExitCurrentThread() {
* @param cycles_late The number of CPU cycles that have passed since the desired wakeup time
*/
static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
- SharedPtr<Thread> thread = wakeup_callback_handle_table.Get<Thread>((Handle)thread_handle);
+ const auto proper_handle = static_cast<Handle>(thread_handle);
+ SharedPtr<Thread> thread = wakeup_callback_handle_table.Get<Thread>(proper_handle);
if (thread == nullptr) {
- LOG_CRITICAL(Kernel, "Callback fired for invalid thread %08X", (Handle)thread_handle);
+ NGLOG_CRITICAL(Kernel, "Callback fired for invalid thread {:08X}", proper_handle);
return;
}
bool resume = true;
if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
- thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB) {
+ thread->status == THREADSTATUS_WAIT_SYNCH_ALL ||
+ thread->status == THREADSTATUS_WAIT_HLE_EVENT) {
// Remove the thread from each of its waiting objects' waitlists
for (auto& object : thread->wait_objects)
@@ -141,6 +124,22 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
resume = thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr, 0);
}
+ if (thread->mutex_wait_address != 0 || thread->condvar_wait_address != 0 ||
+ thread->wait_handle) {
+ ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX);
+ thread->mutex_wait_address = 0;
+ thread->condvar_wait_address = 0;
+ thread->wait_handle = 0;
+
+ auto lock_owner = thread->lock_owner;
+ // Threads waking up by timeout from WaitProcessWideKey do not perform priority inheritance
+ // and don't have a lock owner unless SignalProcessWideKey was called first and the thread
+ // wasn't awakened due to the mutex already being acquired.
+ if (lock_owner) {
+ lock_owner->RemoveMutexWaiter(thread);
+ }
+ }
+
if (resume)
thread->ResumeFromWait();
}
@@ -150,22 +149,36 @@ void Thread::WakeAfterDelay(s64 nanoseconds) {
if (nanoseconds == -1)
return;
- CoreTiming::ScheduleEvent(nsToCycles(nanoseconds), ThreadWakeupEventType, callback_handle);
+ CoreTiming::ScheduleEvent(CoreTiming::nsToCycles(nanoseconds), ThreadWakeupEventType,
+ callback_handle);
}
void Thread::CancelWakeupTimer() {
CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle);
}
+static boost::optional<s32> GetNextProcessorId(u64 mask) {
+ for (s32 index = 0; index < Core::NUM_CPU_CORES; ++index) {
+ if (mask & (1ULL << index)) {
+ if (!Core::System().GetInstance().Scheduler(index)->GetCurrentThread()) {
+ // Core is enabled and not running any threads, use this one
+ return index;
+ }
+ }
+ }
+ return {};
+}
+
void Thread::ResumeFromWait() {
ASSERT_MSG(wait_objects.empty(), "Thread is waking up while waiting for objects");
switch (status) {
case THREADSTATUS_WAIT_SYNCH_ALL:
case THREADSTATUS_WAIT_SYNCH_ANY:
- case THREADSTATUS_WAIT_ARB:
+ case THREADSTATUS_WAIT_HLE_EVENT:
case THREADSTATUS_WAIT_SLEEP:
case THREADSTATUS_WAIT_IPC:
+ case THREADSTATUS_WAIT_MUTEX:
break;
case THREADSTATUS_READY:
@@ -178,11 +191,11 @@ void Thread::ResumeFromWait() {
return;
case THREADSTATUS_RUNNING:
- DEBUG_ASSERT_MSG(false, "Thread with object id %u has already resumed.", GetObjectId());
+ DEBUG_ASSERT_MSG(false, "Thread with object id {} has already resumed.", GetObjectId());
return;
case THREADSTATUS_DEAD:
// This should never happen, as threads must complete before being stopped.
- DEBUG_ASSERT_MSG(false, "Thread with object id %u cannot be resumed because it's DEAD.",
+ DEBUG_ASSERT_MSG(false, "Thread with object id {} cannot be resumed because it's DEAD.",
GetObjectId());
return;
}
@@ -190,8 +203,37 @@ void Thread::ResumeFromWait() {
wakeup_callback = nullptr;
status = THREADSTATUS_READY;
- Core::System::GetInstance().Scheduler().ScheduleThread(this, current_priority);
- Core::System::GetInstance().PrepareReschedule();
+
+ boost::optional<s32> new_processor_id = GetNextProcessorId(affinity_mask);
+ if (!new_processor_id) {
+ new_processor_id = processor_id;
+ }
+ if (ideal_core != -1 &&
+ Core::System().GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) {
+ new_processor_id = ideal_core;
+ }
+
+ ASSERT(*new_processor_id < 4);
+
+ // Add thread to new core's scheduler
+ auto& next_scheduler = Core::System().GetInstance().Scheduler(*new_processor_id);
+
+ if (*new_processor_id != processor_id) {
+ // Remove thread from previous core's scheduler
+ scheduler->RemoveThread(this);
+ next_scheduler->AddThread(this, current_priority);
+ }
+
+ processor_id = *new_processor_id;
+
+ // If the thread was ready, unschedule from the previous core and schedule on the new core
+ scheduler->UnscheduleThread(this, current_priority);
+ next_scheduler->ScheduleThread(this, current_priority);
+
+ // Change thread's scheduler
+ scheduler = next_scheduler;
+
+ Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
}
/**
@@ -242,27 +284,25 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
SharedPtr<Process> owner_process) {
// Check if priority is in ranged. Lowest priority -> highest priority id.
if (priority > THREADPRIO_LOWEST) {
- LOG_ERROR(Kernel_SVC, "Invalid thread priority: %u", priority);
+ NGLOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority);
return ERR_OUT_OF_RANGE;
}
if (processor_id > THREADPROCESSORID_MAX) {
- LOG_ERROR(Kernel_SVC, "Invalid processor id: %d", processor_id);
+ NGLOG_ERROR(Kernel_SVC, "Invalid processor id: {}", processor_id);
return ERR_OUT_OF_RANGE_KERNEL;
}
// TODO(yuriks): Other checks, returning 0xD9001BEA
if (!Memory::IsValidVirtualAddress(*owner_process, entry_point)) {
- LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %016" PRIx64, name.c_str(), entry_point);
+ NGLOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point);
// TODO (bunnei): Find the correct error code to use here
return ResultCode(-1);
}
SharedPtr<Thread> thread(new Thread);
- Core::System::GetInstance().Scheduler().AddThread(thread, priority);
-
thread->thread_id = NewThreadId();
thread->status = THREADSTATUS_DORMANT;
thread->entry_point = entry_point;
@@ -270,11 +310,17 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
thread->nominal_priority = thread->current_priority = priority;
thread->last_running_ticks = CoreTiming::GetTicks();
thread->processor_id = processor_id;
+ thread->ideal_core = processor_id;
+ thread->affinity_mask = 1ULL << processor_id;
thread->wait_objects.clear();
- thread->wait_address = 0;
+ thread->mutex_wait_address = 0;
+ thread->condvar_wait_address = 0;
+ thread->wait_handle = 0;
thread->name = std::move(name);
thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap();
thread->owner_process = owner_process;
+ thread->scheduler = Core::System().GetInstance().Scheduler(processor_id);
+ thread->scheduler->AddThread(thread, priority);
// Find the next available TLS index, and mark it as used
auto& tls_slots = owner_process->tls_slots;
@@ -291,8 +337,8 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
auto& linheap_memory = memory_region->linear_heap_memory;
if (linheap_memory->size() + Memory::PAGE_SIZE > memory_region->size) {
- LOG_ERROR(Kernel_SVC,
- "Not enough space in region to allocate a new TLS page for thread");
+ NGLOG_ERROR(Kernel_SVC,
+ "Not enough space in region to allocate a new TLS page for thread");
return ERR_OUT_OF_MEMORY;
}
@@ -314,7 +360,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
// TODO(Subv): Find the correct MemoryState for this region.
vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE,
linheap_memory, offset, Memory::PAGE_SIZE,
- MemoryState::ThreadLocalStorage);
+ MemoryState::ThreadLocal);
}
// Mark the slot as used
@@ -332,32 +378,23 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
void Thread::SetPriority(u32 priority) {
ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST,
"Invalid priority value.");
- Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority);
- nominal_priority = current_priority = priority;
-}
-
-void Thread::UpdatePriority() {
- u32 best_priority = nominal_priority;
- for (auto& mutex : held_mutexes) {
- if (mutex->priority < best_priority)
- best_priority = mutex->priority;
- }
- BoostPriority(best_priority);
+ nominal_priority = priority;
+ UpdatePriority();
}
void Thread::BoostPriority(u32 priority) {
- Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority);
+ scheduler->SetThreadPriority(this, priority);
current_priority = priority;
}
SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority,
SharedPtr<Process> owner_process) {
// Setup page table so we can write to memory
- SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table);
+ SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table);
// Initialize new "main" thread
auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0,
- Memory::HEAP_VADDR_END, owner_process);
+ Memory::STACK_AREA_VADDR_END, owner_process);
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
@@ -392,13 +429,86 @@ VAddr Thread::GetCommandBufferAddress() const {
return GetTLSAddress() + CommandHeaderOffset;
}
+void Thread::AddMutexWaiter(SharedPtr<Thread> thread) {
+ thread->lock_owner = this;
+ wait_mutex_threads.emplace_back(std::move(thread));
+ UpdatePriority();
+}
+
+void Thread::RemoveMutexWaiter(SharedPtr<Thread> thread) {
+ boost::remove_erase(wait_mutex_threads, thread);
+ thread->lock_owner = nullptr;
+ UpdatePriority();
+}
+
+void Thread::UpdatePriority() {
+ // Find the highest priority among all the threads that are waiting for this thread's lock
+ u32 new_priority = nominal_priority;
+ for (const auto& thread : wait_mutex_threads) {
+ if (thread->nominal_priority < new_priority)
+ new_priority = thread->nominal_priority;
+ }
+
+ if (new_priority == current_priority)
+ return;
+
+ scheduler->SetThreadPriority(this, new_priority);
+
+ current_priority = new_priority;
+
+ // Recursively update the priority of the thread that depends on the priority of this one.
+ if (lock_owner)
+ lock_owner->UpdatePriority();
+}
+
+void Thread::ChangeCore(u32 core, u64 mask) {
+ ideal_core = core;
+ affinity_mask = mask;
+
+ if (status != THREADSTATUS_READY) {
+ return;
+ }
+
+ boost::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)};
+
+ if (!new_processor_id) {
+ new_processor_id = processor_id;
+ }
+ if (ideal_core != -1 &&
+ Core::System().GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) {
+ new_processor_id = ideal_core;
+ }
+
+ ASSERT(*new_processor_id < 4);
+
+ // Add thread to new core's scheduler
+ auto& next_scheduler = Core::System().GetInstance().Scheduler(*new_processor_id);
+
+ if (*new_processor_id != processor_id) {
+ // Remove thread from previous core's scheduler
+ scheduler->RemoveThread(this);
+ next_scheduler->AddThread(this, current_priority);
+ }
+
+ processor_id = *new_processor_id;
+
+ // If the thread was ready, unschedule from the previous core and schedule on the new core
+ scheduler->UnscheduleThread(this, current_priority);
+ next_scheduler->ScheduleThread(this, current_priority);
+
+ // Change thread's scheduler
+ scheduler = next_scheduler;
+
+ Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Gets the current thread
*/
Thread* GetCurrentThread() {
- return Core::System::GetInstance().Scheduler().GetCurrentThread();
+ return Core::System::GetInstance().CurrentScheduler().GetCurrentThread();
}
void ThreadingInit() {
@@ -406,6 +516,8 @@ void ThreadingInit() {
next_thread_id = 1;
}
-void ThreadingShutdown() {}
+void ThreadingShutdown() {
+ Kernel::ClearProcessList();
+}
} // namespace Kernel