mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-10 08:51:28 +12:00
297 lines
6.6 KiB
C++
297 lines
6.6 KiB
C++
#pragma once
|
|
|
|
#include "../Utilities/Thread.h"
|
|
#include "../Utilities/bit_set.h"
|
|
|
|
#include <vector>
|
|
|
|
template <typename Derived, typename Base>
|
|
concept derived_from = std::is_base_of_v<Base, Derived> &&
|
|
std::is_convertible_v<const volatile Derived*, const volatile Base*>;
|
|
|
|
// Thread state flags
|
|
enum class cpu_flag : u32
|
|
{
|
|
stop, // Thread not running (HLE, initial state)
|
|
exit, // Irreversible exit
|
|
wait, // Indicates waiting state, set by the thread itself
|
|
temp, // Indicates that the thread cannot properly return after next check_state()
|
|
pause, // Thread suspended by suspend_all technique
|
|
suspend, // Thread suspended
|
|
ret, // Callback return requested
|
|
signal, // Thread received a signal (HLE)
|
|
memory, // Thread must unlock memory mutex
|
|
|
|
dbg_global_pause, // Emulation paused
|
|
dbg_pause, // Thread paused
|
|
dbg_step, // Thread forced to pause after one step (one instruction, etc)
|
|
|
|
__bitset_enum_max
|
|
};
|
|
|
|
// Test stopped state
|
|
constexpr bool is_stopped(bs_t<cpu_flag> state)
|
|
{
|
|
return !!(state & (cpu_flag::stop + cpu_flag::exit));
|
|
}
|
|
|
|
// Test paused state
|
|
constexpr bool is_paused(bs_t<cpu_flag> state)
|
|
{
|
|
return !!(state & (cpu_flag::suspend + cpu_flag::dbg_global_pause + cpu_flag::dbg_pause)) && !is_stopped(state);
|
|
}
|
|
|
|
class cpu_thread
|
|
{
|
|
public:
|
|
u64 block_hash = 0;
|
|
|
|
protected:
|
|
cpu_thread(u32 id);
|
|
|
|
public:
|
|
cpu_thread(const cpu_thread&) = delete;
|
|
cpu_thread& operator=(const cpu_thread&) = delete;
|
|
|
|
virtual ~cpu_thread();
|
|
void operator()();
|
|
|
|
// Self identifier
|
|
const u32 id;
|
|
|
|
// Public thread state
|
|
atomic_bs_t<cpu_flag> state{cpu_flag::stop + cpu_flag::wait};
|
|
|
|
// Process thread state, return true if the checker must return
|
|
bool check_state() noexcept;
|
|
|
|
// Process thread state (pause)
|
|
[[nodiscard]] bool test_stopped()
|
|
{
|
|
if (state)
|
|
{
|
|
if (check_state())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Wrappers
|
|
static constexpr bool is_stopped(bs_t<cpu_flag> s)
|
|
{
|
|
return ::is_stopped(s);
|
|
}
|
|
|
|
static constexpr bool is_paused(bs_t<cpu_flag> s)
|
|
{
|
|
return ::is_paused(s);
|
|
}
|
|
|
|
bool is_stopped() const
|
|
{
|
|
return ::is_stopped(state);
|
|
}
|
|
|
|
bool is_paused() const
|
|
{
|
|
return ::is_paused(state);
|
|
}
|
|
|
|
bool has_pause_flag() const
|
|
{
|
|
return !!(state & cpu_flag::pause);
|
|
}
|
|
|
|
// Check thread type
|
|
u32 id_type() const
|
|
{
|
|
return id >> 24;
|
|
}
|
|
|
|
template <derived_from<cpu_thread> T>
|
|
T* try_get()
|
|
{
|
|
if constexpr (std::is_same_v<std::remove_const_t<T>, cpu_thread>)
|
|
{
|
|
return this;
|
|
}
|
|
else
|
|
{
|
|
if (id_type() == (T::id_base >> 24))
|
|
{
|
|
return static_cast<T*>(this);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
template <derived_from<cpu_thread> T>
|
|
const T* try_get() const
|
|
{
|
|
return const_cast<cpu_thread*>(this)->try_get<const T>();
|
|
}
|
|
|
|
u32 get_pc() const;
|
|
u32* get_pc2(); // Last PC before stepping for the debugger (may be null)
|
|
|
|
void notify();
|
|
|
|
public:
|
|
// Thread stats for external observation
|
|
static atomic_t<u64> g_threads_created, g_threads_deleted, g_suspend_counter;
|
|
|
|
// Get thread name (as assigned to named_thread)
|
|
std::string get_name() const;
|
|
|
|
// Get CPU state dump (everything)
|
|
virtual std::string dump_all() const;
|
|
|
|
// Get CPU register dump
|
|
virtual std::string dump_regs() const;
|
|
|
|
// Get CPU call stack dump
|
|
virtual std::string dump_callstack() const;
|
|
|
|
// Get CPU call stack list
|
|
virtual std::vector<std::pair<u32, u32>> dump_callstack_list() const;
|
|
|
|
// Get CPU dump of misc information
|
|
virtual std::string dump_misc() const;
|
|
|
|
// Thread entry point function
|
|
virtual void cpu_task() = 0;
|
|
|
|
// Callback for cpu_flag::suspend
|
|
virtual void cpu_sleep() {}
|
|
|
|
// Callback for cpu_flag::ret
|
|
virtual void cpu_return() {}
|
|
|
|
// Callback for thread_ctrl::wait or RSX wait
|
|
virtual void cpu_wait(bs_t<cpu_flag> old);
|
|
|
|
// Callback for function abortion stats on Emu.Stop()
|
|
virtual void cpu_on_stop() {}
|
|
|
|
// For internal use
|
|
struct suspend_work
|
|
{
|
|
// Task priority
|
|
u8 prio;
|
|
bool cancel_if_not_suspended;
|
|
bool was_posted;
|
|
|
|
// Size of prefetch list workload
|
|
u32 prf_size;
|
|
void* const* prf_list;
|
|
|
|
void* func_ptr;
|
|
void* res_buf;
|
|
|
|
// Type-erased op executor
|
|
void (*exec)(void* func, void* res);
|
|
|
|
// Next object in the linked list
|
|
suspend_work* next;
|
|
|
|
// Internal method
|
|
bool push(cpu_thread* _this) noexcept;
|
|
};
|
|
|
|
// Suspend all threads and execute op (may be executed by other thread than caller!)
|
|
template <u8 Prio = 0, typename F>
|
|
static auto suspend_all(cpu_thread* _this, std::initializer_list<void*> hints, F op)
|
|
{
|
|
constexpr u8 prio = Prio > 3 ? 3 : Prio;
|
|
|
|
if constexpr (std::is_void_v<std::invoke_result_t<F>>)
|
|
{
|
|
suspend_work work{prio, false, false, ::size32(hints), hints.begin(), &op, nullptr, [](void* func, void*)
|
|
{
|
|
std::invoke(*static_cast<F*>(func));
|
|
}};
|
|
|
|
work.push(_this);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
std::invoke_result_t<F> result;
|
|
|
|
suspend_work work{prio, false, false, ::size32(hints), hints.begin(), &op, &result, [](void* func, void* res_buf)
|
|
{
|
|
*static_cast<std::invoke_result_t<F>*>(res_buf) = std::invoke(*static_cast<F*>(func));
|
|
}};
|
|
|
|
work.push(_this);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
template <u8 Prio = 0, typename F>
|
|
static suspend_work suspend_post(cpu_thread* /*_this*/, std::initializer_list<void*> hints, F& op)
|
|
{
|
|
constexpr u8 prio = Prio > 3 ? 3 : Prio;
|
|
|
|
static_assert(std::is_void_v<std::invoke_result_t<F>>, "cpu_thread::suspend_post only supports void as return type");
|
|
|
|
return suspend_work{prio, false, true, ::size32(hints), hints.begin(), &op, nullptr, [](void* func, void*)
|
|
{
|
|
std::invoke(*static_cast<F*>(func));
|
|
}};
|
|
}
|
|
|
|
// Push the workload only if threads are being suspended by suspend_all()
|
|
template <u8 Prio = 0, typename F>
|
|
static bool if_suspended(cpu_thread* _this, std::initializer_list<void*> hints, F op)
|
|
{
|
|
constexpr u8 prio = Prio > 3 ? 3 : Prio;
|
|
|
|
static_assert(std::is_void_v<std::invoke_result_t<F>>, "cpu_thread::if_suspended only supports void as return type");
|
|
|
|
{
|
|
suspend_work work{prio, true, false, ::size32(hints), hints.begin(), &op, nullptr, [](void* func, void*)
|
|
{
|
|
std::invoke(*static_cast<F*>(func));
|
|
}};
|
|
|
|
return work.push(_this);
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
|
|
template <derived_from<cpu_thread> T = cpu_thread>
|
|
static inline T* get_current() noexcept
|
|
{
|
|
if (const auto cpu = g_tls_this_thread)
|
|
{
|
|
return cpu->try_get<T>();
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
private:
|
|
static thread_local cpu_thread* g_tls_this_thread;
|
|
};
|
|
|
|
template <derived_from<cpu_thread> T = cpu_thread>
|
|
inline T* get_current_cpu_thread() noexcept
|
|
{
|
|
return cpu_thread::get_current<T>();
|
|
}
|
|
|
|
class ppu_thread;
|
|
class spu_thread;
|