rpcs3/Utilities/Thread.h

358 lines
6.7 KiB
C
Raw Normal View History

#pragma once
2016-08-14 00:22:19 +00:00
#include "types.h"
#include "Atomic.h"
2016-05-13 14:01:48 +00:00
#include <exception>
2016-02-01 21:55:43 +00:00
#include <string>
#include <memory>
#include "sema.h"
#include "cond.h"
2017-11-23 15:37:08 +00:00
// Report error and call std::abort(), defined in main.cpp
[[noreturn]] void report_fatal_error(const std::string&);
2015-12-18 11:11:18 +00:00
// Will report exception and call std::abort() if put in catch(...)
[[noreturn]] void catch_all_exceptions();
// Hardware core layout
enum class native_core_arrangement : u32
{
undefined,
generic,
intel_ht,
amd_ccx
};
enum class thread_class : u32
{
general,
rsx,
spu,
ppu
};
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();
2016-02-01 21:55:43 +00:00
virtual void invoke()
2016-02-01 21:55:43 +00:00
{
if (next)
{
next->invoke();
2016-02-01 21:55:43 +00:00
}
}
};
template <typename F>
struct task_type final : 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
void invoke() final override
2016-05-13 14:01:48 +00:00
{
func();
task_base::invoke();
2016-05-13 14:01:48 +00:00
}
};
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>
2016-05-13 14:01:48 +00:00
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 invoke() const
2016-02-01 21:55:43 +00:00
{
if (m_stack)
{
m_stack->invoke();
2016-02-01 21:55:43 +00:00
}
}
};
2015-11-26 08:06:29 +00:00
// Thread control class
class thread_ctrl final
2015-06-30 22:25:52 +00:00
{
// Current thread
static thread_local thread_ctrl* g_tls_this_thread;
2015-08-21 11:07:31 +00:00
// Target cpu core layout
static atomic_t<native_core_arrangement> g_native_core_layout;
// Self pointer
std::shared_ptr<thread_ctrl> m_self;
// Thread handle (platform-specific)
atomic_t<std::uintptr_t> m_thread{0};
// Thread mutex
mutable semaphore<> m_mutex;
// Thread condition variable
cond_variable m_cond;
// Thread flags
atomic_t<u32> m_signal{0};
// Thread joining condition variable
cond_variable m_jcv;
// Remotely set or caught exception
std::exception_ptr m_exception;
// Thread initial task
task_stack m_task;
2016-05-13 14:01:48 +00:00
// Fixed name
std::string m_name;
2015-11-26 08:06:29 +00:00
// CPU cycles thread has run for
u64 m_cycles{0};
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(std::exception_ptr) noexcept;
// Internal waiting function, may throw. Infinite value is -1.
static bool _wait_for(u64 usec);
2016-05-13 14:01:48 +00:00
// Internal throwing function. Mutex must be locked and will be unlocked.
[[noreturn]] void _throw();
2016-05-13 14:01:48 +00:00
// Internal notification function
void _notify(cond_variable thread_ctrl::*);
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;
}
// Get CPU cycles since last time this function was called. First call returns 0.
u64 get_cycles();
// Get platform-specific thread handle
std::uintptr_t get_native_handle() const
{
return m_thread.load();
}
// Get exception
std::exception_ptr get_exception() const;
// Set exception
void set_exception(std::exception_ptr ptr);
2016-02-01 21:55:43 +00:00
// Get thread result (may throw, simultaneous joining allowed)
void join();
// Notify the thread
2016-05-13 14:01:48 +00:00
void notify();
// Wait once with timeout. Abortable, may throw. May spuriously return false.
static inline bool wait_for(u64 usec)
2016-05-13 14:01:48 +00:00
{
return _wait_for(usec);
2016-05-13 14:01:48 +00:00
}
// Wait. Abortable, may throw.
static inline void wait()
{
_wait_for(-1);
}
// Wait until pred(). Abortable, may throw.
template<typename F, typename RT = std::invoke_result_t<F>>
static inline RT wait(F&& pred)
2016-05-13 14:01:48 +00:00
{
while (true)
{
if (RT result = pred())
2016-05-13 14:01:48 +00:00
{
return result;
}
_wait_for(-1);
2016-05-13 14:01:48 +00:00
}
}
// Wait eternally until aborted.
2016-05-13 14:01:48 +00:00
[[noreturn]] static inline void eternalize()
{
while (true)
{
_wait_for(-1);
2016-05-13 14:01:48 +00:00
}
}
2015-11-26 08:06:29 +00:00
// Test exception (may throw).
static void test();
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;
}
// Create detached named thread
2016-05-13 14:01:48 +00:00
template<typename N, typename F>
static inline void spawn(N&& name, F&& func)
2015-11-26 08:06:29 +00:00
{
auto out = std::make_shared<thread_ctrl>(std::forward<N>(name));
2015-11-26 08:06:29 +00:00
thread_ctrl::start(out, std::forward<F>(func));
}
// Named thread factory
template<typename N, typename F>
static inline void spawn(std::shared_ptr<thread_ctrl>& out, N&& name, F&& func)
{
out = std::make_shared<thread_ctrl>(std::forward<N>(name));
2015-11-26 08:06:29 +00:00
thread_ctrl::start(out, std::forward<F>(func));
2015-11-26 08:06:29 +00:00
}
2017-06-24 15:36:49 +00:00
// Detect layout
static void detect_cpu_layout();
// Returns a core affinity mask. Set whether to generate the high priority set or not
static u16 get_affinity_mask(thread_class group);
// Sets the native thread priority
2017-06-24 15:36:49 +00:00
static void set_native_priority(int priority);
// Sets the preferred affinity mask for this thread
static void set_thread_affinity_mask(u16 mask);
2015-11-26 08:06:29 +00:00
};
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() {}
// Called once upon thread spawn within the thread's own context
virtual void on_spawn() {}
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* get() const
2016-02-01 21:55:43 +00:00
{
return m_thread.get();
}
void join() const
{
return m_thread->join();
}
void notify() const
{
return m_thread->notify();
}
2016-05-13 14:01:48 +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-07-03 23:22:24 +00:00
{
thread_ctrl::spawn(m_thread, 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
}
// Access thread_ctrl
thread_ctrl* get() const
{
return m_thread.get();
}
2015-07-03 23:22:24 +00:00
};