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);
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
if (s == thread_state::aborting && thread::m_sync.fetch_op([](u64& v){ return !(v & 3) && (v |= 1); }).second)
|
||||
{
|
||||
if (s == thread_state::aborting)
|
||||
if (s >= thread_state::aborting && thread::m_sync.fetch_op([](u64& v){ return !(v & 3) && (v |= 1); }).second)
|
||||
{
|
||||
thread::m_sync.notify_one(1);
|
||||
}
|
||||
|
||||
if constexpr (std::is_base_of_v<need_wakeup, Context>)
|
||||
{
|
||||
this->wake_up();
|
||||
}
|
||||
|
||||
if (s == thread_state::finished)
|
||||
{
|
||||
// This participates in emulation stopping, use destruction-alike semantics
|
||||
thread::join(true);
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
@ -634,9 +638,8 @@ public:
|
||||
// Context type doesn't need virtual destructor
|
||||
~named_thread()
|
||||
{
|
||||
// Assign aborting state forcefully
|
||||
operator=(thread_state::aborting);
|
||||
thread::join(true);
|
||||
// Assign aborting state forcefully and join thread
|
||||
operator=(thread_state::finished);
|
||||
|
||||
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
|
||||
{
|
||||
// Downcast to correct type
|
||||
@ -1131,7 +1111,8 @@ void cpu_thread::stop_all() noexcept
|
||||
{
|
||||
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);
|
||||
@ -1139,11 +1120,11 @@ void cpu_thread::stop_all() noexcept
|
||||
}
|
||||
|
||||
sys_log.notice("All CPU threads have been signaled.");
|
||||
}
|
||||
|
||||
while (s_cpu_counter)
|
||||
{
|
||||
std::this_thread::sleep_for(1ms);
|
||||
}
|
||||
void cpu_thread::cleanup() noexcept
|
||||
{
|
||||
ensure(!s_cpu_counter);
|
||||
|
||||
sys_log.notice("All CPU threads have been stopped. [+: %u]", +g_threads_created);
|
||||
|
||||
|
@ -140,9 +140,6 @@ public:
|
||||
|
||||
void notify();
|
||||
|
||||
private:
|
||||
void abort();
|
||||
|
||||
public:
|
||||
// Thread stats for external observation
|
||||
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
|
||||
static void stop_all() noexcept;
|
||||
|
||||
// Cleanup thread counting information
|
||||
static void cleanup() noexcept;
|
||||
|
||||
// Send signal to the profiler(s) to flush results
|
||||
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;
|
||||
|
||||
enum class thread_state : u32;
|
||||
|
||||
// Helper namespace
|
||||
namespace id_manager
|
||||
{
|
||||
@ -141,7 +143,7 @@ namespace id_manager
|
||||
template <typename T>
|
||||
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
|
||||
|
||||
id_map()
|
||||
@ -149,6 +151,29 @@ namespace id_manager
|
||||
// Preallocate memory
|
||||
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();
|
||||
|
||||
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();
|
||||
|
||||
sys_log.notice("All threads have been stopped.");
|
||||
|
@ -8,6 +8,8 @@
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
enum class thread_state : u32;
|
||||
|
||||
namespace stx
|
||||
{
|
||||
// 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
|
||||
}
|
||||
|
||||
// Save default constructor and destructor
|
||||
// Save default constructor and destructor and optional joining operation
|
||||
struct typeinfo
|
||||
{
|
||||
bool(*create)(uchar* ptr, manual_typemap&) noexcept = nullptr;
|
||||
void(*stop)(void* ptr, thread_state) noexcept = nullptr;
|
||||
void(*destroy)(void* ptr) noexcept = nullptr;
|
||||
std::string_view name{};
|
||||
|
||||
@ -86,6 +89,13 @@ namespace stx
|
||||
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>
|
||||
static typeinfo make_typeinfo()
|
||||
{
|
||||
@ -94,6 +104,12 @@ namespace stx
|
||||
typeinfo r;
|
||||
r.create = &call_ctor<T>;
|
||||
r.destroy = &call_dtor<T>;
|
||||
|
||||
if constexpr (std::is_assignable_v<T&, thread_state>)
|
||||
{
|
||||
r.stop = &call_stop<T>;
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
constexpr std::string_view name = parse_type(__FUNCSIG__);
|
||||
#else
|
||||
@ -327,5 +343,21 @@ namespace stx
|
||||
|
||||
[[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