libstdc++
semaphore_base.h
Go to the documentation of this file.
1 // -*- C++ -*- header.
2 
3 // Copyright (C) 2020-2021 Free Software Foundation, Inc.
4 //
5 // This file is part of the GNU ISO C++ Library. This library is free
6 // software; you can redistribute it and/or modify it under the
7 // terms of the GNU General Public License as published by the
8 // Free Software Foundation; either version 3, or (at your option)
9 // any later version.
10 
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 
16 // Under Section 7 of GPL version 3, you are granted additional
17 // permissions described in the GCC Runtime Library Exception, version
18 // 3.1, as published by the Free Software Foundation.
19 
20 // You should have received a copy of the GNU General Public License and
21 // a copy of the GCC Runtime Library Exception along with this program;
22 // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23 // <http://www.gnu.org/licenses/>.
24 
25 /** @file bits/semaphore_base.h
26  * This is an internal header file, included by other library headers.
27  * Do not attempt to use it directly. @headername{semaphore}
28  */
29 
30 #ifndef _GLIBCXX_SEMAPHORE_BASE_H
31 #define _GLIBCXX_SEMAPHORE_BASE_H 1
32 
33 #pragma GCC system_header
34 
35 #include <bits/atomic_base.h>
36 #if __cpp_lib_atomic_wait
37 #include <bits/atomic_timed_wait.h>
38 #include <ext/numeric_traits.h>
39 #endif // __cpp_lib_atomic_wait
40 
41 #ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
42 # include <exception> // std::terminate
43 # include <cerrno> // errno, EINTR, EAGAIN etc.
44 # include <limits.h> // SEM_VALUE_MAX
45 # include <semaphore.h> // sem_t, sem_init, sem_wait, sem_post etc.
46 #endif
47 
48 #include <chrono>
49 #include <type_traits>
50 
51 namespace std _GLIBCXX_VISIBILITY(default)
52 {
53 _GLIBCXX_BEGIN_NAMESPACE_VERSION
54 
55 #ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
56  struct __platform_semaphore
57  {
58  using __clock_t = chrono::system_clock;
59 #ifdef SEM_VALUE_MAX
60  static constexpr ptrdiff_t _S_max = SEM_VALUE_MAX;
61 #else
62  static constexpr ptrdiff_t _S_max = _POSIX_SEM_VALUE_MAX;
63 #endif
64 
65  explicit __platform_semaphore(ptrdiff_t __count) noexcept
66  {
67  sem_init(&_M_semaphore, 0, __count);
68  }
69 
70  __platform_semaphore(const __platform_semaphore&) = delete;
71  __platform_semaphore& operator=(const __platform_semaphore&) = delete;
72 
73  ~__platform_semaphore()
74  { sem_destroy(&_M_semaphore); }
75 
76  _GLIBCXX_ALWAYS_INLINE void
77  _M_acquire() noexcept
78  {
79  for (;;)
80  {
81  auto __err = sem_wait(&_M_semaphore);
82  if (__err && (errno == EINTR))
83  continue;
84  else if (__err)
86  else
87  break;
88  }
89  }
90 
91  _GLIBCXX_ALWAYS_INLINE bool
92  _M_try_acquire() noexcept
93  {
94  for (;;)
95  {
96  auto __err = sem_trywait(&_M_semaphore);
97  if (__err && (errno == EINTR))
98  continue;
99  else if (__err && (errno == EAGAIN))
100  return false;
101  else if (__err)
102  std::terminate();
103  else
104  break;
105  }
106  return true;
107  }
108 
109  _GLIBCXX_ALWAYS_INLINE void
110  _M_release(std::ptrdiff_t __update) noexcept
111  {
112  for(; __update != 0; --__update)
113  {
114  auto __err = sem_post(&_M_semaphore);
115  if (__err)
116  std::terminate();
117  }
118  }
119 
120  bool
121  _M_try_acquire_until_impl(const chrono::time_point<__clock_t>& __atime)
122  noexcept
123  {
124 
125  auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
126  auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
127 
128  struct timespec __ts =
129  {
130  static_cast<std::time_t>(__s.time_since_epoch().count()),
131  static_cast<long>(__ns.count())
132  };
133 
134  for (;;)
135  {
136  if (auto __err = sem_timedwait(&_M_semaphore, &__ts))
137  {
138  if (errno == EINTR)
139  continue;
140  else if (errno == ETIMEDOUT || errno == EINVAL)
141  return false;
142  else
143  std::terminate();
144  }
145  else
146  break;
147  }
148  return true;
149  }
150 
151  template<typename _Clock, typename _Duration>
152  bool
153  _M_try_acquire_until(const chrono::time_point<_Clock,
154  _Duration>& __atime) noexcept
155  {
156  if constexpr (std::is_same_v<__clock_t, _Clock>)
157  {
158  return _M_try_acquire_until_impl(__atime);
159  }
160  else
161  {
162  const typename _Clock::time_point __c_entry = _Clock::now();
163  const auto __s_entry = __clock_t::now();
164  const auto __delta = __atime - __c_entry;
165  const auto __s_atime = __s_entry + __delta;
166  if (_M_try_acquire_until_impl(__s_atime))
167  return true;
168 
169  // We got a timeout when measured against __clock_t but
170  // we need to check against the caller-supplied clock
171  // to tell whether we should return a timeout.
172  return (_Clock::now() < __atime);
173  }
174  }
175 
176  template<typename _Rep, typename _Period>
177  _GLIBCXX_ALWAYS_INLINE bool
178  _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime)
179  noexcept
180  { return _M_try_acquire_until(__clock_t::now() + __rtime); }
181 
182  private:
183  sem_t _M_semaphore;
184  };
185 #endif // _GLIBCXX_HAVE_POSIX_SEMAPHORE
186 
187 #if __cpp_lib_atomic_wait
188  struct __atomic_semaphore
189  {
190  static constexpr ptrdiff_t _S_max = __gnu_cxx::__int_traits<int>::__max;
191  explicit __atomic_semaphore(__detail::__platform_wait_t __count) noexcept
192  : _M_counter(__count)
193  {
194  __glibcxx_assert(__count >= 0 && __count <= _S_max);
195  }
196 
197  __atomic_semaphore(const __atomic_semaphore&) = delete;
198  __atomic_semaphore& operator=(const __atomic_semaphore&) = delete;
199 
200  static _GLIBCXX_ALWAYS_INLINE bool
201  _S_do_try_acquire(__detail::__platform_wait_t* __counter) noexcept
202  {
203  auto __old = __atomic_impl::load(__counter, memory_order::acquire);
204  if (__old == 0)
205  return false;
206 
207  return __atomic_impl::compare_exchange_strong(__counter,
208  __old, __old - 1,
209  memory_order::acquire,
210  memory_order::relaxed);
211  }
212 
213  _GLIBCXX_ALWAYS_INLINE void
214  _M_acquire() noexcept
215  {
216  auto const __pred =
217  [this] { return _S_do_try_acquire(&this->_M_counter); };
218  std::__atomic_wait_address_bare(&_M_counter, __pred);
219  }
220 
221  bool
222  _M_try_acquire() noexcept
223  {
224  auto const __pred =
225  [this] { return _S_do_try_acquire(&this->_M_counter); };
226  return std::__detail::__atomic_spin(__pred);
227  }
228 
229  template<typename _Clock, typename _Duration>
230  _GLIBCXX_ALWAYS_INLINE bool
231  _M_try_acquire_until(const chrono::time_point<_Clock,
232  _Duration>& __atime) noexcept
233  {
234  auto const __pred =
235  [this] { return _S_do_try_acquire(&this->_M_counter); };
236 
237  return __atomic_wait_address_until_bare(&_M_counter, __pred, __atime);
238  }
239 
240  template<typename _Rep, typename _Period>
241  _GLIBCXX_ALWAYS_INLINE bool
242  _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime)
243  noexcept
244  {
245  auto const __pred =
246  [this] { return _S_do_try_acquire(&this->_M_counter); };
247 
248  return __atomic_wait_address_for_bare(&_M_counter, __pred, __rtime);
249  }
250 
251  _GLIBCXX_ALWAYS_INLINE void
252  _M_release(ptrdiff_t __update) noexcept
253  {
254  if (0 < __atomic_impl::fetch_add(&_M_counter, __update, memory_order_release))
255  return;
256  if (__update > 1)
257  __atomic_notify_address_bare(&_M_counter, true);
258  else
259  __atomic_notify_address_bare(&_M_counter, true);
260 // FIXME - Figure out why this does not wake a waiting thread
261 // __atomic_notify_address_bare(&_M_counter, false);
262  }
263 
264  private:
265  alignas(__detail::__platform_wait_alignment)
266  __detail::__platform_wait_t _M_counter;
267  };
268 #endif // __cpp_lib_atomic_wait
269 
270 // Note: the _GLIBCXX_USE_POSIX_SEMAPHORE macro can be used to force the
271 // use of Posix semaphores (sem_t). Doing so however, alters the ABI.
272 #if defined __cpp_lib_atomic_wait && !_GLIBCXX_USE_POSIX_SEMAPHORE
273  using __semaphore_impl = __atomic_semaphore;
274 #elif _GLIBCXX_HAVE_POSIX_SEMAPHORE
275  using __semaphore_impl = __platform_semaphore;
276 #endif
277 
278 _GLIBCXX_END_NAMESPACE_VERSION
279 } // namespace std
280 #endif // _GLIBCXX_SEMAPHORE_BASE_H
void terminate() noexcept
ISO C++ entities toplevel namespace is std.
__numeric_traits_integer< _Tp > __int_traits
Convenience alias for __numeric_traits<integer-type>.