hle: kernel: Allocate a dummy KThread for each host thread, and use it for scheduling.
This commit is contained in:
parent
37f74d8741
commit
6e953f7f02
|
@ -9,12 +9,6 @@
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
static KThread* ToThread(uintptr_t thread_) {
|
|
||||||
ASSERT((thread_ & EmuThreadHandleReserved) == 0);
|
|
||||||
ASSERT((thread_ & 1) == 0);
|
|
||||||
return reinterpret_cast<KThread*>(thread_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void KLightLock::Lock() {
|
void KLightLock::Lock() {
|
||||||
const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
|
const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
|
||||||
const uintptr_t cur_thread_tag = (cur_thread | 1);
|
const uintptr_t cur_thread_tag = (cur_thread | 1);
|
||||||
|
@ -48,7 +42,7 @@ void KLightLock::Unlock() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
|
void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
|
||||||
KThread* cur_thread = ToThread(_cur_thread);
|
KThread* cur_thread = reinterpret_cast<KThread*>(_cur_thread);
|
||||||
|
|
||||||
// Pend the current thread waiting on the owner thread.
|
// Pend the current thread waiting on the owner thread.
|
||||||
{
|
{
|
||||||
|
@ -60,7 +54,7 @@ void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the current thread as a waiter on the owner.
|
// Add the current thread as a waiter on the owner.
|
||||||
KThread* owner_thread = ToThread(_owner & ~1ul);
|
KThread* owner_thread = reinterpret_cast<KThread*>(_owner & ~1ul);
|
||||||
cur_thread->SetAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag)));
|
cur_thread->SetAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag)));
|
||||||
owner_thread->AddWaiter(cur_thread);
|
owner_thread->AddWaiter(cur_thread);
|
||||||
|
|
||||||
|
@ -88,7 +82,7 @@ void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
|
void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
|
||||||
KThread* owner_thread = ToThread(_cur_thread);
|
KThread* owner_thread = reinterpret_cast<KThread*>(_cur_thread);
|
||||||
|
|
||||||
// Unlock.
|
// Unlock.
|
||||||
{
|
{
|
||||||
|
|
|
@ -623,7 +623,7 @@ KThread* KScheduler::GetCurrentThread() const {
|
||||||
if (auto result = current_thread.load(); result) {
|
if (auto result = current_thread.load(); result) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
return idle_thread.get();
|
return idle_thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 KScheduler::GetLastContextSwitchTicks() const {
|
u64 KScheduler::GetLastContextSwitchTicks() const {
|
||||||
|
@ -708,7 +708,7 @@ void KScheduler::ScheduleImpl() {
|
||||||
|
|
||||||
// We never want to schedule a null thread, so use the idle thread if we don't have a next.
|
// We never want to schedule a null thread, so use the idle thread if we don't have a next.
|
||||||
if (next_thread == nullptr) {
|
if (next_thread == nullptr) {
|
||||||
next_thread = idle_thread.get();
|
next_thread = idle_thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're not actually switching thread, there's nothing to do.
|
// If we're not actually switching thread, there's nothing to do.
|
||||||
|
@ -803,7 +803,7 @@ void KScheduler::Initialize() {
|
||||||
auto thread_res = KThread::Create(system, ThreadType::Main, name, 0,
|
auto thread_res = KThread::Create(system, ThreadType::Main, name, 0,
|
||||||
KThread::IdleThreadPriority, 0, static_cast<u32>(core_id), 0,
|
KThread::IdleThreadPriority, 0, static_cast<u32>(core_id), 0,
|
||||||
nullptr, std::move(init_func), init_func_parameter);
|
nullptr, std::move(init_func), init_func_parameter);
|
||||||
idle_thread = thread_res.Unwrap();
|
idle_thread = thread_res.Unwrap().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
KScopedSchedulerLock::KScopedSchedulerLock(KernelCore& kernel)
|
KScopedSchedulerLock::KScopedSchedulerLock(KernelCore& kernel)
|
||||||
|
|
|
@ -54,7 +54,7 @@ public:
|
||||||
|
|
||||||
/// Returns true if the scheduler is idle
|
/// Returns true if the scheduler is idle
|
||||||
[[nodiscard]] bool IsIdle() const {
|
[[nodiscard]] bool IsIdle() const {
|
||||||
return GetCurrentThread() == idle_thread.get();
|
return GetCurrentThread() == idle_thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the timestamp for the last context switch in ticks.
|
/// Gets the timestamp for the last context switch in ticks.
|
||||||
|
@ -176,7 +176,7 @@ private:
|
||||||
KThread* prev_thread{};
|
KThread* prev_thread{};
|
||||||
std::atomic<KThread*> current_thread{};
|
std::atomic<KThread*> current_thread{};
|
||||||
|
|
||||||
std::shared_ptr<KThread> idle_thread;
|
KThread* idle_thread;
|
||||||
|
|
||||||
std::shared_ptr<Common::Fiber> switch_fiber{};
|
std::shared_ptr<Common::Fiber> switch_fiber{};
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/spin_lock.h"
|
#include "common/spin_lock.h"
|
||||||
#include "core/hardware_properties.h"
|
#include "core/hardware_properties.h"
|
||||||
|
#include "core/hle/kernel/k_thread.h"
|
||||||
#include "core/hle/kernel/kernel.h"
|
#include "core/hle/kernel/kernel.h"
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
@ -22,42 +23,42 @@ public:
|
||||||
explicit KAbstractSchedulerLock(KernelCore& kernel_) : kernel{kernel_} {}
|
explicit KAbstractSchedulerLock(KernelCore& kernel_) : kernel{kernel_} {}
|
||||||
|
|
||||||
bool IsLockedByCurrentThread() const {
|
bool IsLockedByCurrentThread() const {
|
||||||
return this->owner_thread == kernel.GetCurrentEmuThreadID();
|
return this->owner_thread == GetCurrentThreadPointer(kernel);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Lock() {
|
void Lock() {
|
||||||
if (this->IsLockedByCurrentThread()) {
|
if (this->IsLockedByCurrentThread()) {
|
||||||
// If we already own the lock, we can just increment the count.
|
// If we already own the lock, we can just increment the count.
|
||||||
ASSERT(this->lock_count > 0);
|
ASSERT(lock_count > 0);
|
||||||
this->lock_count++;
|
lock_count++;
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, we want to disable scheduling and acquire the spinlock.
|
// Otherwise, we want to disable scheduling and acquire the spinlock.
|
||||||
SchedulerType::DisableScheduling(kernel);
|
SchedulerType::DisableScheduling(kernel);
|
||||||
this->spin_lock.lock();
|
spin_lock.lock();
|
||||||
|
|
||||||
// For debug, ensure that our state is valid.
|
// For debug, ensure that our state is valid.
|
||||||
ASSERT(this->lock_count == 0);
|
ASSERT(lock_count == 0);
|
||||||
ASSERT(this->owner_thread == EmuThreadHandleInvalid);
|
ASSERT(owner_thread == nullptr);
|
||||||
|
|
||||||
// Increment count, take ownership.
|
// Increment count, take ownership.
|
||||||
this->lock_count = 1;
|
lock_count = 1;
|
||||||
this->owner_thread = kernel.GetCurrentEmuThreadID();
|
owner_thread = GetCurrentThreadPointer(kernel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unlock() {
|
void Unlock() {
|
||||||
ASSERT(this->IsLockedByCurrentThread());
|
ASSERT(this->IsLockedByCurrentThread());
|
||||||
ASSERT(this->lock_count > 0);
|
ASSERT(lock_count > 0);
|
||||||
|
|
||||||
// Release an instance of the lock.
|
// Release an instance of the lock.
|
||||||
if ((--this->lock_count) == 0) {
|
if ((--lock_count) == 0) {
|
||||||
// We're no longer going to hold the lock. Take note of what cores need scheduling.
|
// We're no longer going to hold the lock. Take note of what cores need scheduling.
|
||||||
const u64 cores_needing_scheduling =
|
const u64 cores_needing_scheduling =
|
||||||
SchedulerType::UpdateHighestPriorityThreads(kernel);
|
SchedulerType::UpdateHighestPriorityThreads(kernel);
|
||||||
|
|
||||||
// Note that we no longer hold the lock, and unlock the spinlock.
|
// Note that we no longer hold the lock, and unlock the spinlock.
|
||||||
this->owner_thread = EmuThreadHandleInvalid;
|
owner_thread = nullptr;
|
||||||
this->spin_lock.unlock();
|
spin_lock.unlock();
|
||||||
|
|
||||||
// Enable scheduling, and perform a rescheduling operation.
|
// Enable scheduling, and perform a rescheduling operation.
|
||||||
SchedulerType::EnableScheduling(kernel, cores_needing_scheduling);
|
SchedulerType::EnableScheduling(kernel, cores_needing_scheduling);
|
||||||
|
@ -68,7 +69,7 @@ private:
|
||||||
KernelCore& kernel;
|
KernelCore& kernel;
|
||||||
Common::SpinLock spin_lock{};
|
Common::SpinLock spin_lock{};
|
||||||
s32 lock_count{};
|
s32 lock_count{};
|
||||||
EmuThreadHandle owner_thread{EmuThreadHandleInvalid};
|
KThread* owner_thread{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
|
|
@ -1034,11 +1034,7 @@ ResultVal<std::shared_ptr<KThread>> KThread::Create(Core::System& system, Thread
|
||||||
}
|
}
|
||||||
|
|
||||||
KThread* GetCurrentThreadPointer(KernelCore& kernel) {
|
KThread* GetCurrentThreadPointer(KernelCore& kernel) {
|
||||||
if (!kernel.CurrentScheduler()) {
|
return kernel.GetCurrentEmuThread();
|
||||||
// We are not called from a core thread
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return kernel.CurrentScheduler()->GetCurrentThread();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
KThread& GetCurrentThread(KernelCore& kernel) {
|
KThread& GetCurrentThread(KernelCore& kernel) {
|
||||||
|
|
|
@ -57,9 +57,10 @@ struct KernelCore::Impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Initialize(KernelCore& kernel) {
|
void Initialize(KernelCore& kernel) {
|
||||||
|
global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
|
||||||
|
|
||||||
RegisterHostThread();
|
RegisterHostThread();
|
||||||
|
|
||||||
global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
|
|
||||||
service_thread_manager =
|
service_thread_manager =
|
||||||
std::make_unique<Common::ThreadWorker>(1, "yuzu:ServiceThreadManager");
|
std::make_unique<Common::ThreadWorker>(1, "yuzu:ServiceThreadManager");
|
||||||
is_phantom_mode_for_singlecore = false;
|
is_phantom_mode_for_singlecore = false;
|
||||||
|
@ -206,6 +207,18 @@ struct KernelCore::Impl {
|
||||||
return host_thread_id;
|
return host_thread_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gets the dummy KThread for the caller, allocating a new one if this is the first time
|
||||||
|
KThread* GetHostDummyThread() {
|
||||||
|
const thread_local auto thread =
|
||||||
|
KThread::Create(
|
||||||
|
system, ThreadType::Main,
|
||||||
|
std::string{"DummyThread:" + GetHostThreadId()}, 0, KThread::DefaultThreadPriority,
|
||||||
|
0, static_cast<u32>(3), 0, nullptr,
|
||||||
|
[]([[maybe_unused]] void* arg) { UNREACHABLE(); }, nullptr)
|
||||||
|
.Unwrap();
|
||||||
|
return thread.get();
|
||||||
|
}
|
||||||
|
|
||||||
/// Registers a CPU core thread by allocating a host thread ID for it
|
/// Registers a CPU core thread by allocating a host thread ID for it
|
||||||
void RegisterCoreThread(std::size_t core_id) {
|
void RegisterCoreThread(std::size_t core_id) {
|
||||||
ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
|
ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
|
||||||
|
@ -218,6 +231,7 @@ struct KernelCore::Impl {
|
||||||
/// Registers a new host thread by allocating a host thread ID for it
|
/// Registers a new host thread by allocating a host thread ID for it
|
||||||
void RegisterHostThread() {
|
void RegisterHostThread() {
|
||||||
[[maybe_unused]] const auto this_id = GetHostThreadId();
|
[[maybe_unused]] const auto this_id = GetHostThreadId();
|
||||||
|
[[maybe_unused]] const auto dummy_thread = GetHostDummyThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] u32 GetCurrentHostThreadID() {
|
[[nodiscard]] u32 GetCurrentHostThreadID() {
|
||||||
|
@ -237,13 +251,12 @@ struct KernelCore::Impl {
|
||||||
is_phantom_mode_for_singlecore = value;
|
is_phantom_mode_for_singlecore = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] EmuThreadHandle GetCurrentEmuThreadID() {
|
KThread* GetCurrentEmuThread() {
|
||||||
const auto thread_id = GetCurrentHostThreadID();
|
const auto thread_id = GetCurrentHostThreadID();
|
||||||
if (thread_id >= Core::Hardware::NUM_CPU_CORES) {
|
if (thread_id >= Core::Hardware::NUM_CPU_CORES) {
|
||||||
// Reserved value for HLE threads
|
return GetHostDummyThread();
|
||||||
return EmuThreadHandleReserved + (static_cast<u64>(thread_id) << 1);
|
|
||||||
}
|
}
|
||||||
return reinterpret_cast<uintptr_t>(schedulers[thread_id].get());
|
return schedulers[thread_id]->GetCurrentThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitializeMemoryLayout() {
|
void InitializeMemoryLayout() {
|
||||||
|
@ -548,8 +561,8 @@ u32 KernelCore::GetCurrentHostThreadID() const {
|
||||||
return impl->GetCurrentHostThreadID();
|
return impl->GetCurrentHostThreadID();
|
||||||
}
|
}
|
||||||
|
|
||||||
EmuThreadHandle KernelCore::GetCurrentEmuThreadID() const {
|
KThread* KernelCore::GetCurrentEmuThread() const {
|
||||||
return impl->GetCurrentEmuThreadID();
|
return impl->GetCurrentEmuThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
Memory::MemoryManager& KernelCore::MemoryManager() {
|
Memory::MemoryManager& KernelCore::MemoryManager() {
|
||||||
|
|
|
@ -165,8 +165,8 @@ public:
|
||||||
/// Determines whether or not the given port is a valid named port.
|
/// Determines whether or not the given port is a valid named port.
|
||||||
bool IsValidNamedPort(NamedPortTable::const_iterator port) const;
|
bool IsValidNamedPort(NamedPortTable::const_iterator port) const;
|
||||||
|
|
||||||
/// Gets the current host_thread/guest_thread handle.
|
/// Gets the current host_thread/guest_thread pointer.
|
||||||
EmuThreadHandle GetCurrentEmuThreadID() const;
|
KThread* GetCurrentEmuThread() const;
|
||||||
|
|
||||||
/// Gets the current host_thread handle.
|
/// Gets the current host_thread handle.
|
||||||
u32 GetCurrentHostThreadID() const;
|
u32 GetCurrentHostThreadID() const;
|
||||||
|
|
|
@ -1039,8 +1039,6 @@ bool GMainWindow::LoadROM(const QString& filename, std::size_t program_index) {
|
||||||
std::make_unique<QtWebBrowser>(*this), // Web Browser
|
std::make_unique<QtWebBrowser>(*this), // Web Browser
|
||||||
});
|
});
|
||||||
|
|
||||||
system.RegisterHostThread();
|
|
||||||
|
|
||||||
const Core::System::ResultStatus result{
|
const Core::System::ResultStatus result{
|
||||||
system.Load(*render_window, filename.toStdString(), program_index)};
|
system.Load(*render_window, filename.toStdString(), program_index)};
|
||||||
|
|
||||||
|
|
Reference in New Issue