libstdc++
semaphore_base.h
Go to the documentation of this file.
1 // -*- C++ -*- header.
2 
3 // Copyright (C) 2020-2025 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 #ifdef _GLIBCXX_SYSHDR
34 #pragma GCC system_header
35 #endif
36 
37 #include <bits/atomic_base.h>
38 #include <bits/chrono.h>
39 #if __glibcxx_atomic_wait
40 #include <bits/atomic_timed_wait.h>
41 #include <ext/numeric_traits.h>
42 #endif // __cpp_lib_atomic_wait
43 
44 #ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
45 # include <cerrno> // errno, EINTR, EAGAIN etc.
46 # include <limits.h> // SEM_VALUE_MAX
47 # include <semaphore.h> // sem_t, sem_init, sem_wait, sem_post etc.
48 #elif defined(_GLIBCXX_USE_POSIX_SEMAPHORE)
49 # warning "POSIX semaphore not available, ignoring _GLIBCXX_USE_POSIX_SEMAPHORE"
50 # undef _GLIBCXX_USE_POSIX_SEMAPHORE
51 #endif
52 
53 namespace std _GLIBCXX_VISIBILITY(default)
54 {
55 _GLIBCXX_BEGIN_NAMESPACE_VERSION
56 
57 #ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
58  struct __platform_semaphore
59  {
60  using __clock_t = chrono::system_clock;
61 #ifdef SEM_VALUE_MAX
62  static constexpr ptrdiff_t _S_max = SEM_VALUE_MAX;
63 #else
64  static constexpr ptrdiff_t _S_max = _POSIX_SEM_VALUE_MAX;
65 #endif
66 
67  explicit __platform_semaphore(ptrdiff_t __count) noexcept
68  {
69  sem_init(&_M_semaphore, 0, __count);
70  }
71 
72  __platform_semaphore(const __platform_semaphore&) = delete;
73  __platform_semaphore& operator=(const __platform_semaphore&) = delete;
74 
75  ~__platform_semaphore()
76  { sem_destroy(&_M_semaphore); }
77 
78  _GLIBCXX_ALWAYS_INLINE void
79  _M_acquire() noexcept
80  {
81  while (sem_wait(&_M_semaphore))
82  if (errno != EINTR)
83  std::__terminate();
84  }
85 
86  _GLIBCXX_ALWAYS_INLINE bool
87  _M_try_acquire() noexcept
88  {
89  while (sem_trywait(&_M_semaphore))
90  {
91  if (errno == EAGAIN) // already locked
92  return false;
93  else if (errno != EINTR)
94  std::__terminate();
95  // else got EINTR so retry
96  }
97  return true;
98  }
99 
100  _GLIBCXX_ALWAYS_INLINE void
101  _M_release(ptrdiff_t __update) noexcept
102  {
103  for(; __update != 0; --__update)
104  if (sem_post(&_M_semaphore))
105  std::__terminate();
106  }
107 
108  bool
109  _M_try_acquire_until_impl(const chrono::time_point<__clock_t>& __atime)
110  noexcept
111  {
112  auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
113  auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
114 
115  struct timespec __ts =
116  {
117  static_cast<std::time_t>(__s.time_since_epoch().count()),
118  static_cast<long>(__ns.count())
119  };
120 
121  while (sem_timedwait(&_M_semaphore, &__ts))
122  {
123  if (errno == ETIMEDOUT)
124  return false;
125  else if (errno != EINTR)
126  std::__terminate();
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  using _Dur = __clock_t::duration;
139  return _M_try_acquire_until_impl(chrono::ceil<_Dur>(__atime));
140  }
141  else
142  {
143  // TODO: if _Clock is monotonic_clock we could use
144  // sem_clockwait with CLOCK_MONOTONIC.
145 
146  const typename _Clock::time_point __c_entry = _Clock::now();
147  const auto __s_entry = __clock_t::now();
148  const auto __delta = __atime - __c_entry;
149  const auto __s_atime = __s_entry + __delta;
150  if (_M_try_acquire_until_impl(__s_atime))
151  return true;
152 
153  // We got a timeout when measured against __clock_t but
154  // we need to check against the caller-supplied clock
155  // to tell whether we should return a timeout.
156  return (_Clock::now() < __atime);
157  }
158  }
159 
160  template<typename _Rep, typename _Period>
161  _GLIBCXX_ALWAYS_INLINE bool
162  _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime)
163  noexcept
164  { return _M_try_acquire_until(__clock_t::now() + __rtime); }
165 
166  private:
167  sem_t _M_semaphore;
168  };
169 #endif // _GLIBCXX_HAVE_POSIX_SEMAPHORE
170 
171 #if __glibcxx_atomic_wait
172  struct __atomic_semaphore
173  {
174  static constexpr ptrdiff_t _S_max = __gnu_cxx::__int_traits<int>::__max;
175  explicit __atomic_semaphore(__detail::__platform_wait_t __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  static _GLIBCXX_ALWAYS_INLINE bool
185  _S_do_try_acquire(__detail::__platform_wait_t* __counter) noexcept
186  {
187  auto __old = __atomic_impl::load(__counter, memory_order::acquire);
188  if (__old == 0)
189  return false;
190 
191  return __atomic_impl::compare_exchange_strong(__counter,
192  __old, __old - 1,
193  memory_order::acquire,
194  memory_order::relaxed);
195  }
196 
197  _GLIBCXX_ALWAYS_INLINE void
198  _M_acquire() noexcept
199  {
200  auto const __pred =
201  [this] { return _S_do_try_acquire(&this->_M_counter); };
202  std::__atomic_wait_address_bare(&_M_counter, __pred);
203  }
204 
205  bool
206  _M_try_acquire() noexcept
207  {
208  auto const __pred =
209  [this] { return _S_do_try_acquire(&this->_M_counter); };
210  return std::__detail::__atomic_spin(__pred);
211  }
212 
213  template<typename _Clock, typename _Duration>
214  _GLIBCXX_ALWAYS_INLINE bool
215  _M_try_acquire_until(const chrono::time_point<_Clock,
216  _Duration>& __atime) noexcept
217  {
218  auto const __pred =
219  [this] { return _S_do_try_acquire(&this->_M_counter); };
220 
221  return __atomic_wait_address_until_bare(&_M_counter, __pred, __atime);
222  }
223 
224  template<typename _Rep, typename _Period>
225  _GLIBCXX_ALWAYS_INLINE bool
226  _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime)
227  noexcept
228  {
229  auto const __pred =
230  [this] { return _S_do_try_acquire(&this->_M_counter); };
231 
232  return __atomic_wait_address_for_bare(&_M_counter, __pred, __rtime);
233  }
234 
235  _GLIBCXX_ALWAYS_INLINE void
236  _M_release(ptrdiff_t __update) noexcept
237  {
238  if (0 < __atomic_impl::fetch_add(&_M_counter, __update, memory_order_release))
239  return;
240  if (__update > 1)
241  __atomic_notify_address_bare(&_M_counter, true);
242  else
243  __atomic_notify_address_bare(&_M_counter, true);
244 // FIXME - Figure out why this does not wake a waiting thread
245 // __atomic_notify_address_bare(&_M_counter, false);
246  }
247 
248  private:
249  alignas(__detail::__platform_wait_alignment)
250  __detail::__platform_wait_t _M_counter;
251  };
252 #endif // __cpp_lib_atomic_wait
253 
254 // Note: the _GLIBCXX_USE_POSIX_SEMAPHORE macro can be used to force the
255 // use of Posix semaphores (sem_t). Doing so however, alters the ABI.
256 #if defined __glibcxx_atomic_wait && !_GLIBCXX_USE_POSIX_SEMAPHORE
257  using __semaphore_impl = __atomic_semaphore;
258 #elif _GLIBCXX_HAVE_POSIX_SEMAPHORE
259  using __semaphore_impl = __platform_semaphore;
260 #endif
261 
262 _GLIBCXX_END_NAMESPACE_VERSION
263 } // namespace std
264 #endif // _GLIBCXX_SEMAPHORE_BASE_H
ISO C++ entities toplevel namespace is std.
__numeric_traits_integer< _Tp > __int_traits
Convenience alias for __numeric_traits<integer-type>.