123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- #pragma once
- #include "../C/Baselib_Thread.h"
- #include "Time.h"
- #include <memory>
- #if !COMPILER_SUPPORTS_GENERIC_LAMBDA_EXPRESSIONS
- #include <functional>
- #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<class FunctionType , class ... Args>
- 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<Container>, smallObject);
- }
- else
- {
- std::unique_ptr<Container> ptr(new Container(std::move(wrapped)));
- thread = CreateThread(ThreadProxyHeap<Container>, 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<class T>
- static void ThreadProxyHeap(void* data)
- {
- std::unique_ptr<T> ptr(reinterpret_cast<T*>(data));
- (*ptr)();
- }
- template<class T>
- static void ThreadProxySmallObject(void* data)
- {
- T* ptr = reinterpret_cast<T*>(&data);
- (*ptr)();
- ptr->~T();
- }
- };
- }
- }
|