#pragma once #include #include #include #include "Platform.h" #include "Atomic.h" // Will report exception and call std::abort() if put in catch(...) [[noreturn]] void catch_all_exceptions(); // Simple list of void() functors class task_stack { struct task_base { std::unique_ptr next; virtual ~task_base() = default; virtual void exec() { if (next) { next->exec(); } } }; std::unique_ptr m_stack; public: template void push(F&& func) { struct task_t : task_base { std::remove_reference_t func; task_t(F&& func) : func(std::forward(func)) { } void exec() override { func(); task_base::exec(); } }; auto _top = new task_t(std::forward(func)); auto _next = m_stack.release(); m_stack.reset(_top); #ifndef _MSC_VER _top->next.reset(_next); #else auto& next = _top->next; next.release(); next.reset(_next); #endif } void reset() { m_stack.reset(); } void exec() const { if (m_stack) { m_stack->exec(); } } }; // Thread control class class thread_ctrl final { struct internal; static thread_local thread_ctrl* g_tls_this_thread; // Thread handle std::thread m_thread; // Thread join contention counter atomic_t m_joining{}; // Fixed name std::string m_name; // Thread internals mutable atomic_t m_data{}; // Called at the thread start void initialize(); // Set std::current_exception void set_exception() noexcept; // Called at the thread end void finalize() noexcept; // Get atexit function task_stack& get_atexit() const; public: template thread_ctrl(N&& name) : m_name(std::forward(name)) { } // Disable copy/move constructors and operators thread_ctrl(const thread_ctrl&) = delete; ~thread_ctrl(); // Get thread name const std::string& get_name() const { return m_name; } // Initialize internal data void initialize_once() const; // Get thread result (may throw, simultaneous joining allowed) void join(); // Lock, unlock, notify the thread (required if the condition changed locklessly) void lock_notify() const; // Notify the thread, beware the condition change void notify() const; // internal* get_data() const; // 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) template static inline void at_exit(F&& func) { return g_tls_this_thread->get_atexit().push(std::forward(func)); } // Named thread factory template static inline std::shared_ptr spawn(N&& name, F&& func, Args&&... args) { auto ctrl = std::make_shared(std::forward(name)); ctrl->m_thread = std::thread([ctrl, task = std::forward(func)](Args&&... args) { try { ctrl->initialize(); task(std::forward(args)...); } catch (...) { ctrl->set_exception(); } ctrl->finalize(); }, std::forward(args)...); return ctrl; } }; class named_thread : public std::enable_shared_from_this { // Pointer to managed resource (shared with actual thread) std::shared_ptr m_thread; public: named_thread(); virtual ~named_thread(); // Deleted copy/move constructors + copy/move operators named_thread(const named_thread&) = delete; // Get thread name virtual std::string get_name() const; protected: // Start thread (cannot be called from the constructor: should throw bad_weak_ptr in such case) void start(); // Thread task (called in the thread) virtual void on_task() = 0; // Thread finalization (called after on_task) virtual void on_exit() {} public: // ID initialization virtual void on_init() { start(); } // ID finalization virtual void on_stop() { m_thread->join(); } // Get thread_ctrl const thread_ctrl* operator->() const { return m_thread.get(); } // Lock mutex, notify condition variable void lock_notify() const { m_thread->lock_notify(); } // Notify condition variable void notify() const { m_thread->notify(); } }; // Wrapper for named thread, joins automatically in the destructor, can only be used in function scope class scope_thread final { std::shared_ptr m_thread; public: template scope_thread(N&& name, F&& func) : m_thread(thread_ctrl::spawn(std::forward(name), std::forward(func))) { } // Deleted copy/move constructors + copy/move operators scope_thread(const scope_thread&) = delete; // Destructor with exceptions allowed ~scope_thread() noexcept(false) { m_thread->join(); } };