Threads improved, ID manager improved

This commit is contained in:
Nekotekina 2015-11-26 11:06:29 +03:00
parent 78bfd54ad4
commit ca6783ba9a
48 changed files with 1113 additions and 990 deletions

View File

@ -199,7 +199,7 @@ void LogManager::log(LogMessage msg)
prefix = "E ";
break;
}
if (auto thr = get_current_thread_ctrl())
if (auto thr = thread_ctrl::get_current())
{
prefix += "{" + thr->get_name() + "} ";
}

View File

@ -5,7 +5,7 @@
void sleep_queue_entry_t::add_entry()
{
m_queue.emplace_back(m_thread.shared_from_this());
m_queue.emplace_back(std::static_pointer_cast<CPUThread>(m_thread.shared_from_this()));
}
void sleep_queue_entry_t::remove_entry()
@ -33,7 +33,7 @@ bool sleep_queue_entry_t::find() const
return false;
}
sleep_queue_entry_t::sleep_queue_entry_t(CPUThread& cpu, sleep_queue_t& queue)
sleep_queue_entry_t::sleep_queue_entry_t(sleep_entry_t& cpu, sleep_queue_t& queue)
: m_thread(cpu)
, m_queue(queue)
{
@ -41,7 +41,7 @@ sleep_queue_entry_t::sleep_queue_entry_t(CPUThread& cpu, sleep_queue_t& queue)
cpu.sleep();
}
sleep_queue_entry_t::sleep_queue_entry_t(CPUThread& cpu, sleep_queue_t& queue, const defer_sleep_t&)
sleep_queue_entry_t::sleep_queue_entry_t(sleep_entry_t& cpu, sleep_queue_t& queue, const defer_sleep_t&)
: m_thread(cpu)
, m_queue(queue)
{

View File

@ -1,15 +1,14 @@
#pragma once
class CPUThread;
using sleep_queue_t = std::deque<std::shared_ptr<CPUThread>>;
using sleep_entry_t = class CPUThread;
using sleep_queue_t = std::deque<std::shared_ptr<sleep_entry_t>>;
static struct defer_sleep_t {} const defer_sleep{};
// automatic object handling a thread entry in the sleep queue
class sleep_queue_entry_t final
{
CPUThread& m_thread;
sleep_entry_t& m_thread;
sleep_queue_t& m_queue;
void add_entry();
@ -18,10 +17,10 @@ class sleep_queue_entry_t final
public:
// add specified thread to the sleep queue
sleep_queue_entry_t(CPUThread& cpu, sleep_queue_t& queue);
sleep_queue_entry_t(sleep_entry_t& entry, sleep_queue_t& queue);
// don't add specified thread to the sleep queue
sleep_queue_entry_t(CPUThread& cpu, sleep_queue_t& queue, const defer_sleep_t&);
sleep_queue_entry_t(sleep_entry_t& entry, sleep_queue_t& queue, const defer_sleep_t&);
// removes specified thread from the sleep queue if added
~sleep_queue_entry_t();

View File

@ -19,6 +19,27 @@
#include <ucontext.h>
#endif
static const auto s_terminate_handler_set = std::set_terminate([]()
{
if (std::uncaught_exception())
{
try
{
throw;
}
catch (const std::exception& ex)
{
std::printf("Unhandled exception: %s\n", ex.what());
}
catch (...)
{
std::printf("Unhandled exception of unknown type.\n");
}
}
std::abort();
});
void SetCurrentThreadDebugName(const char* threadName)
{
#if defined(_MSC_VER) // this is VS-specific way to set thread names for the debugger
@ -113,10 +134,11 @@ enum x64_op_t : u32
X64OP_STOS,
X64OP_XCHG,
X64OP_CMPXCHG,
X64OP_LOAD_AND_STORE, // lock and [mem],reg
X64OP_LOAD_OR_STORE, // TODO: lock or [mem], reg
X64OP_INC, // TODO: lock inc [mem]
X64OP_DEC, // TODO: lock dec [mem]
X64OP_LOAD_AND_STORE, // lock and [mem], reg
X64OP_LOAD_OR_STORE, // lock or [mem], reg (TODO)
X64OP_LOAD_XOR_STORE, // lock xor [mem], reg (TODO)
X64OP_INC, // lock inc [mem] (TODO)
X64OP_DEC, // lock dec [mem] (TODO)
};
void decode_x64_reg_op(const u8* code, x64_op_t& out_op, x64_reg_t& out_reg, size_t& out_size, size_t& out_length)
@ -1132,10 +1154,7 @@ const PVOID exception_handler = (atexit([]{ RemoveVectoredExceptionHandler(excep
const u64 addr64 = (u64)pExp->ExceptionRecord->ExceptionInformation[1] - (u64)vm::base(0);
const bool is_writing = pExp->ExceptionRecord->ExceptionInformation[0] != 0;
if (pExp->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION &&
(u32)addr64 == addr64 &&
get_current_thread_ctrl() &&
handle_access_violation((u32)addr64, is_writing, pExp->ContextRecord))
if (pExp->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && (u32)addr64 == addr64 && thread_ctrl::get_current() && handle_access_violation((u32)addr64, is_writing, pExp->ContextRecord))
{
return EXCEPTION_CONTINUE_EXECUTION;
}
@ -1164,7 +1183,7 @@ void signal_handler(int sig, siginfo_t* info, void* uct)
const bool is_writing = ((ucontext_t*)uct)->uc_mcontext.gregs[REG_ERR] & 0x2;
#endif
if ((u32)addr64 == addr64 && get_current_thread_ctrl())
if ((u32)addr64 == addr64 && thread_ctrl::get_current())
{
if (handle_access_violation((u32)addr64, is_writing, (ucontext_t*)uct))
{
@ -1191,94 +1210,112 @@ const int sigaction_result = []() -> int
#endif
thread_local thread_ctrl_t* g_tls_this_thread = nullptr;
thread_local thread_ctrl* thread_ctrl::g_tls_this_thread = nullptr;
const thread_ctrl_t* get_current_thread_ctrl()
{
return g_tls_this_thread;
}
// TODO
std::atomic<u32> g_thread_count{ 0 };
std::string thread_ctrl_t::get_name() const
void thread_ctrl::initialize()
{
return m_name();
}
SetCurrentThreadDebugName(g_tls_this_thread->m_name().c_str());
named_thread_t::named_thread_t(std::function<std::string()> name, std::function<void()> func)
{
start(std::move(name), std::move(func));
}
#if defined(_MSC_VER)
_set_se_translator(_se_translator); // not essential, disable if necessary
#endif
named_thread_t::~named_thread_t()
{
if (m_thread)
#ifdef _WIN32
if (!exception_handler || !exception_filter)
#else
if (sigaction_result == -1)
#endif
{
std::printf("Fatal: thread neither joined nor detached\n");
std::printf("Exceptions handlers are not set correctly.\n");
std::terminate();
}
// TODO
g_thread_count++;
}
void thread_ctrl::finalize() noexcept
{
// TODO
vm::reservation_free();
// TODO
g_thread_count--;
// Call atexit functions
for (const auto& func : decltype(m_atexit)(std::move(g_tls_this_thread->m_atexit)))
{
func();
}
}
thread_ctrl::~thread_ctrl()
{
m_thread.detach();
if (m_future.valid())
{
try
{
m_future.get();
}
catch (const std::exception& ex)
{
LOG_ERROR(GENERAL, "Abandoned exception: %s", ex.what());
}
catch (EmulationStopped)
{
}
}
}
std::string thread_ctrl::get_name() const
{
CHECK_ASSERTION(m_name);
return m_name();
}
std::string named_thread_t::get_name() const
{
if (!m_thread)
{
throw EXCEPTION("Invalid thread");
}
if (!m_thread->m_name)
{
throw EXCEPTION("Invalid name getter");
}
return m_thread->m_name();
return fmt::format("('%s') Unnamed Thread", typeid(*this).name());
}
std::atomic<u32> g_thread_count{ 0 };
void named_thread_t::start(std::function<std::string()> name, std::function<void()> func)
void named_thread_t::start()
{
if (m_thread)
CHECK_ASSERTION(m_thread == nullptr);
// Get shared_ptr instance (will throw if called from the constructor or the object has been created incorrectly)
auto ptr = shared_from_this();
// Make name getter
auto name = [wptr = std::weak_ptr<named_thread_t>(ptr), type = &typeid(*this)]()
{
throw EXCEPTION("Thread already exists");
}
// Return actual name if available
if (const auto ptr = wptr.lock())
{
return ptr->get_name();
}
else
{
return fmt::format("('%s') Deleted Thread", type->name());
}
};
// create new thread control variable
m_thread = std::make_shared<thread_ctrl_t>(std::move(name));
// start thread
m_thread->m_thread = std::thread([](std::shared_ptr<thread_ctrl_t> ctrl, std::function<void()> func)
// Run thread
m_thread = thread_ctrl::spawn(std::move(name), [thread = std::move(ptr)]()
{
g_tls_this_thread = ctrl.get();
SetCurrentThreadDebugName(ctrl->get_name().c_str());
#if defined(_MSC_VER)
_set_se_translator(_se_translator);
#endif
#ifdef _WIN32
if (!exception_handler || !exception_filter)
{
LOG_ERROR(GENERAL, "exception_handler not set");
return;
}
#else
if (sigaction_result == -1)
{
printf("sigaction() failed");
exit(EXIT_FAILURE);
}
#endif
try
{
g_thread_count++;
if (rpcs3::config.misc.log.hle_logging.value())
{
LOG_NOTICE(GENERAL, "Thread started");
}
func();
thread->on_task();
if (rpcs3::config.misc.log.hle_logging.value())
{
@ -1295,75 +1332,24 @@ void named_thread_t::start(std::function<std::string()> name, std::function<void
LOG_NOTICE(GENERAL, "Thread aborted");
}
for (auto& func : ctrl->m_atexit)
{
func();
func = nullptr;
}
vm::reservation_free();
g_thread_count--;
}, m_thread, std::move(func));
}
void named_thread_t::detach()
{
if (!m_thread)
{
throw EXCEPTION("Invalid thread");
}
// +clear m_thread
const auto ctrl = std::move(m_thread);
// notify if detached by another thread
if (g_tls_this_thread != m_thread.get())
{
// lock for reliable notification
std::lock_guard<std::mutex> lock(mutex);
cv.notify_one();
}
ctrl->m_thread.detach();
thread->on_exit();
});
}
void named_thread_t::join()
{
if (!m_thread)
CHECK_ASSERTION(m_thread != nullptr);
try
{
throw EXCEPTION("Invalid thread");
m_thread->join();
m_thread.reset();
}
if (g_tls_this_thread == m_thread.get())
catch (...)
{
throw EXCEPTION("Deadlock");
m_thread.reset();
throw;
}
// +clear m_thread
const auto ctrl = std::move(m_thread);
{
// lock for reliable notification
std::lock_guard<std::mutex> lock(mutex);
cv.notify_one();
}
ctrl->m_thread.join();
}
bool named_thread_t::is_current() const
{
if (!m_thread)
{
throw EXCEPTION("Invalid thread");
}
return g_tls_this_thread == m_thread.get();
}
const std::function<bool()> SQUEUE_ALWAYS_EXIT = [](){ return true; };

View File

@ -1,107 +1,171 @@
#pragma once
const class thread_ctrl_t* get_current_thread_ctrl();
// Named thread control class
class thread_ctrl_t final
// Thread control class
class thread_ctrl final
{
friend class named_thread_t;
template<typename T> friend void current_thread_register_atexit(T);
// Thread handler
std::thread m_thread;
static thread_local thread_ctrl* g_tls_this_thread;
// Name getter
const std::function<std::string()> m_name;
std::function<std::string()> m_name;
// Functions executed at thread exit (temporarily)
std::vector<std::function<void()>> m_atexit;
// Thread handle (be careful)
std::thread m_thread;
// Thread result
std::future<void> m_future;
// Functions scheduled at thread exit
std::deque<std::function<void()>> m_atexit;
// Called at the thread start
static void initialize();
// Called at the thread end
static void finalize() noexcept;
public:
thread_ctrl_t(std::function<std::string()> name)
: m_name(std::move(name))
template<typename T>
thread_ctrl(T&& name)
: m_name(std::forward<T>(name))
{
}
thread_ctrl_t(const thread_ctrl_t&) = delete;
// Disable copy/move constructors and operators
thread_ctrl(const thread_ctrl&) = delete;
~thread_ctrl();
// Get thread name
std::string get_name() const;
// Get future result (may throw)
void join()
{
return m_future.get();
}
// Get current thread (may be nullptr)
static const thread_ctrl* get_current()
{
return g_tls_this_thread;
}
// Register function at thread exit (for the current thread)
template<typename T>
static inline void at_exit(T&& func)
{
CHECK_ASSERTION(g_tls_this_thread);
g_tls_this_thread->m_atexit.emplace_front(std::forward<T>(func));
}
// Named thread factory
template<typename N, typename F>
static inline std::shared_ptr<thread_ctrl> spawn(N&& name, F&& func)
{
auto ctrl = std::make_shared<thread_ctrl>(std::forward<N>(name));
std::promise<void> promise;
ctrl->m_future = promise.get_future();
ctrl->m_thread = std::thread([ctrl, task = std::forward<F>(func)](std::promise<void> promise)
{
g_tls_this_thread = ctrl.get();
try
{
initialize();
task();
finalize();
promise.set_value();
}
catch (...)
{
finalize();
promise.set_exception(std::current_exception());
}
}, std::move(promise));
return ctrl;
}
};
// Register function at thread exit (temporarily)
template<typename T> void current_thread_register_atexit(T func)
{
extern thread_local thread_ctrl_t* g_tls_this_thread;
g_tls_this_thread->m_atexit.emplace_back(func);
}
class named_thread_t
class named_thread_t : public std::enable_shared_from_this<named_thread_t>
{
// Pointer to managed resource (shared with actual thread)
std::shared_ptr<thread_ctrl_t> m_thread;
std::shared_ptr<thread_ctrl> m_thread;
public:
// Thread mutex for external use
std::mutex mutex;
// Thread condition variable for external use
// Thread condition variable for external use (this thread waits on it, other threads may notify)
std::condition_variable cv;
// Thread mutex for external use (can be used with `cv`)
std::mutex mutex;
protected:
// Thread task (called in the thread)
virtual void on_task() = 0;
// Thread finalization (called after on_task)
virtual void on_exit() {}
// ID initialization (called through id_aux_initialize)
virtual void on_id_aux_initialize() { start(); }
// ID finalization (called through id_aux_finalize)
virtual void on_id_aux_finalize() { join(); }
public:
// Initialize in empty state
named_thread_t() = default;
// Create named thread
named_thread_t(std::function<std::string()> name, std::function<void()> func);
virtual ~named_thread_t() = default;
// Deleted copy/move constructors + copy/move operators
named_thread_t(const named_thread_t&) = delete;
// Destructor, calls std::terminate if the thread is neither joined nor detached
virtual ~named_thread_t();
public:
// Get thread name
std::string get_name() const;
virtual std::string get_name() const;
// Create named thread (current state must be empty)
void start(std::function<std::string()> name, std::function<void()> func);
// Start thread (cannot be called from the constructor: should throw bad_weak_ptr in such case)
void start();
// Detach thread -> empty state
void detach();
// Join thread -> empty state
// Join thread (get future result)
void join();
// Check whether the thread is not in "empty state"
bool joinable() const { return m_thread.operator bool(); }
bool is_started() const { return m_thread.operator bool(); }
// Check whether it is the currently running thread
bool is_current() const;
// Compare with the current thread
bool is_current() const { CHECK_ASSERTION(m_thread); return thread_ctrl::get_current() == m_thread.get(); }
// Get internal thread pointer
const thread_ctrl_t* get_thread_ctrl() const { return m_thread.get(); }
// Get thread_ctrl
const thread_ctrl* get_thread_ctrl() const { return m_thread.get(); }
friend void id_aux_initialize(named_thread_t* ptr) { ptr->on_id_aux_initialize(); }
friend void id_aux_finalize(named_thread_t* ptr) { ptr->on_id_aux_finalize(); }
};
// Wrapper for named_thread_t, joins automatically in the destructor
class autojoin_thread_t final
// Wrapper for named thread, joins automatically in the destructor, can only be used in function scope
class scope_thread_t final
{
named_thread_t m_thread;
std::shared_ptr<thread_ctrl> m_thread;
public:
autojoin_thread_t(std::function<std::string()> name, std::function<void()> func)
: m_thread(std::move(name), std::move(func))
template<typename N, typename F>
scope_thread_t(N&& name, F&& func)
: m_thread(thread_ctrl::spawn(std::forward<N>(name), std::forward<F>(func)))
{
}
autojoin_thread_t(const autojoin_thread_t&) = delete;
// Deleted copy/move constructors + copy/move operators
scope_thread_t(const scope_thread_t&) = delete;
~autojoin_thread_t() noexcept(false) // Allow exceptions
// Destructor with exceptions allowed
~scope_thread_t() noexcept(false)
{
m_thread.join();
m_thread->join();
}
};

View File

@ -80,20 +80,22 @@ void armv7_free_tls(u32 thread)
}
ARMv7Thread::ARMv7Thread(const std::string& name)
: CPUThread(CPU_THREAD_ARMv7, name, WRAP_EXPR(fmt::format("ARMv7[0x%x] Thread (%s)[0x%08x]", m_id, m_name.c_str(), PC)))
: CPUThread(CPU_THREAD_ARMv7, name)
, ARMv7Context({})
{
}
ARMv7Thread::~ARMv7Thread()
{
cv.notify_one();
join();
close_stack();
armv7_free_tls(m_id);
}
std::string ARMv7Thread::get_name() const
{
return fmt::format("ARMv7 Thread[0x%x] (%s)[0x%08x]", m_id, CPUThread::get_name(), PC);
}
void ARMv7Thread::dump_info() const
{
if (hle_func)
@ -191,7 +193,7 @@ void ARMv7Thread::do_run()
}
}
void ARMv7Thread::task()
void ARMv7Thread::cpu_task()
{
if (custom_task)
{
@ -225,7 +227,7 @@ void ARMv7Thread::fast_call(u32 addr)
try
{
task();
cpu_task();
}
catch (CPUThreadReturn)
{

View File

@ -11,11 +11,12 @@ public:
ARMv7Thread(const std::string& name);
virtual ~ARMv7Thread() override;
virtual std::string get_name() const override;
virtual void dump_info() const override;
virtual u32 get_pc() const override { return PC; }
virtual u32 get_offset() const override { return 0; }
virtual void do_run() override;
virtual void task() override;
virtual void cpu_task() override;
virtual void init_regs() override;
virtual void init_stack() override;

View File

@ -9,65 +9,66 @@
thread_local CPUThread* g_tls_current_cpu_thread = nullptr;
CPUThread::CPUThread(CPUThreadType type, const std::string& name, std::function<std::string()> thread_name)
void CPUThread::on_task()
{
g_tls_current_cpu_thread = this;
Emu.SendDbgCommand(DID_CREATE_THREAD, this);
std::unique_lock<std::mutex> lock(mutex);
// check thread status
while (is_alive())
{
CHECK_EMU_STATUS;
// check stop status
if (!is_stopped())
{
if (lock) lock.unlock();
try
{
cpu_task();
}
catch (CPUThreadReturn)
{
;
}
catch (CPUThreadStop)
{
m_state |= CPU_STATE_STOPPED;
}
catch (CPUThreadExit)
{
m_state |= CPU_STATE_DEAD;
break;
}
catch (...)
{
dump_info();
throw;
}
m_state &= ~CPU_STATE_RETURN;
continue;
}
if (!lock)
{
lock.lock();
continue;
}
cv.wait(lock);
}
}
CPUThread::CPUThread(CPUThreadType type, const std::string& name)
: m_id(idm::get_last_id())
, m_type(type)
, m_name(name)
{
start(std::move(thread_name), [this]
{
g_tls_current_cpu_thread = this;
Emu.SendDbgCommand(DID_CREATE_THREAD, this);
std::unique_lock<std::mutex> lock(mutex);
// check thread status
while (joinable() && is_alive())
{
CHECK_EMU_STATUS;
// check stop status
if (!is_stopped())
{
if (lock) lock.unlock();
try
{
task();
}
catch (CPUThreadReturn)
{
;
}
catch (CPUThreadStop)
{
m_state |= CPU_STATE_STOPPED;
}
catch (CPUThreadExit)
{
m_state |= CPU_STATE_DEAD;
break;
}
catch (...)
{
dump_info();
throw;
}
m_state &= ~CPU_STATE_RETURN;
continue;
}
if (!lock)
{
lock.lock();
continue;
}
cv.wait(lock);
}
});
}
CPUThread::~CPUThread()
@ -75,6 +76,11 @@ CPUThread::~CPUThread()
Emu.SendDbgCommand(DID_REMOVE_THREAD, this);
}
std::string CPUThread::get_name() const
{
return m_name;
}
bool CPUThread::is_paused() const
{
return (m_state & CPU_STATE_PAUSED) != 0 || Emu.IsPaused();
@ -149,25 +155,26 @@ void CPUThread::exec()
{
Emu.SendDbgCommand(DID_EXEC_THREAD, this);
m_state &= ~CPU_STATE_STOPPED;
{
// lock for reliable notification
std::lock_guard<std::mutex> lock(mutex);
m_state &= ~CPU_STATE_STOPPED;
cv.notify_one();
}
}
void CPUThread::exit()
{
if (is_current())
m_state |= CPU_STATE_DEAD;
if (!is_current())
{
throw CPUThreadExit{};
}
else
{
throw EXCEPTION("Unable to exit another thread");
// lock for reliable notification
std::lock_guard<std::mutex> lock(mutex);
cv.notify_one();
}
}
@ -259,6 +266,11 @@ bool CPUThread::check_status()
{
CHECK_EMU_STATUS; // check at least once
if (!is_alive())
{
return true;
}
if (!is_paused() && (m_state & CPU_STATE_INTR) == 0)
{
break;

View File

@ -15,52 +15,44 @@ enum : u64
{
CPU_STATE_STOPPED = (1ull << 0), // basic execution state (stopped by default), removed by Exec()
CPU_STATE_PAUSED = (1ull << 1), // pauses thread execution, set by the debugger (manually or after step execution)
CPU_STATE_SLEEP = (1ull << 2), // shouldn't affect thread execution, set by Sleep() call, removed by the latest Awake() call, may possibly indicate waiting state of the thread
CPU_STATE_SLEEP = (1ull << 2), // shouldn't affect thread execution, set by sleep(), removed by the latest awake(), may possibly indicate waiting state of the thread
CPU_STATE_STEP = (1ull << 3), // forces the thread to pause after executing just one instruction or something appropriate, set by the debugger
CPU_STATE_DEAD = (1ull << 4), // indicates irreversible exit of the thread
CPU_STATE_RETURN = (1ull << 5), // used for callback return
CPU_STATE_SIGNAL = (1ull << 6), // used for HLE signaling
CPU_STATE_INTR = (1ull << 7), // thread interrupted
CPU_STATE_MAX = (1ull << 8), // added to (subtracted from) m_state by Sleep()/Awake() calls to trigger status check
CPU_STATE_MAX = (1ull << 8), // added to (subtracted from) m_state by sleep()/awake() calls to trigger status check
};
// "HLE return" exception event
class CPUThreadReturn {};
// CPUThread::Stop exception event
class CPUThreadStop {};
// CPUThread::Exit exception event
class CPUThreadExit {};
class CPUThreadReturn {}; // "HLE return" exception event
class CPUThreadStop {}; // CPUThread::Stop exception event
class CPUThreadExit {}; // CPUThread::Exit exception event
class CPUDecoder;
class CPUThread : public named_thread_t, public std::enable_shared_from_this<CPUThread>
class CPUThread : public named_thread_t
{
using named_thread_t::start;
void on_task() override;
void on_id_aux_finalize() override { exit(); } // call exit() instead of join()
protected:
using named_thread_t::detach;
using named_thread_t::join;
using named_thread_t::joinable;
atomic_t<u64> m_state{ CPU_STATE_STOPPED }; // thread state flags
std::unique_ptr<CPUDecoder> m_dec;
const u32 m_id;
const CPUThreadType m_type;
const std::string m_name; // changing m_name would be terribly thread-unsafe in current implementation
const std::string m_name; // changing m_name is unsafe because it can be read at any moment
CPUThread(CPUThreadType type, const std::string& name, std::function<std::string()> thread_name);
CPUThread(CPUThreadType type, const std::string& name);
public:
virtual ~CPUThread() override;
virtual std::string get_name() const override;
u32 get_id() const { return m_id; }
CPUThreadType get_type() const { return m_type; }
std::string get_name() const { return m_name; }
bool is_alive() const { return (m_state & CPU_STATE_DEAD) == 0; }
bool is_stopped() const { return (m_state & CPU_STATE_STOPPED) != 0; }
@ -70,7 +62,7 @@ public:
virtual u32 get_pc() const = 0;
virtual u32 get_offset() const = 0;
virtual void do_run() = 0;
virtual void task() = 0;
virtual void cpu_task() = 0;
virtual void init_regs() = 0;
virtual void init_stack() = 0;
@ -178,35 +170,6 @@ protected:
std::shared_ptr<CPUThread> thread;
public:
//u32 get_entry() const
//{
// return thread->entry;
//}
virtual cpu_thread& args(std::initializer_list<std::string> values) = 0;
virtual cpu_thread& run() = 0;
//u64 join()
//{
// if (!joinable())
// throw EXCEPTION("thread must be joinable for join");
// thread->SetJoinable(false);
// while (thread->IsRunning())
// std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack
// return thread->GetExitStatus();
//}
//bool joinable() const
//{
// return thread->IsJoinable();
//}
//u32 get_id() const
//{
// return thread->GetId();
//}
};

View File

@ -263,7 +263,6 @@ RecompilationEngine::RecompilationEngine()
RecompilationEngine::~RecompilationEngine() {
m_executable_storage.clear();
join();
memory_helper::free_reserved_memory(FunctionCache, VIRTUAL_INSTRUCTION_COUNT * sizeof(ExecutableStorageType));
free(FunctionCachePagesCommited);
}
@ -306,8 +305,8 @@ void RecompilationEngine::NotifyBlockStart(u32 address) {
m_pending_address_start.push_back(address);
}
if (!joinable()) {
start(WRAP_EXPR("PPU Recompilation Engine"), WRAP_EXPR(Task()));
if (!is_started()) {
start();
}
cv.notify_one();
@ -324,12 +323,12 @@ raw_fd_ostream & RecompilationEngine::Log() {
return *m_log;
}
void RecompilationEngine::Task() {
void RecompilationEngine::on_task() {
std::chrono::nanoseconds idling_time(0);
std::chrono::nanoseconds recompiling_time(0);
auto start = std::chrono::high_resolution_clock::now();
while (joinable() && !Emu.IsStopped()) {
while (!Emu.IsStopped()) {
bool work_done_this_iteration = false;
std::list <u32> m_current_execution_traces;

View File

@ -773,7 +773,7 @@ namespace ppu_recompiler_llvm {
* It then builds them asynchroneously and update the executable mapping
* using atomic based locks to avoid undefined behavior.
**/
class RecompilationEngine final : protected named_thread_t {
class RecompilationEngine final : public named_thread_t {
friend class CPUHybridDecoderRecompiler;
public:
virtual ~RecompilationEngine() override;
@ -790,7 +790,9 @@ namespace ppu_recompiler_llvm {
/// Log
llvm::raw_fd_ostream & Log();
void Task();
std::string get_name() const override { return "PPU Recompilation Engine"; }
void on_task() override;
/// Get a pointer to the instance of this class
static std::shared_ptr<RecompilationEngine> GetInstance();

View File

@ -57,26 +57,22 @@ void ppu_decoder_cache_t::initialize(u32 addr, u32 size)
}
PPUThread::PPUThread(const std::string& name)
: CPUThread(CPU_THREAD_PPU, name, WRAP_EXPR(fmt::format("PPU[0x%x] Thread (%s)[0x%08x]", m_id, m_name.c_str(), PC)))
: CPUThread(CPU_THREAD_PPU, name)
{
InitRotateMask();
}
PPUThread::~PPUThread()
{
if (is_current())
{
detach();
}
else
{
join();
}
close_stack();
ppu_free_tls(m_id);
}
std::string PPUThread::get_name() const
{
return fmt::format("PPU Thread[0x%x] (%s)[0x%08x]", m_id, CPUThread::get_name(), PC);
}
void PPUThread::dump_info() const
{
extern std::string get_ps3_function_name(u64 fid);
@ -239,7 +235,7 @@ void PPUThread::fast_call(u32 addr, u32 rtoc)
try
{
task();
cpu_task();
}
catch (CPUThreadReturn)
{
@ -264,7 +260,7 @@ void PPUThread::fast_stop()
m_state |= CPU_STATE_RETURN;
}
void PPUThread::task()
void PPUThread::cpu_task()
{
SetHostRoundingMode(FPSCR_RN_NEAR);

View File

@ -543,11 +543,12 @@ public:
PPUThread(const std::string& name);
virtual ~PPUThread() override;
virtual std::string get_name() const override;
virtual void dump_info() const override;
virtual u32 get_pc() const override { return PC; }
virtual u32 get_offset() const override { return 0; }
virtual void do_run() override;
virtual void task() override;
virtual void cpu_task() override;
virtual void init_regs() override;
virtual void init_stack() override;

View File

@ -10,20 +10,9 @@
thread_local spu_mfc_arg_t raw_spu_mfc[8] = {};
RawSPUThread::RawSPUThread(const std::string& name, u32 index)
: SPUThread(CPU_THREAD_RAW_SPU, name, COPY_EXPR(fmt::format("RawSPU[%d] Thread (0x%x)[0x%05x]", index, m_id, pc)), index, RAW_SPU_BASE_ADDR + RAW_SPU_OFFSET * index)
: SPUThread(CPU_THREAD_RAW_SPU, name, index, RAW_SPU_BASE_ADDR + RAW_SPU_OFFSET * index)
{
if (!vm::falloc(offset, 0x40000))
{
throw EXCEPTION("Failed to allocate RawSPU local storage");
}
}
RawSPUThread::~RawSPUThread()
{
join();
// Deallocate Local Storage
vm::dealloc_verbose_nothrow(offset);
CHECK_ASSERTION(vm::falloc(offset, 0x40000) == offset);
}
bool RawSPUThread::read_reg(const u32 addr, u32& value)
@ -233,7 +222,7 @@ bool RawSPUThread::write_reg(const u32 addr, const u32 value)
return false;
}
void RawSPUThread::task()
void RawSPUThread::cpu_task()
{
// get next PC and SPU Interrupt status
pc = npc.exchange(0);
@ -242,7 +231,7 @@ void RawSPUThread::task()
pc &= 0x3fffc;
SPUThread::task();
SPUThread::cpu_task();
// save next PC and current SPU Interrupt status
npc = pc | ((ch_event_stat & SPU_EVENT_INTR_ENABLED) != 0);

View File

@ -19,11 +19,10 @@ class RawSPUThread final : public SPUThread
{
public:
RawSPUThread(const std::string& name, u32 index);
virtual ~RawSPUThread();
bool read_reg(const u32 addr, u32& value);
bool write_reg(const u32 addr, const u32 value);
private:
virtual void task() override;
virtual void cpu_task() override;
};

View File

@ -52,33 +52,25 @@ void spu_int_ctrl_t::clear(u64 ints)
const spu_imm_table_t g_spu_imm;
SPUThread::SPUThread(CPUThreadType type, const std::string& name, std::function<std::string()> thread_name, u32 index, u32 offset)
: CPUThread(type, name, std::move(thread_name))
SPUThread::SPUThread(CPUThreadType type, const std::string& name, u32 index, u32 offset)
: CPUThread(type, name)
, index(index)
, offset(offset)
{
}
SPUThread::SPUThread(const std::string& name, u32 index)
: CPUThread(CPU_THREAD_SPU, name, WRAP_EXPR(fmt::format("SPU[0x%x] Thread (%s)[0x%05x]", m_id, m_name.c_str(), pc)))
: CPUThread(CPU_THREAD_SPU, name)
, index(index)
, offset(vm::alloc(0x40000, vm::main))
{
if (!offset)
{
throw EXCEPTION("Failed to allocate SPU local storage");
}
CHECK_ASSERTION(offset);
}
SPUThread::~SPUThread()
{
if (m_type == CPU_THREAD_SPU)
{
join();
// Deallocate Local Storage
vm::dealloc_verbose_nothrow(offset, vm::main);
}
// Deallocate Local Storage
vm::dealloc_verbose_nothrow(offset);
}
bool SPUThread::is_paused() const
@ -99,12 +91,17 @@ bool SPUThread::is_paused() const
return false;
}
std::string SPUThread::get_name() const
{
return fmt::format("%s[0x%x] Thread (%s)[0x%05x]", CPUThread::GetTypeString(), m_id, CPUThread::get_name(), pc);
}
void SPUThread::dump_info() const
{
CPUThread::dump_info();
}
void SPUThread::task()
void SPUThread::cpu_task()
{
std::fesetround(FE_TOWARDZERO);
@ -251,7 +248,7 @@ void SPUThread::fast_call(u32 ls_addr)
try
{
task();
cpu_task();
}
catch (CPUThreadReturn)
{

View File

@ -661,7 +661,7 @@ public:
u32 recursion_level = 0;
protected:
SPUThread(CPUThreadType type, const std::string& name, std::function<std::string()> thread_name, u32 index, u32 offset);
SPUThread(CPUThreadType type, const std::string& name, u32 index, u32 offset);
public:
SPUThread(const std::string& name, u32 index);
@ -669,11 +669,12 @@ public:
virtual bool is_paused() const override;
virtual std::string get_name() const override;
virtual void dump_info() const override;
virtual u32 get_pc() const override { return pc; }
virtual u32 get_offset() const override { return offset; }
virtual void do_run() override;
virtual void task() override;
virtual void cpu_task() override;
virtual void init_regs() override;
virtual void init_stack() override;

View File

@ -1,14 +1,157 @@
#include "stdafx.h"
#include "IdManager.h"
std::mutex idm::g_mutex;
namespace idm
{
std::mutex g_mutex;
std::unordered_map<u32, id_data_t> idm::g_map;
idm::map_t g_map;
u32 idm::g_last_raw_id = 0;
u32 g_last_raw_id = 0;
thread_local u32 idm::g_tls_last_id = 0xdeadbeef;
thread_local u32 g_tls_last_id = 0xdeadbeef;
}
std::mutex fxm::g_mutex;
namespace fxm
{
std::mutex g_mutex;
std::unordered_map<const void*, std::shared_ptr<void>> fxm::g_map;
fxm::map_t g_map;
}
void idm::clear()
{
std::lock_guard<std::mutex> lock{g_mutex};
// Call recorded id_finalize functions for all IDs
for (auto& id : idm::map_t(std::move(g_map)))
{
(*id.second.type_index)(id.second.data.get());
}
g_last_raw_id = 0;
}
bool idm::check(u32 in_id, id_type_index_t type)
{
std::lock_guard<std::mutex> lock(g_mutex);
const auto found = g_map.find(in_id);
return found != g_map.end() && found->second.type_index == type;
}
// check if ID exists and return its type or nullptr
const std::type_info* idm::get_type(u32 raw_id)
{
std::lock_guard<std::mutex> lock(g_mutex);
const auto found = g_map.find(raw_id);
return found == g_map.end() ? nullptr : found->second.info;
}
std::shared_ptr<void> idm::get(u32 in_id, id_type_index_t type)
{
std::lock_guard<std::mutex> lock(g_mutex);
const auto found = g_map.find(in_id);
if (found == g_map.end() || found->second.type_index != type)
{
return nullptr;
}
return found->second.data;
}
idm::map_t idm::get_all(id_type_index_t type)
{
std::lock_guard<std::mutex> lock(g_mutex);
idm::map_t result;
for (auto& id : g_map)
{
if (id.second.type_index == type)
{
result.insert(id);
}
}
return result;
}
std::shared_ptr<void> idm::withdraw(u32 in_id, id_type_index_t type)
{
std::lock_guard<std::mutex> lock(g_mutex);
const auto found = g_map.find(in_id);
if (found == g_map.end() || found->second.type_index != type)
{
return nullptr;
}
auto ptr = std::move(found->second.data);
g_map.erase(found);
return ptr;
}
u32 idm::get_count(id_type_index_t type)
{
std::lock_guard<std::mutex> lock(g_mutex);
u32 result = 0;
for (auto& id : g_map)
{
if (id.second.type_index == type)
{
result++;
}
}
return result;
}
void fxm::clear()
{
std::lock_guard<std::mutex> lock{g_mutex};
// Call recorded id_finalize functions for all IDs
for (auto& id : fxm::map_t(std::move(g_map)))
{
if (id.second) (*id.first)(id.second.get());
}
}
bool fxm::check(id_type_index_t type)
{
std::lock_guard<std::mutex> lock(g_mutex);
const auto found = g_map.find(type);
return found != g_map.end() && found->second;
}
std::shared_ptr<void> fxm::get(id_type_index_t type)
{
std::lock_guard<std::mutex> lock(g_mutex);
const auto found = g_map.find(type);
return found != g_map.end() ? found->second : nullptr;
}
std::shared_ptr<void> fxm::withdraw(id_type_index_t type)
{
std::unique_lock<std::mutex> lock(g_mutex);
const auto found = g_map.find(type);
return found != g_map.end() ? std::move(found->second) : nullptr;
}

View File

@ -2,40 +2,61 @@
#define ID_MANAGER_INCLUDED
template<typename T> struct type_info_t { static char value; };
// TODO: make id_aux_initialize and id_aux_finalize safer against a possible ODR violation
template<typename T> char type_info_t<T>::value = 42;
template<typename T> constexpr inline const void* get_type_index()
// Function called after the successfull creation of an ID (does nothing by default, provide an overload)
inline void id_aux_initialize(void*)
{
return &type_info_t<T>::value;
;
}
// default traits for any arbitrary type
template<typename T> struct id_traits
// Function called after the ID removal (does nothing by default, provide an overload)
inline void id_aux_finalize(void*)
{
// get next mapped id (may return 0 if out of IDs)
static u32 next_id(u32 raw_id) { return raw_id < 0x80000000 ? (raw_id + 1) & 0x7fffffff : 0; }
;
}
// convert "public" id to mapped id (may return 0 if invalid)
static u32 in_id(u32 id) { return id; }
// Type-erased id_aux_* function type
using id_aux_func_t = void(*)(void*);
// convert mapped id to "public" id
static u32 out_id(u32 raw_id) { return raw_id; }
template<typename T>
struct id_type_info_t
{
static const auto size = sizeof(T); // forbid forward declarations
static const id_aux_func_t on_remove;
};
struct id_data_t final
// Type-erased finalization function
template<typename T>
const id_aux_func_t id_type_info_t<T>::on_remove = [](void* ptr)
{
std::shared_ptr<void> data;
const std::type_info* info;
const void* type_index;
return id_aux_finalize(static_cast<T*>(ptr));
};
template<typename T> inline id_data_t(std::shared_ptr<T> data)
: data(std::move(data))
, info(&typeid(T))
, type_index(get_type_index<T>())
{
}
using id_type_index_t = const id_aux_func_t*;
// Get a unique pointer to the on_remove value (will be unique for each type)
template<typename T>
inline constexpr id_type_index_t get_id_type_index()
{
return &id_type_info_t<T>::on_remove;
}
// Default ID traits for any arbitrary type
template<typename T>
struct id_traits
{
static const auto size = sizeof(T); // forbid forward declarations
// Get next mapped id (may return 0 if out of IDs)
static u32 next_id(u32 raw_id) { return raw_id < 0x80000000 ? (raw_id + 1) & 0x7fffffff : 0; }
// Convert "public" id to mapped id (may return 0 if invalid)
static u32 in_id(u32 id) { return id; }
// Convert mapped id to "public" id
static u32 out_id(u32 raw_id) { return raw_id; }
};
// ID Manager
@ -44,52 +65,55 @@ struct id_data_t final
// 0x80000000+ : reserved (may be used through id_traits specializations)
namespace idm
{
extern std::mutex g_mutex;
extern std::unordered_map<u32, id_data_t> g_map;
extern u32 g_last_raw_id;
thread_local extern u32 g_tls_last_id;
// can be called from the constructor called through make() or make_ptr() to get the ID of the object being created
static inline u32 get_last_id()
struct id_data_t final
{
std::shared_ptr<void> data;
const std::type_info* info;
id_type_index_t type_index;
template<typename T> id_data_t(const std::shared_ptr<T>& data)
: data(data)
, info(&typeid(T))
, type_index(get_id_type_index<T>())
{
}
};
using map_t = std::unordered_map<u32, id_data_t>;
// Can be called from the constructor called through make() or make_ptr() to get the ID of the object being created
inline u32 get_last_id()
{
extern thread_local u32 g_tls_last_id;
return g_tls_last_id;
}
// reinitialize ID manager
static void clear()
{
std::lock_guard<std::mutex> lock(g_mutex);
// Remove all objects
void clear();
g_map.clear();
g_last_raw_id = 0;
// Internal
bool check(u32 in_id, id_type_index_t type);
// Check if an ID of specified type exists
template<typename T>
bool check(u32 id)
{
return check(id_traits<T>::in_id(id), get_id_type_index<T>());
}
// check if ID of specified type exists
template<typename T> static bool check(u32 id)
// Check if an ID exists and return its type or nullptr
const std::type_info* get_type(u32 raw_id);
// Internal
template<typename T, typename Ptr>
std::shared_ptr<T> add(Ptr&& get_ptr)
{
std::lock_guard<std::mutex> lock(g_mutex);
const auto found = g_map.find(id_traits<T>::in_id(id));
extern std::mutex g_mutex;
extern idm::map_t g_map;
extern u32 g_last_raw_id;
extern thread_local u32 g_tls_last_id;
return found != g_map.end() && found->second.type_index == get_type_index<T>();
}
// check if ID exists and return its type or nullptr
inline static const std::type_info* get_type(u32 raw_id)
{
std::lock_guard<std::mutex> lock(g_mutex);
const auto found = g_map.find(raw_id);
return found == g_map.end() ? nullptr : found->second.info;
}
// add new ID of specified type with specified constructor arguments (returns object or nullptr)
template<typename T, typename... Args> static std::enable_if_t<std::is_constructible<T, Args...>::value, std::shared_ptr<T>> make_ptr(Args&&... args)
{
std::lock_guard<std::mutex> lock(g_mutex);
for (u32 raw_id = g_last_raw_id; (raw_id = id_traits<T>::next_id(raw_id)); /**/)
@ -98,7 +122,7 @@ namespace idm
g_tls_last_id = id_traits<T>::out_id(raw_id);
auto ptr = std::make_shared<T>(std::forward<Args>(args)...);
std::shared_ptr<T> ptr = get_ptr();
g_map.emplace(raw_id, id_data_t(ptr));
@ -110,332 +134,302 @@ namespace idm
return nullptr;
}
// add new ID of specified type with specified constructor arguments (returns id)
template<typename T, typename... Args> static std::enable_if_t<std::is_constructible<T, Args...>::value, u32> make(Args&&... args)
// Add a new ID of specified type with specified constructor arguments (returns object or nullptr)
template<typename T, typename... Args>
std::enable_if_t<std::is_constructible<T, Args...>::value, std::shared_ptr<T>> make_ptr(Args&&... args)
{
std::lock_guard<std::mutex> lock(g_mutex);
for (u32 raw_id = g_last_raw_id; (raw_id = id_traits<T>::next_id(raw_id)); /**/)
if (auto ptr = add<T>(WRAP_EXPR(std::make_shared<T>(std::forward<Args>(args)...))))
{
if (g_map.find(raw_id) != g_map.end()) continue;
g_tls_last_id = id_traits<T>::out_id(raw_id);
g_map.emplace(raw_id, id_data_t(std::make_shared<T>(std::forward<Args>(args)...)));
if (raw_id < 0x80000000) g_last_raw_id = raw_id;
return id_traits<T>::out_id(raw_id);
id_aux_initialize(ptr.get());
return ptr;
}
throw EXCEPTION("Out of IDs");
return nullptr;
}
// add new ID for an existing object provided (don't use for initial object creation)
template<typename T> static u32 import(const std::shared_ptr<T>& ptr)
// Add a new ID of specified type with specified constructor arguments (returns id)
template<typename T, typename... Args>
std::enable_if_t<std::is_constructible<T, Args...>::value, u32> make(Args&&... args)
{
std::lock_guard<std::mutex> lock(g_mutex);
for (u32 raw_id = g_last_raw_id; (raw_id = id_traits<T>::next_id(raw_id)); /**/)
if (auto ptr = add<T>(WRAP_EXPR(std::make_shared<T>(std::forward<Args>(args)...))))
{
if (g_map.find(raw_id) != g_map.end()) continue;
g_tls_last_id = id_traits<T>::out_id(raw_id);
g_map.emplace(raw_id, id_data_t(ptr));
if (raw_id < 0x80000000) g_last_raw_id = raw_id;
return id_traits<T>::out_id(raw_id);
id_aux_initialize(ptr.get());
return get_last_id();
}
throw EXCEPTION("Out of IDs");
throw EXCEPTION("Out of IDs ('%s')", typeid(T).name());
}
// get ID of specified type
template<typename T> static std::shared_ptr<T> get(u32 id)
// Add a new ID for an existing object provided (returns new id)
template<typename T>
u32 import(const std::shared_ptr<T>& ptr)
{
std::lock_guard<std::mutex> lock(g_mutex);
static const auto size = sizeof(T); // forbid forward declarations
const auto found = g_map.find(id_traits<T>::in_id(id));
if (found == g_map.end() || found->second.type_index != get_type_index<T>())
if (add<T>(WRAP_EXPR(ptr)))
{
return nullptr;
id_aux_initialize(ptr.get());
return get_last_id();
}
return std::static_pointer_cast<T>(found->second.data);
throw EXCEPTION("Out of IDs ('%s')", typeid(T).name());
}
// get all IDs of specified type T (unsorted)
template<typename T> static std::vector<std::shared_ptr<T>> get_all()
{
std::lock_guard<std::mutex> lock(g_mutex);
// Internal
std::shared_ptr<void> get(u32 in_id, id_type_index_t type);
// Get ID of specified type
template<typename T>
std::shared_ptr<T> get(u32 id)
{
return std::static_pointer_cast<T>(get(id_traits<T>::in_id(id), get_id_type_index<T>()));
}
// Internal
idm::map_t get_all(id_type_index_t type);
// Get all IDs of specified type T (unsorted)
template<typename T>
std::vector<std::shared_ptr<T>> get_all()
{
std::vector<std::shared_ptr<T>> result;
const auto type = get_type_index<T>();
for (auto& v : g_map)
for (auto& id : get_all(get_id_type_index<T>()))
{
if (v.second.type_index == type)
{
result.emplace_back(std::static_pointer_cast<T>(v.second.data));
}
result.emplace_back(std::static_pointer_cast<T>(id.second.data));
}
return result;
}
// remove ID created with type T
template<typename T> static bool remove(u32 id)
std::shared_ptr<void> withdraw(u32 in_id, id_type_index_t type);
// Remove the ID created with type T
template<typename T>
bool remove(u32 id)
{
std::lock_guard<std::mutex> lock(g_mutex);
const auto found = g_map.find(id_traits<T>::in_id(id));
if (found == g_map.end() || found->second.type_index != get_type_index<T>())
if (auto ptr = withdraw(id_traits<T>::in_id(id), get_id_type_index<T>()))
{
return false;
id_aux_finalize(static_cast<T*>(ptr.get()));
return true;
}
g_map.erase(found);
return true;
return false;
}
// remove ID created with type T and return the object
template<typename T> static std::shared_ptr<T> withdraw(u32 id)
// Remove the ID created with type T and return it
template<typename T>
std::shared_ptr<T> withdraw(u32 id)
{
std::lock_guard<std::mutex> lock(g_mutex);
const auto found = g_map.find(id_traits<T>::in_id(id));
if (found == g_map.end() || found->second.type_index != get_type_index<T>())
if (auto ptr = std::static_pointer_cast<T>(withdraw(id_traits<T>::in_id(id), get_id_type_index<T>())))
{
return nullptr;
id_aux_finalize(ptr.get());
return ptr;
}
auto ptr = std::static_pointer_cast<T>(found->second.data);
g_map.erase(found);
return ptr;
return nullptr;
}
template<typename T> static u32 get_count()
u32 get_count(id_type_index_t type);
template<typename T>
u32 get_count()
{
std::lock_guard<std::mutex> lock(g_mutex);
u32 result = 0;
const auto type = get_type_index<T>();
for (auto& v : g_map)
{
if (v.second.type_index == type)
{
result++;
}
}
return result;
return get_count(get_id_type_index<T>());
}
// get sorted ID list of specified type
template<typename T> static std::set<u32> get_set()
// Get sorted list of all IDs of specified type
template<typename T>
std::set<u32> get_set()
{
std::lock_guard<std::mutex> lock(g_mutex);
std::set<u32> result;
const auto type = get_type_index<T>();
for (auto& v : g_map)
for (auto& id : get_all(get_id_type_index<T>()))
{
if (v.second.type_index == type)
{
result.insert(id_traits<T>::out_id(v.first));
}
result.emplace(id_traits<T>::out_id(id.first));
}
return result;
}
// get sorted ID map (ID value -> ID data) of specified type
template<typename T> static std::map<u32, std::shared_ptr<T>> get_map()
// Get sorted map (ID value -> ID data) of all IDs of specified type
template<typename T>
std::map<u32, std::shared_ptr<T>> get_map()
{
std::lock_guard<std::mutex> lock(g_mutex);
std::map<u32, std::shared_ptr<T>> result;
const auto type = get_type_index<T>();
for (auto& v : g_map)
for (auto& id : get_all(get_id_type_index<T>()))
{
if (v.second.type_index == type)
{
result[id_traits<T>::out_id(v.first)] = std::static_pointer_cast<T>(v.second.data);
}
result[id_traits<T>::out_id(id.first)] = std::static_pointer_cast<T>(id.second.data);
}
return result;
}
};
}
// Fixed Object Manager
// allows to manage shared objects of any specified type, but only one object per type;
// object are deleted when the emulation is stopped
namespace fxm
{
extern std::mutex g_mutex;
using map_t = std::unordered_map<id_type_index_t, std::shared_ptr<void>>;
extern std::unordered_map<const void*, std::shared_ptr<void>> g_map;
// Remove all objects
void clear();
// reinitialize
static void clear()
// Internal (returns old and new pointers)
template<typename T, bool Always, typename Ptr>
std::pair<std::shared_ptr<T>, std::shared_ptr<T>> add(Ptr&& get_ptr)
{
extern std::mutex g_mutex;
extern fxm::map_t g_map;
std::lock_guard<std::mutex> lock(g_mutex);
g_map.clear();
auto& item = g_map[get_id_type_index<T>()];
if (Always || !item)
{
std::shared_ptr<T> old = std::static_pointer_cast<T>(std::move(item));
std::shared_ptr<T> ptr = get_ptr();
// Set new object
item = ptr;
return{ std::move(old), std::move(ptr) };
}
else
{
return{ std::static_pointer_cast<T>(item), nullptr };
}
}
// add fixed object of specified type only if it doesn't exist (one unique object per type may exist)
template<typename T, typename... Args> static std::enable_if_t<std::is_constructible<T, Args...>::value, std::shared_ptr<T>> make(Args&&... args)
// Create the object (returns nullptr if it already exists)
template<typename T, typename... Args>
std::enable_if_t<std::is_constructible<T, Args...>::value, std::shared_ptr<T>> make(Args&&... args)
{
std::lock_guard<std::mutex> lock(g_mutex);
auto pair = add<T, false>(WRAP_EXPR(std::make_shared<T>(std::forward<Args>(args)...)));
const auto index = get_type_index<T>();
const auto found = g_map.find(index);
// only if object of this type doesn't exist
if (found == g_map.end())
if (pair.second)
{
auto ptr = std::make_shared<T>(std::forward<Args>(args)...);
g_map.emplace(index, ptr);
return ptr;
id_aux_initialize(pair.second.get());
return std::move(pair.second);
}
return nullptr;
}
// add fixed object of specified type, replacing previous one if it exists
template<typename T, typename... Args> static std::enable_if_t<std::is_constructible<T, Args...>::value, std::shared_ptr<T>> make_always(Args&&... args)
// Create the object unconditionally (old object will be removed if it exists)
template<typename T, typename... Args>
std::enable_if_t<std::is_constructible<T, Args...>::value, std::shared_ptr<T>> make_always(Args&&... args)
{
std::lock_guard<std::mutex> lock(g_mutex);
auto pair = add<T, true>(WRAP_EXPR(std::make_shared<T>(std::forward<Args>(args)...)));
auto ptr = std::make_shared<T>(std::forward<Args>(args)...);
g_map[get_type_index<T>()] = ptr;
return ptr;
}
// import existing fixed object of specified type only if it doesn't exist (don't use)
template<typename T> static std::shared_ptr<T> import(std::shared_ptr<T>&& ptr)
{
std::lock_guard<std::mutex> lock(g_mutex);
const auto index = get_type_index<T>();
const auto found = g_map.find(index);
if (found == g_map.end())
if (pair.first)
{
g_map.emplace(index, ptr);
return ptr;
id_aux_finalize(pair.first.get());
}
id_aux_initialize(pair.second.get());
return std::move(pair.second);
}
// Emplace the object
template<typename T>
bool import(const std::shared_ptr<T>& ptr)
{
static const auto size = sizeof(T); // forbid forward declarations
auto pair = add<T, false>(WRAP_EXPR(ptr));
if (pair.second)
{
id_aux_initialize(pair.second.get());
return true;
}
return false;
}
// Emplace the object unconditionally (old object will be removed if it exists)
template<typename T>
void import_always(const std::shared_ptr<T>& ptr)
{
static const auto size = sizeof(T); // forbid forward declarations
auto pair = add<T, true>(WRAP_EXPR(ptr));
if (pair.first)
{
id_aux_finalize(pair.first.get());
}
id_aux_initialize(pair.second.get());
}
// Get the object unconditionally (create an object if it doesn't exist)
template<typename T, typename... Args>
std::enable_if_t<std::is_constructible<T, Args...>::value, std::shared_ptr<T>> get_always(Args&&... args)
{
auto pair = add<T, false>(WRAP_EXPR(std::make_shared<T>(std::forward<Args>(args)...)));
if (pair.second)
{
id_aux_initialize(pair.second.get());
return std::move(pair.second);
}
return std::move(pair.first);
}
// Internal
bool check(id_type_index_t type);
// Check whether the object exists
template<typename T>
bool check()
{
return check(get_id_type_index<T>());
}
// Internal
std::shared_ptr<void> get(id_type_index_t type);
// Get the object (returns nullptr if it doesn't exist)
template<typename T>
std::shared_ptr<T> get()
{
return std::static_pointer_cast<T>(get(get_id_type_index<T>()));
}
// Internal
std::shared_ptr<void> withdraw(id_type_index_t type);
// Delete the object
template<typename T>
bool remove()
{
if (auto ptr = withdraw(get_id_type_index<T>()))
{
id_aux_finalize(static_cast<T*>(ptr.get()));
return true;
}
return false;
}
// Delete the object and return it
template<typename T>
std::shared_ptr<T> withdraw()
{
if (auto ptr = std::static_pointer_cast<T>(withdraw(get_id_type_index<T>())))
{
id_aux_finalize(ptr.get());
return ptr;
}
return nullptr;
}
// import existing fixed object of specified type, replacing previous one if it exists (don't use)
template<typename T> static std::shared_ptr<T> import_always(std::shared_ptr<T>&& ptr)
{
std::lock_guard<std::mutex> lock(g_mutex);
g_map[get_type_index<T>()] = ptr;
return ptr;
}
// get fixed object of specified type (always returns an object, it's created if it doesn't exist)
template<typename T, typename... Args> static std::enable_if_t<std::is_constructible<T, Args...>::value, std::shared_ptr<T>> get_always(Args&&... args)
{
std::lock_guard<std::mutex> lock(g_mutex);
const auto index = get_type_index<T>();
const auto found = g_map.find(index);
if (found == g_map.end())
{
auto ptr = std::make_shared<T>(std::forward<Args>(args)...);
g_map[index] = ptr;
return ptr;
}
return std::static_pointer_cast<T>(found->second);
}
// check whether the object exists
template<typename T> static bool check()
{
std::lock_guard<std::mutex> lock(g_mutex);
return g_map.find(get_type_index<T>()) != g_map.end();
}
// get fixed object of specified type (returns nullptr if it doesn't exist)
template<typename T> static std::shared_ptr<T> get()
{
std::lock_guard<std::mutex> lock(g_mutex);
const auto found = g_map.find(get_type_index<T>());
if (found == g_map.end())
{
return nullptr;
}
return std::static_pointer_cast<T>(found->second);
}
// remove fixed object created with type T
template<typename T> static bool remove()
{
std::lock_guard<std::mutex> lock(g_mutex);
const auto found = g_map.find(get_type_index<T>());
if (found == g_map.end())
{
return false;
}
return g_map.erase(found), true;
}
// remove fixed object created with type T and return it
template<typename T> static std::shared_ptr<T> withdraw()
{
std::lock_guard<std::mutex> lock(g_mutex);
const auto found = g_map.find(get_type_index<T>());
if (found == g_map.end())
{
return nullptr;
}
auto ptr = std::static_pointer_cast<T>(std::move(found->second));
return g_map.erase(found), ptr;
}
};
}

View File

@ -90,13 +90,13 @@ namespace vm
std::vector<std::shared_ptr<block_t>> g_locations; // memory locations
const thread_ctrl_t* const INVALID_THREAD = reinterpret_cast<const thread_ctrl_t*>(~0ull);
//using reservation_mutex_t = std::mutex;
class reservation_mutex_t
{
atomic_t<const thread_ctrl_t*> m_owner{ INVALID_THREAD };
std::atomic<bool> m_lock{ false };
std::thread::id m_owner{};
std::condition_variable m_cv;
std::mutex m_mutex;
@ -105,13 +105,11 @@ namespace vm
never_inline void lock()
{
auto owner = get_current_thread_ctrl();
std::unique_lock<std::mutex> lock(m_mutex, std::defer_lock);
while (!m_owner.compare_and_swap_test(INVALID_THREAD, owner))
while (m_lock.exchange(true) == true)
{
if (m_owner == owner)
if (m_owner == std::this_thread::get_id())
{
throw EXCEPTION("Deadlock");
}
@ -125,26 +123,33 @@ namespace vm
m_cv.wait_for(lock, std::chrono::milliseconds(1));
}
m_owner = std::this_thread::get_id();
do_notify = true;
}
never_inline void unlock()
{
auto owner = get_current_thread_ctrl();
if (m_owner != std::this_thread::get_id())
{
throw EXCEPTION("Mutex not owned");
}
if (!m_owner.compare_and_swap_test(owner, INVALID_THREAD))
m_owner = {};
if (m_lock.exchange(false) == false)
{
throw EXCEPTION("Lost lock");
}
if (do_notify)
{
std::lock_guard<std::mutex> lock(m_mutex);
m_cv.notify_one();
}
}
};
const thread_ctrl_t* volatile g_reservation_owner = nullptr;
const thread_ctrl* volatile g_reservation_owner = nullptr;
u32 g_reservation_addr = 0;
u32 g_reservation_size = 0;
@ -352,7 +357,7 @@ namespace vm
void start()
{
// start notification thread
named_thread_t(COPY_EXPR("vm::start thread"), []()
thread_ctrl::spawn(PURE_EXPR("vm::start thread"s), []()
{
while (!Emu.IsStopped())
{
@ -364,8 +369,7 @@ namespace vm
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}).detach();
});
}
void _reservation_set(u32 addr, bool no_access = false)
@ -436,9 +440,6 @@ namespace vm
throw EXCEPTION("Invalid page flags (addr=0x%x, size=0x%x, flags=0x%x)", addr, size, flags);
}
// silent unlocking to prevent priority boost for threads going to break reservation
//g_reservation_mutex.do_notify = false;
// break the reservation
g_tls_did_break_reservation = g_reservation_owner && _reservation_break(g_reservation_addr);
@ -451,7 +452,7 @@ namespace vm
// set additional information
g_reservation_addr = addr;
g_reservation_size = size;
g_reservation_owner = get_current_thread_ctrl();
g_reservation_owner = thread_ctrl::get_current();
// copy data
std::memcpy(data, vm::base(addr), size);
@ -468,7 +469,7 @@ namespace vm
throw EXCEPTION("Invalid arguments (addr=0x%x, size=0x%x)", addr, size);
}
if (g_reservation_owner != get_current_thread_ctrl() || g_reservation_addr != addr || g_reservation_size != size)
if (g_reservation_owner != thread_ctrl::get_current() || g_reservation_addr != addr || g_reservation_size != size)
{
// atomic update failed
return false;
@ -522,7 +523,7 @@ namespace vm
return true;
}
bool reservation_test(const thread_ctrl_t* current)
bool reservation_test(const thread_ctrl* current)
{
const auto owner = g_reservation_owner;
@ -535,7 +536,7 @@ namespace vm
{
std::lock_guard<reservation_mutex_t> lock(g_reservation_mutex);
if (g_reservation_owner && g_reservation_owner == get_current_thread_ctrl())
if (g_reservation_owner && g_reservation_owner == thread_ctrl::get_current())
{
g_tls_did_break_reservation = _reservation_break(g_reservation_addr);
}
@ -556,7 +557,7 @@ namespace vm
g_tls_did_break_reservation = false;
// check and possibly break previous reservation
if (g_reservation_owner != get_current_thread_ctrl() || g_reservation_addr != addr || g_reservation_size != size)
if (g_reservation_owner != thread_ctrl::get_current() || g_reservation_addr != addr || g_reservation_size != size)
{
if (g_reservation_owner)
{
@ -572,7 +573,7 @@ namespace vm
// set additional information
g_reservation_addr = addr;
g_reservation_size = size;
g_reservation_owner = get_current_thread_ctrl();
g_reservation_owner = thread_ctrl::get_current();
// may not be necessary
_mm_mfence();
@ -783,13 +784,13 @@ namespace vm
if (!block)
{
LOG_ERROR(MEMORY, "%s(): invalid memory location (%d, addr=0x%x)\n", __func__, location, addr);
LOG_ERROR(MEMORY, "vm::dealloc(): invalid memory location (%d, addr=0x%x)\n", location, addr);
return;
}
if (!block->dealloc(addr))
{
LOG_ERROR(MEMORY, "%s(): deallocation failed (addr=0x%x)\n", __func__, addr);
LOG_ERROR(MEMORY, "vm::dealloc(): deallocation failed (addr=0x%x)\n", addr);
return;
}
}

View File

@ -1,9 +1,6 @@
#pragma once
const class thread_ctrl_t* get_current_thread_ctrl();
class named_thread_t;
#include "Utilities/Thread.h"
namespace vm
{
@ -122,7 +119,7 @@ namespace vm
bool reservation_query(u32 addr, u32 size, bool is_writing, std::function<bool()> callback);
// Returns true if the current thread owns reservation
bool reservation_test(const thread_ctrl_t* current = get_current_thread_ctrl());
bool reservation_test(const thread_ctrl* current = thread_ctrl::get_current());
// Break all reservations created by the current thread
void reservation_free();

View File

@ -232,11 +232,11 @@ D3D12GSRender::~D3D12GSRender()
release_d2d_structures();
}
void D3D12GSRender::onexit_thread()
void D3D12GSRender::on_exit()
{
}
bool D3D12GSRender::domethod(u32 cmd, u32 arg)
bool D3D12GSRender::do_method(u32 cmd, u32 arg)
{
switch (cmd)
{

View File

@ -205,8 +205,8 @@ private:
void copy_render_target_to_dma_location();
protected:
virtual void onexit_thread() override;
virtual bool domethod(u32 cmd, u32 arg) override;
virtual void on_exit() override;
virtual bool do_method(u32 cmd, u32 arg) override;
virtual void end() override;
virtual void flip(int buffer) override;

View File

@ -1046,9 +1046,9 @@ void GLGSRender::end()
rsx::thread::end();
}
void GLGSRender::oninit_thread()
void GLGSRender::on_init_thread()
{
GSRender::oninit_thread();
GSRender::on_init_thread();
gl::init();
LOG_NOTICE(Log::RSX, (const char*)glGetString(GL_VERSION));
@ -1071,7 +1071,7 @@ void GLGSRender::oninit_thread()
m_vao.element_array_buffer = m_ebo;
}
void GLGSRender::onexit_thread()
void GLGSRender::on_exit()
{
glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
@ -1184,7 +1184,7 @@ static const std::unordered_map<u32, rsx_method_impl_t> g_gl_method_tbl =
{ NV4097_CLEAR_SURFACE, nv4097_clear_surface }
};
bool GLGSRender::domethod(u32 cmd, u32 arg)
bool GLGSRender::do_method(u32 cmd, u32 arg)
{
auto found = g_gl_method_tbl.find(cmd);

View File

@ -100,9 +100,9 @@ protected:
void begin() override;
void end() override;
void oninit_thread() override;
void onexit_thread() override;
bool domethod(u32 id, u32 arg) override;
void on_init_thread() override;
void on_exit() override;
bool do_method(u32 id, u32 arg) override;
void flip(int buffer) override;
u64 timestamp() const override;
};

View File

@ -22,23 +22,19 @@ void GSInfo::Init()
mode.pitch = 4;
}
GSManager::GSManager() : m_render(nullptr)
{
}
void GSManager::Init()
{
if(m_render) return;
if (m_render) return;
m_info.Init();
switch (rpcs3::state.config.rsx.renderer.value())
{
default:
case rsx_renderer_type::Null : m_render = new NullGSRender(); break;
case rsx_renderer_type::OpenGL: m_render = new GLGSRender(); break;
case rsx_renderer_type::Null : m_render = std::make_shared<NullGSRender>(); break;
case rsx_renderer_type::OpenGL: m_render = std::make_shared<GLGSRender>(); break;
#ifdef _MSC_VER
case rsx_renderer_type::DX12: m_render = new D3D12GSRender(); break;
case rsx_renderer_type::DX12: m_render = std::make_shared<D3D12GSRender>(); break;
#endif
}
@ -47,12 +43,7 @@ void GSManager::Init()
void GSManager::Close()
{
if(m_render)
{
m_render->close();
delete m_render;
m_render = nullptr;
}
m_render.reset();
}
u8 GSManager::GetState()

View File

@ -25,18 +25,16 @@ struct GSInfo
class GSManager
{
GSInfo m_info;
GSRender* m_render;
std::shared_ptr<GSRender> m_render;
public:
GSManager();
void Init();
void Close();
bool IsInited() const { return m_render != nullptr; }
GSInfo& GetInfo() { return m_info; }
GSRender& GetRender() { assert(m_render); return *m_render; }
GSRender& GetRender() { return *m_render; }
u8 GetState();
u8 GetColorSpace();

View File

@ -31,11 +31,12 @@ GSRender::~GSRender()
if (m_frame)
{
m_frame->hide();
m_frame->close();
}
}
void GSRender::oninit()
void GSRender::on_init()
{
if (m_frame)
{
@ -43,7 +44,7 @@ void GSRender::oninit()
}
}
void GSRender::oninit_thread()
void GSRender::on_init_thread()
{
if (m_frame)
{
@ -52,21 +53,10 @@ void GSRender::oninit_thread()
}
}
void GSRender::close()
{
if (m_frame && m_frame->shown())
{
m_frame->hide();
}
if (joinable())
{
join();
}
}
void GSRender::flip(int buffer)
{
if (m_frame)
{
m_frame->flip(m_context);
}
}

View File

@ -49,9 +49,8 @@ public:
GSRender(frame_type type);
virtual ~GSRender();
void oninit() override;
void oninit_thread() override;
void on_init() override;
void on_init_thread() override;
void close();
void flip(int buffer) override;
};

View File

@ -6,11 +6,7 @@ NullGSRender::NullGSRender() : GSRender(frame_type::Null)
{
}
void NullGSRender::onexit_thread()
{
}
bool NullGSRender::domethod(u32 cmd, u32 value)
bool NullGSRender::do_method(u32 cmd, u32 value)
{
return false;
}

View File

@ -7,6 +7,5 @@ public:
NullGSRender();
private:
void onexit_thread() override;
bool domethod(u32 cmd, u32 value) override;
bool do_method(u32 cmd, u32 value) override;
};

View File

@ -689,7 +689,7 @@ namespace rsx
static void wrapper(thread *rsx, u32 arg)
{
// try process using gpu
if (rsx->domethod(id, arg))
if (rsx->do_method(id, arg))
{
if (rsx->capture_current_frame && id == NV4097_CLEAR_SURFACE)
rsx->capture_frame("clear");
@ -974,25 +974,23 @@ namespace rsx
capture_frame("Draw " + std::to_string(vertex_draw_count));
}
void thread::task()
void thread::on_task()
{
u8 inc;
LOG_NOTICE(RSX, "RSX thread started");
on_init_thread();
oninit_thread();
reset();
last_flip_time = get_system_time() - 1000000;
autojoin_thread_t vblank(WRAP_EXPR("VBlank Thread"), [this]()
scope_thread_t vblank(PURE_EXPR("VBlank Thread"s), [this]()
{
const u64 start_time = get_system_time();
vblank_count = 0;
while (joinable())
// TODO: exit condition
while (!Emu.IsStopped())
{
CHECK_EMU_STATUS;
if (get_system_time() - start_time > vblank_count * 1000000 / 60)
{
vblank_count++;
@ -1012,107 +1010,87 @@ namespace rsx
}
});
reset();
try
// TODO: exit condition
while (true)
{
while (joinable())
CHECK_EMU_STATUS;
be_t<u32> get = ctrl->get;
be_t<u32> put = ctrl->put;
if (put == get || !Emu.IsRunning())
{
//TODO: async mode
if (Emu.IsStopped())
{
LOG_WARNING(RSX, "RSX thread aborted");
break;
}
inc = 1;
be_t<u32> get = ctrl->get;
be_t<u32> put = ctrl->put;
if (put == get || !Emu.IsRunning())
{
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack
continue;
}
const u32 cmd = ReadIO32(get);
const u32 count = (cmd >> 18) & 0x7ff;
if (cmd & CELL_GCM_METHOD_FLAG_JUMP)
{
u32 offs = cmd & 0x1fffffff;
//LOG_WARNING(RSX, "rsx jump(0x%x) #addr=0x%x, cmd=0x%x, get=0x%x, put=0x%x", offs, m_ioAddress + get, cmd, get, put);
ctrl->get = offs;
continue;
}
if (cmd & CELL_GCM_METHOD_FLAG_CALL)
{
m_call_stack.push(get + 4);
u32 offs = cmd & ~3;
//LOG_WARNING(RSX, "rsx call(0x%x) #0x%x - 0x%x", offs, cmd, get);
ctrl->get = offs;
continue;
}
if (cmd == CELL_GCM_METHOD_FLAG_RETURN)
{
u32 get = m_call_stack.top();
m_call_stack.pop();
//LOG_WARNING(RSX, "rsx return(0x%x)", get);
ctrl->get = get;
continue;
}
if (cmd & CELL_GCM_METHOD_FLAG_NON_INCREMENT)
{
//LOG_WARNING(RSX, "rsx non increment cmd! 0x%x", cmd);
inc = 0;
}
if (cmd == 0) //nop
{
ctrl->get = get + 4;
continue;
}
auto args = vm::ptr<u32>::make((u32)RSXIOMem.RealAddr(get + 4));
u32 first_cmd = (cmd & 0xffff) >> 2;
if (cmd & 0x3)
{
LOG_WARNING(Log::RSX, "unaligned command: %s (0x%x from 0x%x)", get_method_name(first_cmd).c_str(), first_cmd, cmd & 0xffff);
}
for (u32 i = 0; i < count; i++)
{
u32 reg = first_cmd + (i * inc);
u32 value = args[i];
if (rpcs3::config.misc.log.rsx_logging.value())
{
LOG_NOTICE(Log::RSX, "%s(0x%x) = 0x%x", get_method_name(reg).c_str(), reg, value);
}
method_registers[reg] = value;
if (capture_current_frame)
frame_debug.command_queue.push_back(std::make_pair(reg, value));
if (auto method = methods[reg])
method(this, value);
}
ctrl->get = get + (count + 1) * 4;
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack
continue;
}
}
catch (const std::exception& ex)
{
LOG_ERROR(Log::RSX, ex.what());
throw;
}
LOG_NOTICE(RSX, "RSX thread ended");
const u32 cmd = ReadIO32(get);
const u32 count = (cmd >> 18) & 0x7ff;
onexit_thread();
if (cmd & CELL_GCM_METHOD_FLAG_JUMP)
{
u32 offs = cmd & 0x1fffffff;
//LOG_WARNING(RSX, "rsx jump(0x%x) #addr=0x%x, cmd=0x%x, get=0x%x, put=0x%x", offs, m_ioAddress + get, cmd, get, put);
ctrl->get = offs;
continue;
}
if (cmd & CELL_GCM_METHOD_FLAG_CALL)
{
m_call_stack.push(get + 4);
u32 offs = cmd & ~3;
//LOG_WARNING(RSX, "rsx call(0x%x) #0x%x - 0x%x", offs, cmd, get);
ctrl->get = offs;
continue;
}
if (cmd == CELL_GCM_METHOD_FLAG_RETURN)
{
u32 get = m_call_stack.top();
m_call_stack.pop();
//LOG_WARNING(RSX, "rsx return(0x%x)", get);
ctrl->get = get;
continue;
}
if (cmd == 0) //nop
{
ctrl->get = get + 4;
continue;
}
auto args = vm::ptr<u32>::make((u32)RSXIOMem.RealAddr(get + 4));
u32 first_cmd = (cmd & 0xffff) >> 2;
if (cmd & 0x3)
{
LOG_WARNING(Log::RSX, "unaligned command: %s (0x%x from 0x%x)", get_method_name(first_cmd).c_str(), first_cmd, cmd & 0xffff);
}
for (u32 i = 0; i < count; i++)
{
u32 reg = cmd & CELL_GCM_METHOD_FLAG_NON_INCREMENT ? first_cmd : first_cmd + i;
u32 value = args[i];
if (rpcs3::config.misc.log.rsx_logging.value())
{
LOG_NOTICE(Log::RSX, "%s(0x%x) = 0x%x", get_method_name(reg).c_str(), reg, value);
}
method_registers[reg] = value;
if (capture_current_frame)
frame_debug.command_queue.push_back(std::make_pair(reg, value));
if (auto method = methods[reg])
method(this, value);
}
ctrl->get = get + (count + 1) * 4;
}
}
std::string thread::get_name() const
{
return "rsx::thread"s;
}
void thread::fill_scale_offset_data(void *buffer, bool is_d3d) const noexcept
@ -1240,8 +1218,8 @@ namespace rsx
m_used_gcm_commands.clear();
oninit();
named_thread_t::start(WRAP_EXPR("rsx::thread"), WRAP_EXPR(task()));
on_init();
start();
}
u32 thread::ReadIO32(u32 addr)

View File

@ -255,7 +255,7 @@ namespace rsx
}
};
class thread : protected named_thread_t
class thread : public named_thread_t
{
protected:
std::stack<u32> m_call_stack;
@ -327,19 +327,20 @@ namespace rsx
protected:
virtual ~thread() {}
virtual void on_task() override;
public:
virtual std::string get_name() const override;
virtual void begin();
virtual void end();
virtual void oninit() = 0;
virtual void oninit_thread() = 0;
virtual void onexit_thread() = 0;
virtual bool domethod(u32 cmd, u32 value) { return false; }
virtual void on_init() = 0;
virtual void on_init_thread() = 0;
virtual bool do_method(u32 cmd, u32 value) { return false; }
virtual void flip(int buffer) = 0;
virtual u64 timestamp() const;
void task();
/**
* Fill buffer with 4x4 scale offset matrix.
* Vertex shader's position is to be multiplied by this matrix.

View File

@ -20,6 +20,8 @@ extern u64 get_system_time();
AudioConfig g_audio;
std::shared_ptr<thread_ctrl> g_audio_thread;
s32 cellAudioInit()
{
cellAudio.Warning("cellAudioInit()");
@ -48,17 +50,9 @@ s32 cellAudioInit()
std::memset(vm::base(g_audio.buffer), 0, AUDIO_PORT_OFFSET * AUDIO_PORT_COUNT);
std::memset(vm::base(g_audio.indexes), 0, sizeof32(u64) * AUDIO_PORT_COUNT);
// check thread status
if (g_audio.thread.joinable())
{
g_audio.thread.join();
}
// start audio thread
g_audio.thread.start(WRAP_EXPR("Audio Thread"), []()
g_audio_thread = thread_ctrl::spawn(PURE_EXPR("Audio Thread"s), []()
{
std::unique_lock<std::mutex> lock(g_audio.thread.mutex);
const bool do_dump = rpcs3::config.audio.dump_to_file.value();
AudioDumper m_dump;
@ -81,9 +75,9 @@ s32 cellAudioInit()
squeue_t<float*, BUFFER_NUM - 1> out_queue;
autojoin_thread_t iat(WRAP_EXPR("Internal Audio Thread"), [&out_queue]()
scope_thread_t iat(PURE_EXPR("Internal Audio Thread"s), [&out_queue]()
{
const bool use_u16 = rpcs3::config.audio.convert_to_u16.value();;
const bool use_u16 = rpcs3::config.audio.convert_to_u16.value();
Emu.GetAudioManager().GetAudioOut().Init();
@ -141,7 +135,7 @@ s32 cellAudioInit()
{
if (Emu.IsPaused())
{
g_audio.thread.cv.wait_for(lock, std::chrono::milliseconds(1));
std::this_thread::sleep_for(1ms); // hack
continue;
}
@ -155,7 +149,7 @@ s32 cellAudioInit()
const u64 expected_time = g_audio.counter * AUDIO_SAMPLES * 1000000 / 48000;
if (expected_time >= time_pos)
{
g_audio.thread.cv.wait_for(lock, std::chrono::milliseconds(1));
std::this_thread::sleep_for(1ms); // hack
continue;
}
@ -368,6 +362,8 @@ s32 cellAudioInit()
LV2_LOCK;
std::lock_guard<std::mutex> lock(g_audio.mutex);
for (auto key : g_audio.keys)
{
if (const auto queue = Emu.GetEventManager().GetEventQueue(key))
@ -419,8 +415,10 @@ s32 cellAudioQuit()
return CELL_AUDIO_ERROR_NOT_INIT;
}
g_audio.thread.join();
g_audio_thread->join();
g_audio_thread.reset();
g_audio.state.exchange(AUDIO_STATE_NOT_INITIALIZED);
return CELL_OK;
}
@ -653,8 +651,6 @@ s32 cellAudioGetPortTimestamp(u32 portNum, u64 tag, vm::ptr<u64> stamp)
// TODO: check tag (CELL_AUDIO_ERROR_TAG_NOT_FOUND error)
std::lock_guard<std::mutex> lock(g_audio.thread.mutex);
*stamp = g_audio.start_time + Emu.GetPauseTime() + (port.counter + (tag - port.tag)) * 256000000 / 48000;
return CELL_OK;
@ -686,8 +682,6 @@ s32 cellAudioGetPortBlockTag(u32 portNum, u64 blockNo, vm::ptr<u64> tag)
return CELL_AUDIO_ERROR_PARAM;
}
std::lock_guard<std::mutex> lock(g_audio.thread.mutex);
u64 tag_base = port.tag;
if (tag_base % port.block > blockNo)
{
@ -780,7 +774,7 @@ s32 cellAudioSetNotifyEventQueue(u64 key)
return CELL_AUDIO_ERROR_NOT_INIT;
}
std::lock_guard<std::mutex> lock(g_audio.thread.mutex);
std::lock_guard<std::mutex> lock(g_audio.mutex);
for (auto k : g_audio.keys) // check for duplicates
{
@ -813,7 +807,7 @@ s32 cellAudioRemoveNotifyEventQueue(u64 key)
return CELL_AUDIO_ERROR_NOT_INIT;
}
std::lock_guard<std::mutex> lock(g_audio.thread.mutex);
std::lock_guard<std::mutex> lock(g_audio.mutex);
for (auto i = g_audio.keys.begin(); i != g_audio.keys.end(); i++)
{

View File

@ -99,9 +99,10 @@ enum AudioPortState : u32
struct AudioPortConfig
{
std::mutex mutex;
atomic_t<AudioPortState> state;
std::mutex mutex;
u32 channel;
u32 block;
u64 attr;
@ -124,7 +125,8 @@ struct AudioPortConfig
struct AudioConfig final // custom structure
{
atomic_t<AudioState> state;
named_thread_t thread;
std::mutex mutex;
AudioPortConfig ports[AUDIO_PORT_COUNT];
u32 buffer; // 1 MB memory for audio ports
@ -133,16 +135,6 @@ struct AudioConfig final // custom structure
u64 start_time;
std::vector<u64> keys;
AudioConfig() = default;
~AudioConfig()
{
if (thread.joinable())
{
thread.join();
}
}
u32 open_port()
{
for (u32 i = 0; i < AUDIO_PORT_COUNT; i++)

View File

@ -494,7 +494,7 @@ s32 cellFsStReadStart(u32 fd, u64 offset, u64 size)
file->st_read_size = size;
file->st_thread.start(COPY_EXPR(fmt::format("FS ST Thread[0x%x]", fd)), [=]()
file->st_thread = thread_ctrl::spawn(PURE_EXPR("FS ST Thread"s), [=]()
{
std::unique_lock<std::mutex> lock(file->mutex);
@ -572,7 +572,7 @@ s32 cellFsStReadStop(u32 fd)
}
file->cv.notify_all();
file->st_thread.join();
file->st_thread->join();
return CELL_OK;
}
@ -937,7 +937,7 @@ s32 cellFsAioRead(vm::ptr<CellFsAio> aio, vm::ptr<s32> id, fs_aio_cb_t func)
const s32 xid = (*id = ++g_fs_aio_id);
named_thread_t(WRAP_EXPR("FS AIO Read Thread"), [=]{ fsAio(aio, false, xid, func); }).detach();
thread_ctrl::spawn(PURE_EXPR("FS AIO Read Thread"s), COPY_EXPR(fsAio(aio, false, xid, func)));
return CELL_OK;
}
@ -950,7 +950,7 @@ s32 cellFsAioWrite(vm::ptr<CellFsAio> aio, vm::ptr<s32> id, fs_aio_cb_t func)
const s32 xid = (*id = ++g_fs_aio_id);
named_thread_t(WRAP_EXPR("FS AIO Write Thread"), [=]{ fsAio(aio, true, xid, func); }).detach();
thread_ctrl::spawn(PURE_EXPR("FS AIO Write Thread"s), COPY_EXPR(fsAio(aio, true, xid, func)));
return CELL_OK;
}

View File

@ -61,9 +61,9 @@ s32 cellMsgDialogOpen2(u32 type, vm::cptr<char> msgString, vm::ptr<CellMsgDialog
default: return CELL_MSGDIALOG_ERROR_PARAM;
}
const auto dlg = fxm::import<MsgDialogBase>(Emu.GetCallbacks().get_msg_dialog());
const std::shared_ptr<MsgDialogBase> dlg(Emu.GetCallbacks().get_msg_dialog());
if (!dlg)
if (!fxm::import(dlg))
{
return CELL_SYSUTIL_ERROR_BUSY;
}
@ -206,18 +206,17 @@ s32 cellMsgDialogClose(f32 delay)
const u64 wait_until = get_system_time() + static_cast<s64>(std::max<float>(delay, 0.0f) * 1000);
named_thread_t(WRAP_EXPR("MsgDialog Thread"), [=]()
thread_ctrl::spawn(PURE_EXPR("MsgDialog Thread"s), [=]()
{
while (dlg->state == MsgDialogState::Open && get_system_time() < wait_until)
{
CHECK_EMU_STATUS;
if (Emu.IsStopped()) return;
std::this_thread::sleep_for(1ms);
}
dlg->on_close(CELL_MSGDIALOG_BUTTON_NONE);
}).detach();
});
return CELL_OK;
}

View File

@ -128,7 +128,7 @@ namespace sys_net
{
g_tls_net_data.set(vm::alloc(sizeof(decltype(g_tls_net_data)::type), vm::main));
current_thread_register_atexit([addr = g_tls_net_data.addr()]
thread_ctrl::at_exit([addr = g_tls_net_data.addr()]
{
vm::dealloc_verbose_nothrow(addr, vm::main);
});

View File

@ -43,8 +43,14 @@ void sys_ppu_thread_exit(PPUThread& ppu, u64 val)
// (deallocate TLS)
// ...
// call the syscall
_sys_ppu_thread_exit(ppu, val);
if (ppu.hle_code == 0xaff080a4)
{
// Change sys_ppu_thread_exit code to the syscall code
ppu.hle_code = ~41;
}
// Call the syscall
return _sys_ppu_thread_exit(ppu, val);
}
std::mutex g_once_mutex;

View File

@ -215,7 +215,7 @@ s32 sys_cond_wait(PPUThread& ppu, u32 cond_id, u64 timeout)
// try to reown mutex and exit if timed out
if (!cond->mutex->owner)
{
cond->mutex->owner = ppu.shared_from_this();
cond->mutex->owner = std::static_pointer_cast<CPUThread>(ppu.shared_from_this());
break;
}

View File

@ -184,7 +184,7 @@ struct lv2_file_t
u64 st_trans_rate;
bool st_copyless;
named_thread_t st_thread;
std::shared_ptr<thread_ctrl> st_thread;
u32 st_buffer;
u64 st_read_size;

View File

@ -132,7 +132,7 @@ s32 sys_mutex_lock(PPUThread& ppu, u32 mutex_id, u64 timeout)
// lock immediately if not locked
if (!mutex->owner)
{
mutex->owner = ppu.shared_from_this();
mutex->owner = std::static_pointer_cast<CPUThread>(ppu.shared_from_this());
return CELL_OK;
}
@ -207,7 +207,7 @@ s32 sys_mutex_trylock(PPUThread& ppu, u32 mutex_id)
}
// own the mutex if free
mutex->owner = ppu.shared_from_this();
mutex->owner = std::static_pointer_cast<CPUThread>(ppu.shared_from_this());
return CELL_OK;
}

View File

@ -26,14 +26,20 @@ void _sys_ppu_thread_exit(PPUThread& ppu, u64 errorcode)
}
}
const auto thread = ppu.shared_from_this();
if (!ppu.is_joinable)
{
idm::remove<PPUThread>(ppu.get_id());
}
else
{
ppu.exit();
}
ppu.exit();
// Throw if this syscall was not called directly by the SC instruction
if (~ppu.hle_code != 41)
{
throw CPUThreadExit{};
}
}
void sys_ppu_thread_yield()

View File

@ -228,7 +228,7 @@ s32 sys_rwlock_wlock(PPUThread& ppu, u32 rw_lock_id, u64 timeout)
if (!rwlock->readers && !rwlock->writer)
{
rwlock->writer = ppu.shared_from_this();
rwlock->writer = std::static_pointer_cast<CPUThread>(ppu.shared_from_this());
return CELL_OK;
}
@ -300,7 +300,7 @@ s32 sys_rwlock_trywlock(PPUThread& ppu, u32 rw_lock_id)
return CELL_EBUSY;
}
rwlock->writer = ppu.shared_from_this();
rwlock->writer = std::static_pointer_cast<CPUThread>(ppu.shared_from_this());
return CELL_OK;
}

View File

@ -13,55 +13,55 @@ SysCallBase sys_timer("sys_timer");
extern u64 get_system_time();
lv2_timer_t::lv2_timer_t()
: start(0)
, period(0)
, state(SYS_TIMER_STATE_STOP)
void lv2_timer_t::on_task()
{
auto name = fmt::format("Timer[0x%x] Thread", idm::get_last_id());
std::unique_lock<std::mutex> lock(mutex);
thread.start([name]{ return name; }, [this]()
while (state <= SYS_TIMER_STATE_RUN)
{
std::unique_lock<std::mutex> lock(thread.mutex);
CHECK_EMU_STATUS;
while (thread.joinable())
if (state == SYS_TIMER_STATE_RUN)
{
CHECK_EMU_STATUS;
if (lock) lock.unlock();
if (state == SYS_TIMER_STATE_RUN)
LV2_LOCK;
if (get_system_time() >= expire)
{
LV2_LOCK;
const auto queue = port.lock();
if (get_system_time() >= start)
if (queue)
{
const auto queue = port.lock();
queue->push(lv2_lock, source, data1, data2, expire);
}
if (queue)
{
queue->push(lv2_lock, source, data1, data2, start);
}
if (period && queue)
{
expire += period; // set next expiration time
if (period && queue)
{
start += period; // set next expiration time
continue; // hack: check again
}
else
{
state = SYS_TIMER_STATE_STOP; // stop if oneshot or the event port was disconnected (TODO: is it correct?)
}
continue; // hack: check again
}
else
{
state = SYS_TIMER_STATE_STOP; // stop if oneshot or the event port was disconnected (TODO: is it correct?)
}
}
thread.cv.wait_for(lock, std::chrono::milliseconds(1));
}
});
if (!lock)
{
lock.lock();
continue;
}
cv.wait_for(lock, std::chrono::milliseconds(1));
}
}
lv2_timer_t::~lv2_timer_t()
lv2_timer_t::lv2_timer_t()
: id(idm::get_last_id())
{
thread.join();
}
s32 sys_timer_create(vm::ptr<u32> timer_id)
@ -109,7 +109,7 @@ s32 sys_timer_get_information(u32 timer_id, vm::ptr<sys_timer_information_t> inf
return CELL_ESRCH;
}
info->next_expiration_time = timer->start;
info->next_expiration_time = timer->expire;
info->period = timer->period;
info->timer_state = timer->state;
@ -163,11 +163,14 @@ s32 _sys_timer_start(u32 timer_id, u64 base_time, u64 period)
// sys_timer_start_periodic() will use current time (TODO: is it correct?)
timer->start = base_time ? base_time : start_time + period;
// lock for reliable notification
std::lock_guard<std::mutex> lock(timer->mutex);
timer->expire = base_time ? base_time : start_time + period;
timer->period = period;
timer->state = SYS_TIMER_STATE_RUN;
timer->thread.cv.notify_one();
timer->cv.notify_one();
return CELL_OK;
}
@ -196,8 +199,8 @@ s32 sys_timer_connect_event_queue(u32 timer_id, u32 queue_id, u64 name, u64 data
LV2_LOCK;
const auto timer = idm::get<lv2_timer_t>(timer_id);
const auto queue = idm::get<lv2_event_queue_t>(queue_id);
const auto timer(idm::get<lv2_timer_t>(timer_id));
const auto queue(idm::get<lv2_event_queue_t>(queue_id));
if (!timer || !queue)
{

View File

@ -19,22 +19,35 @@ struct sys_timer_information_t
be_t<u32> pad;
};
struct lv2_timer_t final
class lv2_timer_t final : public named_thread_t
{
void on_task() override;
void on_id_aux_finalize() override
{
// Signal thread using invalid state and join
{ std::lock_guard<std::mutex>{mutex}, state = -1; }
cv.notify_one();
join();
}
public:
lv2_timer_t();
std::string get_name() const override { return fmt::format("Timer Thread[0x%x]", id); }
const u32 id;
std::weak_ptr<lv2_event_queue_t> port; // event queue
u64 source; // event source
u64 data1; // event arg 1
u64 data2; // event arg 2
u64 start; // next expiration time
u64 period; // period (oneshot if 0)
u64 expire = 0; // next expiration time
u64 period = 0; // period (oneshot if 0)
std::atomic<u32> state; // timer state
named_thread_t thread; // timer thread
lv2_timer_t();
~lv2_timer_t();
std::atomic<u32> state{ SYS_TIMER_STATE_RUN }; // timer state
};
s32 sys_timer_create(vm::ptr<u32> timer_id);

View File

@ -125,19 +125,28 @@ public:
m_cb.send_dbg_command(cmd, thread);
}
// returns a future object associated with the result of the function called from the GUI thread
template<typename F, typename RT = std::result_of_t<F()>> inline std::future<RT> CallAfter(F&& func) const
// Returns a future object associated with the result of the function called from the GUI thread
template<typename F>
std::future<void> CallAfter(F&& func) const
{
// create task
auto task = std::make_shared<std::packaged_task<RT()>>(std::forward<F>(func));
// Make "shared" promise to workaround std::function limitation
auto spr = std::make_shared<std::promise<void>>();
// get future
std::future<RT> future = task->get_future();
// Get future
std::future<void> future = spr->get_future();
// run asynchronously in GUI thread
m_cb.call_after([=]
// Run asynchronously in GUI thread
m_cb.call_after([spr = std::move(spr), task = std::forward<F>(func)]()
{
(*task)();
try
{
task();
spr->set_value();
}
catch (...)
{
spr->set_exception(std::current_exception());
}
});
return future;

View File

@ -164,17 +164,19 @@ template<typename T1, typename T2, typename T3 = const char*> struct triplet_t
// return 32 bit .size() for container
template<typename T> inline auto size32(const T& container) -> decltype(static_cast<u32>(container.size()))
{
return container.size() <= UINT32_MAX ? static_cast<u32>(container.size()) : throw std::length_error(__FUNCTION__);
const auto size = container.size();
return size >= 0 && size <= UINT32_MAX ? static_cast<u32>(size) : throw std::length_error(__FUNCTION__);
}
// return 32 bit size for an array
template<typename T, std::size_t Size> constexpr u32 size32(const T(&)[Size])
{
return Size <= UINT32_MAX ? static_cast<u32>(Size) : throw std::length_error(__FUNCTION__);
return Size >= 0 && Size <= UINT32_MAX ? static_cast<u32>(Size) : throw std::length_error(__FUNCTION__);
}
#define WRAP_EXPR(expr) [&]{ return expr; }
#define COPY_EXPR(expr) [=]{ return expr; }
#define PURE_EXPR(expr) [] { return expr; }
#define EXCEPTION(text, ...) fmt::exception(__FILE__, __LINE__, __FUNCTION__, text, ##__VA_ARGS__)
#define VM_CAST(value) vm::impl_cast(value, __FILE__, __LINE__, __FUNCTION__)
#define IS_INTEGRAL(t) (std::is_integral<t>::value || std::is_same<std::decay_t<t>, u128>::value)
@ -183,6 +185,7 @@ template<typename T, std::size_t Size> constexpr u32 size32(const T(&)[Size])
#define CHECK_ASSERTION(expr) if (expr) {} else throw EXCEPTION("Assertion failed: " #expr)
#define CHECK_SUCCESS(expr) if (s32 _r = (expr)) throw EXCEPTION(#expr " failed (0x%x)", _r)
// Some forward declarations for the ID manager
template<typename T> struct id_traits;
#define _PRGNAME_ "RPCS3"