Thread.h 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. #pragma once
  2. #include "../C/Baselib_Thread.h"
  3. #include "Time.h"
  4. #include <memory>
  5. #if !COMPILER_SUPPORTS_GENERIC_LAMBDA_EXPRESSIONS
  6. #include <functional>
  7. #endif
  8. namespace baselib
  9. {
  10. BASELIB_CPP_INTERFACE
  11. {
  12. /*
  13. This class is not supposed to be used as-is.
  14. Instead separate thread class should be created to explicitely define thread lifetime.
  15. This is useful to avoid having timeout constants all over the codebase.
  16. class ApplicationThread : public baselib::Thread
  17. {
  18. public:
  19. // Expose base class constructors.
  20. using baselib::Thread::Thread;
  21. void Join()
  22. {
  23. // Thread must join with-in 10 seconds, or this is an error.
  24. // Use application specific methods to report error and/or try again.
  25. assert(baselib::Thread::TryJoin(10 * 1000) == true);
  26. }
  27. };
  28. */
  29. class BASELIB_API Thread
  30. {
  31. public:
  32. // Default constructor does nothing, useful when declaring thread as field in classes/structs
  33. Thread() = default;
  34. // Generic Constructor
  35. template<class FunctionType , class ... Args>
  36. Thread(FunctionType && f, Args && ... args)
  37. {
  38. #if COMPILER_SUPPORTS_GENERIC_LAMBDA_EXPRESSIONS
  39. // This generates cleaner and nicer-to-debug code
  40. auto wrapped = [ = ] {f(args ...);};
  41. #else
  42. auto wrapped = std::bind(f, args ...);
  43. #endif
  44. using Container = decltype(wrapped);
  45. // Small object optimization.
  46. constexpr bool smallObject = (sizeof(Container) <= sizeof(void*)) && (alignof(Container) <= alignof(void*));
  47. if (smallObject)
  48. {
  49. union
  50. {
  51. // sizeof(void*) will trigger placement new errors
  52. // even if code path is not executed
  53. char buf[sizeof(Container)];
  54. void* smallObject;
  55. };
  56. smallObject = nullptr; // to avoid -Wmaybe-uninitialized
  57. // We have to move it to pointer, otherwise wrapped destructor will be called
  58. new(buf) Container(std::move(wrapped));
  59. thread = CreateThread(ThreadProxySmallObject<Container>, smallObject);
  60. }
  61. else
  62. {
  63. std::unique_ptr<Container> ptr(new Container(std::move(wrapped)));
  64. thread = CreateThread(ThreadProxyHeap<Container>, ptr.get());
  65. if (thread)
  66. ptr.release();
  67. }
  68. }
  69. // Thread has to be joined before destructor is called
  70. ~Thread();
  71. // Non-copyable
  72. Thread(const Thread&) = delete;
  73. Thread& operator=(const Thread&) = delete;
  74. // Movable
  75. Thread(Thread&& other);
  76. Thread& operator=(Thread&& other);
  77. // Return true if threads are supported
  78. static bool SupportsThreads();
  79. // Return true if join succeeded
  80. COMPILER_WARN_UNUSED_RESULT bool TryJoin(timeout_ms timeout);
  81. // Yields execution
  82. static inline void YieldExecution()
  83. {
  84. Baselib_Thread_YieldExecution();
  85. }
  86. // Returns thread id
  87. inline Baselib_Thread_Id GetId()
  88. {
  89. return Baselib_Thread_GetId(thread);
  90. }
  91. // Returns current thread id
  92. static inline Baselib_Thread_Id GetCurrentId()
  93. {
  94. return Baselib_Thread_GetCurrentThreadId();
  95. }
  96. private:
  97. Baselib_Thread* thread = nullptr;
  98. static Baselib_Thread* CreateThread(Baselib_Thread_EntryPointFunction function, void* arg);
  99. template<class T>
  100. static void ThreadProxyHeap(void* data)
  101. {
  102. std::unique_ptr<T> ptr(reinterpret_cast<T*>(data));
  103. (*ptr)();
  104. }
  105. template<class T>
  106. static void ThreadProxySmallObject(void* data)
  107. {
  108. T* ptr = reinterpret_cast<T*>(&data);
  109. (*ptr)();
  110. ptr->~T();
  111. }
  112. };
  113. }
  114. }