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