Skip to content

Commit f3c6dd9

Browse files
committed
Added timed waiting operations to the generic backend.
1 parent e2ae985 commit f3c6dd9

File tree

1 file changed

+198
-16
lines changed

1 file changed

+198
-16
lines changed

include/boost/atomic/detail/wait_ops_generic.hpp

Lines changed: 198 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,31 @@
33
* (See accompanying file LICENSE_1_0.txt or copy at
44
* http://www.boost.org/LICENSE_1_0.txt)
55
*
6-
* Copyright (c) 2020 Andrey Semashev
6+
* Copyright (c) 2020-2025 Andrey Semashev
77
*/
88
/*!
99
* \file atomic/detail/wait_ops_generic.hpp
1010
*
1111
* This header contains generic (lock-based) implementation of the waiting/notifying atomic operations.
12+
*
13+
* This backend is used when lock-free atomic operations are available but native waiting/notifying operations are not.
1214
*/
1315

1416
#ifndef BOOST_ATOMIC_DETAIL_WAIT_OPS_GENERIC_HPP_INCLUDED_
1517
#define BOOST_ATOMIC_DETAIL_WAIT_OPS_GENERIC_HPP_INCLUDED_
1618

1719
#include <cstddef>
20+
#include <chrono>
1821
#include <boost/memory_order.hpp>
22+
#include <boost/atomic/wait_result.hpp>
1923
#include <boost/atomic/detail/config.hpp>
24+
#if !defined(BOOST_WINDOWS)
25+
#include <time.h>
26+
#include <type_traits>
27+
#include <boost/atomic/posix_clock_traits_fwd.hpp>
28+
#include <boost/atomic/detail/has_posix_clock_traits.hpp>
29+
#endif
30+
#include <boost/atomic/detail/chrono.hpp>
2031
#include <boost/atomic/detail/pause.hpp>
2132
#include <boost/atomic/detail/lock_pool.hpp>
2233
#include <boost/atomic/detail/wait_operations_fwd.hpp>
@@ -38,19 +49,19 @@ template< typename Base >
3849
struct wait_operations_generic< Base, false > :
3950
public Base
4051
{
41-
typedef Base base_type;
42-
typedef typename base_type::storage_type storage_type;
43-
typedef lock_pool::scoped_lock< base_type::storage_alignment, true > scoped_lock;
44-
typedef lock_pool::scoped_wait_state< base_type::storage_alignment > scoped_wait_state;
52+
using base_type = Base;
53+
using storage_type = typename base_type::storage_type;
54+
using scoped_lock = lock_pool::scoped_lock< base_type::storage_alignment, true >;
55+
using scoped_wait_state = lock_pool::scoped_wait_state< base_type::storage_alignment >;
4556

46-
static BOOST_CONSTEXPR_OR_CONST bool always_has_native_wait_notify = false;
57+
static constexpr bool always_has_native_wait_notify = false;
4758

48-
static BOOST_FORCEINLINE bool has_native_wait_notify(storage_type const volatile&) BOOST_NOEXCEPT
59+
static BOOST_FORCEINLINE bool has_native_wait_notify(storage_type const volatile&) noexcept
4960
{
5061
return false;
5162
}
5263

53-
static BOOST_FORCEINLINE storage_type wait(storage_type const volatile& storage, storage_type old_val, memory_order order) BOOST_NOEXCEPT
64+
static BOOST_FORCEINLINE storage_type wait(storage_type const volatile& storage, storage_type old_val, memory_order order) noexcept
5465
{
5566
storage_type new_val = base_type::load(storage, order);
5667
if (new_val == old_val)
@@ -67,13 +78,119 @@ struct wait_operations_generic< Base, false > :
6778
return new_val;
6879
}
6980

70-
static BOOST_FORCEINLINE void notify_one(storage_type volatile& storage) BOOST_NOEXCEPT
81+
private:
82+
template< typename Clock >
83+
static BOOST_FORCEINLINE wait_result< storage_type >
84+
wait_until_fallback(storage_type const volatile& storage, storage_type old_val, typename Clock::time_point timeout, typename Clock::time_point now, memory_order order)
85+
noexcept(noexcept(Clock::now()))
86+
{
87+
wait_result< storage_type > res{ base_type::load(storage, order), false };
88+
if (res.value == old_val)
89+
{
90+
scoped_wait_state wait_state(&storage);
91+
while (res.value == old_val)
92+
{
93+
const std::chrono::nanoseconds nsec = atomics::detail::chrono::ceil< std::chrono::nanoseconds >(timeout - now);
94+
if (nsec.count() <= 0)
95+
{
96+
res.timeout = true;
97+
break;
98+
}
99+
100+
wait_state.wait_for(nsec);
101+
102+
now = Clock::now();
103+
res.value = base_type::load(storage, order);
104+
}
105+
}
106+
107+
return res;
108+
}
109+
110+
#if !defined(BOOST_WINDOWS)
111+
112+
template< typename Clock >
113+
static BOOST_FORCEINLINE wait_result< storage_type >
114+
wait_until_abs_timeout(storage_type const volatile& storage, storage_type old_val, typename Clock::time_point timeout, memory_order order) noexcept
115+
{
116+
wait_result< storage_type > res{ base_type::load(storage, order), false };
117+
if (res.value == old_val)
118+
{
119+
scoped_wait_state wait_state(&storage);
120+
const timespec abs_timeout(posix_clock_traits< Clock >::to_timespec(timeout));
121+
if (BOOST_LIKELY(abs_timeout.tv_sec >= 0))
122+
{
123+
while (res.value == old_val)
124+
{
125+
res.timeout = wait_state.wait_until(posix_clock_traits< Clock >::clock_id, abs_timeout);
126+
res.value = base_type::load(storage, order);
127+
128+
if (res.timeout)
129+
goto timeout_expired;
130+
}
131+
}
132+
else
133+
{
134+
timeout_expired:
135+
res.timeout = !(res.value == old_val);
136+
}
137+
}
138+
139+
return res;
140+
}
141+
142+
template< typename Clock >
143+
static BOOST_FORCEINLINE wait_result< storage_type >
144+
wait_until_dispatch(storage_type const volatile& storage, storage_type old_val, typename Clock::time_point timeout, memory_order order, std::true_type) noexcept
145+
{
146+
return wait_until_abs_timeout< Clock >(storage, old_val, timeout, order);
147+
}
148+
149+
template< typename Clock >
150+
static BOOST_FORCEINLINE wait_result< storage_type >
151+
wait_until_dispatch(storage_type const volatile& storage, storage_type old_val, typename Clock::time_point timeout, memory_order order, std::false_type)
152+
noexcept(noexcept(Clock::now()))
153+
{
154+
return wait_until_fallback< Clock >(storage, old_val, timeout, Clock::now(), order);
155+
}
156+
157+
public:
158+
template< typename Clock, typename Duration >
159+
static BOOST_FORCEINLINE wait_result< storage_type >
160+
wait_until(storage_type const volatile& storage, storage_type old_val, std::chrono::time_point< Clock, Duration > timeout, memory_order order)
161+
noexcept(noexcept(wait_until_dispatch< Clock >(storage, old_val, timeout, order, std::integral_constant< bool, has_posix_clock_traits< Clock >::value >())))
162+
{
163+
return wait_until_dispatch< Clock >(storage, old_val, timeout, order, std::integral_constant< bool, has_posix_clock_traits< Clock >::value >());
164+
}
165+
166+
#else // !defined(BOOST_WINDOWS)
167+
168+
public:
169+
template< typename Clock, typename Duration >
170+
static BOOST_FORCEINLINE wait_result< storage_type >
171+
wait_until(storage_type const volatile& storage, storage_type old_val, std::chrono::time_point< Clock, Duration > timeout, memory_order order)
172+
noexcept(noexcept(wait_until_fallback< Clock >(storage, old_val, timeout, Clock::now(), order)))
173+
{
174+
return wait_until_fallback< Clock >(storage, old_val, timeout, Clock::now(), order);
175+
}
176+
177+
#endif // !defined(BOOST_WINDOWS)
178+
179+
template< typename Rep, typename Period >
180+
static BOOST_FORCEINLINE wait_result< storage_type >
181+
wait_for(storage_type const volatile& storage, storage_type old_val, std::chrono::duration< Rep, Period > timeout, memory_order order) noexcept
182+
{
183+
const std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
184+
return wait_until_fallback< std::chrono::steady_clock >(storage, old_val, now + timeout, now, order);
185+
}
186+
187+
static BOOST_FORCEINLINE void notify_one(storage_type volatile& storage) noexcept
71188
{
72189
scoped_lock lock(&storage);
73190
lock_pool::notify_one(lock.get_lock_state(), &storage);
74191
}
75192

76-
static BOOST_FORCEINLINE void notify_all(storage_type volatile& storage) BOOST_NOEXCEPT
193+
static BOOST_FORCEINLINE void notify_all(storage_type volatile& storage) noexcept
77194
{
78195
scoped_lock lock(&storage);
79196
lock_pool::notify_all(lock.get_lock_state(), &storage);
@@ -87,19 +204,23 @@ struct wait_operations_generic< Base, true > :
87204
typedef Base base_type;
88205
typedef typename base_type::storage_type storage_type;
89206

90-
static BOOST_CONSTEXPR_OR_CONST bool always_has_native_wait_notify = false;
207+
static constexpr bool always_has_native_wait_notify = false;
208+
209+
private:
210+
static constexpr unsigned int fast_loop_count = 16u;
91211

92-
static BOOST_FORCEINLINE bool has_native_wait_notify(storage_type const volatile&) BOOST_NOEXCEPT
212+
public:
213+
static BOOST_FORCEINLINE bool has_native_wait_notify(storage_type const volatile&) noexcept
93214
{
94215
return false;
95216
}
96217

97-
static BOOST_FORCEINLINE storage_type wait(storage_type const volatile& storage, storage_type old_val, memory_order order) BOOST_NOEXCEPT
218+
static BOOST_FORCEINLINE storage_type wait(storage_type const volatile& storage, storage_type old_val, memory_order order) noexcept
98219
{
99220
storage_type new_val = base_type::load(storage, order);
100221
if (new_val == old_val)
101222
{
102-
for (unsigned int i = 0u; i < 16u; ++i)
223+
for (unsigned int i = 0u; i < fast_loop_count; ++i)
103224
{
104225
atomics::detail::pause();
105226
new_val = base_type::load(storage, order);
@@ -119,11 +240,72 @@ struct wait_operations_generic< Base, true > :
119240
return new_val;
120241
}
121242

122-
static BOOST_FORCEINLINE void notify_one(storage_type volatile&) BOOST_NOEXCEPT
243+
private:
244+
template< typename Clock >
245+
static BOOST_FORCEINLINE wait_result< storage_type >
246+
wait_until_impl(storage_type const volatile& storage, storage_type old_val, typename Clock::time_point timeout, typename Clock::time_point now, memory_order order)
247+
noexcept(noexcept(Clock::now()))
248+
{
249+
wait_result< storage_type > res{ base_type::load(storage, order), false };
250+
if (res.value == old_val)
251+
{
252+
for (unsigned int i = 0u; i < fast_loop_count; ++i)
253+
{
254+
if ((now - timeout).count() >= 0)
255+
{
256+
res.timeout = true;
257+
goto finish;
258+
}
259+
260+
atomics::detail::pause();
261+
now = Clock::now();
262+
263+
res.value = base_type::load(storage, order);
264+
if (res.value != old_val)
265+
goto finish;
266+
}
267+
268+
do
269+
{
270+
if ((now - timeout).count() >= 0)
271+
{
272+
res.timeout = true;
273+
goto finish;
274+
}
275+
276+
atomics::detail::wait_some();
277+
now = Clock::now();
278+
res.value = base_type::load(storage, order);
279+
}
280+
while (res.value == old_val);
281+
}
282+
283+
finish:
284+
return res;
285+
}
286+
287+
public:
288+
template< typename Clock, typename Duration >
289+
static BOOST_FORCEINLINE wait_result< storage_type >
290+
wait_until(storage_type const volatile& storage, storage_type old_val, std::chrono::time_point< Clock, Duration > timeout, memory_order order)
291+
noexcept(noexcept(wait_until_impl< Clock >(storage, old_val, timeout, Clock::now(), order)))
292+
{
293+
return wait_until_impl< Clock >(storage, old_val, timeout, Clock::now(), order);
294+
}
295+
296+
template< typename Rep, typename Period >
297+
static BOOST_FORCEINLINE wait_result< storage_type >
298+
wait_for(storage_type const volatile& storage, storage_type old_val, std::chrono::duration< Rep, Period > timeout, memory_order order) noexcept
299+
{
300+
const std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
301+
return wait_until_impl< std::chrono::steady_clock >(storage, old_val, now + timeout, now, order);
302+
}
303+
304+
static BOOST_FORCEINLINE void notify_one(storage_type volatile&) noexcept
123305
{
124306
}
125307

126-
static BOOST_FORCEINLINE void notify_all(storage_type volatile&) BOOST_NOEXCEPT
308+
static BOOST_FORCEINLINE void notify_all(storage_type volatile&) noexcept
127309
{
128310
}
129311
};

0 commit comments

Comments
 (0)