1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-25 04:02:42 +01:00

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 "; prefix = "E ";
break; break;
} }
if (auto thr = get_current_thread_ctrl()) if (auto thr = thread_ctrl::get_current())
{ {
prefix += "{" + thr->get_name() + "} "; prefix += "{" + thr->get_name() + "} ";
} }

View File

@ -5,7 +5,7 @@
void sleep_queue_entry_t::add_entry() 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() void sleep_queue_entry_t::remove_entry()
@ -33,7 +33,7 @@ bool sleep_queue_entry_t::find() const
return false; 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_thread(cpu)
, m_queue(queue) , m_queue(queue)
{ {
@ -41,7 +41,7 @@ sleep_queue_entry_t::sleep_queue_entry_t(CPUThread& cpu, sleep_queue_t& queue)
cpu.sleep(); 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_thread(cpu)
, m_queue(queue) , m_queue(queue)
{ {

View File

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

View File

@ -19,6 +19,27 @@
#include <ucontext.h> #include <ucontext.h>
#endif #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) void SetCurrentThreadDebugName(const char* threadName)
{ {
#if defined(_MSC_VER) // this is VS-specific way to set thread names for the debugger #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_STOS,
X64OP_XCHG, X64OP_XCHG,
X64OP_CMPXCHG, X64OP_CMPXCHG,
X64OP_LOAD_AND_STORE, // lock and [mem],reg X64OP_LOAD_AND_STORE, // lock and [mem], reg
X64OP_LOAD_OR_STORE, // TODO: lock or [mem], reg X64OP_LOAD_OR_STORE, // lock or [mem], reg (TODO)
X64OP_INC, // TODO: lock inc [mem] X64OP_LOAD_XOR_STORE, // lock xor [mem], reg (TODO)
X64OP_DEC, // TODO: lock dec [mem] 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) 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 u64 addr64 = (u64)pExp->ExceptionRecord->ExceptionInformation[1] - (u64)vm::base(0);
const bool is_writing = pExp->ExceptionRecord->ExceptionInformation[0] != 0; const bool is_writing = pExp->ExceptionRecord->ExceptionInformation[0] != 0;
if (pExp->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && if (pExp->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && (u32)addr64 == addr64 && thread_ctrl::get_current() && handle_access_violation((u32)addr64, is_writing, pExp->ContextRecord))
(u32)addr64 == addr64 &&
get_current_thread_ctrl() &&
handle_access_violation((u32)addr64, is_writing, pExp->ContextRecord))
{ {
return EXCEPTION_CONTINUE_EXECUTION; 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; const bool is_writing = ((ucontext_t*)uct)->uc_mcontext.gregs[REG_ERR] & 0x2;
#endif #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)) if (handle_access_violation((u32)addr64, is_writing, (ucontext_t*)uct))
{ {
@ -1191,94 +1210,112 @@ const int sigaction_result = []() -> int
#endif #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() // TODO
{ std::atomic<u32> g_thread_count{ 0 };
return g_tls_this_thread;
}
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) #if defined(_MSC_VER)
{ _set_se_translator(_se_translator); // not essential, disable if necessary
start(std::move(name), std::move(func)); #endif
}
named_thread_t::~named_thread_t() #ifdef _WIN32
{ if (!exception_handler || !exception_filter)
if (m_thread) #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(); 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 std::string named_thread_t::get_name() const
{ {
if (!m_thread) return fmt::format("('%s') Unnamed Thread", typeid(*this).name());
{
throw EXCEPTION("Invalid thread");
}
if (!m_thread->m_name)
{
throw EXCEPTION("Invalid name getter");
}
return m_thread->m_name();
} }
std::atomic<u32> g_thread_count{ 0 }; void named_thread_t::start()
void named_thread_t::start(std::function<std::string()> name, std::function<void()> func)
{ {
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 // Run thread
m_thread = std::make_shared<thread_ctrl_t>(std::move(name)); m_thread = thread_ctrl::spawn(std::move(name), [thread = std::move(ptr)]()
// start thread
m_thread->m_thread = std::thread([](std::shared_ptr<thread_ctrl_t> ctrl, std::function<void()> func)
{ {
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 try
{ {
g_thread_count++;
if (rpcs3::config.misc.log.hle_logging.value()) if (rpcs3::config.misc.log.hle_logging.value())
{ {
LOG_NOTICE(GENERAL, "Thread started"); LOG_NOTICE(GENERAL, "Thread started");
} }
func(); thread->on_task();
if (rpcs3::config.misc.log.hle_logging.value()) 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"); LOG_NOTICE(GENERAL, "Thread aborted");
} }
for (auto& func : ctrl->m_atexit) thread->on_exit();
{ });
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();
} }
void named_thread_t::join() void named_thread_t::join()
{ {
if (!m_thread) CHECK_ASSERTION(m_thread != nullptr);
try
{ {
throw EXCEPTION("Invalid thread"); m_thread->join();
m_thread.reset();
} }
catch (...)
if (g_tls_this_thread == m_thread.get())
{ {
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; }; const std::function<bool()> SQUEUE_ALWAYS_EXIT = [](){ return true; };

View File

@ -1,107 +1,171 @@
#pragma once #pragma once
const class thread_ctrl_t* get_current_thread_ctrl(); // Thread control class
class thread_ctrl final
// Named thread control class
class thread_ctrl_t final
{ {
friend class named_thread_t; static thread_local thread_ctrl* g_tls_this_thread;
template<typename T> friend void current_thread_register_atexit(T);
// Thread handler
std::thread m_thread;
// Name getter // Name getter
const std::function<std::string()> m_name; std::function<std::string()> m_name;
// Functions executed at thread exit (temporarily) // Thread handle (be careful)
std::vector<std::function<void()>> m_atexit; 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: public:
thread_ctrl_t(std::function<std::string()> name) template<typename T>
: m_name(std::move(name)) 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 // Get thread name
std::string get_name() const; 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) class named_thread_t : public std::enable_shared_from_this<named_thread_t>
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
{ {
// Pointer to managed resource (shared with actual thread) // Pointer to managed resource (shared with actual thread)
std::shared_ptr<thread_ctrl_t> m_thread; std::shared_ptr<thread_ctrl> m_thread;
public: public:
// Thread mutex for external use // Thread condition variable for external use (this thread waits on it, other threads may notify)
std::mutex mutex;
// Thread condition variable for external use
std::condition_variable cv; 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: public:
// Initialize in empty state
named_thread_t() = default; named_thread_t() = default;
// Create named thread virtual ~named_thread_t() = default;
named_thread_t(std::function<std::string()> name, std::function<void()> func);
// Deleted copy/move constructors + copy/move operators // Deleted copy/move constructors + copy/move operators
named_thread_t(const named_thread_t&) = delete; 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 // Get thread name
std::string get_name() const; virtual std::string get_name() const;
// Create named thread (current state must be empty) // Start thread (cannot be called from the constructor: should throw bad_weak_ptr in such case)
void start(std::function<std::string()> name, std::function<void()> func); void start();
// Detach thread -> empty state // Join thread (get future result)
void detach();
// Join thread -> empty state
void join(); void join();
// Check whether the thread is not in "empty state" // 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 // Compare with the current thread
bool is_current() const; bool is_current() const { CHECK_ASSERTION(m_thread); return thread_ctrl::get_current() == m_thread.get(); }
// Get internal thread pointer // Get thread_ctrl
const thread_ctrl_t* get_thread_ctrl() const { return m_thread.get(); } 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 // Wrapper for named thread, joins automatically in the destructor, can only be used in function scope
class autojoin_thread_t final class scope_thread_t final
{ {
named_thread_t m_thread; std::shared_ptr<thread_ctrl> m_thread;
public: public:
autojoin_thread_t(std::function<std::string()> name, std::function<void()> func) template<typename N, typename F>
: m_thread(std::move(name), std::move(func)) 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) 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({}) , ARMv7Context({})
{ {
} }
ARMv7Thread::~ARMv7Thread() ARMv7Thread::~ARMv7Thread()
{ {
cv.notify_one();
join();
close_stack(); close_stack();
armv7_free_tls(m_id); 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 void ARMv7Thread::dump_info() const
{ {
if (hle_func) if (hle_func)
@ -191,7 +193,7 @@ void ARMv7Thread::do_run()
} }
} }
void ARMv7Thread::task() void ARMv7Thread::cpu_task()
{ {
if (custom_task) if (custom_task)
{ {
@ -225,7 +227,7 @@ void ARMv7Thread::fast_call(u32 addr)
try try
{ {
task(); cpu_task();
} }
catch (CPUThreadReturn) catch (CPUThreadReturn)
{ {

View File

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

View File

@ -9,65 +9,66 @@
thread_local CPUThread* g_tls_current_cpu_thread = nullptr; 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_id(idm::get_last_id())
, m_type(type) , m_type(type)
, m_name(name) , 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() CPUThread::~CPUThread()
@ -75,6 +76,11 @@ CPUThread::~CPUThread()
Emu.SendDbgCommand(DID_REMOVE_THREAD, this); Emu.SendDbgCommand(DID_REMOVE_THREAD, this);
} }
std::string CPUThread::get_name() const
{
return m_name;
}
bool CPUThread::is_paused() const bool CPUThread::is_paused() const
{ {
return (m_state & CPU_STATE_PAUSED) != 0 || Emu.IsPaused(); return (m_state & CPU_STATE_PAUSED) != 0 || Emu.IsPaused();
@ -149,25 +155,26 @@ void CPUThread::exec()
{ {
Emu.SendDbgCommand(DID_EXEC_THREAD, this); Emu.SendDbgCommand(DID_EXEC_THREAD, this);
m_state &= ~CPU_STATE_STOPPED;
{ {
// lock for reliable notification // lock for reliable notification
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
m_state &= ~CPU_STATE_STOPPED;
cv.notify_one(); cv.notify_one();
} }
} }
void CPUThread::exit() void CPUThread::exit()
{ {
if (is_current()) m_state |= CPU_STATE_DEAD;
if (!is_current())
{ {
throw CPUThreadExit{}; // lock for reliable notification
} std::lock_guard<std::mutex> lock(mutex);
else
{ cv.notify_one();
throw EXCEPTION("Unable to exit another thread");
} }
} }
@ -259,6 +266,11 @@ bool CPUThread::check_status()
{ {
CHECK_EMU_STATUS; // check at least once CHECK_EMU_STATUS; // check at least once
if (!is_alive())
{
return true;
}
if (!is_paused() && (m_state & CPU_STATE_INTR) == 0) if (!is_paused() && (m_state & CPU_STATE_INTR) == 0)
{ {
break; 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_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_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_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_DEAD = (1ull << 4), // indicates irreversible exit of the thread
CPU_STATE_RETURN = (1ull << 5), // used for callback return CPU_STATE_RETURN = (1ull << 5), // used for callback return
CPU_STATE_SIGNAL = (1ull << 6), // used for HLE signaling CPU_STATE_SIGNAL = (1ull << 6), // used for HLE signaling
CPU_STATE_INTR = (1ull << 7), // thread interrupted 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 {}; // "HLE return" exception event
class CPUThreadReturn {}; class CPUThreadStop {}; // CPUThread::Stop exception event
class CPUThreadExit {}; // CPUThread::Exit exception event
// CPUThread::Stop exception event
class CPUThreadStop {};
// CPUThread::Exit exception event
class CPUThreadExit {};
class CPUDecoder; 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: 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 atomic_t<u64> m_state{ CPU_STATE_STOPPED }; // thread state flags
std::unique_ptr<CPUDecoder> m_dec; std::unique_ptr<CPUDecoder> m_dec;
const u32 m_id; const u32 m_id;
const CPUThreadType m_type; 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: public:
virtual ~CPUThread() override; virtual ~CPUThread() override;
virtual std::string get_name() const override;
u32 get_id() const { return m_id; } u32 get_id() const { return m_id; }
CPUThreadType get_type() const { return m_type; } 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_alive() const { return (m_state & CPU_STATE_DEAD) == 0; }
bool is_stopped() const { return (m_state & CPU_STATE_STOPPED) != 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_pc() const = 0;
virtual u32 get_offset() const = 0; virtual u32 get_offset() const = 0;
virtual void do_run() = 0; virtual void do_run() = 0;
virtual void task() = 0; virtual void cpu_task() = 0;
virtual void init_regs() = 0; virtual void init_regs() = 0;
virtual void init_stack() = 0; virtual void init_stack() = 0;
@ -178,35 +170,6 @@ protected:
std::shared_ptr<CPUThread> thread; std::shared_ptr<CPUThread> thread;
public: public:
//u32 get_entry() const
//{
// return thread->entry;
//}
virtual cpu_thread& args(std::initializer_list<std::string> values) = 0; virtual cpu_thread& args(std::initializer_list<std::string> values) = 0;
virtual cpu_thread& run() = 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() { RecompilationEngine::~RecompilationEngine() {
m_executable_storage.clear(); m_executable_storage.clear();
join();
memory_helper::free_reserved_memory(FunctionCache, VIRTUAL_INSTRUCTION_COUNT * sizeof(ExecutableStorageType)); memory_helper::free_reserved_memory(FunctionCache, VIRTUAL_INSTRUCTION_COUNT * sizeof(ExecutableStorageType));
free(FunctionCachePagesCommited); free(FunctionCachePagesCommited);
} }
@ -306,8 +305,8 @@ void RecompilationEngine::NotifyBlockStart(u32 address) {
m_pending_address_start.push_back(address); m_pending_address_start.push_back(address);
} }
if (!joinable()) { if (!is_started()) {
start(WRAP_EXPR("PPU Recompilation Engine"), WRAP_EXPR(Task())); start();
} }
cv.notify_one(); cv.notify_one();
@ -324,12 +323,12 @@ raw_fd_ostream & RecompilationEngine::Log() {
return *m_log; return *m_log;
} }
void RecompilationEngine::Task() { void RecompilationEngine::on_task() {
std::chrono::nanoseconds idling_time(0); std::chrono::nanoseconds idling_time(0);
std::chrono::nanoseconds recompiling_time(0); std::chrono::nanoseconds recompiling_time(0);
auto start = std::chrono::high_resolution_clock::now(); auto start = std::chrono::high_resolution_clock::now();
while (joinable() && !Emu.IsStopped()) { while (!Emu.IsStopped()) {
bool work_done_this_iteration = false; bool work_done_this_iteration = false;
std::list <u32> m_current_execution_traces; 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 * It then builds them asynchroneously and update the executable mapping
* using atomic based locks to avoid undefined behavior. * 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; friend class CPUHybridDecoderRecompiler;
public: public:
virtual ~RecompilationEngine() override; virtual ~RecompilationEngine() override;
@ -790,7 +790,9 @@ namespace ppu_recompiler_llvm {
/// Log /// Log
llvm::raw_fd_ostream & 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 /// Get a pointer to the instance of this class
static std::shared_ptr<RecompilationEngine> GetInstance(); 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) 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(); InitRotateMask();
} }
PPUThread::~PPUThread() PPUThread::~PPUThread()
{ {
if (is_current())
{
detach();
}
else
{
join();
}
close_stack(); close_stack();
ppu_free_tls(m_id); 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 void PPUThread::dump_info() const
{ {
extern std::string get_ps3_function_name(u64 fid); extern std::string get_ps3_function_name(u64 fid);
@ -239,7 +235,7 @@ void PPUThread::fast_call(u32 addr, u32 rtoc)
try try
{ {
task(); cpu_task();
} }
catch (CPUThreadReturn) catch (CPUThreadReturn)
{ {
@ -264,7 +260,7 @@ void PPUThread::fast_stop()
m_state |= CPU_STATE_RETURN; m_state |= CPU_STATE_RETURN;
} }
void PPUThread::task() void PPUThread::cpu_task()
{ {
SetHostRoundingMode(FPSCR_RN_NEAR); SetHostRoundingMode(FPSCR_RN_NEAR);

View File

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

View File

@ -10,20 +10,9 @@
thread_local spu_mfc_arg_t raw_spu_mfc[8] = {}; thread_local spu_mfc_arg_t raw_spu_mfc[8] = {};
RawSPUThread::RawSPUThread(const std::string& name, u32 index) 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)) CHECK_ASSERTION(vm::falloc(offset, 0x40000) == offset);
{
throw EXCEPTION("Failed to allocate RawSPU local storage");
}
}
RawSPUThread::~RawSPUThread()
{
join();
// Deallocate Local Storage
vm::dealloc_verbose_nothrow(offset);
} }
bool RawSPUThread::read_reg(const u32 addr, u32& value) 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; return false;
} }
void RawSPUThread::task() void RawSPUThread::cpu_task()
{ {
// get next PC and SPU Interrupt status // get next PC and SPU Interrupt status
pc = npc.exchange(0); pc = npc.exchange(0);
@ -242,7 +231,7 @@ void RawSPUThread::task()
pc &= 0x3fffc; pc &= 0x3fffc;
SPUThread::task(); SPUThread::cpu_task();
// save next PC and current SPU Interrupt status // save next PC and current SPU Interrupt status
npc = pc | ((ch_event_stat & SPU_EVENT_INTR_ENABLED) != 0); npc = pc | ((ch_event_stat & SPU_EVENT_INTR_ENABLED) != 0);

View File

@ -19,11 +19,10 @@ class RawSPUThread final : public SPUThread
{ {
public: public:
RawSPUThread(const std::string& name, u32 index); RawSPUThread(const std::string& name, u32 index);
virtual ~RawSPUThread();
bool read_reg(const u32 addr, u32& value); bool read_reg(const u32 addr, u32& value);
bool write_reg(const u32 addr, const u32 value); bool write_reg(const u32 addr, const u32 value);
private: 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; 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) SPUThread::SPUThread(CPUThreadType type, const std::string& name, u32 index, u32 offset)
: CPUThread(type, name, std::move(thread_name)) : CPUThread(type, name)
, index(index) , index(index)
, offset(offset) , offset(offset)
{ {
} }
SPUThread::SPUThread(const std::string& name, u32 index) 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) , index(index)
, offset(vm::alloc(0x40000, vm::main)) , offset(vm::alloc(0x40000, vm::main))
{ {
if (!offset) CHECK_ASSERTION(offset);
{
throw EXCEPTION("Failed to allocate SPU local storage");
}
} }
SPUThread::~SPUThread() SPUThread::~SPUThread()
{ {
if (m_type == CPU_THREAD_SPU) // Deallocate Local Storage
{ vm::dealloc_verbose_nothrow(offset);
join();
// Deallocate Local Storage
vm::dealloc_verbose_nothrow(offset, vm::main);
}
} }
bool SPUThread::is_paused() const bool SPUThread::is_paused() const
@ -99,12 +91,17 @@ bool SPUThread::is_paused() const
return false; 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 void SPUThread::dump_info() const
{ {
CPUThread::dump_info(); CPUThread::dump_info();
} }
void SPUThread::task() void SPUThread::cpu_task()
{ {
std::fesetround(FE_TOWARDZERO); std::fesetround(FE_TOWARDZERO);
@ -251,7 +248,7 @@ void SPUThread::fast_call(u32 ls_addr)
try try
{ {
task(); cpu_task();
} }
catch (CPUThreadReturn) catch (CPUThreadReturn)
{ {

View File

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

View File

@ -1,14 +1,157 @@
#include "stdafx.h" #include "stdafx.h"
#include "IdManager.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 #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; // Function called after the successfull creation of an ID (does nothing by default, provide an overload)
inline void id_aux_initialize(void*)
template<typename T> constexpr inline const void* get_type_index()
{ {
return &type_info_t<T>::value; ;
} }
// default traits for any arbitrary type // Function called after the ID removal (does nothing by default, provide an overload)
template<typename T> struct id_traits 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) // Type-erased id_aux_* function type
static u32 in_id(u32 id) { return id; } using id_aux_func_t = void(*)(void*);
// convert mapped id to "public" id template<typename T>
static u32 out_id(u32 raw_id) { return raw_id; } 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; return id_aux_finalize(static_cast<T*>(ptr));
const std::type_info* info; };
const void* type_index;
template<typename T> inline id_data_t(std::shared_ptr<T> data) using id_type_index_t = const id_aux_func_t*;
: data(std::move(data))
, info(&typeid(T)) // Get a unique pointer to the on_remove value (will be unique for each type)
, type_index(get_type_index<T>()) 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 // ID Manager
@ -44,52 +65,55 @@ struct id_data_t final
// 0x80000000+ : reserved (may be used through id_traits specializations) // 0x80000000+ : reserved (may be used through id_traits specializations)
namespace idm namespace idm
{ {
extern std::mutex g_mutex; struct id_data_t final
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()
{ {
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; return g_tls_last_id;
} }
// reinitialize ID manager // Remove all objects
static void clear() void clear();
{
std::lock_guard<std::mutex> lock(g_mutex);
g_map.clear(); // Internal
g_last_raw_id = 0; 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 // Check if an ID exists and return its type or nullptr
template<typename T> static bool check(u32 id) 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); extern std::mutex g_mutex;
extern idm::map_t g_map;
const auto found = g_map.find(id_traits<T>::in_id(id)); 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); 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)); /**/) 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); 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)); g_map.emplace(raw_id, id_data_t(ptr));
@ -110,332 +134,302 @@ namespace idm
return nullptr; return nullptr;
} }
// add new ID of specified type with specified constructor arguments (returns id) // Add a 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, u32> make(Args&&... args) 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); if (auto ptr = add<T>(WRAP_EXPR(std::make_shared<T>(std::forward<Args>(args)...))))
for (u32 raw_id = g_last_raw_id; (raw_id = id_traits<T>::next_id(raw_id)); /**/)
{ {
if (g_map.find(raw_id) != g_map.end()) continue; id_aux_initialize(ptr.get());
return ptr;
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);
} }
throw EXCEPTION("Out of IDs"); return nullptr;
} }
// add new ID for an existing object provided (don't use for initial object creation) // Add a new ID of specified type with specified constructor arguments (returns id)
template<typename T> static u32 import(const std::shared_ptr<T>& ptr) 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); if (auto ptr = add<T>(WRAP_EXPR(std::make_shared<T>(std::forward<Args>(args)...))))
for (u32 raw_id = g_last_raw_id; (raw_id = id_traits<T>::next_id(raw_id)); /**/)
{ {
if (g_map.find(raw_id) != g_map.end()) continue; id_aux_initialize(ptr.get());
return get_last_id();
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);
} }
throw EXCEPTION("Out of IDs"); throw EXCEPTION("Out of IDs ('%s')", typeid(T).name());
} }
// get ID of specified type // Add a new ID for an existing object provided (returns new id)
template<typename T> static std::shared_ptr<T> get(u32 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 (add<T>(WRAP_EXPR(ptr)))
if (found == g_map.end() || found->second.type_index != get_type_index<T>())
{ {
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) // Internal
template<typename T> static std::vector<std::shared_ptr<T>> get_all() std::shared_ptr<void> get(u32 in_id, id_type_index_t type);
{
std::lock_guard<std::mutex> lock(g_mutex);
// 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; std::vector<std::shared_ptr<T>> result;
const auto type = get_type_index<T>(); for (auto& id : get_all(get_id_type_index<T>()))
for (auto& v : g_map)
{ {
if (v.second.type_index == type) result.emplace_back(std::static_pointer_cast<T>(id.second.data));
{
result.emplace_back(std::static_pointer_cast<T>(v.second.data));
}
} }
return result; return result;
} }
// remove ID created with type T std::shared_ptr<void> withdraw(u32 in_id, id_type_index_t type);
template<typename T> static bool remove(u32 id)
// Remove the ID created with type T
template<typename T>
bool remove(u32 id)
{ {
std::lock_guard<std::mutex> lock(g_mutex); if (auto ptr = withdraw(id_traits<T>::in_id(id), get_id_type_index<T>()))
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>())
{ {
return false; id_aux_finalize(static_cast<T*>(ptr.get()));
return true;
} }
g_map.erase(found); return false;
return true;
} }
// remove ID created with type T and return the object // Remove the ID created with type T and return it
template<typename T> static std::shared_ptr<T> withdraw(u32 id) template<typename T>
std::shared_ptr<T> withdraw(u32 id)
{ {
std::lock_guard<std::mutex> lock(g_mutex); if (auto ptr = std::static_pointer_cast<T>(withdraw(id_traits<T>::in_id(id), get_id_type_index<T>())))
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>())
{ {
return nullptr; id_aux_finalize(ptr.get());
return ptr;
} }
auto ptr = std::static_pointer_cast<T>(found->second.data); return nullptr;
g_map.erase(found);
return ptr;
} }
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); return get_count(get_id_type_index<T>());
u32 result = 0;
const auto type = get_type_index<T>();
for (auto& v : g_map)
{
if (v.second.type_index == type)
{
result++;
}
}
return result;
} }
// get sorted ID list of specified type // Get sorted list of all IDs of specified type
template<typename T> static std::set<u32> get_set() template<typename T>
std::set<u32> get_set()
{ {
std::lock_guard<std::mutex> lock(g_mutex);
std::set<u32> result; std::set<u32> result;
const auto type = get_type_index<T>(); for (auto& id : get_all(get_id_type_index<T>()))
for (auto& v : g_map)
{ {
if (v.second.type_index == type) result.emplace(id_traits<T>::out_id(id.first));
{
result.insert(id_traits<T>::out_id(v.first));
}
} }
return result; return result;
} }
// get sorted ID map (ID value -> ID data) of specified type // Get sorted map (ID value -> ID data) of all IDs of specified type
template<typename T> static std::map<u32, std::shared_ptr<T>> get_map() 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; std::map<u32, std::shared_ptr<T>> result;
const auto type = get_type_index<T>(); for (auto& id : get_all(get_id_type_index<T>()))
for (auto& v : g_map)
{ {
if (v.second.type_index == type) result[id_traits<T>::out_id(id.first)] = std::static_pointer_cast<T>(id.second.data);
{
result[id_traits<T>::out_id(v.first)] = std::static_pointer_cast<T>(v.second.data);
}
} }
return result; return result;
} }
}; }
// Fixed Object Manager // Fixed Object Manager
// allows to manage shared objects of any specified type, but only one object per type; // allows to manage shared objects of any specified type, but only one object per type;
// object are deleted when the emulation is stopped // object are deleted when the emulation is stopped
namespace fxm 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 // Internal (returns old and new pointers)
static void clear() 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); 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) // Create the object (returns nullptr if it already exists)
template<typename T, typename... Args> static std::enable_if_t<std::is_constructible<T, Args...>::value, std::shared_ptr<T>> make(Args&&... args) 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>(); if (pair.second)
const auto found = g_map.find(index);
// only if object of this type doesn't exist
if (found == g_map.end())
{ {
auto ptr = std::make_shared<T>(std::forward<Args>(args)...); id_aux_initialize(pair.second.get());
return std::move(pair.second);
g_map.emplace(index, ptr);
return ptr;
} }
return nullptr; return nullptr;
} }
// add fixed object of specified type, replacing previous one if it exists // Create the object unconditionally (old object will be removed 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) 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)...); if (pair.first)
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())
{ {
g_map.emplace(index, ptr); id_aux_finalize(pair.first.get());
return ptr;
} }
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; 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 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; //using reservation_mutex_t = std::mutex;
class reservation_mutex_t 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::condition_variable m_cv;
std::mutex m_mutex; std::mutex m_mutex;
@ -105,13 +105,11 @@ namespace vm
never_inline void lock() never_inline void lock()
{ {
auto owner = get_current_thread_ctrl();
std::unique_lock<std::mutex> lock(m_mutex, std::defer_lock); 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"); throw EXCEPTION("Deadlock");
} }
@ -125,26 +123,33 @@ namespace vm
m_cv.wait_for(lock, std::chrono::milliseconds(1)); m_cv.wait_for(lock, std::chrono::milliseconds(1));
} }
m_owner = std::this_thread::get_id();
do_notify = true; do_notify = true;
} }
never_inline void unlock() 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"); throw EXCEPTION("Lost lock");
} }
if (do_notify) if (do_notify)
{ {
std::lock_guard<std::mutex> lock(m_mutex);
m_cv.notify_one(); 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_addr = 0;
u32 g_reservation_size = 0; u32 g_reservation_size = 0;
@ -352,7 +357,7 @@ namespace vm
void start() void start()
{ {
// start notification thread // start notification thread
named_thread_t(COPY_EXPR("vm::start thread"), []() thread_ctrl::spawn(PURE_EXPR("vm::start thread"s), []()
{ {
while (!Emu.IsStopped()) while (!Emu.IsStopped())
{ {
@ -364,8 +369,7 @@ namespace vm
std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::sleep_for(std::chrono::milliseconds(1));
} }
});
}).detach();
} }
void _reservation_set(u32 addr, bool no_access = false) 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); 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 // break the reservation
g_tls_did_break_reservation = g_reservation_owner && _reservation_break(g_reservation_addr); g_tls_did_break_reservation = g_reservation_owner && _reservation_break(g_reservation_addr);
@ -451,7 +452,7 @@ namespace vm
// set additional information // set additional information
g_reservation_addr = addr; g_reservation_addr = addr;
g_reservation_size = size; g_reservation_size = size;
g_reservation_owner = get_current_thread_ctrl(); g_reservation_owner = thread_ctrl::get_current();
// copy data // copy data
std::memcpy(data, vm::base(addr), size); 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); 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 // atomic update failed
return false; return false;
@ -522,7 +523,7 @@ namespace vm
return true; return true;
} }
bool reservation_test(const thread_ctrl_t* current) bool reservation_test(const thread_ctrl* current)
{ {
const auto owner = g_reservation_owner; const auto owner = g_reservation_owner;
@ -535,7 +536,7 @@ namespace vm
{ {
std::lock_guard<reservation_mutex_t> lock(g_reservation_mutex); 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); g_tls_did_break_reservation = _reservation_break(g_reservation_addr);
} }
@ -556,7 +557,7 @@ namespace vm
g_tls_did_break_reservation = false; g_tls_did_break_reservation = false;
// check and possibly break previous reservation // 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) if (g_reservation_owner)
{ {
@ -572,7 +573,7 @@ namespace vm
// set additional information // set additional information
g_reservation_addr = addr; g_reservation_addr = addr;
g_reservation_size = size; g_reservation_size = size;
g_reservation_owner = get_current_thread_ctrl(); g_reservation_owner = thread_ctrl::get_current();
// may not be necessary // may not be necessary
_mm_mfence(); _mm_mfence();
@ -783,13 +784,13 @@ namespace vm
if (!block) 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; return;
} }
if (!block->dealloc(addr)) 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; return;
} }
} }

View File

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

View File

@ -232,11 +232,11 @@ D3D12GSRender::~D3D12GSRender()
release_d2d_structures(); 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) switch (cmd)
{ {

View File

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

View File

@ -1046,9 +1046,9 @@ void GLGSRender::end()
rsx::thread::end(); rsx::thread::end();
} }
void GLGSRender::oninit_thread() void GLGSRender::on_init_thread()
{ {
GSRender::oninit_thread(); GSRender::on_init_thread();
gl::init(); gl::init();
LOG_NOTICE(Log::RSX, (const char*)glGetString(GL_VERSION)); LOG_NOTICE(Log::RSX, (const char*)glGetString(GL_VERSION));
@ -1071,7 +1071,7 @@ void GLGSRender::oninit_thread()
m_vao.element_array_buffer = m_ebo; m_vao.element_array_buffer = m_ebo;
} }
void GLGSRender::onexit_thread() void GLGSRender::on_exit()
{ {
glDisable(GL_VERTEX_PROGRAM_POINT_SIZE); 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 } { 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); auto found = g_gl_method_tbl.find(cmd);

View File

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

View File

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

View File

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

View File

@ -31,11 +31,12 @@ GSRender::~GSRender()
if (m_frame) if (m_frame)
{ {
m_frame->hide();
m_frame->close(); m_frame->close();
} }
} }
void GSRender::oninit() void GSRender::on_init()
{ {
if (m_frame) if (m_frame)
{ {
@ -43,7 +44,7 @@ void GSRender::oninit()
} }
} }
void GSRender::oninit_thread() void GSRender::on_init_thread()
{ {
if (m_frame) 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) void GSRender::flip(int buffer)
{ {
if (m_frame) if (m_frame)
{
m_frame->flip(m_context); m_frame->flip(m_context);
}
} }

View File

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

View File

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

View File

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

View File

@ -689,7 +689,7 @@ namespace rsx
static void wrapper(thread *rsx, u32 arg) static void wrapper(thread *rsx, u32 arg)
{ {
// try process using gpu // try process using gpu
if (rsx->domethod(id, arg)) if (rsx->do_method(id, arg))
{ {
if (rsx->capture_current_frame && id == NV4097_CLEAR_SURFACE) if (rsx->capture_current_frame && id == NV4097_CLEAR_SURFACE)
rsx->capture_frame("clear"); rsx->capture_frame("clear");
@ -974,25 +974,23 @@ namespace rsx
capture_frame("Draw " + std::to_string(vertex_draw_count)); capture_frame("Draw " + std::to_string(vertex_draw_count));
} }
void thread::task() void thread::on_task()
{ {
u8 inc; on_init_thread();
LOG_NOTICE(RSX, "RSX thread started");
oninit_thread(); reset();
last_flip_time = get_system_time() - 1000000; 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(); const u64 start_time = get_system_time();
vblank_count = 0; vblank_count = 0;
while (joinable()) // TODO: exit condition
while (!Emu.IsStopped())
{ {
CHECK_EMU_STATUS;
if (get_system_time() - start_time > vblank_count * 1000000 / 60) if (get_system_time() - start_time > vblank_count * 1000000 / 60)
{ {
vblank_count++; vblank_count++;
@ -1012,107 +1010,87 @@ namespace rsx
} }
}); });
reset(); // TODO: exit condition
while (true)
try
{ {
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 std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack
if (Emu.IsStopped()) continue;
{
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;
} }
}
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 void thread::fill_scale_offset_data(void *buffer, bool is_d3d) const noexcept
@ -1240,8 +1218,8 @@ namespace rsx
m_used_gcm_commands.clear(); m_used_gcm_commands.clear();
oninit(); on_init();
named_thread_t::start(WRAP_EXPR("rsx::thread"), WRAP_EXPR(task())); start();
} }
u32 thread::ReadIO32(u32 addr) 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: protected:
std::stack<u32> m_call_stack; std::stack<u32> m_call_stack;
@ -327,19 +327,20 @@ namespace rsx
protected: protected:
virtual ~thread() {} virtual ~thread() {}
virtual void on_task() override;
public: public:
virtual std::string get_name() const override;
virtual void begin(); virtual void begin();
virtual void end(); virtual void end();
virtual void oninit() = 0; virtual void on_init() = 0;
virtual void oninit_thread() = 0; virtual void on_init_thread() = 0;
virtual void onexit_thread() = 0; virtual bool do_method(u32 cmd, u32 value) { return false; }
virtual bool domethod(u32 cmd, u32 value) { return false; }
virtual void flip(int buffer) = 0; virtual void flip(int buffer) = 0;
virtual u64 timestamp() const; virtual u64 timestamp() const;
void task();
/** /**
* Fill buffer with 4x4 scale offset matrix. * Fill buffer with 4x4 scale offset matrix.
* Vertex shader's position is to be multiplied by this 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; AudioConfig g_audio;
std::shared_ptr<thread_ctrl> g_audio_thread;
s32 cellAudioInit() s32 cellAudioInit()
{ {
cellAudio.Warning("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.buffer), 0, AUDIO_PORT_OFFSET * AUDIO_PORT_COUNT);
std::memset(vm::base(g_audio.indexes), 0, sizeof32(u64) * 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 // 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(); const bool do_dump = rpcs3::config.audio.dump_to_file.value();
AudioDumper m_dump; AudioDumper m_dump;
@ -81,9 +75,9 @@ s32 cellAudioInit()
squeue_t<float*, BUFFER_NUM - 1> out_queue; 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(); Emu.GetAudioManager().GetAudioOut().Init();
@ -141,7 +135,7 @@ s32 cellAudioInit()
{ {
if (Emu.IsPaused()) if (Emu.IsPaused())
{ {
g_audio.thread.cv.wait_for(lock, std::chrono::milliseconds(1)); std::this_thread::sleep_for(1ms); // hack
continue; continue;
} }
@ -155,7 +149,7 @@ s32 cellAudioInit()
const u64 expected_time = g_audio.counter * AUDIO_SAMPLES * 1000000 / 48000; const u64 expected_time = g_audio.counter * AUDIO_SAMPLES * 1000000 / 48000;
if (expected_time >= time_pos) if (expected_time >= time_pos)
{ {
g_audio.thread.cv.wait_for(lock, std::chrono::milliseconds(1)); std::this_thread::sleep_for(1ms); // hack
continue; continue;
} }
@ -368,6 +362,8 @@ s32 cellAudioInit()
LV2_LOCK; LV2_LOCK;
std::lock_guard<std::mutex> lock(g_audio.mutex);
for (auto key : g_audio.keys) for (auto key : g_audio.keys)
{ {
if (const auto queue = Emu.GetEventManager().GetEventQueue(key)) if (const auto queue = Emu.GetEventManager().GetEventQueue(key))
@ -419,8 +415,10 @@ s32 cellAudioQuit()
return CELL_AUDIO_ERROR_NOT_INIT; 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); g_audio.state.exchange(AUDIO_STATE_NOT_INITIALIZED);
return CELL_OK; 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) // 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; *stamp = g_audio.start_time + Emu.GetPauseTime() + (port.counter + (tag - port.tag)) * 256000000 / 48000;
return CELL_OK; return CELL_OK;
@ -686,8 +682,6 @@ s32 cellAudioGetPortBlockTag(u32 portNum, u64 blockNo, vm::ptr<u64> tag)
return CELL_AUDIO_ERROR_PARAM; return CELL_AUDIO_ERROR_PARAM;
} }
std::lock_guard<std::mutex> lock(g_audio.thread.mutex);
u64 tag_base = port.tag; u64 tag_base = port.tag;
if (tag_base % port.block > blockNo) if (tag_base % port.block > blockNo)
{ {
@ -780,7 +774,7 @@ s32 cellAudioSetNotifyEventQueue(u64 key)
return CELL_AUDIO_ERROR_NOT_INIT; 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 for (auto k : g_audio.keys) // check for duplicates
{ {
@ -813,7 +807,7 @@ s32 cellAudioRemoveNotifyEventQueue(u64 key)
return CELL_AUDIO_ERROR_NOT_INIT; 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++) for (auto i = g_audio.keys.begin(); i != g_audio.keys.end(); i++)
{ {

View File

@ -99,9 +99,10 @@ enum AudioPortState : u32
struct AudioPortConfig struct AudioPortConfig
{ {
std::mutex mutex;
atomic_t<AudioPortState> state; atomic_t<AudioPortState> state;
std::mutex mutex;
u32 channel; u32 channel;
u32 block; u32 block;
u64 attr; u64 attr;
@ -124,7 +125,8 @@ struct AudioPortConfig
struct AudioConfig final // custom structure struct AudioConfig final // custom structure
{ {
atomic_t<AudioState> state; atomic_t<AudioState> state;
named_thread_t thread;
std::mutex mutex;
AudioPortConfig ports[AUDIO_PORT_COUNT]; AudioPortConfig ports[AUDIO_PORT_COUNT];
u32 buffer; // 1 MB memory for audio ports u32 buffer; // 1 MB memory for audio ports
@ -133,16 +135,6 @@ struct AudioConfig final // custom structure
u64 start_time; u64 start_time;
std::vector<u64> keys; std::vector<u64> keys;
AudioConfig() = default;
~AudioConfig()
{
if (thread.joinable())
{
thread.join();
}
}
u32 open_port() u32 open_port()
{ {
for (u32 i = 0; i < AUDIO_PORT_COUNT; i++) 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_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); std::unique_lock<std::mutex> lock(file->mutex);
@ -572,7 +572,7 @@ s32 cellFsStReadStop(u32 fd)
} }
file->cv.notify_all(); file->cv.notify_all();
file->st_thread.join(); file->st_thread->join();
return CELL_OK; 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); 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; 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); 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; 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; 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; 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); 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) while (dlg->state == MsgDialogState::Open && get_system_time() < wait_until)
{ {
CHECK_EMU_STATUS; if (Emu.IsStopped()) return;
std::this_thread::sleep_for(1ms); std::this_thread::sleep_for(1ms);
} }
dlg->on_close(CELL_MSGDIALOG_BUTTON_NONE); dlg->on_close(CELL_MSGDIALOG_BUTTON_NONE);
});
}).detach();
return CELL_OK; 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)); 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); vm::dealloc_verbose_nothrow(addr, vm::main);
}); });

View File

@ -43,8 +43,14 @@ void sys_ppu_thread_exit(PPUThread& ppu, u64 val)
// (deallocate TLS) // (deallocate TLS)
// ... // ...
// call the syscall if (ppu.hle_code == 0xaff080a4)
_sys_ppu_thread_exit(ppu, val); {
// 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; 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 // try to reown mutex and exit if timed out
if (!cond->mutex->owner) if (!cond->mutex->owner)
{ {
cond->mutex->owner = ppu.shared_from_this(); cond->mutex->owner = std::static_pointer_cast<CPUThread>(ppu.shared_from_this());
break; break;
} }

View File

@ -184,7 +184,7 @@ struct lv2_file_t
u64 st_trans_rate; u64 st_trans_rate;
bool st_copyless; bool st_copyless;
named_thread_t st_thread; std::shared_ptr<thread_ctrl> st_thread;
u32 st_buffer; u32 st_buffer;
u64 st_read_size; 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 // lock immediately if not locked
if (!mutex->owner) if (!mutex->owner)
{ {
mutex->owner = ppu.shared_from_this(); mutex->owner = std::static_pointer_cast<CPUThread>(ppu.shared_from_this());
return CELL_OK; return CELL_OK;
} }
@ -207,7 +207,7 @@ s32 sys_mutex_trylock(PPUThread& ppu, u32 mutex_id)
} }
// own the mutex if free // 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; 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) if (!ppu.is_joinable)
{ {
idm::remove<PPUThread>(ppu.get_id()); 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() 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) 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; return CELL_OK;
} }
@ -300,7 +300,7 @@ s32 sys_rwlock_trywlock(PPUThread& ppu, u32 rw_lock_id)
return CELL_EBUSY; return CELL_EBUSY;
} }
rwlock->writer = ppu.shared_from_this(); rwlock->writer = std::static_pointer_cast<CPUThread>(ppu.shared_from_this());
return CELL_OK; return CELL_OK;
} }

View File

@ -13,55 +13,55 @@ SysCallBase sys_timer("sys_timer");
extern u64 get_system_time(); extern u64 get_system_time();
lv2_timer_t::lv2_timer_t() void lv2_timer_t::on_task()
: start(0)
, period(0)
, state(SYS_TIMER_STATE_STOP)
{ {
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) if (period && queue)
{ {
queue->push(lv2_lock, source, data1, data2, start); expire += period; // set next expiration time
}
if (period && queue) continue; // hack: check again
{ }
start += period; // set next expiration time else
{
continue; // hack: check again state = SYS_TIMER_STATE_STOP; // stop if oneshot or the event port was disconnected (TODO: is it correct?)
}
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) 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; return CELL_ESRCH;
} }
info->next_expiration_time = timer->start; info->next_expiration_time = timer->expire;
info->period = timer->period; info->period = timer->period;
info->timer_state = timer->state; 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?) // 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->period = period;
timer->state = SYS_TIMER_STATE_RUN; timer->state = SYS_TIMER_STATE_RUN;
timer->thread.cv.notify_one(); timer->cv.notify_one();
return CELL_OK; 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; LV2_LOCK;
const auto timer = idm::get<lv2_timer_t>(timer_id); const auto timer(idm::get<lv2_timer_t>(timer_id));
const auto queue = idm::get<lv2_event_queue_t>(queue_id); const auto queue(idm::get<lv2_event_queue_t>(queue_id));
if (!timer || !queue) if (!timer || !queue)
{ {

View File

@ -19,22 +19,35 @@ struct sys_timer_information_t
be_t<u32> pad; 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 std::weak_ptr<lv2_event_queue_t> port; // event queue
u64 source; // event source u64 source; // event source
u64 data1; // event arg 1 u64 data1; // event arg 1
u64 data2; // event arg 2 u64 data2; // event arg 2
u64 start; // next expiration time u64 expire = 0; // next expiration time
u64 period; // period (oneshot if 0) u64 period = 0; // period (oneshot if 0)
std::atomic<u32> state; // timer state std::atomic<u32> state{ SYS_TIMER_STATE_RUN }; // timer state
named_thread_t thread; // timer thread
lv2_timer_t();
~lv2_timer_t();
}; };
s32 sys_timer_create(vm::ptr<u32> timer_id); s32 sys_timer_create(vm::ptr<u32> timer_id);

View File

@ -125,19 +125,28 @@ public:
m_cb.send_dbg_command(cmd, thread); m_cb.send_dbg_command(cmd, thread);
} }
// returns a future object associated with the result of the function called from the GUI 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 template<typename F>
std::future<void> CallAfter(F&& func) const
{ {
// create task // Make "shared" promise to workaround std::function limitation
auto task = std::make_shared<std::packaged_task<RT()>>(std::forward<F>(func)); auto spr = std::make_shared<std::promise<void>>();
// get future // Get future
std::future<RT> future = task->get_future(); std::future<void> future = spr->get_future();
// run asynchronously in GUI thread // Run asynchronously in GUI thread
m_cb.call_after([=] 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; 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 // return 32 bit .size() for container
template<typename T> inline auto size32(const T& container) -> decltype(static_cast<u32>(container.size())) 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 // return 32 bit size for an array
template<typename T, std::size_t Size> constexpr u32 size32(const T(&)[Size]) 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 WRAP_EXPR(expr) [&]{ return expr; }
#define COPY_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 EXCEPTION(text, ...) fmt::exception(__FILE__, __LINE__, __FUNCTION__, text, ##__VA_ARGS__)
#define VM_CAST(value) vm::impl_cast(value, __FILE__, __LINE__, __FUNCTION__) #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) #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_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) #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; template<typename T> struct id_traits;
#define _PRGNAME_ "RPCS3" #define _PRGNAME_ "RPCS3"