wait_ops_generic.hpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. /*
  2. * Distributed under the Boost Software License, Version 1.0.
  3. * (See accompanying file LICENSE_1_0.txt or copy at
  4. * http://www.boost.org/LICENSE_1_0.txt)
  5. *
  6. * Copyright (c) 2020-2025 Andrey Semashev
  7. */
  8. /*!
  9. * \file atomic/detail/wait_ops_generic.hpp
  10. *
  11. * This header contains generic (lock-based) implementation of the waiting/notifying atomic operations.
  12. *
  13. * This backend is used when lock-free atomic operations are available but native waiting/notifying operations are not.
  14. */
  15. #ifndef BOOST_ATOMIC_DETAIL_WAIT_OPS_GENERIC_HPP_INCLUDED_
  16. #define BOOST_ATOMIC_DETAIL_WAIT_OPS_GENERIC_HPP_INCLUDED_
  17. #include <cstddef>
  18. #include <chrono>
  19. #include <boost/memory_order.hpp>
  20. #include <boost/atomic/thread_pause.hpp>
  21. #include <boost/atomic/detail/config.hpp>
  22. #if !defined(BOOST_WINDOWS)
  23. #include <time.h>
  24. #include <type_traits>
  25. #include <boost/atomic/posix_clock_traits_fwd.hpp>
  26. #include <boost/atomic/detail/has_posix_clock_traits.hpp>
  27. #endif
  28. #include <boost/atomic/detail/chrono.hpp>
  29. #include <boost/atomic/detail/lock_pool.hpp>
  30. #include <boost/atomic/detail/wait_operations_fwd.hpp>
  31. #include <boost/atomic/detail/header.hpp>
  32. #ifdef BOOST_HAS_PRAGMA_ONCE
  33. #pragma once
  34. #endif
  35. namespace boost {
  36. namespace atomics {
  37. namespace detail {
  38. //! Generic implementation of waiting/notifying operations
  39. template< typename Base, bool Interprocess >
  40. struct wait_operations_generic;
  41. template< typename Base >
  42. struct wait_operations_generic< Base, false > :
  43. public Base
  44. {
  45. using base_type = Base;
  46. using storage_type = typename base_type::storage_type;
  47. using scoped_lock = lock_pool::scoped_lock< base_type::storage_alignment, true >;
  48. using scoped_wait_state = lock_pool::scoped_wait_state< base_type::storage_alignment >;
  49. static constexpr bool always_has_native_wait_notify = false;
  50. static BOOST_FORCEINLINE bool has_native_wait_notify(storage_type const volatile&) noexcept
  51. {
  52. return false;
  53. }
  54. static BOOST_FORCEINLINE storage_type wait(storage_type const volatile& storage, storage_type old_val, memory_order order) noexcept
  55. {
  56. storage_type new_val = base_type::load(storage, order);
  57. if (new_val == old_val)
  58. {
  59. scoped_wait_state wait_state(&storage);
  60. new_val = base_type::load(storage, order);
  61. while (new_val == old_val)
  62. {
  63. wait_state.wait();
  64. new_val = base_type::load(storage, order);
  65. }
  66. }
  67. return new_val;
  68. }
  69. private:
  70. template< typename Clock >
  71. static BOOST_FORCEINLINE storage_type wait_until_fallback
  72. (
  73. storage_type const volatile& storage,
  74. storage_type old_val,
  75. typename Clock::time_point timeout,
  76. typename Clock::time_point now,
  77. memory_order order,
  78. bool& timed_out
  79. ) noexcept(noexcept(Clock::now()))
  80. {
  81. storage_type new_val = base_type::load(storage, order);
  82. if (new_val == old_val)
  83. {
  84. scoped_wait_state wait_state(&storage);
  85. while (new_val == old_val)
  86. {
  87. const std::chrono::nanoseconds nsec = atomics::detail::chrono::ceil< std::chrono::nanoseconds >(timeout - now);
  88. if (nsec.count() <= 0)
  89. {
  90. timed_out = true;
  91. break;
  92. }
  93. wait_state.wait_for(nsec);
  94. now = Clock::now();
  95. new_val = base_type::load(storage, order);
  96. }
  97. }
  98. return new_val;
  99. }
  100. #if !defined(BOOST_WINDOWS)
  101. template< typename Clock >
  102. static BOOST_FORCEINLINE storage_type wait_until_abs_timeout
  103. (
  104. storage_type const volatile& storage,
  105. storage_type old_val,
  106. typename Clock::time_point timeout,
  107. memory_order order,
  108. bool& timed_out
  109. ) noexcept
  110. {
  111. storage_type new_val = base_type::load(storage, order);
  112. if (new_val == old_val)
  113. {
  114. scoped_wait_state wait_state(&storage);
  115. const timespec abs_timeout(posix_clock_traits< Clock >::to_timespec(timeout));
  116. if (BOOST_LIKELY(abs_timeout.tv_sec >= 0))
  117. {
  118. while (new_val == old_val)
  119. {
  120. const bool wait_timed_out = wait_state.wait_until(posix_clock_traits< Clock >::clock_id, abs_timeout);
  121. new_val = base_type::load(storage, order);
  122. if (wait_timed_out)
  123. goto timeout_expired;
  124. }
  125. }
  126. else
  127. {
  128. timeout_expired:
  129. timed_out = new_val == old_val;
  130. }
  131. }
  132. return new_val;
  133. }
  134. template< typename Clock >
  135. static BOOST_FORCEINLINE storage_type wait_until_dispatch
  136. (
  137. storage_type const volatile& storage,
  138. storage_type old_val,
  139. typename Clock::time_point timeout,
  140. memory_order order,
  141. bool& timed_out,
  142. std::true_type
  143. ) noexcept
  144. {
  145. return wait_until_abs_timeout< Clock >(storage, old_val, timeout, order, timed_out);
  146. }
  147. template< typename Clock >
  148. static BOOST_FORCEINLINE storage_type wait_until_dispatch
  149. (
  150. storage_type const volatile& storage,
  151. storage_type old_val,
  152. typename Clock::time_point timeout,
  153. memory_order order,
  154. bool& timed_out,
  155. std::false_type
  156. ) noexcept(noexcept(Clock::now()))
  157. {
  158. return wait_until_fallback< Clock >(storage, old_val, timeout, Clock::now(), order, timed_out);
  159. }
  160. public:
  161. template< typename Clock, typename Duration >
  162. static BOOST_FORCEINLINE storage_type wait_until
  163. (
  164. storage_type const volatile& storage,
  165. storage_type old_val,
  166. std::chrono::time_point< Clock, Duration > timeout,
  167. memory_order order,
  168. bool& timed_out
  169. ) noexcept(noexcept(wait_until_dispatch< Clock >(
  170. storage, old_val, timeout, order, timed_out, std::integral_constant< bool, has_posix_clock_traits< Clock >::value >())))
  171. {
  172. return wait_until_dispatch< Clock >(storage, old_val, timeout, order, timed_out, std::integral_constant< bool, has_posix_clock_traits< Clock >::value >());
  173. }
  174. #else // !defined(BOOST_WINDOWS)
  175. public:
  176. template< typename Clock, typename Duration >
  177. static BOOST_FORCEINLINE storage_type wait_until
  178. (
  179. storage_type const volatile& storage,
  180. storage_type old_val,
  181. std::chrono::time_point< Clock, Duration > timeout,
  182. memory_order order,
  183. bool& timed_out
  184. ) noexcept(noexcept(wait_until_fallback< Clock >(storage, old_val, timeout, Clock::now(), order, timed_out)))
  185. {
  186. return wait_until_fallback< Clock >(storage, old_val, timeout, Clock::now(), order, timed_out);
  187. }
  188. #endif // !defined(BOOST_WINDOWS)
  189. template< typename Rep, typename Period >
  190. static BOOST_FORCEINLINE storage_type wait_for
  191. (
  192. storage_type const volatile& storage,
  193. storage_type old_val,
  194. std::chrono::duration< Rep, Period > timeout,
  195. memory_order order,
  196. bool& timed_out
  197. ) noexcept
  198. {
  199. const std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
  200. return wait_until_fallback< std::chrono::steady_clock >(storage, old_val, now + timeout, now, order, timed_out);
  201. }
  202. static BOOST_FORCEINLINE void notify_one(storage_type volatile& storage) noexcept
  203. {
  204. scoped_lock lock(&storage);
  205. lock_pool::notify_one(lock.get_lock_state(), &storage);
  206. }
  207. static BOOST_FORCEINLINE void notify_all(storage_type volatile& storage) noexcept
  208. {
  209. scoped_lock lock(&storage);
  210. lock_pool::notify_all(lock.get_lock_state(), &storage);
  211. }
  212. };
  213. template< typename Base >
  214. struct wait_operations_generic< Base, true > :
  215. public Base
  216. {
  217. using base_type = Base;
  218. using storage_type = typename base_type::storage_type;
  219. static constexpr bool always_has_native_wait_notify = false;
  220. private:
  221. static constexpr unsigned int fast_loop_count = 16u;
  222. public:
  223. static BOOST_FORCEINLINE bool has_native_wait_notify(storage_type const volatile&) noexcept
  224. {
  225. return false;
  226. }
  227. static BOOST_FORCEINLINE storage_type wait(storage_type const volatile& storage, storage_type old_val, memory_order order) noexcept
  228. {
  229. storage_type new_val = base_type::load(storage, order);
  230. if (new_val == old_val)
  231. {
  232. for (unsigned int i = 0u; i < fast_loop_count; ++i)
  233. {
  234. atomics::thread_pause();
  235. new_val = base_type::load(storage, order);
  236. if (new_val != old_val)
  237. goto finish;
  238. }
  239. do
  240. {
  241. atomics::detail::wait_some();
  242. new_val = base_type::load(storage, order);
  243. }
  244. while (new_val == old_val);
  245. }
  246. finish:
  247. return new_val;
  248. }
  249. private:
  250. template< typename Clock >
  251. static BOOST_FORCEINLINE storage_type wait_until_impl
  252. (
  253. storage_type const volatile& storage,
  254. storage_type old_val,
  255. typename Clock::time_point timeout,
  256. typename Clock::time_point now,
  257. memory_order order,
  258. bool& timed_out
  259. ) noexcept(noexcept(Clock::now()))
  260. {
  261. storage_type new_val = base_type::load(storage, order);
  262. if (new_val == old_val)
  263. {
  264. for (unsigned int i = 0u; i < fast_loop_count; ++i)
  265. {
  266. if ((now - timeout).count() >= 0)
  267. {
  268. timed_out = true;
  269. goto finish;
  270. }
  271. atomics::thread_pause();
  272. now = Clock::now();
  273. new_val = base_type::load(storage, order);
  274. if (new_val != old_val)
  275. goto finish;
  276. }
  277. do
  278. {
  279. if ((now - timeout).count() >= 0)
  280. {
  281. timed_out = true;
  282. goto finish;
  283. }
  284. atomics::detail::wait_some();
  285. now = Clock::now();
  286. new_val = base_type::load(storage, order);
  287. }
  288. while (new_val == old_val);
  289. }
  290. finish:
  291. return new_val;
  292. }
  293. public:
  294. template< typename Clock, typename Duration >
  295. static BOOST_FORCEINLINE storage_type wait_until
  296. (
  297. storage_type const volatile& storage,
  298. storage_type old_val,
  299. std::chrono::time_point< Clock, Duration > timeout,
  300. memory_order order,
  301. bool& timed_out
  302. ) noexcept(noexcept(wait_until_impl< Clock >(storage, old_val, timeout, Clock::now(), order, timed_out)))
  303. {
  304. return wait_until_impl< Clock >(storage, old_val, timeout, Clock::now(), order, timed_out);
  305. }
  306. template< typename Rep, typename Period >
  307. static BOOST_FORCEINLINE storage_type wait_for
  308. (
  309. storage_type const volatile& storage,
  310. storage_type old_val,
  311. std::chrono::duration< Rep, Period > timeout,
  312. memory_order order,
  313. bool& timed_out
  314. ) noexcept
  315. {
  316. const std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
  317. return wait_until_impl< std::chrono::steady_clock >(storage, old_val, now + timeout, now, order, timed_out);
  318. }
  319. static BOOST_FORCEINLINE void notify_one(storage_type volatile&) noexcept
  320. {
  321. }
  322. static BOOST_FORCEINLINE void notify_all(storage_type volatile&) noexcept
  323. {
  324. }
  325. };
  326. template< typename Base, std::size_t Size, bool Interprocess >
  327. struct wait_operations< Base, Size, true, Interprocess > :
  328. public wait_operations_generic< Base, Interprocess >
  329. {
  330. };
  331. } // namespace detail
  332. } // namespace atomics
  333. } // namespace boost
  334. #include <boost/atomic/detail/footer.hpp>
  335. #endif // BOOST_ATOMIC_DETAIL_WAIT_OPS_GENERIC_HPP_INCLUDED_