mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-03 21:41:26 +12:00
ThreadBase rewritten (wip)
This commit is contained in:
parent
b7a320fbbd
commit
3aefa2b4e1
85 changed files with 1960 additions and 2183 deletions
|
@ -1108,7 +1108,7 @@ const PVOID exception_handler = (atexit([]{ RemoveVectoredExceptionHandler(excep
|
|||
|
||||
if (pExp->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION &&
|
||||
(u32)addr64 == addr64 &&
|
||||
GetCurrentNamedThread() &&
|
||||
get_current_thread_ctrl() &&
|
||||
handle_access_violation((u32)addr64, is_writing, pExp->ContextRecord))
|
||||
{
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
|
@ -1119,6 +1119,13 @@ const PVOID exception_handler = (atexit([]{ RemoveVectoredExceptionHandler(excep
|
|||
}
|
||||
}));
|
||||
|
||||
const auto exception_filter = SetUnhandledExceptionFilter([](PEXCEPTION_POINTERS pExp) -> LONG
|
||||
{
|
||||
_se_translator(pExp->ExceptionRecord->ExceptionCode, pExp);
|
||||
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
});
|
||||
|
||||
#else
|
||||
|
||||
void signal_handler(int sig, siginfo_t* info, void* uct)
|
||||
|
@ -1131,7 +1138,7 @@ void signal_handler(int sig, siginfo_t* info, void* uct)
|
|||
const bool is_writing = ((ucontext_t*)uct)->uc_mcontext.gregs[REG_ERR] & 0x2;
|
||||
#endif
|
||||
|
||||
if ((u32)addr64 == addr64 && GetCurrentNamedThread())
|
||||
if ((u32)addr64 == addr64 && get_current_thread_ctrl())
|
||||
{
|
||||
if (handle_access_violation((u32)addr64, is_writing, (ucontext_t*)uct))
|
||||
{
|
||||
|
@ -1158,19 +1165,23 @@ const int sigaction_result = []() -> int
|
|||
|
||||
#endif
|
||||
|
||||
thread_local NamedThreadBase* g_tls_this_thread = nullptr;
|
||||
std::atomic<u32> g_thread_count(0);
|
||||
thread_local thread_ctrl_t* g_tls_this_thread = nullptr;
|
||||
|
||||
NamedThreadBase* GetCurrentNamedThread()
|
||||
const thread_ctrl_t* get_current_thread_ctrl()
|
||||
{
|
||||
return g_tls_this_thread;
|
||||
}
|
||||
|
||||
void SetCurrentNamedThread(NamedThreadBase* value)
|
||||
std::string thread_ctrl_t::get_name() const
|
||||
{
|
||||
return name();
|
||||
}
|
||||
|
||||
void thread_ctrl_t::set_current()
|
||||
{
|
||||
const auto old_value = g_tls_this_thread;
|
||||
|
||||
if (old_value == value)
|
||||
if (old_value == this)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -1180,76 +1191,82 @@ void SetCurrentNamedThread(NamedThreadBase* value)
|
|||
vm::reservation_free();
|
||||
}
|
||||
|
||||
if (value && value->m_tls_assigned.exchange(true))
|
||||
if (true && assigned.exchange(true))
|
||||
{
|
||||
LOG_ERROR(GENERAL, "Thread '%s' was already assigned to g_tls_this_thread of another thread", value->GetThreadName());
|
||||
LOG_ERROR(GENERAL, "Thread '%s' was already assigned to g_tls_this_thread of another thread", get_name());
|
||||
g_tls_this_thread = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_tls_this_thread = value;
|
||||
g_tls_this_thread = this;
|
||||
}
|
||||
|
||||
if (old_value)
|
||||
{
|
||||
old_value->m_tls_assigned = false;
|
||||
old_value->assigned = false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string NamedThreadBase::GetThreadName() const
|
||||
thread_t::thread_t(std::function<std::string()> name, std::function<void()> func)
|
||||
{
|
||||
return m_name;
|
||||
start(std::move(name), func);
|
||||
}
|
||||
|
||||
void NamedThreadBase::SetThreadName(const std::string& name)
|
||||
thread_t::~thread_t()
|
||||
{
|
||||
m_name = name;
|
||||
}
|
||||
|
||||
void NamedThreadBase::WaitForAnySignal(u64 time) // wait for Notify() signal or sleep
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_signal_mtx);
|
||||
m_signal_cv.wait_for(lock, std::chrono::milliseconds(time));
|
||||
}
|
||||
|
||||
void NamedThreadBase::Notify() // wake up waiting thread or nothing
|
||||
{
|
||||
m_signal_cv.notify_one();
|
||||
}
|
||||
|
||||
ThreadBase::ThreadBase(const std::string& name)
|
||||
: NamedThreadBase(name)
|
||||
, m_executor(nullptr)
|
||||
, m_destroy(false)
|
||||
, m_alive(false)
|
||||
{
|
||||
}
|
||||
|
||||
ThreadBase::~ThreadBase()
|
||||
{
|
||||
if(IsAlive())
|
||||
Stop(false);
|
||||
|
||||
delete m_executor;
|
||||
m_executor = nullptr;
|
||||
}
|
||||
|
||||
void ThreadBase::Start()
|
||||
{
|
||||
if(m_executor) Stop();
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_main_mutex);
|
||||
|
||||
m_destroy = false;
|
||||
m_alive = true;
|
||||
|
||||
m_executor = new std::thread([this]()
|
||||
if (m_thread)
|
||||
{
|
||||
SetCurrentThreadDebugName(GetThreadName().c_str());
|
||||
if (g_tls_this_thread != m_thread.get())
|
||||
{
|
||||
m_thread->m_thread.join();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_thread->m_thread.detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string thread_t::get_name() const
|
||||
{
|
||||
if (!m_thread)
|
||||
{
|
||||
throw EXCEPTION("Invalid thread");
|
||||
}
|
||||
|
||||
if (!m_thread->name)
|
||||
{
|
||||
throw EXCEPTION("Invalid name getter");
|
||||
}
|
||||
|
||||
return m_thread->name();
|
||||
}
|
||||
|
||||
std::atomic<u32> g_thread_count{ 0 };
|
||||
|
||||
void thread_t::start(std::function<std::string()> name, std::function<void()> func)
|
||||
{
|
||||
if (m_thread)
|
||||
{
|
||||
throw EXCEPTION("Thread already exists");
|
||||
}
|
||||
|
||||
// create new ctrl and assign it
|
||||
auto ctrl = std::make_shared<thread_ctrl_t>(std::move(name));
|
||||
|
||||
// start thread
|
||||
ctrl->m_thread = std::thread([ctrl, func]()
|
||||
{
|
||||
g_thread_count++;
|
||||
|
||||
SetCurrentThreadDebugName(ctrl->get_name().c_str());
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
auto old_se_translator = _set_se_translator(_se_translator);
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
auto old_se_translator = _set_se_translator(_se_translator);
|
||||
if (!exception_handler)
|
||||
if (!exception_handler || !exception_filter)
|
||||
{
|
||||
LOG_ERROR(GENERAL, "exception_handler not set");
|
||||
return;
|
||||
|
@ -1262,238 +1279,139 @@ void ThreadBase::Start()
|
|||
}
|
||||
#endif
|
||||
|
||||
SetCurrentNamedThread(this);
|
||||
g_thread_count++;
|
||||
// error handler
|
||||
const auto error = [&](const char* text)
|
||||
{
|
||||
log_message(GENERAL, Emu.IsStopped() ? Log::Severity::Warning : Log::Severity::Error, "Exception: %s", text);
|
||||
Emu.Pause();
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
Task();
|
||||
}
|
||||
catch (const char* e)
|
||||
{
|
||||
LOG_ERROR(GENERAL, "Exception: %s", e);
|
||||
DumpInformation();
|
||||
Emu.Pause();
|
||||
}
|
||||
catch (const std::string& e)
|
||||
{
|
||||
LOG_ERROR(GENERAL, "Exception: %s", e);
|
||||
DumpInformation();
|
||||
Emu.Pause();
|
||||
}
|
||||
ctrl->set_current();
|
||||
|
||||
m_alive = false;
|
||||
SetCurrentNamedThread(nullptr);
|
||||
g_thread_count--;
|
||||
if (Ini.HLELogging.GetValue())
|
||||
{
|
||||
LOG_NOTICE(GENERAL, "Thread started");
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
_set_se_translator(old_se_translator);
|
||||
#endif
|
||||
});
|
||||
}
|
||||
|
||||
void ThreadBase::Stop(bool wait, bool send_destroy)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_main_mutex);
|
||||
|
||||
if (send_destroy)
|
||||
m_destroy = true;
|
||||
|
||||
if(!m_executor)
|
||||
return;
|
||||
|
||||
if(wait && m_executor->joinable() && m_alive)
|
||||
{
|
||||
m_executor->join();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_executor->detach();
|
||||
}
|
||||
|
||||
delete m_executor;
|
||||
m_executor = nullptr;
|
||||
}
|
||||
|
||||
bool ThreadBase::Join() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_main_mutex);
|
||||
if(m_executor->joinable() && m_alive && m_executor != nullptr)
|
||||
{
|
||||
m_executor->join();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ThreadBase::IsAlive() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_main_mutex);
|
||||
return m_alive;
|
||||
}
|
||||
|
||||
bool ThreadBase::TestDestroy() const
|
||||
{
|
||||
return m_destroy;
|
||||
}
|
||||
|
||||
thread_t::thread_t(const std::string& name, bool autojoin, std::function<void()> func)
|
||||
: m_name(name)
|
||||
, m_state(TS_NON_EXISTENT)
|
||||
, m_autojoin(autojoin)
|
||||
{
|
||||
start(func);
|
||||
}
|
||||
|
||||
thread_t::thread_t(const std::string& name, std::function<void()> func)
|
||||
: m_name(name)
|
||||
, m_state(TS_NON_EXISTENT)
|
||||
, m_autojoin(false)
|
||||
{
|
||||
start(func);
|
||||
}
|
||||
|
||||
thread_t::thread_t(const std::string& name)
|
||||
: m_name(name)
|
||||
, m_state(TS_NON_EXISTENT)
|
||||
, m_autojoin(false)
|
||||
{
|
||||
}
|
||||
|
||||
thread_t::thread_t()
|
||||
: m_state(TS_NON_EXISTENT)
|
||||
, m_autojoin(false)
|
||||
{
|
||||
}
|
||||
|
||||
void thread_t::set_name(const std::string& name)
|
||||
{
|
||||
m_name = name;
|
||||
}
|
||||
|
||||
thread_t::~thread_t()
|
||||
{
|
||||
if (m_state == TS_JOINABLE)
|
||||
{
|
||||
if (m_autojoin)
|
||||
{
|
||||
m_thr.join();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_thr.detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void thread_t::start(std::function<void()> func)
|
||||
{
|
||||
if (m_state.exchange(TS_NON_EXISTENT) == TS_JOINABLE)
|
||||
{
|
||||
m_thr.join(); // forcefully join previously created thread
|
||||
}
|
||||
|
||||
std::string name = m_name;
|
||||
m_thr = std::thread([func, name]()
|
||||
{
|
||||
SetCurrentThreadDebugName(name.c_str());
|
||||
|
||||
#ifdef _WIN32
|
||||
auto old_se_translator = _set_se_translator(_se_translator);
|
||||
#endif
|
||||
|
||||
NamedThreadBase info(name);
|
||||
SetCurrentNamedThread(&info);
|
||||
g_thread_count++;
|
||||
|
||||
if (Ini.HLELogging.GetValue())
|
||||
{
|
||||
LOG_NOTICE(HLE, name + " started");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
func();
|
||||
}
|
||||
catch (const char* e)
|
||||
{
|
||||
LOG_ERROR(GENERAL, "Exception: %s", e);
|
||||
Emu.Pause();
|
||||
error(e);
|
||||
}
|
||||
catch (const std::string& e)
|
||||
{
|
||||
LOG_ERROR(GENERAL, "Exception: %s", e.c_str());
|
||||
Emu.Pause();
|
||||
error(e.c_str());
|
||||
}
|
||||
catch (const fmt::exception& e)
|
||||
{
|
||||
error(e);
|
||||
}
|
||||
|
||||
if (Emu.IsStopped())
|
||||
{
|
||||
LOG_NOTICE(HLE, name + " aborted");
|
||||
LOG_NOTICE(GENERAL, "Thread aborted");
|
||||
}
|
||||
else if (Ini.HLELogging.GetValue())
|
||||
{
|
||||
LOG_NOTICE(HLE, name + " ended");
|
||||
LOG_NOTICE(GENERAL, "Thread ended");
|
||||
}
|
||||
|
||||
SetCurrentNamedThread(nullptr);
|
||||
//ctrl->set_current(false);
|
||||
|
||||
g_thread_count--;
|
||||
|
||||
#ifdef _WIN32
|
||||
ctrl->joinable = false;
|
||||
|
||||
ctrl->join_cv.notify_all();
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
_set_se_translator(old_se_translator);
|
||||
#endif
|
||||
});
|
||||
|
||||
if (m_state.exchange(TS_JOINABLE) == TS_JOINABLE)
|
||||
{
|
||||
assert(!"thread_t::start() failed"); // probably started from another thread
|
||||
}
|
||||
// set
|
||||
m_thread = std::move(ctrl);
|
||||
}
|
||||
|
||||
void thread_t::detach()
|
||||
{
|
||||
if (m_state.exchange(TS_NON_EXISTENT) == TS_JOINABLE)
|
||||
if (!m_thread)
|
||||
{
|
||||
m_thr.detach();
|
||||
throw EXCEPTION("Invalid thread");
|
||||
}
|
||||
else
|
||||
|
||||
const auto ctrl = std::move(m_thread);
|
||||
|
||||
ctrl->m_thread.detach();
|
||||
}
|
||||
|
||||
void thread_t::join(std::unique_lock<std::mutex>& lock)
|
||||
{
|
||||
if (!m_thread)
|
||||
{
|
||||
assert(!"thread_t::detach() failed"); // probably joined or detached
|
||||
throw EXCEPTION("Invalid thread");
|
||||
}
|
||||
|
||||
if (g_tls_this_thread == m_thread.get())
|
||||
{
|
||||
throw EXCEPTION("Deadlock");
|
||||
}
|
||||
|
||||
const auto ctrl = std::move(m_thread);
|
||||
|
||||
// wait for completion
|
||||
while (ctrl->joinable)
|
||||
{
|
||||
CHECK_EMU_STATUS;
|
||||
|
||||
ctrl->join_cv.wait_for(lock, std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
ctrl->m_thread.join();
|
||||
}
|
||||
|
||||
void thread_t::join()
|
||||
{
|
||||
if (m_state.exchange(TS_NON_EXISTENT) == TS_JOINABLE)
|
||||
if (!m_thread)
|
||||
{
|
||||
m_thr.join();
|
||||
throw EXCEPTION("Invalid thread");
|
||||
}
|
||||
else
|
||||
|
||||
if (g_tls_this_thread == m_thread.get())
|
||||
{
|
||||
assert(!"thread_t::join() failed"); // probably joined or detached
|
||||
throw EXCEPTION("Deadlock");
|
||||
}
|
||||
|
||||
const auto ctrl = std::move(m_thread);
|
||||
|
||||
ctrl->m_thread.join();
|
||||
}
|
||||
|
||||
bool thread_t::joinable() const
|
||||
bool thread_t::is_current() const
|
||||
{
|
||||
//return m_thr.joinable();
|
||||
return m_state == TS_JOINABLE;
|
||||
if (!m_thread)
|
||||
{
|
||||
throw EXCEPTION("Invalid thread");
|
||||
}
|
||||
|
||||
return g_tls_this_thread == m_thread.get();
|
||||
}
|
||||
|
||||
bool waiter_map_t::is_stopped(u32 addr)
|
||||
void waiter_map_t::check_emu_status(u32 addr)
|
||||
{
|
||||
if (Emu.IsStopped())
|
||||
{
|
||||
LOG_WARNING(Log::HLE, "%s: waiter_op() aborted (addr=0x%x)", name.c_str(), addr);
|
||||
return true;
|
||||
throw EXCEPTION("Aborted (emulation stopped) (%s, addr=0x%x)", name, addr);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void waiter_map_t::notify(u32 addr)
|
||||
{
|
||||
// signal appropriate condition variable
|
||||
cv[get_hash(addr)].notify_all();
|
||||
// signal an appropriate condition variable
|
||||
cvs[get_hash(addr)].notify_all();
|
||||
}
|
||||
|
||||
const std::function<bool()> SQUEUE_ALWAYS_EXIT = [](){ return true; };
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue