atomic_wait.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. // -*- C++ -*- header.
  2. // Copyright (C) 2020-2021 Free Software Foundation, Inc.
  3. //
  4. // This file is part of the GNU ISO C++ Library. This library is free
  5. // software; you can redistribute it and/or modify it under the
  6. // terms of the GNU General Public License as published by the
  7. // Free Software Foundation; either version 3, or (at your option)
  8. // any later version.
  9. // This library is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. // Under Section 7 of GPL version 3, you are granted additional
  14. // permissions described in the GCC Runtime Library Exception, version
  15. // 3.1, as published by the Free Software Foundation.
  16. // You should have received a copy of the GNU General Public License and
  17. // a copy of the GCC Runtime Library Exception along with this program;
  18. // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
  19. // <http://www.gnu.org/licenses/>.
  20. /** @file bits/atomic_wait.h
  21. * This is an internal header file, included by other library headers.
  22. * Do not attempt to use it directly. @headername{atomic}
  23. */
  24. #ifndef _GLIBCXX_ATOMIC_WAIT_H
  25. #define _GLIBCXX_ATOMIC_WAIT_H 1
  26. #pragma GCC system_header
  27. #include <bits/c++config.h>
  28. #if defined _GLIBCXX_HAS_GTHREADS || defined _GLIBCXX_HAVE_LINUX_FUTEX
  29. #include <bits/functional_hash.h>
  30. #include <bits/gthr.h>
  31. #include <ext/numeric_traits.h>
  32. #ifdef _GLIBCXX_HAVE_LINUX_FUTEX
  33. # include <cerrno>
  34. # include <climits>
  35. # include <unistd.h>
  36. # include <syscall.h>
  37. # include <bits/functexcept.h>
  38. #endif
  39. # include <bits/std_mutex.h> // std::mutex, std::__condvar
  40. #define __cpp_lib_atomic_wait 201907L
  41. namespace std _GLIBCXX_VISIBILITY(default)
  42. {
  43. _GLIBCXX_BEGIN_NAMESPACE_VERSION
  44. namespace __detail
  45. {
  46. #ifdef _GLIBCXX_HAVE_LINUX_FUTEX
  47. using __platform_wait_t = int;
  48. static constexpr size_t __platform_wait_alignment = 4;
  49. #else
  50. using __platform_wait_t = uint64_t;
  51. static constexpr size_t __platform_wait_alignment
  52. = __alignof__(__platform_wait_t);
  53. #endif
  54. } // namespace __detail
  55. template<typename _Tp>
  56. inline constexpr bool __platform_wait_uses_type
  57. #ifdef _GLIBCXX_HAVE_PLATFORM_WAIT
  58. = is_scalar_v<_Tp>
  59. && ((sizeof(_Tp) == sizeof(__detail::__platform_wait_t))
  60. && (alignof(_Tp*) >= __platform_wait_alignment));
  61. #else
  62. = false;
  63. #endif
  64. namespace __detail
  65. {
  66. #ifdef _GLIBCXX_HAVE_LINUX_FUTEX
  67. #define _GLIBCXX_HAVE_PLATFORM_WAIT 1
  68. enum class __futex_wait_flags : int
  69. {
  70. #ifdef _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE
  71. __private_flag = 128,
  72. #else
  73. __private_flag = 0,
  74. #endif
  75. __wait = 0,
  76. __wake = 1,
  77. __wait_bitset = 9,
  78. __wake_bitset = 10,
  79. __wait_private = __wait | __private_flag,
  80. __wake_private = __wake | __private_flag,
  81. __wait_bitset_private = __wait_bitset | __private_flag,
  82. __wake_bitset_private = __wake_bitset | __private_flag,
  83. __bitset_match_any = -1
  84. };
  85. template<typename _Tp>
  86. void
  87. __platform_wait(const _Tp* __addr, __platform_wait_t __val) noexcept
  88. {
  89. auto __e = syscall (SYS_futex, static_cast<const void*>(__addr),
  90. static_cast<int>(__futex_wait_flags::__wait_private),
  91. __val, nullptr);
  92. if (!__e || errno == EAGAIN)
  93. return;
  94. if (errno != EINTR)
  95. __throw_system_error(errno);
  96. }
  97. template<typename _Tp>
  98. void
  99. __platform_notify(const _Tp* __addr, bool __all) noexcept
  100. {
  101. syscall (SYS_futex, static_cast<const void*>(__addr),
  102. static_cast<int>(__futex_wait_flags::__wake_private),
  103. __all ? INT_MAX : 1);
  104. }
  105. #else
  106. // define _GLIBCX_HAVE_PLATFORM_WAIT and implement __platform_wait()
  107. // and __platform_notify() if there is a more efficient primitive supported
  108. // by the platform (e.g. __ulock_wait()/__ulock_wake()) which is better than
  109. // a mutex/condvar based wait
  110. #endif
  111. inline void
  112. __thread_yield() noexcept
  113. {
  114. #if defined _GLIBCXX_HAS_GTHREADS && defined _GLIBCXX_USE_SCHED_YIELD
  115. __gthread_yield();
  116. #endif
  117. }
  118. inline void
  119. __thread_relax() noexcept
  120. {
  121. #if defined __i386__ || defined __x86_64__
  122. __builtin_ia32_pause();
  123. #else
  124. __thread_yield();
  125. #endif
  126. }
  127. constexpr auto __atomic_spin_count_1 = 12;
  128. constexpr auto __atomic_spin_count_2 = 4;
  129. struct __default_spin_policy
  130. {
  131. bool
  132. operator()() const noexcept
  133. { return false; }
  134. };
  135. template<typename _Pred,
  136. typename _Spin = __default_spin_policy>
  137. bool
  138. __atomic_spin(_Pred& __pred, _Spin __spin = _Spin{ }) noexcept
  139. {
  140. for (auto __i = 0; __i < __atomic_spin_count_1; ++__i)
  141. {
  142. if (__pred())
  143. return true;
  144. __detail::__thread_relax();
  145. }
  146. for (auto __i = 0; __i < __atomic_spin_count_2; ++__i)
  147. {
  148. if (__pred())
  149. return true;
  150. __detail::__thread_yield();
  151. }
  152. while (__spin())
  153. {
  154. if (__pred())
  155. return true;
  156. }
  157. return false;
  158. }
  159. template<typename _Tp>
  160. bool __atomic_compare(const _Tp& __a, const _Tp& __b)
  161. {
  162. // TODO make this do the correct padding bit ignoring comparison
  163. return __builtin_memcmp(&__a, &__b, sizeof(_Tp)) != 0;
  164. }
  165. struct __waiter_pool_base
  166. {
  167. #ifdef __cpp_lib_hardware_interference_size
  168. static constexpr auto _S_align = hardware_destructive_interference_size;
  169. #else
  170. static constexpr auto _S_align = 64;
  171. #endif
  172. alignas(_S_align) __platform_wait_t _M_wait = 0;
  173. #ifndef _GLIBCXX_HAVE_PLATFORM_WAIT
  174. mutex _M_mtx;
  175. #endif
  176. alignas(_S_align) __platform_wait_t _M_ver = 0;
  177. #ifndef _GLIBCXX_HAVE_PLATFORM_WAIT
  178. __condvar _M_cv;
  179. #endif
  180. __waiter_pool_base() = default;
  181. void
  182. _M_enter_wait() noexcept
  183. { __atomic_fetch_add(&_M_wait, 1, __ATOMIC_ACQ_REL); }
  184. void
  185. _M_leave_wait() noexcept
  186. { __atomic_fetch_sub(&_M_wait, 1, __ATOMIC_ACQ_REL); }
  187. bool
  188. _M_waiting() const noexcept
  189. {
  190. __platform_wait_t __res;
  191. __atomic_load(&_M_wait, &__res, __ATOMIC_ACQUIRE);
  192. return __res > 0;
  193. }
  194. void
  195. _M_notify(const __platform_wait_t* __addr, bool __all, bool __bare) noexcept
  196. {
  197. if (!(__bare || _M_waiting()))
  198. return;
  199. #ifdef _GLIBCXX_HAVE_PLATFORM_WAIT
  200. __platform_notify(__addr, __all);
  201. #else
  202. if (__all)
  203. _M_cv.notify_all();
  204. else
  205. _M_cv.notify_one();
  206. #endif
  207. }
  208. static __waiter_pool_base&
  209. _S_for(const void* __addr) noexcept
  210. {
  211. constexpr uintptr_t __ct = 16;
  212. static __waiter_pool_base __w[__ct];
  213. auto __key = (uintptr_t(__addr) >> 2) % __ct;
  214. return __w[__key];
  215. }
  216. };
  217. struct __waiter_pool : __waiter_pool_base
  218. {
  219. void
  220. _M_do_wait(const __platform_wait_t* __addr, __platform_wait_t __old) noexcept
  221. {
  222. #ifdef _GLIBCXX_HAVE_PLATFORM_WAIT
  223. __platform_wait(__addr, __old);
  224. #else
  225. __platform_wait_t __val;
  226. __atomic_load(__addr, &__val, __ATOMIC_RELAXED);
  227. if (__val == __old)
  228. {
  229. lock_guard<mutex> __l(_M_mtx);
  230. _M_cv.wait(_M_mtx);
  231. }
  232. #endif // __GLIBCXX_HAVE_PLATFORM_WAIT
  233. }
  234. };
  235. template<typename _Tp>
  236. struct __waiter_base
  237. {
  238. using __waiter_type = _Tp;
  239. __waiter_type& _M_w;
  240. __platform_wait_t* _M_addr;
  241. template<typename _Up>
  242. static __platform_wait_t*
  243. _S_wait_addr(const _Up* __a, __platform_wait_t* __b)
  244. {
  245. if constexpr (__platform_wait_uses_type<_Up>)
  246. return reinterpret_cast<__platform_wait_t*>(const_cast<_Up*>(__a));
  247. else
  248. return __b;
  249. }
  250. static __waiter_type&
  251. _S_for(const void* __addr) noexcept
  252. {
  253. static_assert(sizeof(__waiter_type) == sizeof(__waiter_pool_base));
  254. auto& res = __waiter_pool_base::_S_for(__addr);
  255. return reinterpret_cast<__waiter_type&>(res);
  256. }
  257. template<typename _Up>
  258. explicit __waiter_base(const _Up* __addr) noexcept
  259. : _M_w(_S_for(__addr))
  260. , _M_addr(_S_wait_addr(__addr, &_M_w._M_ver))
  261. {
  262. }
  263. void
  264. _M_notify(bool __all, bool __bare = false)
  265. {
  266. if (_M_addr == &_M_w._M_ver)
  267. __atomic_fetch_add(_M_addr, 1, __ATOMIC_ACQ_REL);
  268. _M_w._M_notify(_M_addr, __all, __bare);
  269. }
  270. template<typename _Up, typename _ValFn,
  271. typename _Spin = __default_spin_policy>
  272. static bool
  273. _S_do_spin_v(__platform_wait_t* __addr,
  274. const _Up& __old, _ValFn __vfn,
  275. __platform_wait_t& __val,
  276. _Spin __spin = _Spin{ })
  277. {
  278. auto const __pred = [=]
  279. { return __detail::__atomic_compare(__old, __vfn()); };
  280. if constexpr (__platform_wait_uses_type<_Up>)
  281. {
  282. __val == __old;
  283. }
  284. else
  285. {
  286. __atomic_load(__addr, &__val, __ATOMIC_RELAXED);
  287. }
  288. return __atomic_spin(__pred, __spin);
  289. }
  290. template<typename _Up, typename _ValFn,
  291. typename _Spin = __default_spin_policy>
  292. bool
  293. _M_do_spin_v(const _Up& __old, _ValFn __vfn,
  294. __platform_wait_t& __val,
  295. _Spin __spin = _Spin{ })
  296. { return _S_do_spin_v(_M_addr, __old, __vfn, __val, __spin); }
  297. template<typename _Pred,
  298. typename _Spin = __default_spin_policy>
  299. static bool
  300. _S_do_spin(const __platform_wait_t* __addr,
  301. _Pred __pred,
  302. __platform_wait_t& __val,
  303. _Spin __spin = _Spin{ })
  304. {
  305. __atomic_load(__addr, &__val, __ATOMIC_RELAXED);
  306. return __atomic_spin(__pred, __spin);
  307. }
  308. template<typename _Pred,
  309. typename _Spin = __default_spin_policy>
  310. bool
  311. _M_do_spin(_Pred __pred, __platform_wait_t& __val,
  312. _Spin __spin = _Spin{ })
  313. { return _S_do_spin(_M_addr, __pred, __val, __spin); }
  314. };
  315. template<typename _EntersWait>
  316. struct __waiter : __waiter_base<__waiter_pool>
  317. {
  318. using __base_type = __waiter_base<__waiter_pool>;
  319. template<typename _Tp>
  320. explicit __waiter(const _Tp* __addr) noexcept
  321. : __base_type(__addr)
  322. {
  323. if constexpr (_EntersWait::value)
  324. _M_w._M_enter_wait();
  325. }
  326. ~__waiter()
  327. {
  328. if constexpr (_EntersWait::value)
  329. _M_w._M_leave_wait();
  330. }
  331. template<typename _Tp, typename _ValFn>
  332. void
  333. _M_do_wait_v(_Tp __old, _ValFn __vfn)
  334. {
  335. __platform_wait_t __val;
  336. if (__base_type::_M_do_spin_v(__old, __vfn, __val))
  337. return;
  338. __base_type::_M_w._M_do_wait(__base_type::_M_addr, __val);
  339. }
  340. template<typename _Pred>
  341. void
  342. _M_do_wait(_Pred __pred) noexcept
  343. {
  344. do
  345. {
  346. __platform_wait_t __val;
  347. if (__base_type::_M_do_spin(__pred, __val))
  348. return;
  349. __base_type::_M_w._M_do_wait(__base_type::_M_addr, __val);
  350. }
  351. while (!__pred());
  352. }
  353. };
  354. using __enters_wait = __waiter<std::true_type>;
  355. using __bare_wait = __waiter<std::false_type>;
  356. } // namespace __detail
  357. template<typename _Tp, typename _ValFn>
  358. void
  359. __atomic_wait_address_v(const _Tp* __addr, _Tp __old,
  360. _ValFn __vfn) noexcept
  361. {
  362. __detail::__enters_wait __w(__addr);
  363. __w._M_do_wait_v(__old, __vfn);
  364. }
  365. template<typename _Tp, typename _Pred>
  366. void
  367. __atomic_wait_address(const _Tp* __addr, _Pred __pred) noexcept
  368. {
  369. __detail::__enters_wait __w(__addr);
  370. __w._M_do_wait(__pred);
  371. }
  372. // This call is to be used by atomic types which track contention externally
  373. template<typename _Pred>
  374. void
  375. __atomic_wait_address_bare(const __detail::__platform_wait_t* __addr,
  376. _Pred __pred) noexcept
  377. {
  378. #ifdef _GLIBCXX_HAVE_PLATFORM_WAIT
  379. do
  380. {
  381. __detail::__platform_wait_t __val;
  382. if (__detail::__bare_wait::_S_do_spin(__addr, __pred, __val))
  383. return;
  384. __detail::__platform_wait(__addr, __val);
  385. }
  386. while (!__pred());
  387. #else // !_GLIBCXX_HAVE_PLATFORM_WAIT
  388. __detail::__bare_wait __w(__addr);
  389. __w._M_do_wait(__pred);
  390. #endif
  391. }
  392. template<typename _Tp>
  393. void
  394. __atomic_notify_address(const _Tp* __addr, bool __all) noexcept
  395. {
  396. __detail::__bare_wait __w(__addr);
  397. __w._M_notify(__all, true);
  398. }
  399. // This call is to be used by atomic types which track contention externally
  400. inline void
  401. __atomic_notify_address_bare(const __detail::__platform_wait_t* __addr,
  402. bool __all) noexcept
  403. {
  404. #ifdef _GLIBCXX_HAVE_PLATFORM_WAIT
  405. __detail::__platform_notify(__addr, __all);
  406. #else
  407. __detail::__bare_wait __w(__addr);
  408. __w._M_notify(__all, true);
  409. #endif
  410. }
  411. _GLIBCXX_END_NAMESPACE_VERSION
  412. } // namespace std
  413. #endif // GTHREADS || LINUX_FUTEX
  414. #endif // _GLIBCXX_ATOMIC_WAIT_H