polyfill_thread.h 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. // SPDX-FileCopyrightText: 2022 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. //
  4. // TODO: remove this file when jthread is supported by all compilation targets
  5. //
  6. #pragma once
  7. #include <version>
  8. #ifdef __cpp_lib_jthread
  9. #include <stop_token>
  10. #include <thread>
  11. namespace Common {
  12. template <typename Condvar, typename Lock, typename Pred>
  13. void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred&& pred) {
  14. cv.wait(lock, token, std::move(pred));
  15. }
  16. } // namespace Common
  17. #else
  18. #include <atomic>
  19. #include <functional>
  20. #include <list>
  21. #include <memory>
  22. #include <mutex>
  23. #include <optional>
  24. #include <thread>
  25. #include <type_traits>
  26. namespace std {
  27. namespace polyfill {
  28. using stop_state_callbacks = list<function<void()>>;
  29. class stop_state {
  30. public:
  31. stop_state() = default;
  32. ~stop_state() = default;
  33. bool request_stop() {
  34. stop_state_callbacks callbacks;
  35. {
  36. scoped_lock lk{m_lock};
  37. if (m_stop_requested.load()) {
  38. // Already set, nothing to do
  39. return false;
  40. }
  41. // Set as requested
  42. m_stop_requested = true;
  43. // Copy callback list
  44. callbacks = m_callbacks;
  45. }
  46. for (auto callback : callbacks) {
  47. callback();
  48. }
  49. return true;
  50. }
  51. bool stop_requested() const {
  52. return m_stop_requested.load();
  53. }
  54. stop_state_callbacks::const_iterator insert_callback(function<void()> f) {
  55. stop_state_callbacks::const_iterator ret{};
  56. bool should_run{};
  57. {
  58. scoped_lock lk{m_lock};
  59. should_run = m_stop_requested.load();
  60. m_callbacks.push_front(f);
  61. ret = m_callbacks.begin();
  62. }
  63. if (should_run) {
  64. f();
  65. }
  66. return ret;
  67. }
  68. void remove_callback(stop_state_callbacks::const_iterator it) {
  69. scoped_lock lk{m_lock};
  70. m_callbacks.erase(it);
  71. }
  72. private:
  73. mutex m_lock;
  74. atomic<bool> m_stop_requested;
  75. stop_state_callbacks m_callbacks;
  76. };
  77. } // namespace polyfill
  78. class stop_token;
  79. class stop_source;
  80. struct nostopstate_t {
  81. explicit nostopstate_t() = default;
  82. };
  83. inline constexpr nostopstate_t nostopstate{};
  84. template <class Callback>
  85. class stop_callback;
  86. class stop_token {
  87. public:
  88. stop_token() noexcept = default;
  89. stop_token(const stop_token&) noexcept = default;
  90. stop_token(stop_token&&) noexcept = default;
  91. stop_token& operator=(const stop_token&) noexcept = default;
  92. stop_token& operator=(stop_token&&) noexcept = default;
  93. ~stop_token() = default;
  94. void swap(stop_token& other) noexcept {
  95. m_stop_state.swap(other.m_stop_state);
  96. }
  97. [[nodiscard]] bool stop_requested() const noexcept {
  98. return m_stop_state && m_stop_state->stop_requested();
  99. }
  100. [[nodiscard]] bool stop_possible() const noexcept {
  101. return m_stop_state != nullptr;
  102. }
  103. private:
  104. friend class stop_source;
  105. template <typename Callback>
  106. friend class stop_callback;
  107. stop_token(shared_ptr<polyfill::stop_state> stop_state) : m_stop_state(move(stop_state)) {}
  108. private:
  109. shared_ptr<polyfill::stop_state> m_stop_state;
  110. };
  111. class stop_source {
  112. public:
  113. stop_source() : m_stop_state(make_shared<polyfill::stop_state>()) {}
  114. explicit stop_source(nostopstate_t) noexcept {}
  115. stop_source(const stop_source&) noexcept = default;
  116. stop_source(stop_source&&) noexcept = default;
  117. stop_source& operator=(const stop_source&) noexcept = default;
  118. stop_source& operator=(stop_source&&) noexcept = default;
  119. ~stop_source() = default;
  120. void swap(stop_source& other) noexcept {
  121. m_stop_state.swap(other.m_stop_state);
  122. }
  123. [[nodiscard]] stop_token get_token() const noexcept {
  124. return stop_token(m_stop_state);
  125. }
  126. [[nodiscard]] bool stop_possible() const noexcept {
  127. return m_stop_state != nullptr;
  128. }
  129. [[nodiscard]] bool stop_requested() const noexcept {
  130. return m_stop_state && m_stop_state->stop_requested();
  131. }
  132. bool request_stop() noexcept {
  133. return m_stop_state && m_stop_state->request_stop();
  134. }
  135. private:
  136. friend class jthread;
  137. explicit stop_source(shared_ptr<polyfill::stop_state> stop_state)
  138. : m_stop_state(move(stop_state)) {}
  139. private:
  140. shared_ptr<polyfill::stop_state> m_stop_state;
  141. };
  142. template <typename Callback>
  143. class stop_callback {
  144. static_assert(is_nothrow_destructible_v<Callback>);
  145. static_assert(is_invocable_v<Callback>);
  146. public:
  147. using callback_type = Callback;
  148. template <typename C>
  149. requires constructible_from<Callback, C>
  150. explicit stop_callback(const stop_token& st,
  151. C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
  152. : m_stop_state(st.m_stop_state) {
  153. if (m_stop_state) {
  154. m_callback = m_stop_state->insert_callback(move(cb));
  155. }
  156. }
  157. template <typename C>
  158. requires constructible_from<Callback, C>
  159. explicit stop_callback(stop_token&& st,
  160. C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
  161. : m_stop_state(move(st.m_stop_state)) {
  162. if (m_stop_state) {
  163. m_callback = m_stop_state->insert_callback(move(cb));
  164. }
  165. }
  166. ~stop_callback() {
  167. if (m_stop_state && m_callback) {
  168. m_stop_state->remove_callback(*m_callback);
  169. }
  170. }
  171. stop_callback(const stop_callback&) = delete;
  172. stop_callback(stop_callback&&) = delete;
  173. stop_callback& operator=(const stop_callback&) = delete;
  174. stop_callback& operator=(stop_callback&&) = delete;
  175. private:
  176. shared_ptr<polyfill::stop_state> m_stop_state;
  177. optional<polyfill::stop_state_callbacks::const_iterator> m_callback;
  178. };
  179. template <typename Callback>
  180. stop_callback(stop_token, Callback) -> stop_callback<Callback>;
  181. class jthread {
  182. public:
  183. using id = thread::id;
  184. using native_handle_type = thread::native_handle_type;
  185. jthread() noexcept = default;
  186. template <typename F, typename... Args,
  187. typename = enable_if_t<!is_same_v<remove_cvref_t<F>, jthread>>>
  188. explicit jthread(F&& f, Args&&... args)
  189. : m_stop_state(make_shared<polyfill::stop_state>()),
  190. m_thread(make_thread(move(f), move(args)...)) {}
  191. ~jthread() {
  192. if (joinable()) {
  193. request_stop();
  194. join();
  195. }
  196. }
  197. jthread(const jthread&) = delete;
  198. jthread(jthread&&) noexcept = default;
  199. jthread& operator=(const jthread&) = delete;
  200. jthread& operator=(jthread&& other) noexcept {
  201. m_thread.swap(other.m_thread);
  202. m_stop_state.swap(other.m_stop_state);
  203. return *this;
  204. }
  205. void swap(jthread& other) noexcept {
  206. m_thread.swap(other.m_thread);
  207. m_stop_state.swap(other.m_stop_state);
  208. }
  209. [[nodiscard]] bool joinable() const noexcept {
  210. return m_thread.joinable();
  211. }
  212. void join() {
  213. m_thread.join();
  214. }
  215. void detach() {
  216. m_thread.detach();
  217. m_stop_state.reset();
  218. }
  219. [[nodiscard]] id get_id() const noexcept {
  220. return m_thread.get_id();
  221. }
  222. [[nodiscard]] native_handle_type native_handle() {
  223. return m_thread.native_handle();
  224. }
  225. [[nodiscard]] stop_source get_stop_source() noexcept {
  226. return stop_source(m_stop_state);
  227. }
  228. [[nodiscard]] stop_token get_stop_token() const noexcept {
  229. return stop_source(m_stop_state).get_token();
  230. }
  231. bool request_stop() noexcept {
  232. return get_stop_source().request_stop();
  233. }
  234. [[nodiscard]] static unsigned int hardware_concurrency() noexcept {
  235. return thread::hardware_concurrency();
  236. }
  237. private:
  238. template <typename F, typename... Args>
  239. thread make_thread(F&& f, Args&&... args) {
  240. if constexpr (is_invocable_v<decay_t<F>, stop_token, decay_t<Args>...>) {
  241. return thread(move(f), get_stop_token(), move(args)...);
  242. } else {
  243. return thread(move(f), move(args)...);
  244. }
  245. }
  246. shared_ptr<polyfill::stop_state> m_stop_state;
  247. thread m_thread;
  248. };
  249. } // namespace std
  250. namespace Common {
  251. template <typename Condvar, typename Lock, typename Pred>
  252. void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred pred) {
  253. if (token.stop_requested()) {
  254. return;
  255. }
  256. std::stop_callback callback(token, [&] { cv.notify_all(); });
  257. cv.wait(lock, [&] { return pred() || token.stop_requested(); });
  258. }
  259. } // namespace Common
  260. #endif