#pragma once #include "../C/Baselib_Thread.h" #include "Time.h" #include #if !COMPILER_SUPPORTS_GENERIC_LAMBDA_EXPRESSIONS #include #endif namespace baselib { BASELIB_CPP_INTERFACE { /* This class is not supposed to be used as-is. Instead separate thread class should be created to explicitely define thread lifetime. This is useful to avoid having timeout constants all over the codebase. class ApplicationThread : public baselib::Thread { public: // Expose base class constructors. using baselib::Thread::Thread; void Join() { // Thread must join with-in 10 seconds, or this is an error. // Use application specific methods to report error and/or try again. assert(baselib::Thread::TryJoin(10 * 1000) == true); } }; */ class BASELIB_API Thread { public: // Default constructor does nothing, useful when declaring thread as field in classes/structs Thread() = default; // Generic Constructor template Thread(FunctionType && f, Args && ... args) { #if COMPILER_SUPPORTS_GENERIC_LAMBDA_EXPRESSIONS // This generates cleaner and nicer-to-debug code auto wrapped = [ = ] {f(args ...);}; #else auto wrapped = std::bind(f, args ...); #endif using Container = decltype(wrapped); // Small object optimization. constexpr bool smallObject = (sizeof(Container) <= sizeof(void*)) && (alignof(Container) <= alignof(void*)); if (smallObject) { union { // sizeof(void*) will trigger placement new errors // even if code path is not executed char buf[sizeof(Container)]; void* smallObject; }; smallObject = nullptr; // to avoid -Wmaybe-uninitialized // We have to move it to pointer, otherwise wrapped destructor will be called new(buf) Container(std::move(wrapped)); thread = CreateThread(ThreadProxySmallObject, smallObject); } else { std::unique_ptr ptr(new Container(std::move(wrapped))); thread = CreateThread(ThreadProxyHeap, ptr.get()); if (thread) ptr.release(); } } // Thread has to be joined before destructor is called ~Thread(); // Non-copyable Thread(const Thread&) = delete; Thread& operator=(const Thread&) = delete; // Movable Thread(Thread&& other); Thread& operator=(Thread&& other); // Return true if threads are supported static bool SupportsThreads(); // Return true if join succeeded COMPILER_WARN_UNUSED_RESULT bool TryJoin(timeout_ms timeout); // Yields execution static inline void YieldExecution() { Baselib_Thread_YieldExecution(); } // Returns thread id inline Baselib_Thread_Id GetId() { return Baselib_Thread_GetId(thread); } // Returns current thread id static inline Baselib_Thread_Id GetCurrentId() { return Baselib_Thread_GetCurrentThreadId(); } private: Baselib_Thread* thread = nullptr; static Baselib_Thread* CreateThread(Baselib_Thread_EntryPointFunction function, void* arg); template static void ThreadProxyHeap(void* data) { std::unique_ptr ptr(reinterpret_cast(data)); (*ptr)(); } template static void ThreadProxySmallObject(void* data) { T* ptr = reinterpret_cast(&data); (*ptr)(); ptr->~T(); } }; } }