No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Thread.h 4.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  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, typename std::enable_if<std::is_convertible<FunctionType, size_t>::value == false, bool>::type = 0>
  36. Thread(FunctionType && f, Args && ... args) : Thread(0, f, args ...)
  37. {
  38. }
  39. template<class FunctionType , class ... Args>
  40. Thread(const size_t stackSize, FunctionType && f, Args && ... args)
  41. {
  42. #if COMPILER_SUPPORTS_GENERIC_LAMBDA_EXPRESSIONS
  43. // This generates cleaner and nicer-to-debug code
  44. auto wrapped = [ = ] {f(args ...);};
  45. #else
  46. auto wrapped = std::bind(f, args ...);
  47. #endif
  48. using Container = decltype(wrapped);
  49. // Small object optimization.
  50. constexpr bool smallObject = (sizeof(Container) <= sizeof(void*)) && (alignof(Container) <= alignof(void*));
  51. if (smallObject)
  52. {
  53. union
  54. {
  55. // sizeof(void*) will trigger placement new errors
  56. // even if code path is not executed
  57. char buf[sizeof(Container)];
  58. void* smallObject;
  59. };
  60. smallObject = nullptr; // to avoid -Wmaybe-uninitialized
  61. // We have to move it to pointer, otherwise wrapped destructor will be called
  62. new(buf) Container(std::move(wrapped));
  63. thread = CreateThread(ThreadProxySmallObject<Container>, smallObject, stackSize);
  64. }
  65. else
  66. {
  67. std::unique_ptr<Container> ptr(new Container(std::move(wrapped)));
  68. thread = CreateThread(ThreadProxyHeap<Container>, ptr.get(), stackSize);
  69. if (thread)
  70. ptr.release();
  71. }
  72. }
  73. // Thread has to be joined before destructor is called
  74. ~Thread();
  75. // Non-copyable
  76. Thread(const Thread&) = delete;
  77. Thread& operator=(const Thread&) = delete;
  78. // Movable
  79. Thread(Thread&& other);
  80. Thread& operator=(Thread&& other);
  81. // Return true if threads are supported
  82. static bool SupportsThreads();
  83. // Return true if join succeeded
  84. COMPILER_WARN_UNUSED_RESULT bool TryJoin(timeout_ms timeout);
  85. // Yields execution
  86. static inline void YieldExecution()
  87. {
  88. Baselib_Thread_YieldExecution();
  89. }
  90. // Returns thread id
  91. inline Baselib_Thread_Id GetId()
  92. {
  93. return Baselib_Thread_GetId(thread);
  94. }
  95. // Returns current thread id
  96. static inline Baselib_Thread_Id GetCurrentId()
  97. {
  98. return Baselib_Thread_GetCurrentThreadId();
  99. }
  100. private:
  101. Baselib_Thread* thread = nullptr;
  102. static Baselib_Thread* CreateThread(Baselib_Thread_EntryPointFunction function, void* arg, size_t stackSize);
  103. template<class T>
  104. static void ThreadProxyHeap(void* data)
  105. {
  106. std::unique_ptr<T> ptr(reinterpret_cast<T*>(data));
  107. (*ptr)();
  108. }
  109. template<class T>
  110. static void ThreadProxySmallObject(void* data)
  111. {
  112. T* ptr = reinterpret_cast<T*>(&data);
  113. (*ptr)();
  114. ptr->~T();
  115. }
  116. };
  117. }
  118. }