Merge pull request #8172 from bunnei/kernel-mutex
hle: kernel: Use std::mutex instead of spin locks for most kernel locking.
This commit is contained in:
commit
34710065e8
|
@ -2,9 +2,10 @@
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/fiber.h"
|
#include "common/fiber.h"
|
||||||
#include "common/spin_lock.h"
|
|
||||||
#include "common/virtual_buffer.h"
|
#include "common/virtual_buffer.h"
|
||||||
|
|
||||||
#include <boost/context/detail/fcontext.hpp>
|
#include <boost/context/detail/fcontext.hpp>
|
||||||
|
@ -19,7 +20,7 @@ struct Fiber::FiberImpl {
|
||||||
VirtualBuffer<u8> stack;
|
VirtualBuffer<u8> stack;
|
||||||
VirtualBuffer<u8> rewind_stack;
|
VirtualBuffer<u8> rewind_stack;
|
||||||
|
|
||||||
SpinLock guard{};
|
std::mutex guard;
|
||||||
std::function<void(void*)> entry_point;
|
std::function<void(void*)> entry_point;
|
||||||
std::function<void(void*)> rewind_point;
|
std::function<void(void*)> rewind_point;
|
||||||
void* rewind_parameter{};
|
void* rewind_parameter{};
|
||||||
|
|
|
@ -8,13 +8,13 @@
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/spin_lock.h"
|
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
#include "common/wall_clock.h"
|
#include "common/wall_clock.h"
|
||||||
|
|
||||||
|
@ -149,8 +149,8 @@ private:
|
||||||
std::shared_ptr<EventType> ev_lost;
|
std::shared_ptr<EventType> ev_lost;
|
||||||
Common::Event event{};
|
Common::Event event{};
|
||||||
Common::Event pause_event{};
|
Common::Event pause_event{};
|
||||||
Common::SpinLock basic_lock{};
|
std::mutex basic_lock;
|
||||||
Common::SpinLock advance_lock{};
|
std::mutex advance_lock;
|
||||||
std::unique_ptr<std::thread> timer_thread;
|
std::unique_ptr<std::thread> timer_thread;
|
||||||
std::atomic<bool> paused{};
|
std::atomic<bool> paused{};
|
||||||
std::atomic<bool> paused_set{};
|
std::atomic<bool> paused_set{};
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/spin_lock.h"
|
|
||||||
#include "core/hardware_properties.h"
|
#include "core/hardware_properties.h"
|
||||||
#include "core/hle/kernel/k_priority_queue.h"
|
#include "core/hle/kernel/k_priority_queue.h"
|
||||||
#include "core/hle/kernel/k_scheduler_lock.h"
|
#include "core/hle/kernel/k_scheduler_lock.h"
|
||||||
|
@ -80,7 +79,7 @@ private:
|
||||||
|
|
||||||
/// Lists all thread ids that aren't deleted/etc.
|
/// Lists all thread ids that aren't deleted/etc.
|
||||||
std::vector<KThread*> thread_list;
|
std::vector<KThread*> thread_list;
|
||||||
Common::SpinLock global_list_guard{};
|
std::mutex global_list_guard;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
|
|
@ -705,7 +705,7 @@ void KScheduler::Unload(KThread* thread) {
|
||||||
prev_thread = nullptr;
|
prev_thread = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
thread->context_guard.Unlock();
|
thread->context_guard.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void KScheduler::Reload(KThread* thread) {
|
void KScheduler::Reload(KThread* thread) {
|
||||||
|
@ -794,13 +794,13 @@ void KScheduler::SwitchToCurrent() {
|
||||||
do {
|
do {
|
||||||
auto next_thread = current_thread.load();
|
auto next_thread = current_thread.load();
|
||||||
if (next_thread != nullptr) {
|
if (next_thread != nullptr) {
|
||||||
const auto locked = next_thread->context_guard.TryLock();
|
const auto locked = next_thread->context_guard.try_lock();
|
||||||
if (state.needs_scheduling.load()) {
|
if (state.needs_scheduling.load()) {
|
||||||
next_thread->context_guard.Unlock();
|
next_thread->context_guard.unlock();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (next_thread->GetActiveCore() != core_id) {
|
if (next_thread->GetActiveCore() != core_id) {
|
||||||
next_thread->context_guard.Unlock();
|
next_thread->context_guard.unlock();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!locked) {
|
if (!locked) {
|
||||||
|
|
|
@ -4,51 +4,18 @@
|
||||||
|
|
||||||
#include "core/hle/kernel/k_spin_lock.h"
|
#include "core/hle/kernel/k_spin_lock.h"
|
||||||
|
|
||||||
#if _MSC_VER
|
|
||||||
#include <intrin.h>
|
|
||||||
#if _M_AMD64
|
|
||||||
#define __x86_64__ 1
|
|
||||||
#endif
|
|
||||||
#if _M_ARM64
|
|
||||||
#define __aarch64__ 1
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#if __x86_64__
|
|
||||||
#include <xmmintrin.h>
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
void ThreadPause() {
|
|
||||||
#if __x86_64__
|
|
||||||
_mm_pause();
|
|
||||||
#elif __aarch64__ && _MSC_VER
|
|
||||||
__yield();
|
|
||||||
#elif __aarch64__
|
|
||||||
asm("yield");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
void KSpinLock::Lock() {
|
void KSpinLock::Lock() {
|
||||||
while (lck.test_and_set(std::memory_order_acquire)) {
|
lck.lock();
|
||||||
ThreadPause();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KSpinLock::Unlock() {
|
void KSpinLock::Unlock() {
|
||||||
lck.clear(std::memory_order_release);
|
lck.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool KSpinLock::TryLock() {
|
bool KSpinLock::TryLock() {
|
||||||
if (lck.test_and_set(std::memory_order_acquire)) {
|
return lck.try_lock();
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <atomic>
|
#include <mutex>
|
||||||
|
|
||||||
#include "core/hle/kernel/k_scoped_lock.h"
|
#include "core/hle/kernel/k_scoped_lock.h"
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ public:
|
||||||
[[nodiscard]] bool TryLock();
|
[[nodiscard]] bool TryLock();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::atomic_flag lck = ATOMIC_FLAG_INIT;
|
std::mutex lck;
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO(bunnei): Alias for now, in case we want to implement these accurately in the future.
|
// TODO(bunnei): Alias for now, in case we want to implement these accurately in the future.
|
||||||
|
|
|
@ -723,10 +723,10 @@ void KThread::UpdateState() {
|
||||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||||
|
|
||||||
// Set our suspend flags in state.
|
// Set our suspend flags in state.
|
||||||
const ThreadState old_state = thread_state;
|
const ThreadState old_state = thread_state.load(std::memory_order_relaxed);
|
||||||
const auto new_state =
|
const auto new_state =
|
||||||
static_cast<ThreadState>(this->GetSuspendFlags()) | (old_state & ThreadState::Mask);
|
static_cast<ThreadState>(this->GetSuspendFlags()) | (old_state & ThreadState::Mask);
|
||||||
thread_state = new_state;
|
thread_state.store(new_state, std::memory_order_relaxed);
|
||||||
|
|
||||||
// Note the state change in scheduler.
|
// Note the state change in scheduler.
|
||||||
if (new_state != old_state) {
|
if (new_state != old_state) {
|
||||||
|
@ -738,8 +738,8 @@ void KThread::Continue() {
|
||||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||||
|
|
||||||
// Clear our suspend flags in state.
|
// Clear our suspend flags in state.
|
||||||
const ThreadState old_state = thread_state;
|
const ThreadState old_state = thread_state.load(std::memory_order_relaxed);
|
||||||
thread_state = old_state & ThreadState::Mask;
|
thread_state.store(old_state & ThreadState::Mask, std::memory_order_relaxed);
|
||||||
|
|
||||||
// Note the state change in scheduler.
|
// Note the state change in scheduler.
|
||||||
KScheduler::OnThreadStateChanged(kernel, this, old_state);
|
KScheduler::OnThreadStateChanged(kernel, this, old_state);
|
||||||
|
@ -1079,17 +1079,10 @@ void KThread::IfDummyThreadTryWait() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Block until we can grab the lock.
|
// Block until we are no longer waiting.
|
||||||
KScopedSpinLock lk{dummy_wait_lock};
|
std::unique_lock lk(dummy_wait_lock);
|
||||||
}
|
dummy_wait_cv.wait(
|
||||||
|
lk, [&] { return GetState() != ThreadState::Waiting || kernel.IsShuttingDown(); });
|
||||||
void KThread::IfDummyThreadBeginWait() {
|
|
||||||
if (!IsDummyThread()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the thread will block when IfDummyThreadTryWait is called.
|
|
||||||
dummy_wait_lock.Lock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KThread::IfDummyThreadEndWait() {
|
void KThread::IfDummyThreadEndWait() {
|
||||||
|
@ -1097,8 +1090,8 @@ void KThread::IfDummyThreadEndWait() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the thread will no longer block.
|
// Wake up the waiting thread.
|
||||||
dummy_wait_lock.Unlock();
|
dummy_wait_cv.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
void KThread::BeginWait(KThreadQueue* queue) {
|
void KThread::BeginWait(KThreadQueue* queue) {
|
||||||
|
@ -1107,9 +1100,6 @@ void KThread::BeginWait(KThreadQueue* queue) {
|
||||||
|
|
||||||
// Set our wait queue.
|
// Set our wait queue.
|
||||||
wait_queue = queue;
|
wait_queue = queue;
|
||||||
|
|
||||||
// Special case for dummy threads to ensure they block.
|
|
||||||
IfDummyThreadBeginWait();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_) {
|
void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_) {
|
||||||
|
@ -1158,10 +1148,11 @@ void KThread::SetState(ThreadState state) {
|
||||||
SetMutexWaitAddressForDebugging({});
|
SetMutexWaitAddressForDebugging({});
|
||||||
SetWaitReasonForDebugging({});
|
SetWaitReasonForDebugging({});
|
||||||
|
|
||||||
const ThreadState old_state = thread_state;
|
const ThreadState old_state = thread_state.load(std::memory_order_relaxed);
|
||||||
thread_state =
|
thread_state.store(
|
||||||
static_cast<ThreadState>((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask));
|
static_cast<ThreadState>((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask)),
|
||||||
if (thread_state != old_state) {
|
std::memory_order_relaxed);
|
||||||
|
if (thread_state.load(std::memory_order_relaxed) != old_state) {
|
||||||
KScheduler::OnThreadStateChanged(kernel, this, old_state);
|
KScheduler::OnThreadStateChanged(kernel, this, old_state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <mutex>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
@ -15,6 +17,7 @@
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/intrusive_red_black_tree.h"
|
#include "common/intrusive_red_black_tree.h"
|
||||||
|
#include "common/spin_lock.h"
|
||||||
#include "core/arm/arm_interface.h"
|
#include "core/arm/arm_interface.h"
|
||||||
#include "core/hle/kernel/k_affinity_mask.h"
|
#include "core/hle/kernel/k_affinity_mask.h"
|
||||||
#include "core/hle/kernel/k_light_lock.h"
|
#include "core/hle/kernel/k_light_lock.h"
|
||||||
|
@ -256,11 +259,11 @@ public:
|
||||||
[[nodiscard]] std::shared_ptr<Common::Fiber>& GetHostContext();
|
[[nodiscard]] std::shared_ptr<Common::Fiber>& GetHostContext();
|
||||||
|
|
||||||
[[nodiscard]] ThreadState GetState() const {
|
[[nodiscard]] ThreadState GetState() const {
|
||||||
return thread_state & ThreadState::Mask;
|
return thread_state.load(std::memory_order_relaxed) & ThreadState::Mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] ThreadState GetRawState() const {
|
[[nodiscard]] ThreadState GetRawState() const {
|
||||||
return thread_state;
|
return thread_state.load(std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetState(ThreadState state);
|
void SetState(ThreadState state);
|
||||||
|
@ -642,7 +645,6 @@ public:
|
||||||
// blocking as needed.
|
// blocking as needed.
|
||||||
|
|
||||||
void IfDummyThreadTryWait();
|
void IfDummyThreadTryWait();
|
||||||
void IfDummyThreadBeginWait();
|
|
||||||
void IfDummyThreadEndWait();
|
void IfDummyThreadEndWait();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -762,13 +764,14 @@ private:
|
||||||
s8 priority_inheritance_count{};
|
s8 priority_inheritance_count{};
|
||||||
bool resource_limit_release_hint{};
|
bool resource_limit_release_hint{};
|
||||||
StackParameters stack_parameters{};
|
StackParameters stack_parameters{};
|
||||||
KSpinLock context_guard{};
|
Common::SpinLock context_guard{};
|
||||||
KSpinLock dummy_wait_lock{};
|
|
||||||
|
|
||||||
// For emulation
|
// For emulation
|
||||||
std::shared_ptr<Common::Fiber> host_context{};
|
std::shared_ptr<Common::Fiber> host_context{};
|
||||||
bool is_single_core{};
|
bool is_single_core{};
|
||||||
ThreadType thread_type{};
|
ThreadType thread_type{};
|
||||||
|
std::mutex dummy_wait_lock;
|
||||||
|
std::condition_variable dummy_wait_cv;
|
||||||
|
|
||||||
// For debugging
|
// For debugging
|
||||||
std::vector<KSynchronizationObject*> wait_objects_for_debugging;
|
std::vector<KSynchronizationObject*> wait_objects_for_debugging;
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include "common/spin_lock.h"
|
|
||||||
#include "core/arm/cpu_interrupt_handler.h"
|
#include "core/arm/cpu_interrupt_handler.h"
|
||||||
#include "core/arm/dynarmic/arm_dynarmic_32.h"
|
#include "core/arm/dynarmic/arm_dynarmic_32.h"
|
||||||
#include "core/arm/dynarmic/arm_dynarmic_64.h"
|
#include "core/arm/dynarmic/arm_dynarmic_64.h"
|
||||||
|
@ -16,7 +15,7 @@ namespace Kernel {
|
||||||
PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_,
|
PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_,
|
||||||
Core::CPUInterrupts& interrupts_)
|
Core::CPUInterrupts& interrupts_)
|
||||||
: core_index{core_index_}, system{system_}, scheduler{scheduler_},
|
: core_index{core_index_}, system{system_}, scheduler{scheduler_},
|
||||||
interrupts{interrupts_}, guard{std::make_unique<Common::SpinLock>()} {
|
interrupts{interrupts_}, guard{std::make_unique<std::mutex>()} {
|
||||||
#ifdef ARCHITECTURE_x86_64
|
#ifdef ARCHITECTURE_x86_64
|
||||||
// TODO(bunnei): Initialization relies on a core being available. We may later replace this with
|
// TODO(bunnei): Initialization relies on a core being available. We may later replace this with
|
||||||
// a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager.
|
// a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager.
|
||||||
|
|
|
@ -6,13 +6,10 @@
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#include "core/arm/arm_interface.h"
|
#include "core/arm/arm_interface.h"
|
||||||
|
|
||||||
namespace Common {
|
|
||||||
class SpinLock;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
class KScheduler;
|
class KScheduler;
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
@ -91,7 +88,7 @@ private:
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
Kernel::KScheduler& scheduler;
|
Kernel::KScheduler& scheduler;
|
||||||
Core::CPUInterrupts& interrupts;
|
Core::CPUInterrupts& interrupts;
|
||||||
std::unique_ptr<Common::SpinLock> guard;
|
std::unique_ptr<std::mutex> guard;
|
||||||
std::unique_ptr<Core::ARM_Interface> arm_interface;
|
std::unique_ptr<Core::ARM_Interface> arm_interface;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -160,7 +160,8 @@ public:
|
||||||
|
|
||||||
class RelocatableObject final : public ServiceFramework<RelocatableObject> {
|
class RelocatableObject final : public ServiceFramework<RelocatableObject> {
|
||||||
public:
|
public:
|
||||||
explicit RelocatableObject(Core::System& system_) : ServiceFramework{system_, "ldr:ro"} {
|
explicit RelocatableObject(Core::System& system_)
|
||||||
|
: ServiceFramework{system_, "ldr:ro", ServiceThreadType::CreateNew} {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, &RelocatableObject::LoadModule, "LoadModule"},
|
{0, &RelocatableObject::LoadModule, "LoadModule"},
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <boost/container/flat_map.hpp>
|
#include <boost/container/flat_map.hpp>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/spin_lock.h"
|
|
||||||
#include "core/hle/kernel/hle_ipc.h"
|
#include "core/hle/kernel/hle_ipc.h"
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -90,7 +89,7 @@ protected:
|
||||||
using HandlerFnP = void (Self::*)(Kernel::HLERequestContext&);
|
using HandlerFnP = void (Self::*)(Kernel::HLERequestContext&);
|
||||||
|
|
||||||
/// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
|
/// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
|
||||||
[[nodiscard]] std::scoped_lock<Common::SpinLock> LockService() {
|
[[nodiscard]] std::scoped_lock<std::mutex> LockService() {
|
||||||
return std::scoped_lock{lock_service};
|
return std::scoped_lock{lock_service};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +134,7 @@ private:
|
||||||
boost::container::flat_map<u32, FunctionInfoBase> handlers_tipc;
|
boost::container::flat_map<u32, FunctionInfoBase> handlers_tipc;
|
||||||
|
|
||||||
/// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
|
/// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
|
||||||
Common::SpinLock lock_service;
|
std::mutex lock_service;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Reference in New Issue