rpcs3/Utilities/Thread.h

445 lines
7.8 KiB
C
Raw Normal View History

#pragma once
2016-05-13 14:01:48 +00:00
#include <exception>
2016-02-01 21:55:43 +00:00
#include <string>
#include <memory>
#include "Platform.h"
#include "Atomic.h"
2016-02-01 21:55:43 +00:00
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();
}
}
};
template<typename F>
2016-05-13 14:01:48 +00:00
struct task_type : task_base
2016-02-01 21:55:43 +00:00
{
2016-05-13 14:01:48 +00:00
std::remove_reference_t<F> func;
task_type(F&& func)
: func(std::forward<F>(func))
2016-02-01 21:55:43 +00:00
{
2016-05-13 14:01:48 +00:00
}
2016-02-01 21:55:43 +00:00
2016-05-13 14:01:48 +00:00
void exec() override
{
func();
task_base::exec();
}
};
2016-02-01 21:55:43 +00:00
2016-05-13 14:01:48 +00:00
std::unique_ptr<task_base> m_stack;
2016-02-01 21:55:43 +00:00
2016-05-13 14:01:48 +00:00
public:
task_stack() = default;
template<typename F>
task_stack(F&& func)
: m_stack(new task_type<F>(std::forward<F>(func)))
{
}
void push(task_stack stack)
{
auto _top = stack.m_stack.release();
auto _next = m_stack.release();
m_stack.reset(_top);
2016-05-13 14:01:48 +00:00
while (UNLIKELY(_top->next)) _top = _top->next.get();
_top->next.reset(_next);
2016-02-01 21:55:43 +00:00
}
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
{
2016-05-13 14:01:48 +00:00
public: // TODO
struct internal;
2016-05-13 14:01:48 +00:00
private:
static thread_local thread_ctrl* g_tls_this_thread;
2015-08-21 11:07:31 +00:00
2016-05-13 14:01:48 +00:00
// Thread handle storage
std::aligned_storage_t<16> m_thread;
// Thread join contention counter
2016-05-13 14:01:48 +00:00
atomic_t<u32> m_joining{};
2016-07-16 17:58:42 +00:00
// Thread interrupt guard counter
volatile u32 m_guard = 0x80000000;
2016-07-16 17:58:42 +00:00
2016-05-13 14:01:48 +00:00
// Thread internals
atomic_t<internal*> m_data{};
// Fixed name
std::string m_name;
2015-11-26 08:06:29 +00:00
2016-05-13 14:01:48 +00:00
// Start thread
static void start(const std::shared_ptr<thread_ctrl>&, task_stack);
2015-11-26 08:06:29 +00:00
// Called at the thread start
void initialize();
2015-11-26 08:06:29 +00:00
// Called at the thread end
void finalize() noexcept;
// Get atexit function
2016-05-13 14:01:48 +00:00
void push_atexit(task_stack);
// Start waiting
void wait_start(u64 timeout);
// Proceed waiting
bool wait_wait(u64 timeout);
// Check exception
void test();
public:
2016-05-13 14:01:48 +00:00
thread_ctrl(std::string&& name);
2015-11-26 08:06:29 +00:00
thread_ctrl(const thread_ctrl&) = delete;
~thread_ctrl();
// Get thread name
2016-02-01 21:55:43 +00:00
const std::string& get_name() const
{
return m_name;
}
// Initialize internal data
2016-05-13 14:01:48 +00:00
void initialize_once();
2016-02-01 21:55:43 +00:00
// Get thread result (may throw, simultaneous joining allowed)
void join();
2016-05-13 14:01:48 +00:00
// Lock thread mutex
void lock();
// Lock conditionally (double-checked)
template<typename F>
bool lock_if(F&& pred)
{
if (pred())
{
lock();
try
{
if (LIKELY(pred()))
{
return true;
}
else
{
unlock();
return false;
}
}
catch (...)
{
unlock();
throw;
}
}
else
{
return false;
}
}
// Unlock thread mutex (internal data must be initialized)
void unlock();
// Lock, unlock, notify the thread (required if the condition changed locklessly)
2016-05-13 14:01:48 +00:00
void lock_notify();
// Notify the thread (internal data must be initialized)
void notify();
// Set exception (internal data must be initialized, thread mutex must be locked)
void set_exception(std::exception_ptr);
2016-07-16 17:58:42 +00:00
// Internal
static void handle_interrupt();
// Interrupt thread with specified handler call (thread mutex must be locked)
void interrupt(void(*handler)());
// Interrupt guard recursive enter
void guard_enter()
{
m_guard++;
}
// Interrupt guard recursive leave
void guard_leave()
{
if (UNLIKELY(--m_guard & 0x40000000))
{
test_interrupt();
}
}
// Allow interrupts
void interrupt_enable()
{
m_guard &= ~0x80000000;
}
// Disable and discard any interrupt
void interrupt_disable()
{
m_guard |= 0x80000000;
}
// Check interrupt if delayed by guard scope
void test_interrupt();
2016-05-13 14:01:48 +00:00
// Current thread sleeps for specified amount of microseconds.
// Wrapper for std::this_thread::sleep, doesn't require valid thread_ctrl.
[[deprecated]] static void sleep(u64 useconds);
2016-05-13 14:01:48 +00:00
// Wait until pred(). Abortable, may throw. Thread must be locked.
// Timeout in microseconds (zero means infinite).
template<typename F>
static inline auto wait(u64 useconds, F&& pred)
{
g_tls_this_thread->wait_start(useconds);
2016-05-13 14:01:48 +00:00
while (true)
{
g_tls_this_thread->test();
if (auto&& result = pred())
{
return result;
}
else if (!g_tls_this_thread->wait_wait(useconds) && useconds)
{
return result;
}
}
}
// Wait until pred(). Abortable, may throw. Thread must be locked.
template<typename F>
static inline auto wait(F&& pred)
{
while (true)
{
g_tls_this_thread->test();
if (auto&& result = pred())
{
return result;
}
g_tls_this_thread->wait_wait(0);
}
}
// Wait once. Thread must be locked.
static inline void wait()
{
g_tls_this_thread->test();
g_tls_this_thread->wait_wait(0);
g_tls_this_thread->test();
}
// Wait unconditionally until aborted. Thread must be locked.
[[noreturn]] static inline void eternalize()
{
while (true)
{
g_tls_this_thread->test();
g_tls_this_thread->wait_wait(0);
}
}
2015-11-26 08:06:29 +00:00
// Get current thread (may be nullptr)
2016-05-13 14:01:48 +00:00
static thread_ctrl* get_current()
2015-11-26 08:06:29 +00:00
{
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>
2016-05-13 14:01:48 +00:00
static inline void atexit(F&& func)
2015-11-26 08:06:29 +00:00
{
2016-05-13 14:01:48 +00:00
return g_tls_this_thread->push_atexit(std::forward<F>(func));
2015-11-26 08:06:29 +00:00
}
// Named thread factory
2016-05-13 14:01:48 +00:00
template<typename N, typename F>
static inline std::shared_ptr<thread_ctrl> spawn(N&& name, F&& func)
2015-11-26 08:06:29 +00:00
{
auto ctrl = std::make_shared<thread_ctrl>(std::forward<N>(name));
2016-05-13 14:01:48 +00:00
thread_ctrl::start(ctrl, std::forward<F>(func));
2015-11-26 08:06:29 +00:00
return ctrl;
}
};
class 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:
named_thread();
2015-11-26 08:06:29 +00:00
virtual ~named_thread();
2015-06-30 22:25:52 +00:00
// Deleted copy/move constructors + copy/move operators
named_thread(const named_thread&) = delete;
2016-02-01 21:55:43 +00:00
// Get thread name
virtual std::string get_name() const;
2016-02-01 21:55:43 +00:00
protected:
// Start thread (cannot be called from the constructor: should throw in such case)
void start_thread(const std::shared_ptr<void>& _this);
// 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
// ID initialization
virtual void on_init(const std::shared_ptr<void>& _this)
2016-02-01 21:55:43 +00:00
{
return start_thread(_this);
2016-02-01 21:55:43 +00:00
}
// ID finalization
virtual void on_stop()
{
m_thread->join();
2016-02-01 21:55:43 +00:00
}
2016-05-13 14:01:48 +00:00
// Access thread_ctrl
thread_ctrl* operator->() const
2016-02-01 21:55:43 +00:00
{
return m_thread.get();
}
2016-05-13 14:01:48 +00:00
};
// Simple thread mutex locker
class thread_lock final
{
thread_ctrl* m_thread;
public:
thread_lock(const thread_lock&) = delete;
// Lock specified thread
thread_lock(thread_ctrl* thread)
: m_thread(thread)
{
m_thread->lock();
}
// Lock specified named_thread
thread_lock(named_thread& thread)
: thread_lock(thread.operator->())
{
}
2015-11-26 08:06:29 +00:00
2016-05-13 14:01:48 +00:00
// Lock current thread
thread_lock()
: thread_lock(thread_ctrl::get_current())
{
}
2016-05-13 14:01:48 +00:00
~thread_lock()
2016-02-01 21:55:43 +00:00
{
2016-05-13 14:01:48 +00:00
m_thread->unlock();
2016-02-01 21:55:43 +00:00
}
2014-10-17 20:13:25 +00:00
};
2016-07-16 17:58:42 +00:00
// Interrupt guard scope
class thread_guard final
{
thread_ctrl* m_thread;
public:
thread_guard(const thread_guard&) = delete;
thread_guard(thread_ctrl* thread)
: m_thread(thread)
{
m_thread->guard_enter();
}
thread_guard(named_thread& thread)
: thread_guard(thread.operator->())
{
}
thread_guard()
: thread_guard(thread_ctrl::get_current())
{
}
~thread_guard() noexcept(false)
{
m_thread->guard_leave();
}
};
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
}
};