rpcs3/Utilities/Thread.h

273 lines
4.9 KiB
C
Raw Normal View History

#pragma once
2016-02-01 21:55:43 +00:00
#include <exception>
#include <string>
#include <memory>
#include <thread>
#include <mutex>
#include <condition_variable>
#include "Platform.h"
2015-12-18 11:11:18 +00:00
// Will report exception and call std::abort() if put in catch(...)
[[noreturn]] void catch_all_exceptions();
2016-02-01 21:55:43 +00:00
// Simple list of void() functors
class task_stack
{
struct task_base
{
std::unique_ptr<task_base> next;
virtual ~task_base() = default;
virtual void exec()
{
if (next)
{
next->exec();
}
}
};
std::unique_ptr<task_base> m_stack;
never_inline void push(std::unique_ptr<task_base> task)
{
m_stack.swap(task->next);
m_stack.swap(task);
}
public:
template<typename F>
void push(F&& func)
{
struct task_t : task_base
{
std::remove_reference_t<F> func;
task_t(F&& func)
: func(std::forward<F>(func))
{
}
void exec() override
{
func();
task_base::exec();
}
};
return push(std::unique_ptr<task_base>{ new task_t(std::forward<F>(func)) });
}
void reset()
{
m_stack.reset();
}
void exec() const
{
if (m_stack)
{
m_stack->exec();
}
}
};
2015-11-26 08:06:29 +00:00
// Thread control class
class thread_ctrl final
2015-06-30 22:25:52 +00:00
{
2015-11-26 08:06:29 +00:00
static thread_local thread_ctrl* g_tls_this_thread;
2016-02-01 21:55:43 +00:00
// Fixed name
std::string m_name;
2015-08-21 11:07:31 +00:00
2015-11-26 08:06:29 +00:00
// Thread handle (be careful)
2015-06-30 22:25:52 +00:00
std::thread m_thread;
2016-02-01 21:55:43 +00:00
// Thread result (exception)
std::exception_ptr m_exception;
2015-11-26 08:06:29 +00:00
// Functions scheduled at thread exit
2016-02-01 21:55:43 +00:00
task_stack m_atexit;
2015-11-26 08:06:29 +00:00
// Called at the thread start
static void initialize();
2015-08-21 11:07:31 +00:00
2015-11-26 08:06:29 +00:00
// Called at the thread end
static void finalize() noexcept;
public:
2016-02-01 21:55:43 +00:00
template<typename N>
thread_ctrl(N&& name)
: m_name(std::forward<N>(name))
2015-06-30 22:25:52 +00:00
{
}
2015-11-26 08:06:29 +00:00
// Disable copy/move constructors and operators
thread_ctrl(const thread_ctrl&) = delete;
2016-02-01 21:55:43 +00:00
~thread_ctrl()
{
if (m_thread.joinable())
{
m_thread.detach();
}
}
// Get thread name
2016-02-01 21:55:43 +00:00
const std::string& get_name() const
{
return m_name;
}
2016-02-01 21:55:43 +00:00
// Get thread result (may throw)
2015-11-26 08:06:29 +00:00
void join()
{
2016-02-01 21:55:43 +00:00
if (m_thread.joinable())
{
m_thread.join();
}
if (auto&& e = std::move(m_exception))
{
std::rethrow_exception(e);
}
2015-11-26 08:06:29 +00:00
}
// 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)
2016-02-01 21:55:43 +00:00
template<typename F>
static inline void at_exit(F&& func)
2015-11-26 08:06:29 +00:00
{
2016-02-01 21:55:43 +00:00
return g_tls_this_thread->m_atexit.push(std::forward<F>(func));
2015-11-26 08:06:29 +00:00
}
// 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));
2016-02-01 21:55:43 +00:00
ctrl->m_thread = std::thread([ctrl, task = std::forward<F>(func)]()
2015-11-26 08:06:29 +00:00
{
2016-02-01 21:55:43 +00:00
// Initialize TLS variable
2015-11-26 08:06:29 +00:00
g_tls_this_thread = ctrl.get();
try
{
initialize();
task();
finalize();
}
catch (...)
{
finalize();
2016-02-01 21:55:43 +00:00
// Set exception
ctrl->m_exception = std::current_exception();
}
});
2015-11-26 08:06:29 +00:00
return ctrl;
}
};
2016-02-01 21:55:43 +00:00
class named_thread : public std::enable_shared_from_this<named_thread>
{
// Pointer to managed resource (shared with actual thread)
2015-11-26 08:06:29 +00:00
std::shared_ptr<thread_ctrl> m_thread;
2015-01-16 14:36:53 +00:00
2015-06-30 22:25:52 +00:00
public:
2015-11-26 08:06:29 +00:00
// Thread condition variable for external use (this thread waits on it, other threads may notify)
std::condition_variable cv;
// Thread mutex for external use (can be used with `cv`)
2015-06-30 22:25:52 +00:00
std::mutex mutex;
2016-02-01 21:55:43 +00:00
// Lock mutex, notify condition variable
void safe_notify()
{
// Lock for reliable notification, condition is assumed to be changed externally
std::unique_lock<std::mutex> lock(mutex);
cv.notify_one();
}
// ID initialization
virtual void on_init()
{
start();
}
// ID finalization
virtual void on_stop()
{
join();
}
2015-11-26 08:06:29 +00:00
protected:
// Thread task (called in the thread)
virtual void on_task() = 0;
// Thread finalization (called after on_task)
virtual void on_exit() {}
public:
2016-02-01 21:55:43 +00:00
named_thread() = default;
2015-06-30 22:25:52 +00:00
2016-02-01 21:55:43 +00:00
virtual ~named_thread() = default;
2015-06-30 22:25:52 +00:00
// Deleted copy/move constructors + copy/move operators
2016-02-01 21:55:43 +00:00
named_thread(const named_thread&) = delete;
// Get thread name
2015-11-26 08:06:29 +00:00
virtual std::string get_name() const;
2014-10-17 20:13:25 +00:00
2015-11-26 08:06:29 +00:00
// Start thread (cannot be called from the constructor: should throw bad_weak_ptr in such case)
void start();
2014-10-17 20:13:25 +00:00
2016-02-01 21:55:43 +00:00
// Join thread (get thread result)
2015-06-30 22:25:52 +00:00
void join();
2014-10-17 20:13:25 +00:00
2015-11-26 08:06:29 +00:00
// Get thread_ctrl
2016-02-01 21:55:43 +00:00
const thread_ctrl* get_thread_ctrl() const
{
return m_thread.get();
}
2015-11-26 08:06:29 +00:00
2016-02-01 21:55:43 +00:00
// Compare with the current thread
bool is_current() const
{
return m_thread && thread_ctrl::get_current() == m_thread.get();
}
2014-10-17 20:13:25 +00:00
};
2015-11-26 08:06:29 +00:00
// Wrapper for named thread, joins automatically in the destructor, can only be used in function scope
2016-02-01 21:55:43 +00:00
class scope_thread final
2015-07-03 23:22:24 +00:00
{
2015-11-26 08:06:29 +00:00
std::shared_ptr<thread_ctrl> m_thread;
2015-07-03 23:22:24 +00:00
public:
2015-11-26 08:06:29 +00:00
template<typename N, typename F>
2016-02-01 21:55:43 +00:00
scope_thread(N&& name, F&& func)
2015-11-26 08:06:29 +00:00
: m_thread(thread_ctrl::spawn(std::forward<N>(name), std::forward<F>(func)))
2015-07-03 23:22:24 +00:00
{
}
2015-11-26 08:06:29 +00:00
// Deleted copy/move constructors + copy/move operators
2016-02-01 21:55:43 +00:00
scope_thread(const scope_thread&) = delete;
2015-11-26 08:06:29 +00:00
// Destructor with exceptions allowed
2016-02-01 21:55:43 +00:00
~scope_thread() noexcept(false)
2015-07-03 23:22:24 +00:00
{
2015-11-26 08:06:29 +00:00
m_thread->join();
2015-07-03 23:22:24 +00:00
}
};