3 // Copyright (C) 2020-2023 Free Software Foundation, Inc.
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)
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.
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.
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/>.
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}
30 #ifndef _GLIBCXX_SEMAPHORE_BASE_H
31 #define _GLIBCXX_SEMAPHORE_BASE_H 1
33 #pragma GCC system_header
35 #include <bits/atomic_base.h>
36 #include <bits/chrono.h>
37 #if __cpp_lib_atomic_wait
38 #include <bits/atomic_timed_wait.h>
39 #include <ext/numeric_traits.h>
40 #endif // __cpp_lib_atomic_wait
42 #ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
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.
48 namespace std
_GLIBCXX_VISIBILITY(default)
50 _GLIBCXX_BEGIN_NAMESPACE_VERSION
52 #ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
53 struct __platform_semaphore
55 using __clock_t
= chrono::system_clock
;
57 static constexpr ptrdiff_t _S_max
= SEM_VALUE_MAX
;
59 static constexpr ptrdiff_t _S_max
= _POSIX_SEM_VALUE_MAX
;
62 explicit __platform_semaphore(ptrdiff_t __count
) noexcept
64 sem_init(&_M_semaphore
, 0, __count
);
67 __platform_semaphore(const __platform_semaphore
&) = delete;
68 __platform_semaphore
& operator=(const __platform_semaphore
&) = delete;
70 ~__platform_semaphore()
71 { sem_destroy(&_M_semaphore
); }
73 _GLIBCXX_ALWAYS_INLINE
void
78 auto __err
= sem_wait(&_M_semaphore
);
79 if (__err
&& (errno
== EINTR
))
88 _GLIBCXX_ALWAYS_INLINE
bool
89 _M_try_acquire() noexcept
93 auto __err
= sem_trywait(&_M_semaphore
);
94 if (__err
&& (errno
== EINTR
))
96 else if (__err
&& (errno
== EAGAIN
))
106 _GLIBCXX_ALWAYS_INLINE
void
107 _M_release(std::ptrdiff_t __update
) noexcept
109 for(; __update
!= 0; --__update
)
111 auto __err
= sem_post(&_M_semaphore
);
118 _M_try_acquire_until_impl(const chrono::time_point
<__clock_t
>& __atime
)
122 auto __s
= chrono::time_point_cast
<chrono::seconds
>(__atime
);
123 auto __ns
= chrono::duration_cast
<chrono::nanoseconds
>(__atime
- __s
);
125 struct timespec __ts
=
127 static_cast<std::time_t>(__s
.time_since_epoch().count()),
128 static_cast<long>(__ns
.count())
133 if (auto __err
= sem_timedwait(&_M_semaphore
, &__ts
))
137 else if (errno
== ETIMEDOUT
|| errno
== EINVAL
)
148 template<typename _Clock
, typename _Duration
>
150 _M_try_acquire_until(const chrono::time_point
<_Clock
,
151 _Duration
>& __atime
) noexcept
153 if constexpr (std::is_same_v
<__clock_t
, _Clock
>)
155 return _M_try_acquire_until_impl(__atime
);
159 const typename
_Clock::time_point __c_entry
= _Clock::now();
160 const auto __s_entry
= __clock_t::now();
161 const auto __delta
= __atime
- __c_entry
;
162 const auto __s_atime
= __s_entry
+ __delta
;
163 if (_M_try_acquire_until_impl(__s_atime
))
166 // We got a timeout when measured against __clock_t but
167 // we need to check against the caller-supplied clock
168 // to tell whether we should return a timeout.
169 return (_Clock::now() < __atime
);
173 template<typename _Rep
, typename _Period
>
174 _GLIBCXX_ALWAYS_INLINE
bool
175 _M_try_acquire_for(const chrono::duration
<_Rep
, _Period
>& __rtime
)
177 { return _M_try_acquire_until(__clock_t::now() + __rtime
); }
182 #endif // _GLIBCXX_HAVE_POSIX_SEMAPHORE
184 #if __cpp_lib_atomic_wait
185 struct __atomic_semaphore
187 static constexpr ptrdiff_t _S_max
= __gnu_cxx::__int_traits
<int>::__max
;
188 explicit __atomic_semaphore(__detail::__platform_wait_t __count
) noexcept
189 : _M_counter(__count
)
191 __glibcxx_assert(__count
>= 0 && __count
<= _S_max
);
194 __atomic_semaphore(const __atomic_semaphore
&) = delete;
195 __atomic_semaphore
& operator=(const __atomic_semaphore
&) = delete;
197 static _GLIBCXX_ALWAYS_INLINE
bool
198 _S_do_try_acquire(__detail::__platform_wait_t
* __counter
) noexcept
200 auto __old
= __atomic_impl::load(__counter
, memory_order::acquire
);
204 return __atomic_impl::compare_exchange_strong(__counter
,
206 memory_order::acquire
,
207 memory_order::relaxed
);
210 _GLIBCXX_ALWAYS_INLINE
void
211 _M_acquire() noexcept
214 [this] { return _S_do_try_acquire(&this->_M_counter
); };
215 std::__atomic_wait_address_bare(&_M_counter
, __pred
);
219 _M_try_acquire() noexcept
222 [this] { return _S_do_try_acquire(&this->_M_counter
); };
223 return std::__detail::__atomic_spin(__pred
);
226 template<typename _Clock
, typename _Duration
>
227 _GLIBCXX_ALWAYS_INLINE
bool
228 _M_try_acquire_until(const chrono::time_point
<_Clock
,
229 _Duration
>& __atime
) noexcept
232 [this] { return _S_do_try_acquire(&this->_M_counter
); };
234 return __atomic_wait_address_until_bare(&_M_counter
, __pred
, __atime
);
237 template<typename _Rep
, typename _Period
>
238 _GLIBCXX_ALWAYS_INLINE
bool
239 _M_try_acquire_for(const chrono::duration
<_Rep
, _Period
>& __rtime
)
243 [this] { return _S_do_try_acquire(&this->_M_counter
); };
245 return __atomic_wait_address_for_bare(&_M_counter
, __pred
, __rtime
);
248 _GLIBCXX_ALWAYS_INLINE
void
249 _M_release(ptrdiff_t __update
) noexcept
251 if (0 < __atomic_impl::fetch_add(&_M_counter
, __update
, memory_order_release
))
254 __atomic_notify_address_bare(&_M_counter
, true);
256 __atomic_notify_address_bare(&_M_counter
, true);
257 // FIXME - Figure out why this does not wake a waiting thread
258 // __atomic_notify_address_bare(&_M_counter, false);
262 alignas(__detail::__platform_wait_alignment
)
263 __detail::__platform_wait_t _M_counter
;
265 #endif // __cpp_lib_atomic_wait
267 // Note: the _GLIBCXX_USE_POSIX_SEMAPHORE macro can be used to force the
268 // use of Posix semaphores (sem_t). Doing so however, alters the ABI.
269 #if defined __cpp_lib_atomic_wait && !_GLIBCXX_USE_POSIX_SEMAPHORE
270 using __semaphore_impl
= __atomic_semaphore
;
271 #elif _GLIBCXX_HAVE_POSIX_SEMAPHORE
272 using __semaphore_impl
= __platform_semaphore
;
275 _GLIBCXX_END_NAMESPACE_VERSION
277 #endif // _GLIBCXX_SEMAPHORE_BASE_H