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:
Fernando S 2022-04-16 00:05:04 +02:00 committed by GitHub
commit 34710065e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 46 additions and 89 deletions

View File

@ -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{};

View File

@ -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{};

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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.

View File

@ -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);
} }
} }

View File

@ -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;

View File

@ -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.

View File

@ -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;
}; };

View File

@ -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"},

View File

@ -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;
}; };
/** /**