mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-22 02:32:36 +01:00
Improve emulation stopping speed
Split phases of signalling threads and joining them.
This commit is contained in:
parent
2169e8d935
commit
76bf720adf
@ -612,20 +612,24 @@ public:
|
|||||||
return static_cast<thread_state>(thread::m_sync.load() & 3);
|
return static_cast<thread_state>(thread::m_sync.load() & 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to abort by assigning thread_state::aborting (UB if assigning different state)
|
// Try to abort by assigning thread_state::aborting/finished
|
||||||
|
// Join thread by thread_state::finished
|
||||||
named_thread& operator=(thread_state s)
|
named_thread& operator=(thread_state s)
|
||||||
{
|
{
|
||||||
if (s == thread_state::aborting && thread::m_sync.fetch_op([](u64& v){ return !(v & 3) && (v |= 1); }).second)
|
if (s >= thread_state::aborting && thread::m_sync.fetch_op([](u64& v){ return !(v & 3) && (v |= 1); }).second)
|
||||||
{
|
{
|
||||||
if (s == thread_state::aborting)
|
thread::m_sync.notify_one(1);
|
||||||
{
|
|
||||||
thread::m_sync.notify_one(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if constexpr (std::is_base_of_v<need_wakeup, Context>)
|
if constexpr (std::is_base_of_v<need_wakeup, Context>)
|
||||||
{
|
{
|
||||||
this->wake_up();
|
this->wake_up();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (s == thread_state::finished)
|
||||||
|
{
|
||||||
|
// This participates in emulation stopping, use destruction-alike semantics
|
||||||
|
thread::join(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
@ -634,9 +638,8 @@ public:
|
|||||||
// Context type doesn't need virtual destructor
|
// Context type doesn't need virtual destructor
|
||||||
~named_thread()
|
~named_thread()
|
||||||
{
|
{
|
||||||
// Assign aborting state forcefully
|
// Assign aborting state forcefully and join thread
|
||||||
operator=(thread_state::aborting);
|
operator=(thread_state::finished);
|
||||||
thread::join(true);
|
|
||||||
|
|
||||||
if constexpr (!result::empty)
|
if constexpr (!result::empty)
|
||||||
{
|
{
|
||||||
|
@ -827,26 +827,6 @@ void cpu_thread::notify()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cpu_thread::abort()
|
|
||||||
{
|
|
||||||
state += cpu_flag::exit;
|
|
||||||
state.notify_one(cpu_flag::exit);
|
|
||||||
|
|
||||||
// Downcast to correct type
|
|
||||||
if (id_type() == 1)
|
|
||||||
{
|
|
||||||
*static_cast<named_thread<ppu_thread>*>(this) = thread_state::aborting;
|
|
||||||
}
|
|
||||||
else if (id_type() == 2)
|
|
||||||
{
|
|
||||||
*static_cast<named_thread<spu_thread>*>(this) = thread_state::aborting;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fmt::throw_exception("Invalid cpu_thread type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string cpu_thread::get_name() const
|
std::string cpu_thread::get_name() const
|
||||||
{
|
{
|
||||||
// Downcast to correct type
|
// Downcast to correct type
|
||||||
@ -1131,7 +1111,8 @@ void cpu_thread::stop_all() noexcept
|
|||||||
{
|
{
|
||||||
auto on_stop = [](u32, cpu_thread& cpu)
|
auto on_stop = [](u32, cpu_thread& cpu)
|
||||||
{
|
{
|
||||||
cpu.abort();
|
cpu.state += cpu_flag::exit;
|
||||||
|
cpu.state.notify_one(cpu_flag::exit);
|
||||||
};
|
};
|
||||||
|
|
||||||
idm::select<named_thread<ppu_thread>>(on_stop);
|
idm::select<named_thread<ppu_thread>>(on_stop);
|
||||||
@ -1139,11 +1120,11 @@ void cpu_thread::stop_all() noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
sys_log.notice("All CPU threads have been signaled.");
|
sys_log.notice("All CPU threads have been signaled.");
|
||||||
|
}
|
||||||
|
|
||||||
while (s_cpu_counter)
|
void cpu_thread::cleanup() noexcept
|
||||||
{
|
{
|
||||||
std::this_thread::sleep_for(1ms);
|
ensure(!s_cpu_counter);
|
||||||
}
|
|
||||||
|
|
||||||
sys_log.notice("All CPU threads have been stopped. [+: %u]", +g_threads_created);
|
sys_log.notice("All CPU threads have been stopped. [+: %u]", +g_threads_created);
|
||||||
|
|
||||||
|
@ -140,9 +140,6 @@ public:
|
|||||||
|
|
||||||
void notify();
|
void notify();
|
||||||
|
|
||||||
private:
|
|
||||||
void abort();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Thread stats for external observation
|
// Thread stats for external observation
|
||||||
static atomic_t<u64> g_threads_created, g_threads_deleted, g_suspend_counter;
|
static atomic_t<u64> g_threads_created, g_threads_deleted, g_suspend_counter;
|
||||||
@ -269,6 +266,9 @@ public:
|
|||||||
// Stop all threads with cpu_flag::exit
|
// Stop all threads with cpu_flag::exit
|
||||||
static void stop_all() noexcept;
|
static void stop_all() noexcept;
|
||||||
|
|
||||||
|
// Cleanup thread counting information
|
||||||
|
static void cleanup() noexcept;
|
||||||
|
|
||||||
// Send signal to the profiler(s) to flush results
|
// Send signal to the profiler(s) to flush results
|
||||||
static void flush_profilers() noexcept;
|
static void flush_profilers() noexcept;
|
||||||
|
|
||||||
|
@ -12,6 +12,8 @@ extern stx::manual_typemap<void, 0x20'00000, 128> g_fixed_typemap;
|
|||||||
|
|
||||||
constexpr auto* g_fxo = &g_fixed_typemap;
|
constexpr auto* g_fxo = &g_fixed_typemap;
|
||||||
|
|
||||||
|
enum class thread_state : u32;
|
||||||
|
|
||||||
// Helper namespace
|
// Helper namespace
|
||||||
namespace id_manager
|
namespace id_manager
|
||||||
{
|
{
|
||||||
@ -141,7 +143,7 @@ namespace id_manager
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
struct id_map
|
struct id_map
|
||||||
{
|
{
|
||||||
std::vector<std::pair<id_key, std::shared_ptr<void>>> vec{};
|
std::vector<std::pair<id_key, std::shared_ptr<void>>> vec{}, private_copy{};
|
||||||
shared_mutex mutex{}; // TODO: Use this instead of global mutex
|
shared_mutex mutex{}; // TODO: Use this instead of global mutex
|
||||||
|
|
||||||
id_map()
|
id_map()
|
||||||
@ -149,6 +151,29 @@ namespace id_manager
|
|||||||
// Preallocate memory
|
// Preallocate memory
|
||||||
vec.reserve(T::id_count);
|
vec.reserve(T::id_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <bool dummy = false> requires (std::is_assignable_v<T&, thread_state>)
|
||||||
|
id_map& operator=(thread_state state)
|
||||||
|
{
|
||||||
|
if (private_copy.empty())
|
||||||
|
{
|
||||||
|
reader_lock lock(g_mutex);
|
||||||
|
|
||||||
|
// Save all entries
|
||||||
|
private_copy = vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signal or join threads
|
||||||
|
for (const auto& [key, ptr] : private_copy)
|
||||||
|
{
|
||||||
|
if (ptr)
|
||||||
|
{
|
||||||
|
*static_cast<T*>(ptr.get()) = state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1543,6 +1543,35 @@ void Emulator::Stop(bool restart)
|
|||||||
}
|
}
|
||||||
|
|
||||||
cpu_thread::stop_all();
|
cpu_thread::stop_all();
|
||||||
|
|
||||||
|
using fxo_t = std::remove_pointer_t<decltype(g_fxo)>;
|
||||||
|
|
||||||
|
// Signal threads
|
||||||
|
for (const auto& type : fxo_t::view_typelist())
|
||||||
|
{
|
||||||
|
if (type.stop)
|
||||||
|
{
|
||||||
|
if (auto data = g_fxo->try_get(type))
|
||||||
|
{
|
||||||
|
type.stop(data, thread_state::aborting);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Join threads
|
||||||
|
for (const auto& type : fxo_t::view_typelist())
|
||||||
|
{
|
||||||
|
if (type.stop)
|
||||||
|
{
|
||||||
|
if (auto data = g_fxo->try_get(type))
|
||||||
|
{
|
||||||
|
type.stop(data, thread_state::finished);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu_thread::cleanup();
|
||||||
|
|
||||||
g_fxo->reset();
|
g_fxo->reset();
|
||||||
|
|
||||||
sys_log.notice("All threads have been stopped.");
|
sys_log.notice("All threads have been stopped.");
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
|
enum class thread_state : u32;
|
||||||
|
|
||||||
namespace stx
|
namespace stx
|
||||||
{
|
{
|
||||||
// Simplified typemap with exactly one object of each used type, non-moveable. Initialized on init(). Destroyed on clear().
|
// Simplified typemap with exactly one object of each used type, non-moveable. Initialized on init(). Destroyed on clear().
|
||||||
@ -53,10 +55,11 @@ namespace stx
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save default constructor and destructor
|
// Save default constructor and destructor and optional joining operation
|
||||||
struct typeinfo
|
struct typeinfo
|
||||||
{
|
{
|
||||||
bool(*create)(uchar* ptr, manual_typemap&) noexcept = nullptr;
|
bool(*create)(uchar* ptr, manual_typemap&) noexcept = nullptr;
|
||||||
|
void(*stop)(void* ptr, thread_state) noexcept = nullptr;
|
||||||
void(*destroy)(void* ptr) noexcept = nullptr;
|
void(*destroy)(void* ptr) noexcept = nullptr;
|
||||||
std::string_view name{};
|
std::string_view name{};
|
||||||
|
|
||||||
@ -86,6 +89,13 @@ namespace stx
|
|||||||
std::launder(static_cast<T*>(ptr))->~T();
|
std::launder(static_cast<T*>(ptr))->~T();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static void call_stop(void* ptr, thread_state state) noexcept
|
||||||
|
{
|
||||||
|
// Abort and/or join (expected thread_state::aborting or thread_state::finished)
|
||||||
|
*std::launder(static_cast<T*>(ptr)) = state;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static typeinfo make_typeinfo()
|
static typeinfo make_typeinfo()
|
||||||
{
|
{
|
||||||
@ -94,6 +104,12 @@ namespace stx
|
|||||||
typeinfo r;
|
typeinfo r;
|
||||||
r.create = &call_ctor<T>;
|
r.create = &call_ctor<T>;
|
||||||
r.destroy = &call_dtor<T>;
|
r.destroy = &call_dtor<T>;
|
||||||
|
|
||||||
|
if constexpr (std::is_assignable_v<T&, thread_state>)
|
||||||
|
{
|
||||||
|
r.stop = &call_stop<T>;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
constexpr std::string_view name = parse_type(__FUNCSIG__);
|
constexpr std::string_view name = parse_type(__FUNCSIG__);
|
||||||
#else
|
#else
|
||||||
@ -327,5 +343,21 @@ namespace stx
|
|||||||
|
|
||||||
[[unlikely]] return nullptr;
|
[[unlikely]] return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const auto& view_typelist() noexcept
|
||||||
|
{
|
||||||
|
return stx::typelist<typeinfo>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get type-erased raw pointer to storage of type
|
||||||
|
uchar* try_get(const type_info<typeinfo>& type) const noexcept
|
||||||
|
{
|
||||||
|
if (m_init[type.index()])
|
||||||
|
{
|
||||||
|
[[likely]] return (Size ? +m_data : m_list) + type.pos();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[unlikely]] return nullptr;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user