kernel/thread: Make all instance variables private
Many of the member variables of the thread class aren't even used outside of the class itself, so there's no need to make those variables public. This change follows in the steps of the previous changes that made other kernel types' members private. The main motivation behind this is that the Thread class will likely change in the future as emulation becomes more accurate, and letting random bits of the emulator access data members of the Thread class directly makes it a pain to shuffle around and/or modify internals. Having all data members public like this also makes it difficult to reason about certain bits of behavior without first verifying what parts of the core actually use them. Everything being public also generally follows the tendency for changes to be introduced in completely different translation units that would otherwise be better introduced as an addition to the Thread class' public interface.
This commit is contained in:
parent
bc679c9b8c
commit
baed7e1fba
|
@ -86,7 +86,7 @@ public:
|
||||||
parent.jit->HaltExecution();
|
parent.jit->HaltExecution();
|
||||||
parent.SetPC(pc);
|
parent.SetPC(pc);
|
||||||
Kernel::Thread* thread = Kernel::GetCurrentThread();
|
Kernel::Thread* thread = Kernel::GetCurrentThread();
|
||||||
parent.SaveContext(thread->context);
|
parent.SaveContext(thread->GetContext());
|
||||||
GDBStub::Break();
|
GDBStub::Break();
|
||||||
GDBStub::SendTrap(thread, 5);
|
GDBStub::SendTrap(thread, 5);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -195,7 +195,7 @@ void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
|
||||||
uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address);
|
uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address);
|
||||||
}
|
}
|
||||||
Kernel::Thread* thread = Kernel::GetCurrentThread();
|
Kernel::Thread* thread = Kernel::GetCurrentThread();
|
||||||
SaveContext(thread->context);
|
SaveContext(thread->GetContext());
|
||||||
if (last_bkpt_hit || GDBStub::GetCpuStepFlag()) {
|
if (last_bkpt_hit || GDBStub::GetCpuStepFlag()) {
|
||||||
last_bkpt_hit = false;
|
last_bkpt_hit = false;
|
||||||
GDBStub::Break();
|
GDBStub::Break();
|
||||||
|
|
|
@ -209,7 +209,7 @@ static Kernel::Thread* FindThreadById(int id) {
|
||||||
for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
|
for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
|
||||||
const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
|
const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
|
||||||
for (auto& thread : threads) {
|
for (auto& thread : threads) {
|
||||||
if (thread->GetThreadId() == static_cast<u32>(id)) {
|
if (thread->GetThreadID() == static_cast<u32>(id)) {
|
||||||
current_core = core;
|
current_core = core;
|
||||||
return thread.get();
|
return thread.get();
|
||||||
}
|
}
|
||||||
|
@ -223,16 +223,18 @@ static u64 RegRead(std::size_t id, Kernel::Thread* thread = nullptr) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto& thread_context = thread->GetContext();
|
||||||
|
|
||||||
if (id < SP_REGISTER) {
|
if (id < SP_REGISTER) {
|
||||||
return thread->context.cpu_registers[id];
|
return thread_context.cpu_registers[id];
|
||||||
} else if (id == SP_REGISTER) {
|
} else if (id == SP_REGISTER) {
|
||||||
return thread->context.sp;
|
return thread_context.sp;
|
||||||
} else if (id == PC_REGISTER) {
|
} else if (id == PC_REGISTER) {
|
||||||
return thread->context.pc;
|
return thread_context.pc;
|
||||||
} else if (id == PSTATE_REGISTER) {
|
} else if (id == PSTATE_REGISTER) {
|
||||||
return thread->context.pstate;
|
return thread_context.pstate;
|
||||||
} else if (id > PSTATE_REGISTER && id < FPCR_REGISTER) {
|
} else if (id > PSTATE_REGISTER && id < FPCR_REGISTER) {
|
||||||
return thread->context.vector_registers[id - UC_ARM64_REG_Q0][0];
|
return thread_context.vector_registers[id - UC_ARM64_REG_Q0][0];
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -243,16 +245,18 @@ static void RegWrite(std::size_t id, u64 val, Kernel::Thread* thread = nullptr)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto& thread_context = thread->GetContext();
|
||||||
|
|
||||||
if (id < SP_REGISTER) {
|
if (id < SP_REGISTER) {
|
||||||
thread->context.cpu_registers[id] = val;
|
thread_context.cpu_registers[id] = val;
|
||||||
} else if (id == SP_REGISTER) {
|
} else if (id == SP_REGISTER) {
|
||||||
thread->context.sp = val;
|
thread_context.sp = val;
|
||||||
} else if (id == PC_REGISTER) {
|
} else if (id == PC_REGISTER) {
|
||||||
thread->context.pc = val;
|
thread_context.pc = val;
|
||||||
} else if (id == PSTATE_REGISTER) {
|
} else if (id == PSTATE_REGISTER) {
|
||||||
thread->context.pstate = static_cast<u32>(val);
|
thread_context.pstate = static_cast<u32>(val);
|
||||||
} else if (id > PSTATE_REGISTER && id < FPCR_REGISTER) {
|
} else if (id > PSTATE_REGISTER && id < FPCR_REGISTER) {
|
||||||
thread->context.vector_registers[id - (PSTATE_REGISTER + 1)][0] = val;
|
thread_context.vector_registers[id - (PSTATE_REGISTER + 1)][0] = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -595,7 +599,7 @@ static void HandleQuery() {
|
||||||
for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
|
for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
|
||||||
const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
|
const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
|
||||||
for (const auto& thread : threads) {
|
for (const auto& thread : threads) {
|
||||||
val += fmt::format("{:x}", thread->GetThreadId());
|
val += fmt::format("{:x}", thread->GetThreadID());
|
||||||
val += ",";
|
val += ",";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -612,7 +616,7 @@ static void HandleQuery() {
|
||||||
for (const auto& thread : threads) {
|
for (const auto& thread : threads) {
|
||||||
buffer +=
|
buffer +=
|
||||||
fmt::format(R"*(<thread id="{:x}" core="{:d}" name="Thread {:x}"></thread>)*",
|
fmt::format(R"*(<thread id="{:x}" core="{:d}" name="Thread {:x}"></thread>)*",
|
||||||
thread->GetThreadId(), core, thread->GetThreadId());
|
thread->GetThreadID(), core, thread->GetThreadID());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buffer += "</threads>";
|
buffer += "</threads>";
|
||||||
|
@ -693,7 +697,7 @@ static void SendSignal(Kernel::Thread* thread, u32 signal, bool full = true) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (thread) {
|
if (thread) {
|
||||||
buffer += fmt::format(";thread:{:x};", thread->GetThreadId());
|
buffer += fmt::format(";thread:{:x};", thread->GetThreadID());
|
||||||
}
|
}
|
||||||
|
|
||||||
SendReply(buffer.c_str());
|
SendReply(buffer.c_str());
|
||||||
|
@ -857,7 +861,9 @@ static void WriteRegister() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update Unicorn context skipping scheduler, no running threads at this point
|
// Update Unicorn context skipping scheduler, no running threads at this point
|
||||||
Core::System::GetInstance().ArmInterface(current_core).LoadContext(current_thread->context);
|
Core::System::GetInstance()
|
||||||
|
.ArmInterface(current_core)
|
||||||
|
.LoadContext(current_thread->GetContext());
|
||||||
|
|
||||||
SendReply("OK");
|
SendReply("OK");
|
||||||
}
|
}
|
||||||
|
@ -886,7 +892,9 @@ static void WriteRegisters() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update Unicorn context skipping scheduler, no running threads at this point
|
// Update Unicorn context skipping scheduler, no running threads at this point
|
||||||
Core::System::GetInstance().ArmInterface(current_core).LoadContext(current_thread->context);
|
Core::System::GetInstance()
|
||||||
|
.ArmInterface(current_core)
|
||||||
|
.LoadContext(current_thread->GetContext());
|
||||||
|
|
||||||
SendReply("OK");
|
SendReply("OK");
|
||||||
}
|
}
|
||||||
|
@ -960,7 +968,9 @@ static void Step() {
|
||||||
if (command_length > 1) {
|
if (command_length > 1) {
|
||||||
RegWrite(PC_REGISTER, GdbHexToLong(command_buffer + 1), current_thread);
|
RegWrite(PC_REGISTER, GdbHexToLong(command_buffer + 1), current_thread);
|
||||||
// Update Unicorn context skipping scheduler, no running threads at this point
|
// Update Unicorn context skipping scheduler, no running threads at this point
|
||||||
Core::System::GetInstance().ArmInterface(current_core).LoadContext(current_thread->context);
|
Core::System::GetInstance()
|
||||||
|
.ArmInterface(current_core)
|
||||||
|
.LoadContext(current_thread->GetContext());
|
||||||
}
|
}
|
||||||
step_loop = true;
|
step_loop = true;
|
||||||
halt_loop = true;
|
halt_loop = true;
|
||||||
|
|
|
@ -23,13 +23,13 @@ namespace AddressArbiter {
|
||||||
// Performs actual address waiting logic.
|
// Performs actual address waiting logic.
|
||||||
static ResultCode WaitForAddress(VAddr address, s64 timeout) {
|
static ResultCode WaitForAddress(VAddr address, s64 timeout) {
|
||||||
SharedPtr<Thread> current_thread = GetCurrentThread();
|
SharedPtr<Thread> current_thread = GetCurrentThread();
|
||||||
current_thread->arb_wait_address = address;
|
current_thread->SetArbiterWaitAddress(address);
|
||||||
current_thread->status = ThreadStatus::WaitArb;
|
current_thread->SetStatus(ThreadStatus::WaitArb);
|
||||||
current_thread->wakeup_callback = nullptr;
|
current_thread->InvalidateWakeupCallback();
|
||||||
|
|
||||||
current_thread->WakeAfterDelay(timeout);
|
current_thread->WakeAfterDelay(timeout);
|
||||||
|
|
||||||
Core::System::GetInstance().CpuCore(current_thread->processor_id).PrepareReschedule();
|
Core::System::GetInstance().CpuCore(current_thread->GetProcessorID()).PrepareReschedule();
|
||||||
return RESULT_TIMEOUT;
|
return RESULT_TIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,10 +39,10 @@ static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address)
|
||||||
std::vector<SharedPtr<Thread>>& waiting_threads,
|
std::vector<SharedPtr<Thread>>& waiting_threads,
|
||||||
VAddr arb_addr) {
|
VAddr arb_addr) {
|
||||||
const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
|
const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
|
||||||
auto& thread_list = scheduler->GetThreadList();
|
const auto& thread_list = scheduler->GetThreadList();
|
||||||
|
|
||||||
for (auto& thread : thread_list) {
|
for (const auto& thread : thread_list) {
|
||||||
if (thread->arb_wait_address == arb_addr)
|
if (thread->GetArbiterWaitAddress() == arb_addr)
|
||||||
waiting_threads.push_back(thread);
|
waiting_threads.push_back(thread);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -57,7 +57,7 @@ static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address)
|
||||||
// Sort them by priority, such that the highest priority ones come first.
|
// Sort them by priority, such that the highest priority ones come first.
|
||||||
std::sort(threads.begin(), threads.end(),
|
std::sort(threads.begin(), threads.end(),
|
||||||
[](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
|
[](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
|
||||||
return lhs->current_priority < rhs->current_priority;
|
return lhs->GetPriority() < rhs->GetPriority();
|
||||||
});
|
});
|
||||||
|
|
||||||
return threads;
|
return threads;
|
||||||
|
@ -73,9 +73,9 @@ static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num
|
||||||
|
|
||||||
// Signal the waiting threads.
|
// Signal the waiting threads.
|
||||||
for (std::size_t i = 0; i < last; i++) {
|
for (std::size_t i = 0; i < last; i++) {
|
||||||
ASSERT(waiting_threads[i]->status == ThreadStatus::WaitArb);
|
ASSERT(waiting_threads[i]->GetStatus() == ThreadStatus::WaitArb);
|
||||||
waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS);
|
waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS);
|
||||||
waiting_threads[i]->arb_wait_address = 0;
|
waiting_threads[i]->SetArbiterWaitAddress(0);
|
||||||
waiting_threads[i]->ResumeFromWait();
|
waiting_threads[i]->ResumeFromWait();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,14 +42,14 @@ SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
|
||||||
Kernel::SharedPtr<Kernel::Event> event) {
|
Kernel::SharedPtr<Kernel::Event> event) {
|
||||||
|
|
||||||
// Put the client thread to sleep until the wait event is signaled or the timeout expires.
|
// Put the client thread to sleep until the wait event is signaled or the timeout expires.
|
||||||
thread->wakeup_callback = [context = *this, callback](
|
thread->SetWakeupCallback([context = *this, callback](
|
||||||
ThreadWakeupReason reason, SharedPtr<Thread> thread,
|
ThreadWakeupReason reason, SharedPtr<Thread> thread,
|
||||||
SharedPtr<WaitObject> object, std::size_t index) mutable -> bool {
|
SharedPtr<WaitObject> object, std::size_t index) mutable -> bool {
|
||||||
ASSERT(thread->status == ThreadStatus::WaitHLEEvent);
|
ASSERT(thread->GetStatus() == ThreadStatus::WaitHLEEvent);
|
||||||
callback(thread, context, reason);
|
callback(thread, context, reason);
|
||||||
context.WriteToOutgoingCommandBuffer(*thread);
|
context.WriteToOutgoingCommandBuffer(*thread);
|
||||||
return true;
|
return true;
|
||||||
};
|
});
|
||||||
|
|
||||||
if (!event) {
|
if (!event) {
|
||||||
// Create event if not provided
|
// Create event if not provided
|
||||||
|
@ -59,8 +59,8 @@ SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
|
||||||
}
|
}
|
||||||
|
|
||||||
event->Clear();
|
event->Clear();
|
||||||
thread->status = ThreadStatus::WaitHLEEvent;
|
thread->SetStatus(ThreadStatus::WaitHLEEvent);
|
||||||
thread->wait_objects = {event};
|
thread->SetWaitObjects({event});
|
||||||
event->AddWaitingThread(thread);
|
event->AddWaitingThread(thread);
|
||||||
|
|
||||||
if (timeout > 0) {
|
if (timeout > 0) {
|
||||||
|
@ -209,7 +209,7 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb
|
||||||
|
|
||||||
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) {
|
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) {
|
||||||
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf;
|
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf;
|
||||||
Memory::ReadBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
|
Memory::ReadBlock(*thread.GetOwnerProcess(), thread.GetTLSAddress(), dst_cmdbuf.data(),
|
||||||
dst_cmdbuf.size() * sizeof(u32));
|
dst_cmdbuf.size() * sizeof(u32));
|
||||||
|
|
||||||
// The header was already built in the internal command buffer. Attempt to parse it to verify
|
// The header was already built in the internal command buffer. Attempt to parse it to verify
|
||||||
|
@ -268,7 +268,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the translated command buffer back into the thread's command buffer area.
|
// Copy the translated command buffer back into the thread's command buffer area.
|
||||||
Memory::WriteBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
|
Memory::WriteBlock(*thread.GetOwnerProcess(), thread.GetTLSAddress(), dst_cmdbuf.data(),
|
||||||
dst_cmdbuf.size() * sizeof(u32));
|
dst_cmdbuf.size() * sizeof(u32));
|
||||||
|
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
|
|
|
@ -46,40 +46,40 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_
|
||||||
|
|
||||||
bool resume = true;
|
bool resume = true;
|
||||||
|
|
||||||
if (thread->status == ThreadStatus::WaitSynchAny ||
|
if (thread->GetStatus() == ThreadStatus::WaitSynchAny ||
|
||||||
thread->status == ThreadStatus::WaitSynchAll ||
|
thread->GetStatus() == ThreadStatus::WaitSynchAll ||
|
||||||
thread->status == ThreadStatus::WaitHLEEvent) {
|
thread->GetStatus() == ThreadStatus::WaitHLEEvent) {
|
||||||
// Remove the thread from each of its waiting objects' waitlists
|
// Remove the thread from each of its waiting objects' waitlists
|
||||||
for (auto& object : thread->wait_objects) {
|
for (const auto& object : thread->GetWaitObjects()) {
|
||||||
object->RemoveWaitingThread(thread.get());
|
object->RemoveWaitingThread(thread.get());
|
||||||
}
|
}
|
||||||
thread->wait_objects.clear();
|
thread->ClearWaitObjects();
|
||||||
|
|
||||||
// Invoke the wakeup callback before clearing the wait objects
|
// Invoke the wakeup callback before clearing the wait objects
|
||||||
if (thread->wakeup_callback) {
|
if (thread->HasWakeupCallback()) {
|
||||||
resume = thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr, 0);
|
resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Timeout, thread, nullptr, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (thread->mutex_wait_address != 0 || thread->condvar_wait_address != 0 ||
|
if (thread->GetMutexWaitAddress() != 0 || thread->GetCondVarWaitAddress() != 0 ||
|
||||||
thread->wait_handle) {
|
thread->GetWaitHandle() != 0) {
|
||||||
ASSERT(thread->status == ThreadStatus::WaitMutex);
|
ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
|
||||||
thread->mutex_wait_address = 0;
|
thread->SetMutexWaitAddress(0);
|
||||||
thread->condvar_wait_address = 0;
|
thread->SetCondVarWaitAddress(0);
|
||||||
thread->wait_handle = 0;
|
thread->SetWaitHandle(0);
|
||||||
|
|
||||||
auto lock_owner = thread->lock_owner;
|
auto* const lock_owner = thread->GetLockOwner();
|
||||||
// Threads waking up by timeout from WaitProcessWideKey do not perform priority inheritance
|
// 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
|
// 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.
|
// wasn't awakened due to the mutex already being acquired.
|
||||||
if (lock_owner) {
|
if (lock_owner != nullptr) {
|
||||||
lock_owner->RemoveMutexWaiter(thread);
|
lock_owner->RemoveMutexWaiter(thread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (thread->arb_wait_address != 0) {
|
if (thread->GetArbiterWaitAddress() != 0) {
|
||||||
ASSERT(thread->status == ThreadStatus::WaitArb);
|
ASSERT(thread->GetStatus() == ThreadStatus::WaitArb);
|
||||||
thread->arb_wait_address = 0;
|
thread->SetArbiterWaitAddress(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resume) {
|
if (resume) {
|
||||||
|
|
|
@ -28,11 +28,11 @@ static std::pair<SharedPtr<Thread>, u32> GetHighestPriorityMutexWaitingThread(
|
||||||
SharedPtr<Thread> highest_priority_thread;
|
SharedPtr<Thread> highest_priority_thread;
|
||||||
u32 num_waiters = 0;
|
u32 num_waiters = 0;
|
||||||
|
|
||||||
for (auto& thread : current_thread->wait_mutex_threads) {
|
for (const auto& thread : current_thread->GetMutexWaitingThreads()) {
|
||||||
if (thread->mutex_wait_address != mutex_addr)
|
if (thread->GetMutexWaitAddress() != mutex_addr)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ASSERT(thread->status == ThreadStatus::WaitMutex);
|
ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
|
||||||
|
|
||||||
++num_waiters;
|
++num_waiters;
|
||||||
if (highest_priority_thread == nullptr ||
|
if (highest_priority_thread == nullptr ||
|
||||||
|
@ -47,12 +47,12 @@ static std::pair<SharedPtr<Thread>, u32> GetHighestPriorityMutexWaitingThread(
|
||||||
/// Update the mutex owner field of all threads waiting on the mutex to point to the new owner.
|
/// Update the mutex owner field of all threads waiting on the mutex to point to the new owner.
|
||||||
static void TransferMutexOwnership(VAddr mutex_addr, SharedPtr<Thread> current_thread,
|
static void TransferMutexOwnership(VAddr mutex_addr, SharedPtr<Thread> current_thread,
|
||||||
SharedPtr<Thread> new_owner) {
|
SharedPtr<Thread> new_owner) {
|
||||||
auto threads = current_thread->wait_mutex_threads;
|
const auto& threads = current_thread->GetMutexWaitingThreads();
|
||||||
for (auto& thread : threads) {
|
for (const auto& thread : threads) {
|
||||||
if (thread->mutex_wait_address != mutex_addr)
|
if (thread->GetMutexWaitAddress() != mutex_addr)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ASSERT(thread->lock_owner == current_thread);
|
ASSERT(thread->GetLockOwner() == current_thread);
|
||||||
current_thread->RemoveMutexWaiter(thread);
|
current_thread->RemoveMutexWaiter(thread);
|
||||||
if (new_owner != thread)
|
if (new_owner != thread)
|
||||||
new_owner->AddMutexWaiter(thread);
|
new_owner->AddMutexWaiter(thread);
|
||||||
|
@ -84,11 +84,11 @@ ResultCode Mutex::TryAcquire(HandleTable& handle_table, VAddr address, Handle ho
|
||||||
return ERR_INVALID_HANDLE;
|
return ERR_INVALID_HANDLE;
|
||||||
|
|
||||||
// Wait until the mutex is released
|
// Wait until the mutex is released
|
||||||
GetCurrentThread()->mutex_wait_address = address;
|
GetCurrentThread()->SetMutexWaitAddress(address);
|
||||||
GetCurrentThread()->wait_handle = requesting_thread_handle;
|
GetCurrentThread()->SetWaitHandle(requesting_thread_handle);
|
||||||
|
|
||||||
GetCurrentThread()->status = ThreadStatus::WaitMutex;
|
GetCurrentThread()->SetStatus(ThreadStatus::WaitMutex);
|
||||||
GetCurrentThread()->wakeup_callback = nullptr;
|
GetCurrentThread()->InvalidateWakeupCallback();
|
||||||
|
|
||||||
// Update the lock holder thread's priority to prevent priority inversion.
|
// Update the lock holder thread's priority to prevent priority inversion.
|
||||||
holding_thread->AddMutexWaiter(GetCurrentThread());
|
holding_thread->AddMutexWaiter(GetCurrentThread());
|
||||||
|
@ -115,7 +115,7 @@ ResultCode Mutex::Release(VAddr address) {
|
||||||
// Transfer the ownership of the mutex from the previous owner to the new one.
|
// Transfer the ownership of the mutex from the previous owner to the new one.
|
||||||
TransferMutexOwnership(address, GetCurrentThread(), thread);
|
TransferMutexOwnership(address, GetCurrentThread(), thread);
|
||||||
|
|
||||||
u32 mutex_value = thread->wait_handle;
|
u32 mutex_value = thread->GetWaitHandle();
|
||||||
|
|
||||||
if (num_waiters >= 2) {
|
if (num_waiters >= 2) {
|
||||||
// Notify the guest that there are still some threads waiting for the mutex
|
// Notify the guest that there are still some threads waiting for the mutex
|
||||||
|
@ -125,13 +125,13 @@ ResultCode Mutex::Release(VAddr address) {
|
||||||
// Grant the mutex to the next waiting thread and resume it.
|
// Grant the mutex to the next waiting thread and resume it.
|
||||||
Memory::Write32(address, mutex_value);
|
Memory::Write32(address, mutex_value);
|
||||||
|
|
||||||
ASSERT(thread->status == ThreadStatus::WaitMutex);
|
ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
|
||||||
thread->ResumeFromWait();
|
thread->ResumeFromWait();
|
||||||
|
|
||||||
thread->lock_owner = nullptr;
|
thread->SetLockOwner(nullptr);
|
||||||
thread->condvar_wait_address = 0;
|
thread->SetCondVarWaitAddress(0);
|
||||||
thread->mutex_wait_address = 0;
|
thread->SetMutexWaitAddress(0);
|
||||||
thread->wait_handle = 0;
|
thread->SetWaitHandle(0);
|
||||||
|
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,15 +144,15 @@ void Process::PrepareForTermination() {
|
||||||
|
|
||||||
const auto stop_threads = [this](const std::vector<SharedPtr<Thread>>& thread_list) {
|
const auto stop_threads = [this](const std::vector<SharedPtr<Thread>>& thread_list) {
|
||||||
for (auto& thread : thread_list) {
|
for (auto& thread : thread_list) {
|
||||||
if (thread->owner_process != this)
|
if (thread->GetOwnerProcess() != this)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (thread == GetCurrentThread())
|
if (thread == GetCurrentThread())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// TODO(Subv): When are the other running/ready threads terminated?
|
// TODO(Subv): When are the other running/ready threads terminated?
|
||||||
ASSERT_MSG(thread->status == ThreadStatus::WaitSynchAny ||
|
ASSERT_MSG(thread->GetStatus() == ThreadStatus::WaitSynchAny ||
|
||||||
thread->status == ThreadStatus::WaitSynchAll,
|
thread->GetStatus() == ThreadStatus::WaitSynchAll,
|
||||||
"Exiting processes with non-waiting threads is currently unimplemented");
|
"Exiting processes with non-waiting threads is currently unimplemented");
|
||||||
|
|
||||||
thread->Stop();
|
thread->Stop();
|
||||||
|
|
|
@ -38,10 +38,10 @@ Thread* Scheduler::PopNextReadyThread() {
|
||||||
Thread* next = nullptr;
|
Thread* next = nullptr;
|
||||||
Thread* thread = GetCurrentThread();
|
Thread* thread = GetCurrentThread();
|
||||||
|
|
||||||
if (thread && thread->status == ThreadStatus::Running) {
|
if (thread && thread->GetStatus() == ThreadStatus::Running) {
|
||||||
// We have to do better than the current thread.
|
// We have to do better than the current thread.
|
||||||
// This call returns null when that's not possible.
|
// This call returns null when that's not possible.
|
||||||
next = ready_queue.pop_first_better(thread->current_priority);
|
next = ready_queue.pop_first_better(thread->GetPriority());
|
||||||
if (!next) {
|
if (!next) {
|
||||||
// Otherwise just keep going with the current thread
|
// Otherwise just keep going with the current thread
|
||||||
next = thread;
|
next = thread;
|
||||||
|
@ -58,22 +58,21 @@ void Scheduler::SwitchContext(Thread* new_thread) {
|
||||||
|
|
||||||
// Save context for previous thread
|
// Save context for previous thread
|
||||||
if (previous_thread) {
|
if (previous_thread) {
|
||||||
previous_thread->last_running_ticks = CoreTiming::GetTicks();
|
cpu_core.SaveContext(previous_thread->GetContext());
|
||||||
cpu_core.SaveContext(previous_thread->context);
|
|
||||||
// Save the TPIDR_EL0 system register in case it was modified.
|
// Save the TPIDR_EL0 system register in case it was modified.
|
||||||
previous_thread->tpidr_el0 = cpu_core.GetTPIDR_EL0();
|
previous_thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
|
||||||
|
|
||||||
if (previous_thread->status == ThreadStatus::Running) {
|
if (previous_thread->GetStatus() == ThreadStatus::Running) {
|
||||||
// This is only the case when a reschedule is triggered without the current thread
|
// This is only the case when a reschedule is triggered without the current thread
|
||||||
// yielding execution (i.e. an event triggered, system core time-sliced, etc)
|
// yielding execution (i.e. an event triggered, system core time-sliced, etc)
|
||||||
ready_queue.push_front(previous_thread->current_priority, previous_thread);
|
ready_queue.push_front(previous_thread->GetPriority(), previous_thread);
|
||||||
previous_thread->status = ThreadStatus::Ready;
|
previous_thread->SetStatus(ThreadStatus::Ready);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load context of new thread
|
// Load context of new thread
|
||||||
if (new_thread) {
|
if (new_thread) {
|
||||||
ASSERT_MSG(new_thread->status == ThreadStatus::Ready,
|
ASSERT_MSG(new_thread->GetStatus() == ThreadStatus::Ready,
|
||||||
"Thread must be ready to become running.");
|
"Thread must be ready to become running.");
|
||||||
|
|
||||||
// Cancel any outstanding wakeup events for this thread
|
// Cancel any outstanding wakeup events for this thread
|
||||||
|
@ -83,15 +82,16 @@ void Scheduler::SwitchContext(Thread* new_thread) {
|
||||||
|
|
||||||
current_thread = new_thread;
|
current_thread = new_thread;
|
||||||
|
|
||||||
ready_queue.remove(new_thread->current_priority, new_thread);
|
ready_queue.remove(new_thread->GetPriority(), new_thread);
|
||||||
new_thread->status = ThreadStatus::Running;
|
new_thread->SetStatus(ThreadStatus::Running);
|
||||||
|
|
||||||
if (previous_process != current_thread->owner_process) {
|
const auto thread_owner_process = current_thread->GetOwnerProcess();
|
||||||
Core::CurrentProcess() = current_thread->owner_process;
|
if (previous_process != thread_owner_process) {
|
||||||
|
Core::CurrentProcess() = thread_owner_process;
|
||||||
SetCurrentPageTable(&Core::CurrentProcess()->VMManager().page_table);
|
SetCurrentPageTable(&Core::CurrentProcess()->VMManager().page_table);
|
||||||
}
|
}
|
||||||
|
|
||||||
cpu_core.LoadContext(new_thread->context);
|
cpu_core.LoadContext(new_thread->GetContext());
|
||||||
cpu_core.SetTlsAddress(new_thread->GetTLSAddress());
|
cpu_core.SetTlsAddress(new_thread->GetTLSAddress());
|
||||||
cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0());
|
cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0());
|
||||||
cpu_core.ClearExclusiveState();
|
cpu_core.ClearExclusiveState();
|
||||||
|
@ -136,14 +136,14 @@ void Scheduler::RemoveThread(Thread* thread) {
|
||||||
void Scheduler::ScheduleThread(Thread* thread, u32 priority) {
|
void Scheduler::ScheduleThread(Thread* thread, u32 priority) {
|
||||||
std::lock_guard<std::mutex> lock(scheduler_mutex);
|
std::lock_guard<std::mutex> lock(scheduler_mutex);
|
||||||
|
|
||||||
ASSERT(thread->status == ThreadStatus::Ready);
|
ASSERT(thread->GetStatus() == ThreadStatus::Ready);
|
||||||
ready_queue.push_back(priority, thread);
|
ready_queue.push_back(priority, thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::UnscheduleThread(Thread* thread, u32 priority) {
|
void Scheduler::UnscheduleThread(Thread* thread, u32 priority) {
|
||||||
std::lock_guard<std::mutex> lock(scheduler_mutex);
|
std::lock_guard<std::mutex> lock(scheduler_mutex);
|
||||||
|
|
||||||
ASSERT(thread->status == ThreadStatus::Ready);
|
ASSERT(thread->GetStatus() == ThreadStatus::Ready);
|
||||||
ready_queue.remove(priority, thread);
|
ready_queue.remove(priority, thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,8 +151,8 @@ void Scheduler::SetThreadPriority(Thread* thread, u32 priority) {
|
||||||
std::lock_guard<std::mutex> lock(scheduler_mutex);
|
std::lock_guard<std::mutex> lock(scheduler_mutex);
|
||||||
|
|
||||||
// If thread was ready, adjust queues
|
// If thread was ready, adjust queues
|
||||||
if (thread->status == ThreadStatus::Ready)
|
if (thread->GetStatus() == ThreadStatus::Ready)
|
||||||
ready_queue.move(thread, thread->current_priority, priority);
|
ready_queue.move(thread, thread->GetPriority(), priority);
|
||||||
else
|
else
|
||||||
ready_queue.prepare(priority);
|
ready_queue.prepare(priority);
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,10 +120,10 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
|
||||||
result = hle_handler->HandleSyncRequest(context);
|
result = hle_handler->HandleSyncRequest(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (thread->status == ThreadStatus::Running) {
|
if (thread->GetStatus() == ThreadStatus::Running) {
|
||||||
// Put the thread to sleep until the server replies, it will be awoken in
|
// Put the thread to sleep until the server replies, it will be awoken in
|
||||||
// svcReplyAndReceive for LLE servers.
|
// svcReplyAndReceive for LLE servers.
|
||||||
thread->status = ThreadStatus::WaitIPC;
|
thread->SetStatus(ThreadStatus::WaitIPC);
|
||||||
|
|
||||||
if (hle_handler != nullptr) {
|
if (hle_handler != nullptr) {
|
||||||
// For HLE services, we put the request threads to sleep for a short duration to
|
// For HLE services, we put the request threads to sleep for a short duration to
|
||||||
|
|
|
@ -156,7 +156,7 @@ static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) {
|
||||||
return ERR_INVALID_HANDLE;
|
return ERR_INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
*thread_id = thread->GetThreadId();
|
*thread_id = thread->GetThreadID();
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,7 +177,7 @@ static ResultCode GetProcessId(u32* process_id, Handle process_handle) {
|
||||||
/// Default thread wakeup callback for WaitSynchronization
|
/// Default thread wakeup callback for WaitSynchronization
|
||||||
static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread,
|
static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread,
|
||||||
SharedPtr<WaitObject> object, std::size_t index) {
|
SharedPtr<WaitObject> object, std::size_t index) {
|
||||||
ASSERT(thread->status == ThreadStatus::WaitSynchAny);
|
ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny);
|
||||||
|
|
||||||
if (reason == ThreadWakeupReason::Timeout) {
|
if (reason == ThreadWakeupReason::Timeout) {
|
||||||
thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
|
thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
|
||||||
|
@ -204,10 +204,10 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
|
||||||
if (handle_count > MaxHandles)
|
if (handle_count > MaxHandles)
|
||||||
return ResultCode(ErrorModule::Kernel, ErrCodes::TooLarge);
|
return ResultCode(ErrorModule::Kernel, ErrCodes::TooLarge);
|
||||||
|
|
||||||
auto thread = GetCurrentThread();
|
auto* const thread = GetCurrentThread();
|
||||||
|
|
||||||
using ObjectPtr = SharedPtr<WaitObject>;
|
using ObjectPtr = Thread::ThreadWaitObjects::value_type;
|
||||||
std::vector<ObjectPtr> objects(handle_count);
|
Thread::ThreadWaitObjects objects(handle_count);
|
||||||
auto& kernel = Core::System::GetInstance().Kernel();
|
auto& kernel = Core::System::GetInstance().Kernel();
|
||||||
|
|
||||||
for (u64 i = 0; i < handle_count; ++i) {
|
for (u64 i = 0; i < handle_count; ++i) {
|
||||||
|
@ -244,14 +244,14 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
|
||||||
for (auto& object : objects)
|
for (auto& object : objects)
|
||||||
object->AddWaitingThread(thread);
|
object->AddWaitingThread(thread);
|
||||||
|
|
||||||
thread->wait_objects = std::move(objects);
|
thread->SetWaitObjects(std::move(objects));
|
||||||
thread->status = ThreadStatus::WaitSynchAny;
|
thread->SetStatus(ThreadStatus::WaitSynchAny);
|
||||||
|
|
||||||
// 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
|
||||||
thread->WakeAfterDelay(nano_seconds);
|
thread->WakeAfterDelay(nano_seconds);
|
||||||
thread->wakeup_callback = DefaultThreadWakeupCallback;
|
thread->SetWakeupCallback(DefaultThreadWakeupCallback);
|
||||||
|
|
||||||
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
|
Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
|
||||||
|
|
||||||
return RESULT_TIMEOUT;
|
return RESULT_TIMEOUT;
|
||||||
}
|
}
|
||||||
|
@ -266,7 +266,7 @@ static ResultCode CancelSynchronization(Handle thread_handle) {
|
||||||
return ERR_INVALID_HANDLE;
|
return ERR_INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(thread->status == ThreadStatus::WaitSynchAny);
|
ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny);
|
||||||
thread->SetWaitSynchronizationResult(
|
thread->SetWaitSynchronizationResult(
|
||||||
ResultCode(ErrorModule::Kernel, ErrCodes::SynchronizationCanceled));
|
ResultCode(ErrorModule::Kernel, ErrCodes::SynchronizationCanceled));
|
||||||
thread->ResumeFromWait();
|
thread->ResumeFromWait();
|
||||||
|
@ -425,7 +425,7 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto current_process = Core::CurrentProcess();
|
const auto current_process = Core::CurrentProcess();
|
||||||
if (thread->owner_process != current_process) {
|
if (thread->GetOwnerProcess() != current_process) {
|
||||||
return ERR_INVALID_HANDLE;
|
return ERR_INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,7 +433,7 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
|
||||||
return ERR_ALREADY_REGISTERED;
|
return ERR_ALREADY_REGISTERED;
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::ARM_Interface::ThreadContext ctx = thread->context;
|
Core::ARM_Interface::ThreadContext ctx = thread->GetContext();
|
||||||
// Mask away mode bits, interrupt bits, IL bit, and other reserved bits.
|
// Mask away mode bits, interrupt bits, IL bit, and other reserved bits.
|
||||||
ctx.pstate &= 0xFF0FFE20;
|
ctx.pstate &= 0xFF0FFE20;
|
||||||
|
|
||||||
|
@ -479,14 +479,14 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
|
||||||
|
|
||||||
thread->SetPriority(priority);
|
thread->SetPriority(priority);
|
||||||
|
|
||||||
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
|
Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get which CPU core is executing the current thread
|
/// Get which CPU core is executing the current thread
|
||||||
static u32 GetCurrentProcessorNumber() {
|
static u32 GetCurrentProcessorNumber() {
|
||||||
LOG_TRACE(Kernel_SVC, "called");
|
LOG_TRACE(Kernel_SVC, "called");
|
||||||
return GetCurrentThread()->processor_id;
|
return GetCurrentThread()->GetProcessorID();
|
||||||
}
|
}
|
||||||
|
|
||||||
static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size,
|
static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size,
|
||||||
|
@ -622,10 +622,14 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
|
||||||
CASCADE_RESULT(SharedPtr<Thread> thread,
|
CASCADE_RESULT(SharedPtr<Thread> thread,
|
||||||
Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top,
|
Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top,
|
||||||
Core::CurrentProcess()));
|
Core::CurrentProcess()));
|
||||||
CASCADE_RESULT(thread->guest_handle, kernel.HandleTable().Create(thread));
|
const auto new_guest_handle = kernel.HandleTable().Create(thread);
|
||||||
*out_handle = thread->guest_handle;
|
if (new_guest_handle.Failed()) {
|
||||||
|
return new_guest_handle.Code();
|
||||||
|
}
|
||||||
|
thread->SetGuestHandle(*new_guest_handle);
|
||||||
|
*out_handle = *new_guest_handle;
|
||||||
|
|
||||||
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
|
Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
|
||||||
|
|
||||||
LOG_TRACE(Kernel_SVC,
|
LOG_TRACE(Kernel_SVC,
|
||||||
"called entrypoint=0x{:08X} ({}), arg=0x{:08X}, stacktop=0x{:08X}, "
|
"called entrypoint=0x{:08X} ({}), arg=0x{:08X}, stacktop=0x{:08X}, "
|
||||||
|
@ -645,10 +649,10 @@ static ResultCode StartThread(Handle thread_handle) {
|
||||||
return ERR_INVALID_HANDLE;
|
return ERR_INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(thread->status == ThreadStatus::Dormant);
|
ASSERT(thread->GetStatus() == ThreadStatus::Dormant);
|
||||||
|
|
||||||
thread->ResumeFromWait();
|
thread->ResumeFromWait();
|
||||||
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
|
Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
|
||||||
|
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -694,17 +698,17 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var
|
||||||
CASCADE_CODE(Mutex::Release(mutex_addr));
|
CASCADE_CODE(Mutex::Release(mutex_addr));
|
||||||
|
|
||||||
SharedPtr<Thread> current_thread = GetCurrentThread();
|
SharedPtr<Thread> current_thread = GetCurrentThread();
|
||||||
current_thread->condvar_wait_address = condition_variable_addr;
|
current_thread->SetCondVarWaitAddress(condition_variable_addr);
|
||||||
current_thread->mutex_wait_address = mutex_addr;
|
current_thread->SetMutexWaitAddress(mutex_addr);
|
||||||
current_thread->wait_handle = thread_handle;
|
current_thread->SetWaitHandle(thread_handle);
|
||||||
current_thread->status = ThreadStatus::WaitMutex;
|
current_thread->SetStatus(ThreadStatus::WaitMutex);
|
||||||
current_thread->wakeup_callback = nullptr;
|
current_thread->InvalidateWakeupCallback();
|
||||||
|
|
||||||
current_thread->WakeAfterDelay(nano_seconds);
|
current_thread->WakeAfterDelay(nano_seconds);
|
||||||
|
|
||||||
// Note: Deliberately don't attempt to inherit the lock owner's priority.
|
// Note: Deliberately don't attempt to inherit the lock owner's priority.
|
||||||
|
|
||||||
Core::System::GetInstance().CpuCore(current_thread->processor_id).PrepareReschedule();
|
Core::System::GetInstance().CpuCore(current_thread->GetProcessorID()).PrepareReschedule();
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -713,14 +717,14 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
|
||||||
LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}",
|
LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}",
|
||||||
condition_variable_addr, target);
|
condition_variable_addr, target);
|
||||||
|
|
||||||
auto RetrieveWaitingThreads = [](std::size_t core_index,
|
const auto RetrieveWaitingThreads = [](std::size_t core_index,
|
||||||
std::vector<SharedPtr<Thread>>& waiting_threads,
|
std::vector<SharedPtr<Thread>>& waiting_threads,
|
||||||
VAddr condvar_addr) {
|
VAddr condvar_addr) {
|
||||||
const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
|
const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
|
||||||
auto& thread_list = scheduler->GetThreadList();
|
const auto& thread_list = scheduler->GetThreadList();
|
||||||
|
|
||||||
for (auto& thread : thread_list) {
|
for (const auto& thread : thread_list) {
|
||||||
if (thread->condvar_wait_address == condvar_addr)
|
if (thread->GetCondVarWaitAddress() == condvar_addr)
|
||||||
waiting_threads.push_back(thread);
|
waiting_threads.push_back(thread);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -734,7 +738,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
|
||||||
// Sort them by priority, such that the highest priority ones come first.
|
// Sort them by priority, such that the highest priority ones come first.
|
||||||
std::sort(waiting_threads.begin(), waiting_threads.end(),
|
std::sort(waiting_threads.begin(), waiting_threads.end(),
|
||||||
[](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
|
[](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
|
||||||
return lhs->current_priority < rhs->current_priority;
|
return lhs->GetPriority() < rhs->GetPriority();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Only process up to 'target' threads, unless 'target' is -1, in which case process
|
// Only process up to 'target' threads, unless 'target' is -1, in which case process
|
||||||
|
@ -750,7 +754,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
|
||||||
for (std::size_t index = 0; index < last; ++index) {
|
for (std::size_t index = 0; index < last; ++index) {
|
||||||
auto& thread = waiting_threads[index];
|
auto& thread = waiting_threads[index];
|
||||||
|
|
||||||
ASSERT(thread->condvar_wait_address == condition_variable_addr);
|
ASSERT(thread->GetCondVarWaitAddress() == condition_variable_addr);
|
||||||
|
|
||||||
std::size_t current_core = Core::System::GetInstance().CurrentCoreIndex();
|
std::size_t current_core = Core::System::GetInstance().CurrentCoreIndex();
|
||||||
|
|
||||||
|
@ -759,42 +763,43 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
|
||||||
// Atomically read the value of the mutex.
|
// Atomically read the value of the mutex.
|
||||||
u32 mutex_val = 0;
|
u32 mutex_val = 0;
|
||||||
do {
|
do {
|
||||||
monitor.SetExclusive(current_core, thread->mutex_wait_address);
|
monitor.SetExclusive(current_core, thread->GetMutexWaitAddress());
|
||||||
|
|
||||||
// If the mutex is not yet acquired, acquire it.
|
// If the mutex is not yet acquired, acquire it.
|
||||||
mutex_val = Memory::Read32(thread->mutex_wait_address);
|
mutex_val = Memory::Read32(thread->GetMutexWaitAddress());
|
||||||
|
|
||||||
if (mutex_val != 0) {
|
if (mutex_val != 0) {
|
||||||
monitor.ClearExclusive();
|
monitor.ClearExclusive();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while (!monitor.ExclusiveWrite32(current_core, thread->mutex_wait_address,
|
} while (!monitor.ExclusiveWrite32(current_core, thread->GetMutexWaitAddress(),
|
||||||
thread->wait_handle));
|
thread->GetWaitHandle()));
|
||||||
|
|
||||||
if (mutex_val == 0) {
|
if (mutex_val == 0) {
|
||||||
// We were able to acquire the mutex, resume this thread.
|
// We were able to acquire the mutex, resume this thread.
|
||||||
ASSERT(thread->status == ThreadStatus::WaitMutex);
|
ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
|
||||||
thread->ResumeFromWait();
|
thread->ResumeFromWait();
|
||||||
|
|
||||||
auto lock_owner = thread->lock_owner;
|
auto* const lock_owner = thread->GetLockOwner();
|
||||||
if (lock_owner)
|
if (lock_owner != nullptr) {
|
||||||
lock_owner->RemoveMutexWaiter(thread);
|
lock_owner->RemoveMutexWaiter(thread);
|
||||||
|
}
|
||||||
|
|
||||||
thread->lock_owner = nullptr;
|
thread->SetLockOwner(nullptr);
|
||||||
thread->mutex_wait_address = 0;
|
thread->SetMutexWaitAddress(0);
|
||||||
thread->condvar_wait_address = 0;
|
thread->SetCondVarWaitAddress(0);
|
||||||
thread->wait_handle = 0;
|
thread->SetWaitHandle(0);
|
||||||
} else {
|
} else {
|
||||||
// Atomically signal that the mutex now has a waiting thread.
|
// Atomically signal that the mutex now has a waiting thread.
|
||||||
do {
|
do {
|
||||||
monitor.SetExclusive(current_core, thread->mutex_wait_address);
|
monitor.SetExclusive(current_core, thread->GetMutexWaitAddress());
|
||||||
|
|
||||||
// Ensure that the mutex value is still what we expect.
|
// Ensure that the mutex value is still what we expect.
|
||||||
u32 value = Memory::Read32(thread->mutex_wait_address);
|
u32 value = Memory::Read32(thread->GetMutexWaitAddress());
|
||||||
// TODO(Subv): When this happens, the kernel just clears the exclusive state and
|
// TODO(Subv): When this happens, the kernel just clears the exclusive state and
|
||||||
// retries the initial read for this thread.
|
// retries the initial read for this thread.
|
||||||
ASSERT_MSG(mutex_val == value, "Unhandled synchronization primitive case");
|
ASSERT_MSG(mutex_val == value, "Unhandled synchronization primitive case");
|
||||||
} while (!monitor.ExclusiveWrite32(current_core, thread->mutex_wait_address,
|
} while (!monitor.ExclusiveWrite32(current_core, thread->GetMutexWaitAddress(),
|
||||||
mutex_val | Mutex::MutexHasWaitersFlag));
|
mutex_val | Mutex::MutexHasWaitersFlag));
|
||||||
|
|
||||||
// The mutex is already owned by some other thread, make this thread wait on it.
|
// The mutex is already owned by some other thread, make this thread wait on it.
|
||||||
|
@ -802,12 +807,12 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
|
||||||
Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
|
Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
|
||||||
auto owner = kernel.HandleTable().Get<Thread>(owner_handle);
|
auto owner = kernel.HandleTable().Get<Thread>(owner_handle);
|
||||||
ASSERT(owner);
|
ASSERT(owner);
|
||||||
ASSERT(thread->status == ThreadStatus::WaitMutex);
|
ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
|
||||||
thread->wakeup_callback = nullptr;
|
thread->InvalidateWakeupCallback();
|
||||||
|
|
||||||
owner->AddMutexWaiter(thread);
|
owner->AddMutexWaiter(thread);
|
||||||
|
|
||||||
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
|
Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -913,8 +918,8 @@ static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask)
|
||||||
return ERR_INVALID_HANDLE;
|
return ERR_INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
*core = thread->ideal_core;
|
*core = thread->GetIdealCore();
|
||||||
*mask = thread->affinity_mask;
|
*mask = thread->GetAffinityMask();
|
||||||
|
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -930,11 +935,13 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (core == static_cast<u32>(THREADPROCESSORID_DEFAULT)) {
|
if (core == static_cast<u32>(THREADPROCESSORID_DEFAULT)) {
|
||||||
ASSERT(thread->owner_process->GetDefaultProcessorID() !=
|
const u8 default_processor_id = thread->GetOwnerProcess()->GetDefaultProcessorID();
|
||||||
static_cast<u8>(THREADPROCESSORID_DEFAULT));
|
|
||||||
|
ASSERT(default_processor_id != static_cast<u8>(THREADPROCESSORID_DEFAULT));
|
||||||
|
|
||||||
// Set the target CPU to the one specified in the process' exheader.
|
// Set the target CPU to the one specified in the process' exheader.
|
||||||
core = thread->owner_process->GetDefaultProcessorID();
|
core = default_processor_id;
|
||||||
mask = 1ull << core;
|
mask = 1ULL << core;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mask == 0) {
|
if (mask == 0) {
|
||||||
|
@ -945,7 +952,7 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
|
||||||
static constexpr u32 OnlyChangeMask = static_cast<u32>(-3);
|
static constexpr u32 OnlyChangeMask = static_cast<u32>(-3);
|
||||||
|
|
||||||
if (core == OnlyChangeMask) {
|
if (core == OnlyChangeMask) {
|
||||||
core = thread->ideal_core;
|
core = thread->GetIdealCore();
|
||||||
} else if (core >= Core::NUM_CPU_CORES && core != static_cast<u32>(-1)) {
|
} else if (core >= Core::NUM_CPU_CORES && core != static_cast<u32>(-1)) {
|
||||||
return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidProcessorId);
|
return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidProcessorId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ void Thread::Stop() {
|
||||||
|
|
||||||
void WaitCurrentThread_Sleep() {
|
void WaitCurrentThread_Sleep() {
|
||||||
Thread* thread = GetCurrentThread();
|
Thread* thread = GetCurrentThread();
|
||||||
thread->status = ThreadStatus::WaitSleep;
|
thread->SetStatus(ThreadStatus::WaitSleep);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExitCurrentThread() {
|
void ExitCurrentThread() {
|
||||||
|
@ -269,9 +269,9 @@ SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 pri
|
||||||
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
|
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
|
||||||
|
|
||||||
// Register 1 must be a handle to the main thread
|
// Register 1 must be a handle to the main thread
|
||||||
thread->guest_handle = kernel.HandleTable().Create(thread).Unwrap();
|
const Handle guest_handle = kernel.HandleTable().Create(thread).Unwrap();
|
||||||
|
thread->SetGuestHandle(guest_handle);
|
||||||
thread->context.cpu_registers[1] = thread->guest_handle;
|
thread->GetContext().cpu_registers[1] = guest_handle;
|
||||||
|
|
||||||
// Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
|
// Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
|
||||||
thread->ResumeFromWait();
|
thread->ResumeFromWait();
|
||||||
|
@ -299,6 +299,18 @@ VAddr Thread::GetCommandBufferAddress() const {
|
||||||
return GetTLSAddress() + CommandHeaderOffset;
|
return GetTLSAddress() + CommandHeaderOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Thread::SetStatus(ThreadStatus new_status) {
|
||||||
|
if (new_status == status) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status == ThreadStatus::Running) {
|
||||||
|
last_running_ticks = CoreTiming::GetTicks();
|
||||||
|
}
|
||||||
|
|
||||||
|
status = new_status;
|
||||||
|
}
|
||||||
|
|
||||||
void Thread::AddMutexWaiter(SharedPtr<Thread> thread) {
|
void Thread::AddMutexWaiter(SharedPtr<Thread> thread) {
|
||||||
if (thread->lock_owner == this) {
|
if (thread->lock_owner == this) {
|
||||||
// If the thread is already waiting for this thread to release the mutex, ensure that the
|
// If the thread is already waiting for this thread to release the mutex, ensure that the
|
||||||
|
@ -393,6 +405,18 @@ void Thread::ChangeCore(u32 core, u64 mask) {
|
||||||
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
|
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Thread::AllWaitObjectsReady() {
|
||||||
|
return std::none_of(
|
||||||
|
wait_objects.begin(), wait_objects.end(),
|
||||||
|
[this](const SharedPtr<WaitObject>& object) { return object->ShouldWait(this); });
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Thread::InvokeWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread,
|
||||||
|
SharedPtr<WaitObject> object, std::size_t index) {
|
||||||
|
ASSERT(wakeup_callback);
|
||||||
|
return wakeup_callback(reason, std::move(thread), std::move(object), index);
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -65,6 +65,15 @@ public:
|
||||||
using TLSMemory = std::vector<u8>;
|
using TLSMemory = std::vector<u8>;
|
||||||
using TLSMemoryPtr = std::shared_ptr<TLSMemory>;
|
using TLSMemoryPtr = std::shared_ptr<TLSMemory>;
|
||||||
|
|
||||||
|
using MutexWaitingThreads = std::vector<SharedPtr<Thread>>;
|
||||||
|
|
||||||
|
using ThreadContext = Core::ARM_Interface::ThreadContext;
|
||||||
|
|
||||||
|
using ThreadWaitObjects = std::vector<SharedPtr<WaitObject>>;
|
||||||
|
|
||||||
|
using WakeupCallback = std::function<bool(ThreadWakeupReason reason, SharedPtr<Thread> thread,
|
||||||
|
SharedPtr<WaitObject> object, std::size_t index)>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates and returns a new thread. The new thread is immediately scheduled
|
* Creates and returns a new thread. The new thread is immediately scheduled
|
||||||
* @param kernel The kernel instance this thread will be created under.
|
* @param kernel The kernel instance this thread will be created under.
|
||||||
|
@ -105,6 +114,14 @@ public:
|
||||||
return current_priority;
|
return current_priority;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the thread's nominal priority.
|
||||||
|
* @return The current thread's nominal priority.
|
||||||
|
*/
|
||||||
|
u32 GetNominalPriority() const {
|
||||||
|
return nominal_priority;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the thread's current priority
|
* Sets the thread's current priority
|
||||||
* @param priority The new priority
|
* @param priority The new priority
|
||||||
|
@ -133,7 +150,7 @@ public:
|
||||||
* Gets the thread's thread ID
|
* Gets the thread's thread ID
|
||||||
* @return The thread's ID
|
* @return The thread's ID
|
||||||
*/
|
*/
|
||||||
u32 GetThreadId() const {
|
u32 GetThreadID() const {
|
||||||
return thread_id;
|
return thread_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,6 +220,11 @@ public:
|
||||||
return tpidr_el0;
|
return tpidr_el0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the value of the TPIDR_EL0 Read/Write system register for this thread.
|
||||||
|
void SetTPIDR_EL0(u64 value) {
|
||||||
|
tpidr_el0 = value;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns the address of the current thread's command buffer, located in the TLS.
|
* Returns the address of the current thread's command buffer, located in the TLS.
|
||||||
* @returns VAddr of the thread's command buffer.
|
* @returns VAddr of the thread's command buffer.
|
||||||
|
@ -218,69 +240,193 @@ public:
|
||||||
return status == ThreadStatus::WaitSynchAll;
|
return status == ThreadStatus::WaitSynchAll;
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::ARM_Interface::ThreadContext context;
|
ThreadContext& GetContext() {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
u32 thread_id;
|
const ThreadContext& GetContext() const {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
ThreadStatus status;
|
ThreadStatus GetStatus() const {
|
||||||
VAddr entry_point;
|
return status;
|
||||||
VAddr stack_top;
|
}
|
||||||
|
|
||||||
u32 nominal_priority; ///< Nominal thread priority, as set by the emulated application
|
void SetStatus(ThreadStatus new_status);
|
||||||
u32 current_priority; ///< Current thread priority, can be temporarily changed
|
|
||||||
|
|
||||||
u64 last_running_ticks; ///< CPU tick when thread was last running
|
u64 GetLastRunningTicks() const {
|
||||||
|
return last_running_ticks;
|
||||||
|
}
|
||||||
|
|
||||||
s32 processor_id;
|
s32 GetProcessorID() const {
|
||||||
|
return processor_id;
|
||||||
|
}
|
||||||
|
|
||||||
VAddr tls_address; ///< Virtual address of the Thread Local Storage of the thread
|
SharedPtr<Process>& GetOwnerProcess() {
|
||||||
u64 tpidr_el0; ///< TPIDR_EL0 read/write system register.
|
return owner_process;
|
||||||
|
}
|
||||||
|
|
||||||
SharedPtr<Process> owner_process; ///< Process that owns this thread
|
const SharedPtr<Process>& GetOwnerProcess() const {
|
||||||
|
return owner_process;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ThreadWaitObjects& GetWaitObjects() const {
|
||||||
|
return wait_objects;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetWaitObjects(ThreadWaitObjects objects) {
|
||||||
|
wait_objects = std::move(objects);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearWaitObjects() {
|
||||||
|
wait_objects.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines whether all the objects this thread is waiting on are ready.
|
||||||
|
bool AllWaitObjectsReady();
|
||||||
|
|
||||||
|
const MutexWaitingThreads& GetMutexWaitingThreads() const {
|
||||||
|
return wait_mutex_threads;
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread* GetLockOwner() const {
|
||||||
|
return lock_owner.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetLockOwner(SharedPtr<Thread> owner) {
|
||||||
|
lock_owner = std::move(owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
VAddr GetCondVarWaitAddress() const {
|
||||||
|
return condvar_wait_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetCondVarWaitAddress(VAddr address) {
|
||||||
|
condvar_wait_address = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
VAddr GetMutexWaitAddress() const {
|
||||||
|
return mutex_wait_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetMutexWaitAddress(VAddr address) {
|
||||||
|
mutex_wait_address = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle GetWaitHandle() const {
|
||||||
|
return wait_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetWaitHandle(Handle handle) {
|
||||||
|
wait_handle = handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
VAddr GetArbiterWaitAddress() const {
|
||||||
|
return arb_wait_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetArbiterWaitAddress(VAddr address) {
|
||||||
|
arb_wait_address = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetGuestHandle(Handle handle) {
|
||||||
|
guest_handle = handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasWakeupCallback() const {
|
||||||
|
return wakeup_callback != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetWakeupCallback(WakeupCallback callback) {
|
||||||
|
wakeup_callback = std::move(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InvalidateWakeupCallback() {
|
||||||
|
SetWakeupCallback(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invokes the thread's wakeup callback.
|
||||||
|
*
|
||||||
|
* @pre A valid wakeup callback has been set. Violating this precondition
|
||||||
|
* will cause an assertion to trigger.
|
||||||
|
*/
|
||||||
|
bool InvokeWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread,
|
||||||
|
SharedPtr<WaitObject> object, std::size_t index);
|
||||||
|
|
||||||
|
u32 GetIdealCore() const {
|
||||||
|
return ideal_core;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 GetAffinityMask() const {
|
||||||
|
return affinity_mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit Thread(KernelCore& kernel);
|
||||||
|
~Thread() override;
|
||||||
|
|
||||||
|
Core::ARM_Interface::ThreadContext context{};
|
||||||
|
|
||||||
|
u32 thread_id = 0;
|
||||||
|
|
||||||
|
ThreadStatus status = ThreadStatus::Dormant;
|
||||||
|
|
||||||
|
VAddr entry_point = 0;
|
||||||
|
VAddr stack_top = 0;
|
||||||
|
|
||||||
|
u32 nominal_priority = 0; ///< Nominal thread priority, as set by the emulated application
|
||||||
|
u32 current_priority = 0; ///< Current thread priority, can be temporarily changed
|
||||||
|
|
||||||
|
u64 last_running_ticks = 0; ///< CPU tick when thread was last running
|
||||||
|
|
||||||
|
s32 processor_id = 0;
|
||||||
|
|
||||||
|
VAddr tls_address = 0; ///< Virtual address of the Thread Local Storage of the thread
|
||||||
|
u64 tpidr_el0 = 0; ///< TPIDR_EL0 read/write system register.
|
||||||
|
|
||||||
|
/// Process that owns this thread
|
||||||
|
SharedPtr<Process> owner_process;
|
||||||
|
|
||||||
/// Objects that the thread is waiting on, in the same order as they were
|
/// Objects that the thread is waiting on, in the same order as they were
|
||||||
// passed to WaitSynchronization1/N.
|
/// passed to WaitSynchronization1/N.
|
||||||
std::vector<SharedPtr<WaitObject>> wait_objects;
|
ThreadWaitObjects wait_objects;
|
||||||
|
|
||||||
/// List of threads that are waiting for a mutex that is held by this thread.
|
/// List of threads that are waiting for a mutex that is held by this thread.
|
||||||
std::vector<SharedPtr<Thread>> wait_mutex_threads;
|
MutexWaitingThreads wait_mutex_threads;
|
||||||
|
|
||||||
/// Thread that owns the lock that this thread is waiting for.
|
/// Thread that owns the lock that this thread is waiting for.
|
||||||
SharedPtr<Thread> lock_owner;
|
SharedPtr<Thread> lock_owner;
|
||||||
|
|
||||||
// If waiting on a ConditionVariable, this is the ConditionVariable address
|
/// If waiting on a ConditionVariable, this is the ConditionVariable address
|
||||||
VAddr condvar_wait_address;
|
VAddr condvar_wait_address = 0;
|
||||||
VAddr mutex_wait_address; ///< If waiting on a Mutex, this is the mutex address
|
/// If waiting on a Mutex, this is the mutex address
|
||||||
Handle wait_handle; ///< The handle used to wait for the mutex.
|
VAddr mutex_wait_address = 0;
|
||||||
|
/// The handle used to wait for the mutex.
|
||||||
|
Handle wait_handle = 0;
|
||||||
|
|
||||||
// If waiting for an AddressArbiter, this is the address being waited on.
|
/// If waiting for an AddressArbiter, this is the address being waited on.
|
||||||
VAddr arb_wait_address{0};
|
VAddr arb_wait_address{0};
|
||||||
|
|
||||||
std::string name;
|
|
||||||
|
|
||||||
/// Handle used by guest emulated application to access this thread
|
/// Handle used by guest emulated application to access this thread
|
||||||
Handle guest_handle;
|
Handle guest_handle = 0;
|
||||||
|
|
||||||
/// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
|
/// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
|
||||||
Handle callback_handle;
|
Handle callback_handle = 0;
|
||||||
|
|
||||||
using WakeupCallback = bool(ThreadWakeupReason reason, SharedPtr<Thread> thread,
|
/// Callback that will be invoked when the thread is resumed from a waiting state. If the thread
|
||||||
SharedPtr<WaitObject> object, std::size_t index);
|
/// was waiting via WaitSynchronizationN then the object will be the last object that became
|
||||||
// Callback that will be invoked when the thread is resumed from a waiting state. If the thread
|
/// available. In case of a timeout, the object will be nullptr.
|
||||||
// was waiting via WaitSynchronizationN then the object will be the last object that became
|
WakeupCallback wakeup_callback;
|
||||||
// available. In case of a timeout, the object will be nullptr.
|
|
||||||
std::function<WakeupCallback> wakeup_callback;
|
|
||||||
|
|
||||||
std::shared_ptr<Scheduler> scheduler;
|
std::shared_ptr<Scheduler> scheduler;
|
||||||
|
|
||||||
u32 ideal_core{0xFFFFFFFF};
|
u32 ideal_core{0xFFFFFFFF};
|
||||||
u64 affinity_mask{0x1};
|
u64 affinity_mask{0x1};
|
||||||
|
|
||||||
private:
|
|
||||||
explicit Thread(KernelCore& kernel);
|
|
||||||
~Thread() override;
|
|
||||||
|
|
||||||
TLSMemoryPtr tls_memory = std::make_shared<TLSMemory>();
|
TLSMemoryPtr tls_memory = std::make_shared<TLSMemory>();
|
||||||
|
|
||||||
|
std::string name;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -35,13 +35,15 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
|
||||||
u32 candidate_priority = THREADPRIO_LOWEST + 1;
|
u32 candidate_priority = THREADPRIO_LOWEST + 1;
|
||||||
|
|
||||||
for (const auto& thread : waiting_threads) {
|
for (const auto& thread : waiting_threads) {
|
||||||
|
const ThreadStatus thread_status = thread->GetStatus();
|
||||||
|
|
||||||
// The list of waiting threads must not contain threads that are not waiting to be awakened.
|
// The list of waiting threads must not contain threads that are not waiting to be awakened.
|
||||||
ASSERT_MSG(thread->status == ThreadStatus::WaitSynchAny ||
|
ASSERT_MSG(thread_status == ThreadStatus::WaitSynchAny ||
|
||||||
thread->status == ThreadStatus::WaitSynchAll ||
|
thread_status == ThreadStatus::WaitSynchAll ||
|
||||||
thread->status == ThreadStatus::WaitHLEEvent,
|
thread_status == ThreadStatus::WaitHLEEvent,
|
||||||
"Inconsistent thread statuses in waiting_threads");
|
"Inconsistent thread statuses in waiting_threads");
|
||||||
|
|
||||||
if (thread->current_priority >= candidate_priority)
|
if (thread->GetPriority() >= candidate_priority)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (ShouldWait(thread.get()))
|
if (ShouldWait(thread.get()))
|
||||||
|
@ -50,16 +52,13 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
|
||||||
// A thread is ready to run if it's either in ThreadStatus::WaitSynchAny or
|
// A thread is ready to run if it's either in ThreadStatus::WaitSynchAny or
|
||||||
// in ThreadStatus::WaitSynchAll and the rest of the objects it is waiting on are ready.
|
// in ThreadStatus::WaitSynchAll and the rest of the objects it is waiting on are ready.
|
||||||
bool ready_to_run = true;
|
bool ready_to_run = true;
|
||||||
if (thread->status == ThreadStatus::WaitSynchAll) {
|
if (thread_status == ThreadStatus::WaitSynchAll) {
|
||||||
ready_to_run = std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(),
|
ready_to_run = thread->AllWaitObjectsReady();
|
||||||
[&thread](const SharedPtr<WaitObject>& object) {
|
|
||||||
return object->ShouldWait(thread.get());
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ready_to_run) {
|
if (ready_to_run) {
|
||||||
candidate = thread.get();
|
candidate = thread.get();
|
||||||
candidate_priority = thread->current_priority;
|
candidate_priority = thread->GetPriority();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,24 +74,24 @@ void WaitObject::WakeupWaitingThread(SharedPtr<Thread> thread) {
|
||||||
if (!thread->IsSleepingOnWaitAll()) {
|
if (!thread->IsSleepingOnWaitAll()) {
|
||||||
Acquire(thread.get());
|
Acquire(thread.get());
|
||||||
} else {
|
} else {
|
||||||
for (auto& object : thread->wait_objects) {
|
for (const auto& object : thread->GetWaitObjects()) {
|
||||||
ASSERT(!object->ShouldWait(thread.get()));
|
ASSERT(!object->ShouldWait(thread.get()));
|
||||||
object->Acquire(thread.get());
|
object->Acquire(thread.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t index = thread->GetWaitObjectIndex(this);
|
const std::size_t index = thread->GetWaitObjectIndex(this);
|
||||||
|
|
||||||
for (auto& object : thread->wait_objects)
|
for (const auto& object : thread->GetWaitObjects())
|
||||||
object->RemoveWaitingThread(thread.get());
|
object->RemoveWaitingThread(thread.get());
|
||||||
thread->wait_objects.clear();
|
thread->ClearWaitObjects();
|
||||||
|
|
||||||
thread->CancelWakeupTimer();
|
thread->CancelWakeupTimer();
|
||||||
|
|
||||||
bool resume = true;
|
bool resume = true;
|
||||||
|
|
||||||
if (thread->wakeup_callback)
|
if (thread->HasWakeupCallback())
|
||||||
resume = thread->wakeup_callback(ThreadWakeupReason::Signal, thread, this, index);
|
resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Signal, thread, this, index);
|
||||||
|
|
||||||
if (resume)
|
if (resume)
|
||||||
thread->ResumeFromWait();
|
thread->ResumeFromWait();
|
||||||
|
|
|
@ -119,7 +119,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() cons
|
||||||
std::vector<std::unique_ptr<WaitTreeItem>> list;
|
std::vector<std::unique_ptr<WaitTreeItem>> list;
|
||||||
|
|
||||||
constexpr std::size_t BaseRegister = 29;
|
constexpr std::size_t BaseRegister = 29;
|
||||||
u64 base_pointer = thread.context.cpu_registers[BaseRegister];
|
u64 base_pointer = thread.GetContext().cpu_registers[BaseRegister];
|
||||||
|
|
||||||
while (base_pointer != 0) {
|
while (base_pointer != 0) {
|
||||||
u64 lr = Memory::Read64(base_pointer + sizeof(u64));
|
u64 lr = Memory::Read64(base_pointer + sizeof(u64));
|
||||||
|
@ -213,7 +213,7 @@ WaitTreeThread::~WaitTreeThread() = default;
|
||||||
QString WaitTreeThread::GetText() const {
|
QString WaitTreeThread::GetText() const {
|
||||||
const auto& thread = static_cast<const Kernel::Thread&>(object);
|
const auto& thread = static_cast<const Kernel::Thread&>(object);
|
||||||
QString status;
|
QString status;
|
||||||
switch (thread.status) {
|
switch (thread.GetStatus()) {
|
||||||
case Kernel::ThreadStatus::Running:
|
case Kernel::ThreadStatus::Running:
|
||||||
status = tr("running");
|
status = tr("running");
|
||||||
break;
|
break;
|
||||||
|
@ -246,15 +246,17 @@ QString WaitTreeThread::GetText() const {
|
||||||
status = tr("dead");
|
status = tr("dead");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
QString pc_info = tr(" PC = 0x%1 LR = 0x%2")
|
|
||||||
.arg(thread.context.pc, 8, 16, QLatin1Char('0'))
|
const auto& context = thread.GetContext();
|
||||||
.arg(thread.context.cpu_registers[30], 8, 16, QLatin1Char('0'));
|
const QString pc_info = tr(" PC = 0x%1 LR = 0x%2")
|
||||||
|
.arg(context.pc, 8, 16, QLatin1Char('0'))
|
||||||
|
.arg(context.cpu_registers[30], 8, 16, QLatin1Char('0'));
|
||||||
return WaitTreeWaitObject::GetText() + pc_info + " (" + status + ") ";
|
return WaitTreeWaitObject::GetText() + pc_info + " (" + status + ") ";
|
||||||
}
|
}
|
||||||
|
|
||||||
QColor WaitTreeThread::GetColor() const {
|
QColor WaitTreeThread::GetColor() const {
|
||||||
const auto& thread = static_cast<const Kernel::Thread&>(object);
|
const auto& thread = static_cast<const Kernel::Thread&>(object);
|
||||||
switch (thread.status) {
|
switch (thread.GetStatus()) {
|
||||||
case Kernel::ThreadStatus::Running:
|
case Kernel::ThreadStatus::Running:
|
||||||
return QColor(Qt::GlobalColor::darkGreen);
|
return QColor(Qt::GlobalColor::darkGreen);
|
||||||
case Kernel::ThreadStatus::Ready:
|
case Kernel::ThreadStatus::Ready:
|
||||||
|
@ -284,7 +286,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
|
||||||
const auto& thread = static_cast<const Kernel::Thread&>(object);
|
const auto& thread = static_cast<const Kernel::Thread&>(object);
|
||||||
|
|
||||||
QString processor;
|
QString processor;
|
||||||
switch (thread.processor_id) {
|
switch (thread.GetProcessorID()) {
|
||||||
case Kernel::ThreadProcessorId::THREADPROCESSORID_DEFAULT:
|
case Kernel::ThreadProcessorId::THREADPROCESSORID_DEFAULT:
|
||||||
processor = tr("default");
|
processor = tr("default");
|
||||||
break;
|
break;
|
||||||
|
@ -292,32 +294,35 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
|
||||||
case Kernel::ThreadProcessorId::THREADPROCESSORID_1:
|
case Kernel::ThreadProcessorId::THREADPROCESSORID_1:
|
||||||
case Kernel::ThreadProcessorId::THREADPROCESSORID_2:
|
case Kernel::ThreadProcessorId::THREADPROCESSORID_2:
|
||||||
case Kernel::ThreadProcessorId::THREADPROCESSORID_3:
|
case Kernel::ThreadProcessorId::THREADPROCESSORID_3:
|
||||||
processor = tr("core %1").arg(thread.processor_id);
|
processor = tr("core %1").arg(thread.GetProcessorID());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
processor = tr("Unknown processor %1").arg(thread.processor_id);
|
processor = tr("Unknown processor %1").arg(thread.GetProcessorID());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
list.push_back(std::make_unique<WaitTreeText>(tr("processor = %1").arg(processor)));
|
list.push_back(std::make_unique<WaitTreeText>(tr("processor = %1").arg(processor)));
|
||||||
list.push_back(std::make_unique<WaitTreeText>(tr("ideal core = %1").arg(thread.ideal_core)));
|
|
||||||
list.push_back(
|
list.push_back(
|
||||||
std::make_unique<WaitTreeText>(tr("affinity mask = %1").arg(thread.affinity_mask)));
|
std::make_unique<WaitTreeText>(tr("ideal core = %1").arg(thread.GetIdealCore())));
|
||||||
list.push_back(std::make_unique<WaitTreeText>(tr("thread id = %1").arg(thread.GetThreadId())));
|
list.push_back(
|
||||||
|
std::make_unique<WaitTreeText>(tr("affinity mask = %1").arg(thread.GetAffinityMask())));
|
||||||
|
list.push_back(std::make_unique<WaitTreeText>(tr("thread id = %1").arg(thread.GetThreadID())));
|
||||||
list.push_back(std::make_unique<WaitTreeText>(tr("priority = %1(current) / %2(normal)")
|
list.push_back(std::make_unique<WaitTreeText>(tr("priority = %1(current) / %2(normal)")
|
||||||
.arg(thread.current_priority)
|
.arg(thread.GetPriority())
|
||||||
.arg(thread.nominal_priority)));
|
.arg(thread.GetNominalPriority())));
|
||||||
list.push_back(std::make_unique<WaitTreeText>(
|
list.push_back(std::make_unique<WaitTreeText>(
|
||||||
tr("last running ticks = %1").arg(thread.last_running_ticks)));
|
tr("last running ticks = %1").arg(thread.GetLastRunningTicks())));
|
||||||
|
|
||||||
if (thread.mutex_wait_address != 0)
|
const VAddr mutex_wait_address = thread.GetMutexWaitAddress();
|
||||||
list.push_back(std::make_unique<WaitTreeMutexInfo>(thread.mutex_wait_address));
|
if (mutex_wait_address != 0) {
|
||||||
else
|
list.push_back(std::make_unique<WaitTreeMutexInfo>(mutex_wait_address));
|
||||||
|
} else {
|
||||||
list.push_back(std::make_unique<WaitTreeText>(tr("not waiting for mutex")));
|
list.push_back(std::make_unique<WaitTreeText>(tr("not waiting for mutex")));
|
||||||
|
}
|
||||||
|
|
||||||
if (thread.status == Kernel::ThreadStatus::WaitSynchAny ||
|
if (thread.GetStatus() == Kernel::ThreadStatus::WaitSynchAny ||
|
||||||
thread.status == Kernel::ThreadStatus::WaitSynchAll) {
|
thread.GetStatus() == Kernel::ThreadStatus::WaitSynchAll) {
|
||||||
list.push_back(std::make_unique<WaitTreeObjectList>(thread.wait_objects,
|
list.push_back(std::make_unique<WaitTreeObjectList>(thread.GetWaitObjects(),
|
||||||
thread.IsSleepingOnWaitAll()));
|
thread.IsSleepingOnWaitAll()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue