libstdc++: semaphore_base.h Source File

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
45namespace 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
54 = __gnu_cxx::__int_traits<__count_type>::__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)
190 return __gnu_cxx::__int_traits<ptrdiff_t>::__max;
191 else
192 return __gnu_cxx::__int_traits<__count_type>::__max;
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
atomic_base.h
version.h
atomic_timed_wait.h
chrono.h
std
ISO C++ entities toplevel namespace is std.
numeric_traits.h