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 <limits.h>
43 # include <semaphore.h>
44 #endif
45 
46 #include <chrono>
47 #include <type_traits>
48 
49 namespace std _GLIBCXX_VISIBILITY(default)
50 {
51 _GLIBCXX_BEGIN_NAMESPACE_VERSION
52 
53 #ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
54  struct __platform_semaphore
55  {
56  using __clock_t = chrono::system_clock;
57 #ifdef SEM_VALUE_MAX
58  static constexpr ptrdiff_t _S_max = SEM_VALUE_MAX;
59 #else
60  static constexpr ptrdiff_t _S_max = _POSIX_SEM_VALUE_MAX;
61 #endif
62 
63  explicit __platform_semaphore(ptrdiff_t __count) noexcept
64  {
65  sem_init(&_M_semaphore, 0, __count);
66  }
67 
68  __platform_semaphore(const __platform_semaphore&) = delete;
69  __platform_semaphore& operator=(const __platform_semaphore&) = delete;
70 
71  ~__platform_semaphore()
72  { sem_destroy(&_M_semaphore); }
73 
74  _GLIBCXX_ALWAYS_INLINE void
75  _M_acquire() noexcept
76  {
77  for (;;)
78  {
79  auto __err = sem_wait(&_M_semaphore);
80  if (__err && (errno == EINTR))
81  continue;
82  else if (__err)
84  else
85  break;
86  }
87  }
88 
89  _GLIBCXX_ALWAYS_INLINE bool
90  _M_try_acquire() noexcept
91  {
92  for (;;)
93  {
94  auto __err = sem_trywait(&_M_semaphore);
95  if (__err && (errno == EINTR))
96  continue;
97  else if (__err && (errno == EAGAIN))
98  return false;
99  else if (__err)
100  std::terminate();
101  else
102  break;
103  }
104  return true;
105  }
106 
107  _GLIBCXX_ALWAYS_INLINE void
108  _M_release(std::ptrdiff_t __update) noexcept
109  {
110  for(; __update != 0; --__update)
111  {
112  auto __err = sem_post(&_M_semaphore);
113  if (__err)
114  std::terminate();
115  }
116  }
117 
118  bool
119  _M_try_acquire_until_impl(const chrono::time_point<__clock_t>& __atime)
120  noexcept
121  {
122 
123  auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
124  auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
125 
126  struct timespec __ts =
127  {
128  static_cast<std::time_t>(__s.time_since_epoch().count()),
129  static_cast<long>(__ns.count())
130  };
131 
132  for (;;)
133  {
134  if (auto __err = sem_timedwait(&_M_semaphore, &__ts))
135  {
136  if (errno == EINTR)
137  continue;
138  else if (errno == ETIMEDOUT || errno == EINVAL)
139  return false;
140  else
141  std::terminate();
142  }
143  else
144  break;
145  }
146  return true;
147  }
148 
149  template<typename _Clock, typename _Duration>
150  bool
151  _M_try_acquire_until(const chrono::time_point<_Clock,
152  _Duration>& __atime) noexcept
153  {
154  if constexpr (std::is_same_v<__clock_t, _Clock>)
155  {
156  return _M_try_acquire_until_impl(__atime);
157  }
158  else
159  {
160  const typename _Clock::time_point __c_entry = _Clock::now();
161  const auto __s_entry = __clock_t::now();
162  const auto __delta = __atime - __c_entry;
163  const auto __s_atime = __s_entry + __delta;
164  if (_M_try_acquire_until_impl(__s_atime))
165  return true;
166 
167  // We got a timeout when measured against __clock_t but
168  // we need to check against the caller-supplied clock
169  // to tell whether we should return a timeout.
170  return (_Clock::now() < __atime);
171  }
172  }
173 
174  template<typename _Rep, typename _Period>
175  _GLIBCXX_ALWAYS_INLINE bool
176  _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime)
177  noexcept
178  { return _M_try_acquire_until(__clock_t::now() + __rtime); }
179 
180  private:
181  sem_t _M_semaphore;
182  };
183 #endif // _GLIBCXX_HAVE_POSIX_SEMAPHORE
184 
185 #if __cpp_lib_atomic_wait
186  struct __atomic_semaphore
187  {
188  static constexpr ptrdiff_t _S_max = __gnu_cxx::__int_traits<int>::__max;
189  explicit __atomic_semaphore(__detail::__platform_wait_t __count) noexcept
190  : _M_counter(__count)
191  {
192  __glibcxx_assert(__count >= 0 && __count <= _S_max);
193  }
194 
195  __atomic_semaphore(const __atomic_semaphore&) = delete;
196  __atomic_semaphore& operator=(const __atomic_semaphore&) = delete;
197 
198  static _GLIBCXX_ALWAYS_INLINE bool
199  _S_do_try_acquire(__detail::__platform_wait_t* __counter) noexcept
200  {
201  auto __old = __atomic_impl::load(__counter, memory_order::acquire);
202  if (__old == 0)
203  return false;
204 
205  return __atomic_impl::compare_exchange_strong(__counter,
206  __old, __old - 1,
207  memory_order::acquire,
208  memory_order::relaxed);
209  }
210 
211  _GLIBCXX_ALWAYS_INLINE void
212  _M_acquire() noexcept
213  {
214  auto const __pred =
215  [this] { return _S_do_try_acquire(&this->_M_counter); };
216  std::__atomic_wait_address_bare(&_M_counter, __pred);
217  }
218 
219  bool
220  _M_try_acquire() noexcept
221  {
222  auto const __pred =
223  [this] { return _S_do_try_acquire(&this->_M_counter); };
224  return std::__detail::__atomic_spin(__pred);
225  }
226 
227  template<typename _Clock, typename _Duration>
228  _GLIBCXX_ALWAYS_INLINE bool
229  _M_try_acquire_until(const chrono::time_point<_Clock,
230  _Duration>& __atime) noexcept
231  {
232  auto const __pred =
233  [this] { return _S_do_try_acquire(&this->_M_counter); };
234 
235  return __atomic_wait_address_until_bare(&_M_counter, __pred, __atime);
236  }
237 
238  template<typename _Rep, typename _Period>
239  _GLIBCXX_ALWAYS_INLINE bool
240  _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime)
241  noexcept
242  {
243  auto const __pred =
244  [this] { return _S_do_try_acquire(&this->_M_counter); };
245 
246  return __atomic_wait_address_for_bare(&_M_counter, __pred, __rtime);
247  }
248 
249  _GLIBCXX_ALWAYS_INLINE void
250  _M_release(ptrdiff_t __update) noexcept
251  {
252  if (0 < __atomic_impl::fetch_add(&_M_counter, __update, memory_order_release))
253  return;
254  if (__update > 1)
255  __atomic_notify_address_bare(&_M_counter, true);
256  else
257  __atomic_notify_address_bare(&_M_counter, true);
258 // FIXME - Figure out why this does not wake a waiting thread
259 // __atomic_notify_address_bare(&_M_counter, false);
260  }
261 
262  private:
263  alignas(__detail::__platform_wait_alignment)
264  __detail::__platform_wait_t _M_counter;
265  };
266 #endif // __cpp_lib_atomic_wait
267 
268 // Note: the _GLIBCXX_USE_POSIX_SEMAPHORE macro can be used to force the
269 // use of Posix semaphores (sem_t). Doing so however, alters the ABI.
270 #if defined __cpp_lib_atomic_wait && !_GLIBCXX_USE_POSIX_SEMAPHORE
271  using __semaphore_impl = __atomic_semaphore;
272 #elif _GLIBCXX_HAVE_POSIX_SEMAPHORE
273  using __semaphore_impl = __platform_semaphore;
274 #endif
275 
276 _GLIBCXX_END_NAMESPACE_VERSION
277 } // namespace std
278 #endif // _GLIBCXX_SEMAPHORE_BASE_H
constexpr enable_if< __is_duration< _ToDur >::value, time_point< _Clock, _ToDur > >::type time_point_cast(const time_point< _Clock, _Dur > &__t)
time_point_cast
Definition: chrono:936
duration< int64_t, nano > nanoseconds
nanoseconds
Definition: chrono:816
duration< int64_t > seconds
seconds
Definition: chrono:825
constexpr __enable_if_is_duration< _ToDur > duration_cast(const duration< _Rep, _Period > &__d)
duration_cast
Definition: chrono:254
__numeric_traits_integer< _Tp > __int_traits
Convenience alias for __numeric_traits&lt;integer-type&gt;.
void terminate() noexcept
auto_ptr & operator=(auto_ptr &__a)
auto_ptr assignment operator.
Definition: auto_ptr.h:128