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

source code of include/c++/11/bits/semaphore_base.h