Badminton-Scoreboard/Libraries/external/baselib/Include/Cpp/Thread.h
2023-10-08 10:24:48 +08:00

136 lines
4.4 KiB
C++

#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();
}
};
}
}