libstdc++
semaphore_base.h
Go to the documentation of this file.
1 // -*- C++ -*- header.
2 
3 // Copyright (C) 2020-2026 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/version.h>
38 
39 #ifdef __glibcxx_semaphore // C++ >= 20 && hosted && atomic_wait
40 #include <bits/atomic_base.h>
41 #include <bits/chrono.h>
42 #include <bits/atomic_timed_wait.h>
43 #include <ext/numeric_traits.h>
44 
45 namespace std _GLIBCXX_VISIBILITY(default)
46 {
47 _GLIBCXX_BEGIN_NAMESPACE_VERSION
48 
49  struct __semaphore_impl
50  {
51  using __count_type = ptrdiff_t;
52 
53  static constexpr ptrdiff_t _S_max
55 
56  constexpr explicit
57  __semaphore_impl(__count_type __count) noexcept
58  : _M_counter(__count)
59  { }
60 
61  __semaphore_impl(const __semaphore_impl&) = delete;
62  __semaphore_impl& operator=(const __semaphore_impl&) = delete;
63 
64  // Load the current counter value.
65  _GLIBCXX_ALWAYS_INLINE __count_type
66  _M_get_current() const noexcept
67  { return __atomic_impl::load(&_M_counter, memory_order::acquire); }
68 
69  // Try to acquire the semaphore (i.e. decrement the counter).
70  // Returns false if the current counter is zero, or if another thread
71  // changes the value first. In the latter case, __cur is set to the new
72  // value.
73  _GLIBCXX_ALWAYS_INLINE bool
74  _M_do_try_acquire(__count_type& __cur) noexcept
75  {
76  if (__cur == 0)
77  return false; // Cannot decrement when it's already zero.
78 
79  return __atomic_impl::compare_exchange_strong(&_M_counter,
80  __cur, __cur - 1,
81  memory_order::acquire,
82  memory_order::relaxed);
83  }
84 
85  // Keep trying to acquire the semaphore in a loop until it succeeds.
86  void
87  _M_acquire() noexcept
88  {
89  auto __vfn = [this]{ return _M_get_current(); };
90  _Available __is_available{__vfn()};
91  while (!_M_do_try_acquire(__is_available._M_val))
92  if (!__is_available())
93  std::__atomic_wait_address(&_M_counter, __is_available, __vfn, true);
94  }
95 
96  // Try to acquire the semaphore, retrying a small number of times
97  // in case of contention.
98  bool
99  _M_try_acquire() noexcept
100  {
101  // The fastest implementation of this function is just _M_do_try_acquire
102  // but that can fail under contention even when _M_count > 0.
103 
104  auto __vfn = [this]{ return _M_get_current(); };
105  _Available __is_available{__vfn()};
106 
107  // Retry the compare exchange a few times in case of contention:
108  for (int __i = 0; __i < 10 && __is_available(); ++__i)
109  if (_M_do_try_acquire(__is_available._M_val))
110  return true;
111 
112  // Spinloop to see if it becomes available.
113  constexpr auto __zero = __detail::__wait_clock_t::duration{};
114  if (std::__atomic_wait_address_for(&_M_counter, __is_available,
115  __vfn, __zero, true))
116  return false; // timed out
117 
118  // Should be available, try once more to acquire it:
119  return _M_do_try_acquire(__is_available._M_val);
120  }
121 
122  template<typename _Clock, typename _Duration>
123  bool
124  _M_try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept
125  {
126  auto __vfn = [this]{ return _M_get_current(); };
127  _Available __is_available{__vfn()};
128  while (!_M_do_try_acquire(__is_available._M_val))
129  if (!__is_available())
130  if (!std::__atomic_wait_address_until(&_M_counter, __is_available,
131  __vfn, __atime, true))
132  return false; // timed out
133  return true;
134  }
135 
136  template<typename _Rep, typename _Period>
137  bool
138  _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept
139  {
140  return _M_try_acquire_until(__detail::__wait_clock_t::now() + __rtime);
141  }
142 
143  _GLIBCXX_ALWAYS_INLINE ptrdiff_t
144  _M_release(ptrdiff_t __update) noexcept
145  {
146  auto __old = __atomic_impl::fetch_add(&_M_counter, __update,
147  memory_order::release);
148  if (__old == 0 && __update > 0)
149  __atomic_notify_address(&_M_counter, true, true);
150  return __old;
151  }
152 
153  private:
154  struct _Available
155  {
156  __count_type _M_val; // Cache of the last value loaded from _M_counter.
157 
158  // Returns true if the cached value is non-zero and so it should be
159  // possible to acquire the semaphore.
160  bool operator()() const noexcept { return _M_val > 0; }
161 
162  // Argument should be the latest value of the counter.
163  // Returns true (and caches the value) if it's non-zero, meaning it
164  // should be possible to acquire the semaphore. Returns false otherwise.
165  bool operator()(__count_type __cur) noexcept
166  {
167  if (__cur == 0)
168  return false;
169  _M_val = __cur;
170  return true;
171  }
172  };
173 
174  alignas(__atomic_ref<__count_type>::required_alignment)
175  __count_type _M_counter;
176  };
177 
178  // Optimized specialization using __platform_wait (if available)
179  template<bool _Binary>
180  struct __platform_semaphore_impl
181  {
182  using __count_type = __detail::__platform_wait_t;
183 
184  static consteval ptrdiff_t
185  _S_calc_max()
186  {
187  if (_Binary)
188  return 1;
189  else if ((ptrdiff_t)__gnu_cxx::__int_traits<__count_type>::__max < 0)
191  else
193  }
194 
195  static constexpr ptrdiff_t _S_max = _S_calc_max();
196 
197  constexpr explicit
198  __platform_semaphore_impl(__count_type __count) noexcept
199  : _M_counter(__count)
200  { }
201 
202  __platform_semaphore_impl(__platform_semaphore_impl&) = delete;
203  __platform_semaphore_impl& operator=(const __platform_semaphore_impl&) = delete;
204 
205  // Load the current counter value.
206  _GLIBCXX_ALWAYS_INLINE __count_type
207  _M_get_current() const noexcept
208  {
209  if constexpr (_Binary)
210  return 1; // Not necessarily true, but optimistically assume it is.
211  else
212  return __atomic_impl::load(&_M_counter, memory_order::acquire);
213  }
214 
215  // Try to acquire the semaphore (i.e. decrement the counter).
216  // Returns false if the current counter is zero, or if another thread
217  // changes the value first. In the latter case, __cur is set to the new
218  // value.
219  _GLIBCXX_ALWAYS_INLINE bool
220  _M_do_try_acquire(__count_type& __cur) noexcept
221  {
222  if (__cur == 0)
223  return false; // Cannot decrement when it's already zero.
224 
225  return __atomic_impl::compare_exchange_strong(&_M_counter,
226  __cur, __cur - 1,
227  memory_order::acquire,
228  memory_order::relaxed);
229  }
230 
231  // Keep trying to acquire the semaphore in a loop until it succeeds.
232  void
233  _M_acquire() noexcept
234  {
235  auto __val = _M_get_current();
236  while (!_M_do_try_acquire(__val))
237  if (__val == 0)
238  {
239  std::__atomic_wait_address_v(&_M_counter, __val, __ATOMIC_ACQUIRE,
240  true);
241  __val = _M_get_current();
242  }
243  }
244 
245  // Try to acquire the semaphore.
246  bool
247  _M_try_acquire() noexcept
248  {
249  if constexpr (_Binary)
250  {
251  __count_type __val = 1;
252  // Do not expect much contention on binary semaphore, only try once.
253  return _M_do_try_acquire(__val);
254  }
255  else
256  // Fastest implementation of this function is just _M_do_try_acquire
257  // but that can fail under contention even when _M_count > 0.
258  // Using _M_try_acquire_for(0ns) will retry a few times in a loop.
259  return _M_try_acquire_for(__detail::__wait_clock_t::duration{});
260  }
261 
262  template<typename _Clock, typename _Duration>
263  bool
264  _M_try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept
265  {
266  auto __val = _M_get_current();
267  while (!_M_do_try_acquire(__val))
268  if (__val == 0)
269  {
270  if (!std::__atomic_wait_address_until_v(&_M_counter, 0,
271  __ATOMIC_ACQUIRE,
272  __atime, true))
273  return false; // timed out
274  __val = _M_get_current();
275  }
276  return true;
277  }
278 
279  template<typename _Rep, typename _Period>
280  bool
281  _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept
282  {
283  auto __val = _M_get_current();
284  while (!_M_do_try_acquire(__val))
285  if (__val == 0)
286  {
287  if (!std::__atomic_wait_address_for_v(&_M_counter, 0,
288  __ATOMIC_ACQUIRE,
289  __rtime, true))
290  return false; // timed out
291  __val = _M_get_current();
292  }
293  return true;
294  }
295 
296  _GLIBCXX_ALWAYS_INLINE ptrdiff_t
297  _M_release(ptrdiff_t __update) noexcept
298  {
299  auto __old = __atomic_impl::fetch_add(&_M_counter, __update,
300  memory_order::release);
301  if (__old == 0 && __update > 0)
302  __atomic_notify_address(&_M_counter, true, true);
303  return __old;
304  }
305 
306  protected:
307  alignas(__detail::__platform_wait_alignment) __count_type _M_counter;
308  };
309 
310  template<ptrdiff_t _Max, typename _Tp = __detail::__platform_wait_t>
311  using _Semaphore_impl
312  = __conditional_t<__platform_wait_uses_type<_Tp>
313  && _Max <= __gnu_cxx::__int_traits<_Tp>::__max,
314  __platform_semaphore_impl<(_Max <= 1)>,
315  __semaphore_impl>;
316 
317 _GLIBCXX_END_NAMESPACE_VERSION
318 } // namespace std
319 #endif // __glibcxx_semaphore
320 #endif // _GLIBCXX_SEMAPHORE_BASE_H
chrono.h
numeric_traits.h
std
ISO C++ entities toplevel namespace is std.
atomic_base.h
atomic_timed_wait.h
__gnu_cxx::__int_traits
__numeric_traits_integer< _Tp > __int_traits
Convenience alias for __numeric_traits<integer-type>.
Definition: ext/numeric_traits.h:134
version.h