mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-04-24 22:34:54 +00:00
Common: Make Event and WaitableFlag use std::atomic::wait.
This commit is contained in:
parent
d04e9e79a6
commit
c8c144a20e
13 changed files with 51 additions and 64 deletions
|
@ -239,8 +239,8 @@ private:
|
||||||
Flag m_stopped; // If this is set, Wait() shall not block.
|
Flag m_stopped; // If this is set, Wait() shall not block.
|
||||||
Flag m_shutdown; // If this is set, the loop shall end.
|
Flag m_shutdown; // If this is set, the loop shall end.
|
||||||
|
|
||||||
Event m_new_work_event;
|
TimedEvent m_new_work_event;
|
||||||
Event m_done_event;
|
TimedEvent m_done_event;
|
||||||
|
|
||||||
enum RUNNING_TYPE
|
enum RUNNING_TYPE
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,15 +11,17 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
#include "Common/Flag.h"
|
#include "Common/Flag.h"
|
||||||
|
#include "Common/WaitableFlag.h"
|
||||||
|
|
||||||
namespace Common
|
namespace Common
|
||||||
{
|
{
|
||||||
class Event final
|
class TimedEvent final
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void Set()
|
void Set()
|
||||||
|
@ -77,4 +79,28 @@ private:
|
||||||
std::mutex m_mutex;
|
std::mutex m_mutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// An auto-resetting WaitableFlag. Only sensible for one waiting thread.
|
||||||
|
class Event final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void Set() { m_flag.Set(); }
|
||||||
|
|
||||||
|
void Wait()
|
||||||
|
{
|
||||||
|
m_flag.Wait(true);
|
||||||
|
|
||||||
|
// This might run concurrently with the next Set, clearing m_flag before notification.
|
||||||
|
// "Missing" that event later is fine as long as all the data is visible *now*.
|
||||||
|
m_flag.Reset();
|
||||||
|
// This store-load barrier prevents the Reset-store ordering after pertinent data loads.
|
||||||
|
// Without it, we could observe stale data AND miss the next event, i.e. deadlock.
|
||||||
|
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reset() { m_flag.Reset(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
WaitableFlag m_flag{};
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
// Copyright 2025 Dolphin Emulator Project
|
// Copyright 2025 Dolphin Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
// Wrapper around Flag that lets callers wait for the flag to change.
|
// class that allows threads to wait for a bool to take on a value.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <chrono>
|
#include <atomic>
|
||||||
#include <condition_variable>
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
#include "Common/Flag.h"
|
|
||||||
|
|
||||||
namespace Common
|
namespace Common
|
||||||
{
|
{
|
||||||
|
@ -18,52 +14,17 @@ class WaitableFlag final
|
||||||
public:
|
public:
|
||||||
void Set(bool value = true)
|
void Set(bool value = true)
|
||||||
{
|
{
|
||||||
if (m_flag.TestAndSet(value))
|
m_flag.store(value, std::memory_order_release);
|
||||||
{
|
m_flag.notify_all();
|
||||||
// Lock and immediately unlock m_mutex.
|
|
||||||
{
|
|
||||||
// Holding the lock at any time between the change of our flag and notify call
|
|
||||||
// is sufficient to prevent a race where both of these actions
|
|
||||||
// happen between the other thread's predicate test and wait call
|
|
||||||
// which would cause wait to block until the next spurious wakeup or timeout.
|
|
||||||
|
|
||||||
// Unlocking before notification is a micro-optimization to prevent
|
|
||||||
// the notified thread from immediately blocking on the mutex.
|
|
||||||
std::lock_guard<std::mutex> lk(m_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_condvar.notify_all();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reset() { Set(false); }
|
void Wait(bool expected_value) { m_flag.wait(!expected_value, std::memory_order_acquire); }
|
||||||
|
|
||||||
void Wait(bool expected_value)
|
// Note that this does not awake Wait'ing threads. Use Set(false) if that's needed.
|
||||||
{
|
void Reset() { m_flag.store(false, std::memory_order_relaxed); }
|
||||||
if (m_flag.IsSet() == expected_value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::unique_lock<std::mutex> lk(m_mutex);
|
|
||||||
m_condvar.wait(lk, [&] { return m_flag.IsSet() == expected_value; });
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class Rep, class Period>
|
|
||||||
bool WaitFor(bool expected_value, const std::chrono::duration<Rep, Period>& rel_time)
|
|
||||||
{
|
|
||||||
if (m_flag.IsSet() == expected_value)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
std::unique_lock<std::mutex> lk(m_mutex);
|
|
||||||
bool signaled =
|
|
||||||
m_condvar.wait_for(lk, rel_time, [&] { return m_flag.IsSet() == expected_value; });
|
|
||||||
|
|
||||||
return signaled;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Flag m_flag;
|
std::atomic_bool m_flag{};
|
||||||
std::condition_variable m_condvar;
|
|
||||||
std::mutex m_mutex;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|
|
@ -121,7 +121,7 @@ struct HostJob
|
||||||
};
|
};
|
||||||
static std::mutex s_host_jobs_lock;
|
static std::mutex s_host_jobs_lock;
|
||||||
static std::queue<HostJob> s_host_jobs_queue;
|
static std::queue<HostJob> s_host_jobs_queue;
|
||||||
static Common::Event s_cpu_thread_job_finished;
|
static Common::TimedEvent s_cpu_thread_job_finished;
|
||||||
|
|
||||||
static thread_local bool tls_is_cpu_thread = false;
|
static thread_local bool tls_is_cpu_thread = false;
|
||||||
static thread_local bool tls_is_gpu_thread = false;
|
static thread_local bool tls_is_gpu_thread = false;
|
||||||
|
|
|
@ -127,7 +127,7 @@ void CPUManager::Run()
|
||||||
ExecutePendingJobs(state_lock);
|
ExecutePendingJobs(state_lock);
|
||||||
CPUThreadConfigCallback::CheckForConfigChanges();
|
CPUThreadConfigCallback::CheckForConfigChanges();
|
||||||
|
|
||||||
Common::Event gdb_step_sync_event;
|
Common::TimedEvent gdb_step_sync_event;
|
||||||
switch (m_state)
|
switch (m_state)
|
||||||
{
|
{
|
||||||
case State::Running:
|
case State::Running:
|
||||||
|
@ -272,7 +272,7 @@ void CPUManager::Reset()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPUManager::StepOpcode(Common::Event* event)
|
void CPUManager::StepOpcode(Common::TimedEvent* event)
|
||||||
{
|
{
|
||||||
std::lock_guard state_lock(m_state_change_lock);
|
std::lock_guard state_lock(m_state_change_lock);
|
||||||
// If we're not stepping then this is pointless
|
// If we're not stepping then this is pointless
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
namespace Common
|
namespace Common
|
||||||
{
|
{
|
||||||
class Event;
|
class TimedEvent;
|
||||||
}
|
}
|
||||||
namespace Core
|
namespace Core
|
||||||
{
|
{
|
||||||
|
@ -61,7 +61,7 @@ public:
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
// StepOpcode (Steps one Opcode)
|
// StepOpcode (Steps one Opcode)
|
||||||
void StepOpcode(Common::Event* event = nullptr);
|
void StepOpcode(Common::TimedEvent* event = nullptr);
|
||||||
|
|
||||||
// Enable or Disable Stepping. [Will deadlock if called from a system thread]
|
// Enable or Disable Stepping. [Will deadlock if called from a system thread]
|
||||||
void SetStepping(bool stepping);
|
void SetStepping(bool stepping);
|
||||||
|
@ -134,9 +134,9 @@ private:
|
||||||
bool m_state_paused_and_locked = false;
|
bool m_state_paused_and_locked = false;
|
||||||
bool m_state_system_request_stepping = false;
|
bool m_state_system_request_stepping = false;
|
||||||
bool m_state_cpu_step_instruction = false;
|
bool m_state_cpu_step_instruction = false;
|
||||||
Common::Event* m_state_cpu_step_instruction_sync = nullptr;
|
Common::TimedEvent* m_state_cpu_step_instruction_sync = nullptr;
|
||||||
std::queue<std::function<void()>> m_pending_jobs;
|
std::queue<std::function<void()>> m_pending_jobs;
|
||||||
Common::Event m_time_played_finish_sync;
|
Common::TimedEvent m_time_played_finish_sync;
|
||||||
|
|
||||||
Core::System& m_system;
|
Core::System& m_system;
|
||||||
};
|
};
|
||||||
|
|
|
@ -61,7 +61,7 @@ private:
|
||||||
std::vector<Memcard::GCIFile> m_saves;
|
std::vector<Memcard::GCIFile> m_saves;
|
||||||
|
|
||||||
std::string m_save_directory;
|
std::string m_save_directory;
|
||||||
Common::Event m_flush_trigger;
|
Common::TimedEvent m_flush_trigger;
|
||||||
std::mutex m_write_mutex;
|
std::mutex m_write_mutex;
|
||||||
Common::Flag m_exiting;
|
Common::Flag m_exiting;
|
||||||
std::thread m_flush_thread;
|
std::thread m_flush_thread;
|
||||||
|
|
|
@ -41,7 +41,7 @@ private:
|
||||||
std::unique_ptr<u8[]> m_flush_buffer;
|
std::unique_ptr<u8[]> m_flush_buffer;
|
||||||
std::thread m_flush_thread;
|
std::thread m_flush_thread;
|
||||||
std::mutex m_flush_mutex;
|
std::mutex m_flush_mutex;
|
||||||
Common::Event m_flush_trigger;
|
Common::TimedEvent m_flush_trigger;
|
||||||
Common::Flag m_dirty;
|
Common::Flag m_dirty;
|
||||||
u32 m_memory_card_size;
|
u32 m_memory_card_size;
|
||||||
};
|
};
|
||||||
|
|
|
@ -202,7 +202,7 @@ private:
|
||||||
std::thread m_scan_thread;
|
std::thread m_scan_thread;
|
||||||
Common::Flag m_scan_thread_running;
|
Common::Flag m_scan_thread_running;
|
||||||
Common::Flag m_populate_devices;
|
Common::Flag m_populate_devices;
|
||||||
Common::Event m_scan_mode_changed_or_population_event;
|
Common::TimedEvent m_scan_mode_changed_or_population_event;
|
||||||
std::atomic<WiimoteScanMode> m_scan_mode{WiimoteScanMode::DO_NOT_SCAN};
|
std::atomic<WiimoteScanMode> m_scan_mode{WiimoteScanMode::DO_NOT_SCAN};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -123,7 +123,7 @@ private:
|
||||||
u32 m_download_span = 2;
|
u32 m_download_span = 2;
|
||||||
u32 m_mail_span = 1;
|
u32 m_mail_span = 1;
|
||||||
bool m_handle_mail;
|
bool m_handle_mail;
|
||||||
Common::Event m_shutdown_event;
|
Common::TimedEvent m_shutdown_event;
|
||||||
std::mutex m_scheduler_lock;
|
std::mutex m_scheduler_lock;
|
||||||
std::thread m_scheduler_timer_thread;
|
std::thread m_scheduler_timer_thread;
|
||||||
};
|
};
|
||||||
|
|
|
@ -501,7 +501,7 @@ void CodeWidget::Step()
|
||||||
if (!cpu.IsStepping())
|
if (!cpu.IsStepping())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Common::Event sync_event;
|
Common::TimedEvent sync_event;
|
||||||
|
|
||||||
auto& power_pc = m_system.GetPowerPC();
|
auto& power_pc = m_system.GetPowerPC();
|
||||||
PowerPC::CoreMode old_mode = power_pc.GetMode();
|
PowerPC::CoreMode old_mode = power_pc.GetMode();
|
||||||
|
|
|
@ -42,7 +42,7 @@ private:
|
||||||
void Run();
|
void Run();
|
||||||
QWidget* m_parent;
|
QWidget* m_parent;
|
||||||
Common::Flag m_stop_requested;
|
Common::Flag m_stop_requested;
|
||||||
Common::Event m_wakeup_event;
|
Common::TimedEvent m_wakeup_event;
|
||||||
std::thread m_thread;
|
std::thread m_thread;
|
||||||
std::list<DiscordJoinRequestDialog> m_request_dialogs;
|
std::list<DiscordJoinRequestDialog> m_request_dialogs;
|
||||||
std::mutex m_request_dialogs_mutex;
|
std::mutex m_request_dialogs_mutex;
|
||||||
|
|
|
@ -68,7 +68,7 @@ private:
|
||||||
std::string m_last_error;
|
std::string m_last_error;
|
||||||
std::thread m_session_thread;
|
std::thread m_session_thread;
|
||||||
|
|
||||||
Common::Event m_session_thread_exit_event;
|
Common::TimedEvent m_session_thread_exit_event;
|
||||||
|
|
||||||
std::function<void()> m_error_callback = nullptr;
|
std::function<void()> m_error_callback = nullptr;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Reference in a new issue