semaphore_base.h 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. // -*- C++ -*- header.
  2. // Copyright (C) 2020-2022 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/semaphore_base.h
  21. * This is an internal header file, included by other library headers.
  22. * Do not attempt to use it directly. @headername{semaphore}
  23. */
  24. #ifndef _GLIBCXX_SEMAPHORE_BASE_H
  25. #define _GLIBCXX_SEMAPHORE_BASE_H 1
  26. #pragma GCC system_header
  27. #include <bits/atomic_base.h>
  28. #include <bits/chrono.h>
  29. #if __cpp_lib_atomic_wait
  30. #include <bits/atomic_timed_wait.h>
  31. #include <ext/numeric_traits.h>
  32. #endif // __cpp_lib_atomic_wait
  33. #ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
  34. # include <cerrno> // errno, EINTR, EAGAIN etc.
  35. # include <limits.h> // SEM_VALUE_MAX
  36. # include <semaphore.h> // sem_t, sem_init, sem_wait, sem_post etc.
  37. #endif
  38. namespace std _GLIBCXX_VISIBILITY(default)
  39. {
  40. _GLIBCXX_BEGIN_NAMESPACE_VERSION
  41. #ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
  42. struct __platform_semaphore
  43. {
  44. using __clock_t = chrono::system_clock;
  45. #ifdef SEM_VALUE_MAX
  46. static constexpr ptrdiff_t _S_max = SEM_VALUE_MAX;
  47. #else
  48. static constexpr ptrdiff_t _S_max = _POSIX_SEM_VALUE_MAX;
  49. #endif
  50. explicit __platform_semaphore(ptrdiff_t __count) noexcept
  51. {
  52. sem_init(&_M_semaphore, 0, __count);
  53. }
  54. __platform_semaphore(const __platform_semaphore&) = delete;
  55. __platform_semaphore& operator=(const __platform_semaphore&) = delete;
  56. ~__platform_semaphore()
  57. { sem_destroy(&_M_semaphore); }
  58. _GLIBCXX_ALWAYS_INLINE void
  59. _M_acquire() noexcept
  60. {
  61. for (;;)
  62. {
  63. auto __err = sem_wait(&_M_semaphore);
  64. if (__err && (errno == EINTR))
  65. continue;
  66. else if (__err)
  67. std::__terminate();
  68. else
  69. break;
  70. }
  71. }
  72. _GLIBCXX_ALWAYS_INLINE bool
  73. _M_try_acquire() noexcept
  74. {
  75. for (;;)
  76. {
  77. auto __err = sem_trywait(&_M_semaphore);
  78. if (__err && (errno == EINTR))
  79. continue;
  80. else if (__err && (errno == EAGAIN))
  81. return false;
  82. else if (__err)
  83. std::__terminate();
  84. else
  85. break;
  86. }
  87. return true;
  88. }
  89. _GLIBCXX_ALWAYS_INLINE void
  90. _M_release(std::ptrdiff_t __update) noexcept
  91. {
  92. for(; __update != 0; --__update)
  93. {
  94. auto __err = sem_post(&_M_semaphore);
  95. if (__err)
  96. std::__terminate();
  97. }
  98. }
  99. bool
  100. _M_try_acquire_until_impl(const chrono::time_point<__clock_t>& __atime)
  101. noexcept
  102. {
  103. auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
  104. auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
  105. struct timespec __ts =
  106. {
  107. static_cast<std::time_t>(__s.time_since_epoch().count()),
  108. static_cast<long>(__ns.count())
  109. };
  110. for (;;)
  111. {
  112. if (auto __err = sem_timedwait(&_M_semaphore, &__ts))
  113. {
  114. if (errno == EINTR)
  115. continue;
  116. else if (errno == ETIMEDOUT || errno == EINVAL)
  117. return false;
  118. else
  119. std::__terminate();
  120. }
  121. else
  122. break;
  123. }
  124. return true;
  125. }
  126. template<typename _Clock, typename _Duration>
  127. bool
  128. _M_try_acquire_until(const chrono::time_point<_Clock,
  129. _Duration>& __atime) noexcept
  130. {
  131. if constexpr (std::is_same_v<__clock_t, _Clock>)
  132. {
  133. return _M_try_acquire_until_impl(__atime);
  134. }
  135. else
  136. {
  137. const typename _Clock::time_point __c_entry = _Clock::now();
  138. const auto __s_entry = __clock_t::now();
  139. const auto __delta = __atime - __c_entry;
  140. const auto __s_atime = __s_entry + __delta;
  141. if (_M_try_acquire_until_impl(__s_atime))
  142. return true;
  143. // We got a timeout when measured against __clock_t but
  144. // we need to check against the caller-supplied clock
  145. // to tell whether we should return a timeout.
  146. return (_Clock::now() < __atime);
  147. }
  148. }
  149. template<typename _Rep, typename _Period>
  150. _GLIBCXX_ALWAYS_INLINE bool
  151. _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime)
  152. noexcept
  153. { return _M_try_acquire_until(__clock_t::now() + __rtime); }
  154. private:
  155. sem_t _M_semaphore;
  156. };
  157. #endif // _GLIBCXX_HAVE_POSIX_SEMAPHORE
  158. #if __cpp_lib_atomic_wait
  159. struct __atomic_semaphore
  160. {
  161. static constexpr ptrdiff_t _S_max = __gnu_cxx::__int_traits<int>::__max;
  162. explicit __atomic_semaphore(__detail::__platform_wait_t __count) noexcept
  163. : _M_counter(__count)
  164. {
  165. __glibcxx_assert(__count >= 0 && __count <= _S_max);
  166. }
  167. __atomic_semaphore(const __atomic_semaphore&) = delete;
  168. __atomic_semaphore& operator=(const __atomic_semaphore&) = delete;
  169. static _GLIBCXX_ALWAYS_INLINE bool
  170. _S_do_try_acquire(__detail::__platform_wait_t* __counter) noexcept
  171. {
  172. auto __old = __atomic_impl::load(__counter, memory_order::acquire);
  173. if (__old == 0)
  174. return false;
  175. return __atomic_impl::compare_exchange_strong(__counter,
  176. __old, __old - 1,
  177. memory_order::acquire,
  178. memory_order::relaxed);
  179. }
  180. _GLIBCXX_ALWAYS_INLINE void
  181. _M_acquire() noexcept
  182. {
  183. auto const __pred =
  184. [this] { return _S_do_try_acquire(&this->_M_counter); };
  185. std::__atomic_wait_address_bare(&_M_counter, __pred);
  186. }
  187. bool
  188. _M_try_acquire() noexcept
  189. {
  190. auto const __pred =
  191. [this] { return _S_do_try_acquire(&this->_M_counter); };
  192. return std::__detail::__atomic_spin(__pred);
  193. }
  194. template<typename _Clock, typename _Duration>
  195. _GLIBCXX_ALWAYS_INLINE bool
  196. _M_try_acquire_until(const chrono::time_point<_Clock,
  197. _Duration>& __atime) noexcept
  198. {
  199. auto const __pred =
  200. [this] { return _S_do_try_acquire(&this->_M_counter); };
  201. return __atomic_wait_address_until_bare(&_M_counter, __pred, __atime);
  202. }
  203. template<typename _Rep, typename _Period>
  204. _GLIBCXX_ALWAYS_INLINE bool
  205. _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime)
  206. noexcept
  207. {
  208. auto const __pred =
  209. [this] { return _S_do_try_acquire(&this->_M_counter); };
  210. return __atomic_wait_address_for_bare(&_M_counter, __pred, __rtime);
  211. }
  212. _GLIBCXX_ALWAYS_INLINE void
  213. _M_release(ptrdiff_t __update) noexcept
  214. {
  215. if (0 < __atomic_impl::fetch_add(&_M_counter, __update, memory_order_release))
  216. return;
  217. if (__update > 1)
  218. __atomic_notify_address_bare(&_M_counter, true);
  219. else
  220. __atomic_notify_address_bare(&_M_counter, true);
  221. // FIXME - Figure out why this does not wake a waiting thread
  222. // __atomic_notify_address_bare(&_M_counter, false);
  223. }
  224. private:
  225. alignas(__detail::__platform_wait_alignment)
  226. __detail::__platform_wait_t _M_counter;
  227. };
  228. #endif // __cpp_lib_atomic_wait
  229. // Note: the _GLIBCXX_USE_POSIX_SEMAPHORE macro can be used to force the
  230. // use of Posix semaphores (sem_t). Doing so however, alters the ABI.
  231. #if defined __cpp_lib_atomic_wait && !_GLIBCXX_USE_POSIX_SEMAPHORE
  232. using __semaphore_impl = __atomic_semaphore;
  233. #elif _GLIBCXX_HAVE_POSIX_SEMAPHORE
  234. using __semaphore_impl = __platform_semaphore;
  235. #endif
  236. _GLIBCXX_END_NAMESPACE_VERSION
  237. } // namespace std
  238. #endif // _GLIBCXX_SEMAPHORE_BASE_H