1
0
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:
Eladash 2021-06-05 22:15:15 +03:00 committed by Ivan
parent 2169e8d935
commit 76bf720adf
6 changed files with 109 additions and 39 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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.");

View File

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