2012-11-14 23:39:56 +00:00
|
|
|
#include "stdafx.h"
|
2014-10-10 21:33:57 +00:00
|
|
|
#include "Emu/System.h"
|
2014-09-11 19:18:19 +00:00
|
|
|
#include "Log.h"
|
2012-11-14 23:39:56 +00:00
|
|
|
#include "Thread.h"
|
|
|
|
|
2014-06-19 13:50:18 +00:00
|
|
|
thread_local NamedThreadBase* g_tls_this_thread = nullptr;
|
2014-06-20 11:00:36 +00:00
|
|
|
std::atomic<u32> g_thread_count(0);
|
2014-01-31 18:40:18 +00:00
|
|
|
|
|
|
|
NamedThreadBase* GetCurrentNamedThread()
|
|
|
|
{
|
2014-02-02 19:42:32 +00:00
|
|
|
return g_tls_this_thread;
|
2013-06-30 08:46:29 +00:00
|
|
|
}
|
|
|
|
|
2014-08-20 14:23:48 +00:00
|
|
|
void SetCurrentNamedThread(NamedThreadBase* value)
|
|
|
|
{
|
2014-09-14 22:17:24 +00:00
|
|
|
auto old_value = g_tls_this_thread;
|
|
|
|
|
|
|
|
if (old_value == value)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (value && value->m_tls_assigned.exchange(true))
|
|
|
|
{
|
|
|
|
LOG_ERROR(GENERAL, "Thread '%s' was already assigned to g_tls_this_thread of another thread", value->GetThreadName().c_str());
|
|
|
|
g_tls_this_thread = nullptr;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
g_tls_this_thread = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (old_value)
|
|
|
|
{
|
|
|
|
old_value->m_tls_assigned = false;
|
|
|
|
}
|
2014-08-20 14:23:48 +00:00
|
|
|
}
|
|
|
|
|
2014-01-31 18:40:18 +00:00
|
|
|
std::string NamedThreadBase::GetThreadName() const
|
2013-06-30 08:46:29 +00:00
|
|
|
{
|
2014-01-31 18:40:18 +00:00
|
|
|
return m_name;
|
2013-06-30 08:46:29 +00:00
|
|
|
}
|
|
|
|
|
2014-01-31 18:40:18 +00:00
|
|
|
void NamedThreadBase::SetThreadName(const std::string& name)
|
2012-11-14 23:39:56 +00:00
|
|
|
{
|
2014-01-31 18:40:18 +00:00
|
|
|
m_name = name;
|
|
|
|
}
|
2012-11-14 23:39:56 +00:00
|
|
|
|
2014-09-12 19:27:33 +00:00
|
|
|
void NamedThreadBase::WaitForAnySignal(u64 time) // wait for Notify() signal or sleep
|
2014-08-25 18:09:48 +00:00
|
|
|
{
|
|
|
|
std::unique_lock<std::mutex> lock(m_signal_mtx);
|
2014-09-12 19:27:33 +00:00
|
|
|
m_signal_cv.wait_for(lock, std::chrono::milliseconds(time));
|
2014-08-25 18:09:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void NamedThreadBase::Notify() // wake up waiting thread or nothing
|
|
|
|
{
|
|
|
|
m_signal_cv.notify_one();
|
|
|
|
}
|
|
|
|
|
2014-01-31 18:40:18 +00:00
|
|
|
ThreadBase::ThreadBase(const std::string& name)
|
|
|
|
: NamedThreadBase(name)
|
|
|
|
, m_executor(nullptr)
|
|
|
|
, m_destroy(false)
|
|
|
|
, m_alive(false)
|
|
|
|
{
|
2012-11-14 23:39:56 +00:00
|
|
|
}
|
|
|
|
|
2014-01-31 18:40:18 +00:00
|
|
|
ThreadBase::~ThreadBase()
|
2013-06-30 08:46:29 +00:00
|
|
|
{
|
2014-01-31 18:40:18 +00:00
|
|
|
if(IsAlive())
|
|
|
|
Stop(false);
|
2014-04-15 14:12:15 +00:00
|
|
|
|
2014-08-25 18:09:48 +00:00
|
|
|
delete m_executor;
|
|
|
|
m_executor = nullptr;
|
2013-06-30 08:46:29 +00:00
|
|
|
}
|
|
|
|
|
2014-01-31 18:40:18 +00:00
|
|
|
void ThreadBase::Start()
|
2013-06-30 08:46:29 +00:00
|
|
|
{
|
2014-01-31 18:40:18 +00:00
|
|
|
if(m_executor) Stop();
|
|
|
|
|
|
|
|
std::lock_guard<std::mutex> lock(m_main_mutex);
|
|
|
|
|
|
|
|
m_destroy = false;
|
|
|
|
m_alive = true;
|
|
|
|
|
2014-08-20 14:23:48 +00:00
|
|
|
m_executor = new std::thread([this]()
|
|
|
|
{
|
|
|
|
SetCurrentNamedThread(this);
|
|
|
|
g_thread_count++;
|
|
|
|
|
2014-09-11 19:18:19 +00:00
|
|
|
try
|
|
|
|
{
|
|
|
|
Task();
|
|
|
|
}
|
|
|
|
catch (const char* e)
|
|
|
|
{
|
2014-09-14 22:17:24 +00:00
|
|
|
LOG_ERROR(GENERAL, "%s: %s", GetThreadName().c_str(), e);
|
2014-09-11 19:18:19 +00:00
|
|
|
}
|
|
|
|
catch (const std::string& e)
|
|
|
|
{
|
2014-09-14 22:17:24 +00:00
|
|
|
LOG_ERROR(GENERAL, "%s: %s", GetThreadName().c_str(), e.c_str());
|
2014-09-11 19:18:19 +00:00
|
|
|
}
|
2014-08-20 14:23:48 +00:00
|
|
|
|
|
|
|
m_alive = false;
|
2014-09-14 22:17:24 +00:00
|
|
|
SetCurrentNamedThread(nullptr);
|
2014-08-20 14:23:48 +00:00
|
|
|
g_thread_count--;
|
|
|
|
});
|
2013-06-30 08:46:29 +00:00
|
|
|
}
|
|
|
|
|
2014-02-14 19:50:02 +00:00
|
|
|
void ThreadBase::Stop(bool wait, bool send_destroy)
|
2012-11-14 23:39:56 +00:00
|
|
|
{
|
2014-01-31 18:40:18 +00:00
|
|
|
std::lock_guard<std::mutex> lock(m_main_mutex);
|
2013-06-30 08:46:29 +00:00
|
|
|
|
2014-02-14 19:50:02 +00:00
|
|
|
if (send_destroy)
|
|
|
|
m_destroy = true;
|
2013-06-30 08:46:29 +00:00
|
|
|
|
2014-01-31 18:40:18 +00:00
|
|
|
if(!m_executor)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if(wait && m_executor->joinable() && m_alive)
|
|
|
|
{
|
|
|
|
m_executor->join();
|
2013-06-30 08:46:29 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-01-31 18:40:18 +00:00
|
|
|
m_executor->detach();
|
2013-06-30 08:46:29 +00:00
|
|
|
}
|
2014-01-31 18:40:18 +00:00
|
|
|
|
|
|
|
delete m_executor;
|
|
|
|
m_executor = nullptr;
|
2012-11-14 23:39:56 +00:00
|
|
|
}
|
|
|
|
|
2014-01-31 18:40:18 +00:00
|
|
|
bool ThreadBase::Join() const
|
2013-06-30 08:46:29 +00:00
|
|
|
{
|
2014-01-31 18:40:18 +00:00
|
|
|
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;
|
2013-06-30 08:46:29 +00:00
|
|
|
}
|
|
|
|
|
2014-01-31 18:40:18 +00:00
|
|
|
bool ThreadBase::IsAlive() const
|
2013-06-30 08:46:29 +00:00
|
|
|
{
|
2014-01-31 18:40:18 +00:00
|
|
|
std::lock_guard<std::mutex> lock(m_main_mutex);
|
|
|
|
return m_alive;
|
2013-06-30 08:46:29 +00:00
|
|
|
}
|
|
|
|
|
2014-01-31 18:40:18 +00:00
|
|
|
bool ThreadBase::TestDestroy() const
|
2013-06-30 08:46:29 +00:00
|
|
|
{
|
2014-01-31 18:40:18 +00:00
|
|
|
return m_destroy;
|
2013-06-30 08:46:29 +00:00
|
|
|
}
|
|
|
|
|
2014-01-31 18:40:18 +00:00
|
|
|
thread::thread(const std::string& name, std::function<void()> func) : m_name(name)
|
2012-11-14 23:39:56 +00:00
|
|
|
{
|
2014-01-31 18:40:18 +00:00
|
|
|
start(func);
|
2012-11-14 23:39:56 +00:00
|
|
|
}
|
|
|
|
|
2014-01-31 18:40:18 +00:00
|
|
|
thread::thread(const std::string& name) : m_name(name)
|
2012-11-14 23:39:56 +00:00
|
|
|
{
|
2014-01-31 18:40:18 +00:00
|
|
|
}
|
2012-11-14 23:39:56 +00:00
|
|
|
|
2014-01-31 18:40:18 +00:00
|
|
|
thread::thread()
|
|
|
|
{
|
2013-06-30 08:46:29 +00:00
|
|
|
}
|
|
|
|
|
2014-01-31 18:40:18 +00:00
|
|
|
void thread::start(std::function<void()> func)
|
2014-02-27 18:25:32 +00:00
|
|
|
{
|
|
|
|
std::string name = m_name;
|
|
|
|
|
|
|
|
m_thr = std::thread([func, name]()
|
2014-02-19 17:27:52 +00:00
|
|
|
{
|
2014-02-27 18:25:32 +00:00
|
|
|
NamedThreadBase info(name);
|
2014-08-20 14:23:48 +00:00
|
|
|
SetCurrentNamedThread(&info);
|
2014-06-19 13:50:18 +00:00
|
|
|
g_thread_count++;
|
2014-02-22 12:06:23 +00:00
|
|
|
|
2014-09-11 19:18:19 +00:00
|
|
|
try
|
|
|
|
{
|
|
|
|
func();
|
|
|
|
}
|
|
|
|
catch (const char* e)
|
|
|
|
{
|
2014-09-14 22:17:24 +00:00
|
|
|
LOG_ERROR(GENERAL, "%s: %s", name.c_str(), e);
|
2014-09-11 19:18:19 +00:00
|
|
|
}
|
|
|
|
catch (const std::string& e)
|
|
|
|
{
|
2014-09-14 22:17:24 +00:00
|
|
|
LOG_ERROR(GENERAL, "%s: %s", name.c_str(), e.c_str());
|
2014-09-11 19:18:19 +00:00
|
|
|
}
|
2014-06-19 13:50:18 +00:00
|
|
|
|
2014-09-14 22:17:24 +00:00
|
|
|
SetCurrentNamedThread(nullptr);
|
2014-06-19 13:50:18 +00:00
|
|
|
g_thread_count--;
|
2014-02-19 17:27:52 +00:00
|
|
|
});
|
2013-06-30 08:46:29 +00:00
|
|
|
}
|
|
|
|
|
2014-01-31 18:40:18 +00:00
|
|
|
void thread::detach()
|
2013-06-30 08:46:29 +00:00
|
|
|
{
|
2014-01-31 18:40:18 +00:00
|
|
|
m_thr.detach();
|
|
|
|
}
|
|
|
|
|
|
|
|
void thread::join()
|
|
|
|
{
|
|
|
|
m_thr.join();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool thread::joinable() const
|
|
|
|
{
|
|
|
|
return m_thr.joinable();
|
2014-02-23 16:52:52 +00:00
|
|
|
}
|
2014-10-10 21:33:57 +00:00
|
|
|
|
|
|
|
struct g_waiter_map_t
|
|
|
|
{
|
|
|
|
// TODO: optimize (use custom lightweight readers-writer lock)
|
|
|
|
|
|
|
|
std::mutex m_mutex;
|
|
|
|
|
|
|
|
struct waiter
|
|
|
|
{
|
|
|
|
u64 signal_id;
|
|
|
|
NamedThreadBase* thread;
|
|
|
|
};
|
|
|
|
|
|
|
|
std::vector<waiter> m_waiters;
|
|
|
|
|
|
|
|
} g_waiter_map;
|
|
|
|
|
|
|
|
bool waiter_is_stopped(const char* func_name, u64 signal_id)
|
|
|
|
{
|
|
|
|
if (Emu.IsStopped())
|
|
|
|
{
|
|
|
|
LOG_WARNING(Log::HLE, "%s() aborted (signal_id=0x%llx)", func_name, signal_id);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-10-10 22:37:20 +00:00
|
|
|
waiter_reg_t::waiter_reg_t(u64 signal_id)
|
|
|
|
: signal_id(signal_id)
|
|
|
|
, thread(GetCurrentNamedThread())
|
2014-10-10 21:33:57 +00:00
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(g_waiter_map.m_mutex);
|
|
|
|
|
|
|
|
// add waiter
|
|
|
|
g_waiter_map.m_waiters.push_back({ signal_id, thread });
|
|
|
|
}
|
|
|
|
|
2014-10-10 22:37:20 +00:00
|
|
|
waiter_reg_t::~waiter_reg_t()
|
2014-10-10 21:33:57 +00:00
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(g_waiter_map.m_mutex);
|
|
|
|
|
|
|
|
// remove waiter
|
2014-10-10 22:37:20 +00:00
|
|
|
for (size_t i = g_waiter_map.m_waiters.size() - 1; i >= 0; i--)
|
2014-10-10 21:33:57 +00:00
|
|
|
{
|
|
|
|
if (g_waiter_map.m_waiters[i].signal_id == signal_id && g_waiter_map.m_waiters[i].thread == thread)
|
|
|
|
{
|
|
|
|
g_waiter_map.m_waiters.erase(g_waiter_map.m_waiters.begin() + i);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void waiter_signal(u64 signal_id)
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(g_waiter_map.m_mutex);
|
|
|
|
|
|
|
|
// find waiter and signal
|
|
|
|
for (auto& v : g_waiter_map.m_waiters)
|
|
|
|
{
|
|
|
|
if (v.signal_id == signal_id)
|
|
|
|
{
|
|
|
|
v.thread->Notify();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|