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 
39 #include <ext/numeric_traits.h>
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 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)
97  }
98  }
99 
100  bool
101  _M_try_acquire_until_impl(const chrono::time_point<__clock_t>& __atime)
102  noexcept
103  {
104 
105  auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
106  auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
107 
108  struct timespec __ts =
109  {
110  static_cast<std::time_t>(__s.time_since_epoch().count()),
111  static_cast<long>(__ns.count())
112  };
113 
114  for (;;)
115  {
116  if (auto __err = sem_timedwait(&_M_semaphore, &__ts))
117  {
118  if (errno == EINTR)
119  continue;
120  else if (errno == ETIMEDOUT || errno == EINVAL)
121  return false;
122  else
123  std::terminate();
124  }
125  else
126  break;
127  }
128  return true;
129  }
130 
131  template<typename _Clock, typename _Duration>
132  bool
133  _M_try_acquire_until(const chrono::time_point<_Clock,
134  _Duration>& __atime) noexcept
135  {
136  if constexpr (std::is_same_v<__clock_t, _Clock>)
137  {
138  return _M_try_acquire_until_impl(__atime);
139  }
140  else
141  {
142  const typename _Clock::time_point __c_entry = _Clock::now();
143  const auto __s_entry = __clock_t::now();
144  const auto __delta = __atime - __c_entry;
145  const auto __s_atime = __s_entry + __delta;
146  if (_M_try_acquire_until_impl(__s_atime))
147  return true;
148 
149  // We got a timeout when measured against __clock_t but
150  // we need to check against the caller-supplied clock
151  // to tell whether we should return a timeout.
152  return (_Clock::now() < __atime);
153  }
154  }
155 
156  template<typename _Rep, typename _Period>
157  _GLIBCXX_ALWAYS_INLINE bool
158  _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime)
159  noexcept
160  { return _M_try_acquire_until(__clock_t::now() + __rtime); }
161 
162  private:
163  sem_t _M_semaphore;
164  };
165 #endif // _GLIBCXX_HAVE_POSIX_SEMAPHORE
166 
167  template<typename _Tp>
168  struct __atomic_semaphore
169  {
170  static_assert(std::is_integral_v<_Tp>);
173  static constexpr ptrdiff_t _S_max = __gnu_cxx::__int_traits<_Tp>::__max;
174 
175  explicit __atomic_semaphore(_Tp __count) noexcept
176  : _M_counter(__count)
177  {
178  __glibcxx_assert(__count >= 0 && __count <= _S_max);
179  }
180 
181  __atomic_semaphore(const __atomic_semaphore&) = delete;
182  __atomic_semaphore& operator=(const __atomic_semaphore&) = delete;
183 
184  _GLIBCXX_ALWAYS_INLINE void
185  _M_acquire() noexcept
186  {
187  auto const __pred = [this]
188  {
189  auto __old = __atomic_impl::load(&this->_M_counter,
190  memory_order::acquire);
191  if (__old == 0)
192  return false;
193  return __atomic_impl::compare_exchange_strong(&this->_M_counter,
194  __old, __old - 1,
195  memory_order::acquire,
197  };
198  auto __old = __atomic_impl::load(&_M_counter, memory_order_relaxed);
199  std::__atomic_wait(&_M_counter, __old, __pred);
200  }
201 
202  bool
203  _M_try_acquire() noexcept
204  {
205  auto __old = __atomic_impl::load(&_M_counter, memory_order::acquire);
206  auto const __pred = [this, __old]
207  {
208  if (__old == 0)
209  return false;
210 
211  auto __prev = __old;
212  return __atomic_impl::compare_exchange_weak(&this->_M_counter,
213  __prev, __prev - 1,
214  memory_order::acquire,
216  };
217  return std::__atomic_spin(__pred);
218  }
219 
220  template<typename _Clock, typename _Duration>
221  _GLIBCXX_ALWAYS_INLINE bool
222  _M_try_acquire_until(const chrono::time_point<_Clock,
223  _Duration>& __atime) noexcept
224  {
225  auto const __pred = [this]
226  {
227  auto __old = __atomic_impl::load(&this->_M_counter,
228  memory_order::acquire);
229  if (__old == 0)
230  return false;
231  return __atomic_impl::compare_exchange_strong(&this->_M_counter,
232  __old, __old - 1,
233  memory_order::acquire,
235  };
236 
237  auto __old = __atomic_impl::load(&_M_counter, memory_order_relaxed);
238  return __atomic_wait_until(&_M_counter, __old, __pred, __atime);
239  }
240 
241  template<typename _Rep, typename _Period>
242  _GLIBCXX_ALWAYS_INLINE bool
243  _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime)
244  noexcept
245  {
246  auto const __pred = [this]
247  {
248  auto __old = __atomic_impl::load(&this->_M_counter,
249  memory_order::acquire);
250  if (__old == 0)
251  return false;
252  return __atomic_impl::compare_exchange_strong(&this->_M_counter,
253  __old, __old - 1,
254  memory_order::acquire,
256  };
257 
258  auto __old = __atomic_impl::load(&_M_counter, memory_order_relaxed);
259  return __atomic_wait_for(&_M_counter, __old, __pred, __rtime);
260  }
261 
262  _GLIBCXX_ALWAYS_INLINE void
263  _M_release(ptrdiff_t __update) noexcept
264  {
265  if (0 < __atomic_impl::fetch_add(&_M_counter, __update, memory_order_release))
266  return;
267  if (__update > 1)
268  __atomic_impl::notify_all(&_M_counter);
269  else
270  __atomic_impl::notify_one(&_M_counter);
271  }
272 
273  private:
274  alignas(__alignof__(_Tp)) _Tp _M_counter;
275  };
276 
277 // Note: the _GLIBCXX_REQUIRE_POSIX_SEMAPHORE macro can be used to force the
278 // use of Posix semaphores (sem_t). Doing so however, alters the ABI.
279 #if defined _GLIBCXX_HAVE_LINUX_FUTEX && !_GLIBCXX_REQUIRE_POSIX_SEMAPHORE
280  // Use futex if available and didn't force use of POSIX
281  using __fast_semaphore = __atomic_semaphore<__detail::__platform_wait_t>;
282 #elif _GLIBCXX_HAVE_POSIX_SEMAPHORE
283  using __fast_semaphore = __platform_semaphore;
284 #else
285  using __fast_semaphore = __atomic_semaphore<ptrdiff_t>;
286 #endif
287 
288 template<ptrdiff_t __least_max_value>
289  using __semaphore_impl = conditional_t<
290  (__least_max_value > 1),
292  (__least_max_value <= __fast_semaphore::_S_max),
293  __fast_semaphore,
294  __atomic_semaphore<ptrdiff_t>>,
295  __fast_semaphore>;
296 
297 _GLIBCXX_END_NAMESPACE_VERSION
298 } // namespace std
299 
300 #endif // __cpp_lib_atomic_wait
301 #endif // _GLIBCXX_SEMAPHORE_BASE_H
auto_ptr & operator=(auto_ptr &__a)
auto_ptr assignment operator.
Definition: auto_ptr.h:47
element_type * release()
Bypassing the smart pointer.
Definition: auto_ptr.h:136
typename conditional< _Cond, _Iftrue, _Iffalse >::type conditional_t
Alias template for conditional.
Definition: type_traits:2518
void terminate() noexcept
ISO C++ entities toplevel namespace is std.
__numeric_traits_integer< _Tp > __int_traits
Convenience alias for __numeric_traits<integer-type>.