diff --git a/sfw/core/thread.cpp b/sfw/core/thread.cpp new file mode 100644 index 0000000..a761ccb --- /dev/null +++ b/sfw/core/thread.cpp @@ -0,0 +1,110 @@ +/*************************************************************************/ +/* thread.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + +#include "thread.h" + +#if !defined(NO_THREADS) + +#include "core/error_macros.h" +#include "core/safe_refcount.h" + +Error (*Thread::set_name_func)(const String &) = nullptr; +void (*Thread::set_priority_func)(Thread::Priority) = nullptr; +void (*Thread::init_func)() = nullptr; +void (*Thread::term_func)() = nullptr; + +uint64_t Thread::_thread_id_hash(const std::thread::id &p_t) { + static std::hash hasher; + return hasher(p_t); +} + +Thread::ID Thread::main_thread_id = _thread_id_hash(std::this_thread::get_id()); +static thread_local Thread::ID caller_id = 0; +static thread_local bool caller_id_cached = false; + +void Thread::_set_platform_funcs( + Error (*p_set_name_func)(const String &), + void (*p_set_priority_func)(Thread::Priority), + void (*p_init_func)(), + void (*p_term_func)()) { + Thread::set_name_func = p_set_name_func; + Thread::set_priority_func = p_set_priority_func; + Thread::init_func = p_init_func; + Thread::term_func = p_term_func; +} + +void Thread::callback(Thread *p_self, const Settings &p_settings, Callback p_callback, void *p_userdata) { + caller_id = _thread_id_hash(p_self->thread.get_id()); + caller_id_cached = true; + + if (set_priority_func) { + set_priority_func(p_settings.priority); + } + if (init_func) { + init_func(); + } + + p_callback(p_userdata); + + if (term_func) { + term_func(); + } +} + +void Thread::start(Thread::Callback p_callback, void *p_user, const Settings &p_settings) { + if (id != _thread_id_hash(std::thread::id())) { +#ifdef DEBUG_ENABLED + WARN_PRINT("A Thread object has been re-started without wait_to_finish() having been called on it. Please do so to ensure correct cleanup of the thread."); +#endif + thread.detach(); + std::thread empty_thread; + thread.swap(empty_thread); + } + std::thread new_thread(&Thread::callback, this, p_settings, p_callback, p_user); + thread.swap(new_thread); + id = _thread_id_hash(thread.get_id()); +} + +bool Thread::is_started() const { + return id != _thread_id_hash(std::thread::id()); +} + +void Thread::wait_to_finish() { + if (id != _thread_id_hash(std::thread::id())) { + ERR_FAIL_COND_MSG(id == get_caller_id(), "A Thread can't wait for itself to finish."); + thread.join(); + std::thread empty_thread; + thread.swap(empty_thread); + id = _thread_id_hash(std::thread::id()); + } +} + +Error Thread::set_name(const String &p_name) { + if (set_name_func) { + return set_name_func(p_name); + } + + return ERR_UNAVAILABLE; +} + +Thread::~Thread() { + if (id != _thread_id_hash(std::thread::id())) { +#ifdef DEBUG_ENABLED + LOG_WARN("A Thread object has been destroyed without wait_to_finish() having been called on it. Please do so to ensure correct cleanup of the thread."); +#endif + thread.detach(); + } +} + +Thread::ID Thread::get_caller_id() { + if (likely(caller_id_cached)) { + return caller_id; + } else { + caller_id = _thread_id_hash(std::this_thread::get_id()); + caller_id_cached = true; + return caller_id; + } +} +#endif diff --git a/sfw/core/thread.h b/sfw/core/thread.h new file mode 100644 index 0000000..a8b3276 --- /dev/null +++ b/sfw/core/thread.h @@ -0,0 +1,95 @@ +#ifndef THREAD_H +#define THREAD_H + +/*************************************************************************/ +/* thread.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + +#include "core/typedefs.h" + +#if !defined(NO_THREADS) +#include "core/safe_refcount.h" +#include +#endif + +class String; + +class Thread { +public: + typedef void (*Callback)(void *p_userdata); + + typedef uint64_t ID; + + enum Priority { + PRIORITY_LOW, + PRIORITY_NORMAL, + PRIORITY_HIGH + }; + + struct Settings { + Priority priority; + Settings() { priority = PRIORITY_NORMAL; } + }; + +private: +#if !defined(NO_THREADS) + friend class Main; + + static ID main_thread_id; + + static uint64_t _thread_id_hash(const std::thread::id &p_t); + + ID id = _thread_id_hash(std::thread::id()); + std::thread thread; + + static void callback(Thread *p_self, const Settings &p_settings, Thread::Callback p_callback, void *p_userdata); + + static Error (*set_name_func)(const String &); + static void (*set_priority_func)(Thread::Priority); + static void (*init_func)(); + static void (*term_func)(); +#endif + +public: + static void _set_platform_funcs( + Error (*p_set_name_func)(const String &), + void (*p_set_priority_func)(Thread::Priority), + void (*p_init_func)() = nullptr, + void (*p_term_func)() = nullptr); + +#if !defined(NO_THREADS) + _FORCE_INLINE_ ID get_id() const { return id; } + // get the ID of the caller thread + static ID get_caller_id(); + // get the ID of the main thread + _FORCE_INLINE_ static ID get_main_id() { return main_thread_id; } + + _FORCE_INLINE_ static bool is_main_thread() { return get_caller_id() == main_thread_id; } + + static Error set_name(const String &p_name); + + void start(Thread::Callback p_callback, void *p_user, const Settings &p_settings = Settings()); + bool is_started() const; + ///< waits until thread is finished, and deallocates it. + void wait_to_finish(); + + ~Thread(); +#else + _FORCE_INLINE_ ID get_id() const { return 0; } + // get the ID of the caller thread + _FORCE_INLINE_ static ID get_caller_id() { return 0; } + // get the ID of the main thread + _FORCE_INLINE_ static ID get_main_id() { return 0; } + + _FORCE_INLINE_ static bool is_main_thread() { return true; } + + static Error set_name(const String &p_name) { return ERR_UNAVAILABLE; } + + void start(Thread::Callback p_callback, void *p_user, const Settings &p_settings = Settings()) {} + bool is_started() const { return false; } + void wait_to_finish() {} +#endif +}; + +#endif // THREAD_H diff --git a/sfwl/core/thread.cpp b/sfwl/core/thread.cpp new file mode 100644 index 0000000..a761ccb --- /dev/null +++ b/sfwl/core/thread.cpp @@ -0,0 +1,110 @@ +/*************************************************************************/ +/* thread.cpp */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + +#include "thread.h" + +#if !defined(NO_THREADS) + +#include "core/error_macros.h" +#include "core/safe_refcount.h" + +Error (*Thread::set_name_func)(const String &) = nullptr; +void (*Thread::set_priority_func)(Thread::Priority) = nullptr; +void (*Thread::init_func)() = nullptr; +void (*Thread::term_func)() = nullptr; + +uint64_t Thread::_thread_id_hash(const std::thread::id &p_t) { + static std::hash hasher; + return hasher(p_t); +} + +Thread::ID Thread::main_thread_id = _thread_id_hash(std::this_thread::get_id()); +static thread_local Thread::ID caller_id = 0; +static thread_local bool caller_id_cached = false; + +void Thread::_set_platform_funcs( + Error (*p_set_name_func)(const String &), + void (*p_set_priority_func)(Thread::Priority), + void (*p_init_func)(), + void (*p_term_func)()) { + Thread::set_name_func = p_set_name_func; + Thread::set_priority_func = p_set_priority_func; + Thread::init_func = p_init_func; + Thread::term_func = p_term_func; +} + +void Thread::callback(Thread *p_self, const Settings &p_settings, Callback p_callback, void *p_userdata) { + caller_id = _thread_id_hash(p_self->thread.get_id()); + caller_id_cached = true; + + if (set_priority_func) { + set_priority_func(p_settings.priority); + } + if (init_func) { + init_func(); + } + + p_callback(p_userdata); + + if (term_func) { + term_func(); + } +} + +void Thread::start(Thread::Callback p_callback, void *p_user, const Settings &p_settings) { + if (id != _thread_id_hash(std::thread::id())) { +#ifdef DEBUG_ENABLED + WARN_PRINT("A Thread object has been re-started without wait_to_finish() having been called on it. Please do so to ensure correct cleanup of the thread."); +#endif + thread.detach(); + std::thread empty_thread; + thread.swap(empty_thread); + } + std::thread new_thread(&Thread::callback, this, p_settings, p_callback, p_user); + thread.swap(new_thread); + id = _thread_id_hash(thread.get_id()); +} + +bool Thread::is_started() const { + return id != _thread_id_hash(std::thread::id()); +} + +void Thread::wait_to_finish() { + if (id != _thread_id_hash(std::thread::id())) { + ERR_FAIL_COND_MSG(id == get_caller_id(), "A Thread can't wait for itself to finish."); + thread.join(); + std::thread empty_thread; + thread.swap(empty_thread); + id = _thread_id_hash(std::thread::id()); + } +} + +Error Thread::set_name(const String &p_name) { + if (set_name_func) { + return set_name_func(p_name); + } + + return ERR_UNAVAILABLE; +} + +Thread::~Thread() { + if (id != _thread_id_hash(std::thread::id())) { +#ifdef DEBUG_ENABLED + LOG_WARN("A Thread object has been destroyed without wait_to_finish() having been called on it. Please do so to ensure correct cleanup of the thread."); +#endif + thread.detach(); + } +} + +Thread::ID Thread::get_caller_id() { + if (likely(caller_id_cached)) { + return caller_id; + } else { + caller_id = _thread_id_hash(std::this_thread::get_id()); + caller_id_cached = true; + return caller_id; + } +} +#endif diff --git a/sfwl/core/thread.h b/sfwl/core/thread.h new file mode 100644 index 0000000..a8b3276 --- /dev/null +++ b/sfwl/core/thread.h @@ -0,0 +1,95 @@ +#ifndef THREAD_H +#define THREAD_H + +/*************************************************************************/ +/* thread.h */ +/* From https://github.com/Relintai/pandemonium_engine (MIT) */ +/*************************************************************************/ + +#include "core/typedefs.h" + +#if !defined(NO_THREADS) +#include "core/safe_refcount.h" +#include +#endif + +class String; + +class Thread { +public: + typedef void (*Callback)(void *p_userdata); + + typedef uint64_t ID; + + enum Priority { + PRIORITY_LOW, + PRIORITY_NORMAL, + PRIORITY_HIGH + }; + + struct Settings { + Priority priority; + Settings() { priority = PRIORITY_NORMAL; } + }; + +private: +#if !defined(NO_THREADS) + friend class Main; + + static ID main_thread_id; + + static uint64_t _thread_id_hash(const std::thread::id &p_t); + + ID id = _thread_id_hash(std::thread::id()); + std::thread thread; + + static void callback(Thread *p_self, const Settings &p_settings, Thread::Callback p_callback, void *p_userdata); + + static Error (*set_name_func)(const String &); + static void (*set_priority_func)(Thread::Priority); + static void (*init_func)(); + static void (*term_func)(); +#endif + +public: + static void _set_platform_funcs( + Error (*p_set_name_func)(const String &), + void (*p_set_priority_func)(Thread::Priority), + void (*p_init_func)() = nullptr, + void (*p_term_func)() = nullptr); + +#if !defined(NO_THREADS) + _FORCE_INLINE_ ID get_id() const { return id; } + // get the ID of the caller thread + static ID get_caller_id(); + // get the ID of the main thread + _FORCE_INLINE_ static ID get_main_id() { return main_thread_id; } + + _FORCE_INLINE_ static bool is_main_thread() { return get_caller_id() == main_thread_id; } + + static Error set_name(const String &p_name); + + void start(Thread::Callback p_callback, void *p_user, const Settings &p_settings = Settings()); + bool is_started() const; + ///< waits until thread is finished, and deallocates it. + void wait_to_finish(); + + ~Thread(); +#else + _FORCE_INLINE_ ID get_id() const { return 0; } + // get the ID of the caller thread + _FORCE_INLINE_ static ID get_caller_id() { return 0; } + // get the ID of the main thread + _FORCE_INLINE_ static ID get_main_id() { return 0; } + + _FORCE_INLINE_ static bool is_main_thread() { return true; } + + static Error set_name(const String &p_name) { return ERR_UNAVAILABLE; } + + void start(Thread::Callback p_callback, void *p_user, const Settings &p_settings = Settings()) {} + bool is_started() const { return false; } + void wait_to_finish() {} +#endif +}; + +#endif // THREAD_H diff --git a/tools/merger/sfw_core.h.inl b/tools/merger/sfw_core.h.inl index 08b9292..8ea899b 100644 --- a/tools/merger/sfw_core.h.inl +++ b/tools/merger/sfw_core.h.inl @@ -45,6 +45,14 @@ //#include "core/typedefs.h" //--STRIP {{FILE:sfw/core/safe_refcount.h}} +//--STRIP +//#include "core/typedefs.h" +//#if !defined(NO_THREADS) +//#include "core/safe_refcount.h" +//#include +//#endif +//--STRIP +{{FILE:sfw/core/thread.h}} //--STRIP //#include "core/logger.h" diff --git a/tools/merger/sfw_full.h.inl b/tools/merger/sfw_full.h.inl index 3acd0c1..fe185b4 100644 --- a/tools/merger/sfw_full.h.inl +++ b/tools/merger/sfw_full.h.inl @@ -45,6 +45,14 @@ //#include "core/typedefs.h" //--STRIP {{FILE:sfw/core/safe_refcount.h}} +//--STRIP +//#include "core/typedefs.h" +//#if !defined(NO_THREADS) +//#include "core/safe_refcount.h" +//#include +//#endif +//--STRIP +{{FILE:sfw/core/thread.h}} //--STRIP //#include "core/logger.h" diff --git a/tools/merger/sfwl/sfwl_core.h.inl b/tools/merger/sfwl/sfwl_core.h.inl index 1d85756..1444aaa 100644 --- a/tools/merger/sfwl/sfwl_core.h.inl +++ b/tools/merger/sfwl/sfwl_core.h.inl @@ -45,6 +45,14 @@ //#include "core/typedefs.h" //--STRIP {{FILE:sfwl/core/safe_refcount.h}} +//--STRIP +//#include "core/typedefs.h" +//#if !defined(NO_THREADS) +//#include "core/safe_refcount.h" +//#include +//#endif +//--STRIP +{{FILE:sfwl/core/thread.h}} //--STRIP //#include "core/logger.h" diff --git a/tools/merger/sfwl/sfwl_full.h.inl b/tools/merger/sfwl/sfwl_full.h.inl index 6b874af..a51ed14 100644 --- a/tools/merger/sfwl/sfwl_full.h.inl +++ b/tools/merger/sfwl/sfwl_full.h.inl @@ -45,6 +45,14 @@ //#include "core/typedefs.h" //--STRIP {{FILE:sfwl/core/safe_refcount.h}} +//--STRIP +//#include "core/typedefs.h" +//#if !defined(NO_THREADS) +//#include "core/safe_refcount.h" +//#include +//#endif +//--STRIP +{{FILE:sfwl/core/thread.h}} //--STRIP //#include "core/logger.h"