include/boost/capy/timeout.hpp

89.4% Lines (168/188) 100.0% List of functions (34/34)
f(x) Functions (34)
Function Calls Lines Blocks
boost::capy::detail::timeout_state<boost::capy::io_result<> >::timeout_state() :40 2x 100.0% 100.0% boost::capy::detail::timeout_state<boost::capy::io_result<int> >::timeout_state() :40 4x 100.0% 100.0% boost::capy::detail::timeout_state<boost::capy::io_result<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >::timeout_state() :40 1x 100.0% 100.0% boost::capy::detail::timeout_state<boost::capy::io_result<unsigned long> >::timeout_state() :40 1x 100.0% 100.0% boost::capy::detail::when_all_runner<boost::capy::detail::timeout_state<boost::capy::io_result<> > > boost::capy::detail::make_timeout_inner_runner<boost::capy::task<boost::capy::io_result<> >, boost::capy::io_result<> >(boost::capy::task<boost::capy::io_result<> >, boost::capy::detail::timeout_state<boost::capy::io_result<> >*) :48 2x 100.0% 47.0% boost::capy::detail::when_all_runner<boost::capy::detail::timeout_state<boost::capy::io_result<int> > > boost::capy::detail::make_timeout_inner_runner<boost::capy::task<boost::capy::io_result<int> >, boost::capy::io_result<int> >(boost::capy::task<boost::capy::io_result<int> >, boost::capy::detail::timeout_state<boost::capy::io_result<int> >*) :48 4x 100.0% 47.0% boost::capy::detail::when_all_runner<boost::capy::detail::timeout_state<boost::capy::io_result<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > > boost::capy::detail::make_timeout_inner_runner<boost::capy::task<boost::capy::io_result<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, boost::capy::io_result<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >(boost::capy::task<boost::capy::io_result<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, boost::capy::detail::timeout_state<boost::capy::io_result<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >*) :48 1x 100.0% 47.0% boost::capy::detail::when_all_runner<boost::capy::detail::timeout_state<boost::capy::io_result<unsigned long> > > boost::capy::detail::make_timeout_inner_runner<boost::capy::task<boost::capy::io_result<unsigned long> >, boost::capy::io_result<unsigned long> >(boost::capy::task<boost::capy::io_result<unsigned long> >, boost::capy::detail::timeout_state<boost::capy::io_result<unsigned long> >*) :48 1x 100.0% 47.0% boost::capy::detail::when_all_runner<boost::capy::detail::timeout_state<boost::capy::io_result<> > > boost::capy::detail::make_timeout_delay_runner<boost::capy::delay_awaitable, boost::capy::io_result<> >(boost::capy::delay_awaitable, boost::capy::detail::timeout_state<boost::capy::io_result<> >*) :69 2x 100.0% 47.0% boost::capy::detail::when_all_runner<boost::capy::detail::timeout_state<boost::capy::io_result<int> > > boost::capy::detail::make_timeout_delay_runner<boost::capy::delay_awaitable, boost::capy::io_result<int> >(boost::capy::delay_awaitable, boost::capy::detail::timeout_state<boost::capy::io_result<int> >*) :69 4x 100.0% 47.0% boost::capy::detail::when_all_runner<boost::capy::detail::timeout_state<boost::capy::io_result<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > > boost::capy::detail::make_timeout_delay_runner<boost::capy::delay_awaitable, boost::capy::io_result<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >(boost::capy::delay_awaitable, boost::capy::detail::timeout_state<boost::capy::io_result<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >*) :69 1x 100.0% 47.0% boost::capy::detail::when_all_runner<boost::capy::detail::timeout_state<boost::capy::io_result<unsigned long> > > boost::capy::detail::make_timeout_delay_runner<boost::capy::delay_awaitable, boost::capy::io_result<unsigned long> >(boost::capy::delay_awaitable, boost::capy::detail::timeout_state<boost::capy::io_result<unsigned long> >*) :69 1x 100.0% 47.0% boost::capy::detail::timeout_launcher<boost::capy::task<boost::capy::io_result<> >, boost::capy::delay_awaitable, boost::capy::io_result<> >::timeout_launcher(boost::capy::task<boost::capy::io_result<> >*, boost::capy::delay_awaitable*, boost::capy::detail::timeout_state<boost::capy::io_result<> >*) :91 2x 100.0% 100.0% boost::capy::detail::timeout_launcher<boost::capy::task<boost::capy::io_result<int> >, boost::capy::delay_awaitable, boost::capy::io_result<int> >::timeout_launcher(boost::capy::task<boost::capy::io_result<int> >*, boost::capy::delay_awaitable*, boost::capy::detail::timeout_state<boost::capy::io_result<int> >*) :91 4x 100.0% 100.0% boost::capy::detail::timeout_launcher<boost::capy::task<boost::capy::io_result<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, boost::capy::delay_awaitable, boost::capy::io_result<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >::timeout_launcher(boost::capy::task<boost::capy::io_result<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >*, boost::capy::delay_awaitable*, boost::capy::detail::timeout_state<boost::capy::io_result<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >*) :91 1x 100.0% 100.0% boost::capy::detail::timeout_launcher<boost::capy::task<boost::capy::io_result<unsigned long> >, boost::capy::delay_awaitable, boost::capy::io_result<unsigned long> >::timeout_launcher(boost::capy::task<boost::capy::io_result<unsigned long> >*, boost::capy::delay_awaitable*, boost::capy::detail::timeout_state<boost::capy::io_result<unsigned long> >*) :91 1x 100.0% 100.0% boost::capy::detail::timeout_launcher<boost::capy::task<boost::capy::io_result<> >, boost::capy::delay_awaitable, boost::capy::io_result<> >::await_ready() const :100 2x 100.0% 100.0% boost::capy::detail::timeout_launcher<boost::capy::task<boost::capy::io_result<int> >, boost::capy::delay_awaitable, boost::capy::io_result<int> >::await_ready() const :100 4x 100.0% 100.0% boost::capy::detail::timeout_launcher<boost::capy::task<boost::capy::io_result<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, boost::capy::delay_awaitable, boost::capy::io_result<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >::await_ready() const :100 1x 100.0% 100.0% boost::capy::detail::timeout_launcher<boost::capy::task<boost::capy::io_result<unsigned long> >, boost::capy::delay_awaitable, boost::capy::io_result<unsigned long> >::await_ready() const :100 1x 100.0% 100.0% boost::capy::detail::timeout_launcher<boost::capy::task<boost::capy::io_result<> >, boost::capy::delay_awaitable, boost::capy::io_result<> >::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :102 2x 83.3% 76.0% boost::capy::detail::timeout_launcher<boost::capy::task<boost::capy::io_result<int> >, boost::capy::delay_awaitable, boost::capy::io_result<int> >::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :102 4x 83.3% 76.0% boost::capy::detail::timeout_launcher<boost::capy::task<boost::capy::io_result<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, boost::capy::delay_awaitable, boost::capy::io_result<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :102 1x 83.3% 76.0% boost::capy::detail::timeout_launcher<boost::capy::task<boost::capy::io_result<unsigned long> >, boost::capy::delay_awaitable, boost::capy::io_result<unsigned long> >::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :102 1x 83.3% 76.0% boost::capy::detail::timeout_launcher<boost::capy::task<boost::capy::io_result<> >, boost::capy::delay_awaitable, boost::capy::io_result<> >::await_resume() const :150 2x 100.0% 100.0% boost::capy::detail::timeout_launcher<boost::capy::task<boost::capy::io_result<int> >, boost::capy::delay_awaitable, boost::capy::io_result<int> >::await_resume() const :150 4x 100.0% 100.0% boost::capy::detail::timeout_launcher<boost::capy::task<boost::capy::io_result<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, boost::capy::delay_awaitable, boost::capy::io_result<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >::await_resume() const :150 1x 100.0% 100.0% boost::capy::detail::timeout_launcher<boost::capy::task<boost::capy::io_result<unsigned long> >, boost::capy::delay_awaitable, boost::capy::io_result<unsigned long> >::await_resume() const :150 1x 100.0% 100.0% boost::capy::task<decltype ((((declval<std::decay<boost::capy::task<boost::capy::io_result<> > >::type&>)()).await_resume)())> boost::capy::timeout<boost::capy::task<boost::capy::io_result<> >, long, std::ratio<1l, 1000l> >(boost::capy::task<boost::capy::io_result<> >, std::chrono::duration<long, std::ratio<1l, 1000l> >) :204 1x 100.0% 42.0% boost::capy::task<decltype ((((declval<std::decay<boost::capy::task<boost::capy::io_result<> > >::type&>)()).await_resume)())> boost::capy::timeout<boost::capy::task<boost::capy::io_result<> >, long, std::ratio<1l, 1l> >(boost::capy::task<boost::capy::io_result<> >, std::chrono::duration<long, std::ratio<1l, 1l> >) :204 1x 100.0% 42.0% boost::capy::task<decltype ((((declval<std::decay<boost::capy::task<boost::capy::io_result<int> > >::type&>)()).await_resume)())> boost::capy::timeout<boost::capy::task<boost::capy::io_result<int> >, long, std::ratio<1l, 1000l> >(boost::capy::task<boost::capy::io_result<int> >, std::chrono::duration<long, std::ratio<1l, 1000l> >) :204 2x 100.0% 42.0% boost::capy::task<decltype ((((declval<std::decay<boost::capy::task<boost::capy::io_result<int> > >::type&>)()).await_resume)())> boost::capy::timeout<boost::capy::task<boost::capy::io_result<int> >, long, std::ratio<1l, 1l> >(boost::capy::task<boost::capy::io_result<int> >, std::chrono::duration<long, std::ratio<1l, 1l> >) :204 2x 100.0% 42.0% boost::capy::task<decltype ((((declval<std::decay<boost::capy::task<boost::capy::io_result<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >::type&>)()).await_resume)())> boost::capy::timeout<boost::capy::task<boost::capy::io_result<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, long, std::ratio<1l, 1l> >(boost::capy::task<boost::capy::io_result<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >, std::chrono::duration<long, std::ratio<1l, 1l> >) :204 1x 100.0% 42.0% boost::capy::task<decltype ((((declval<std::decay<boost::capy::task<boost::capy::io_result<unsigned long> > >::type&>)()).await_resume)())> boost::capy::timeout<boost::capy::task<boost::capy::io_result<unsigned long> >, long, std::ratio<1l, 1000l> >(boost::capy::task<boost::capy::io_result<unsigned long> >, std::chrono::duration<long, std::ratio<1l, 1000l> >) :204 1x 100.0% 42.0%
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Michael Vandeberg
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/cppalliance/capy
8 //
9
10 #ifndef BOOST_CAPY_TIMEOUT_HPP
11 #define BOOST_CAPY_TIMEOUT_HPP
12
13 #include <boost/capy/detail/config.hpp>
14 #include <boost/capy/concept/io_awaitable.hpp>
15 #include <boost/capy/delay.hpp>
16 #include <boost/capy/detail/io_result_combinators.hpp>
17 #include <boost/capy/error.hpp>
18 #include <boost/capy/io_result.hpp>
19 #include <boost/capy/task.hpp>
20 #include <boost/capy/when_all.hpp>
21
22 #include <atomic>
23 #include <chrono>
24 #include <exception>
25 #include <optional>
26
27 namespace boost {
28 namespace capy {
29 namespace detail {
30
31 template<typename T>
32 struct timeout_state
33 {
34 when_all_core core_;
35 std::atomic<int> winner_{-1}; // -1=none, 0=inner, 1=delay
36 std::optional<T> inner_result_;
37 std::exception_ptr inner_exception_;
38 std::array<std::coroutine_handle<>, 2> runner_handles_{};
39
40 8x timeout_state()
41 8x : core_(2)
42 {
43 8x }
44 };
45
46 template<IoAwaitable Awaitable, typename T>
47 when_all_runner<timeout_state<T>>
48 8x make_timeout_inner_runner(
49 Awaitable inner, timeout_state<T>* state)
50 {
51 try
52 {
53 auto result = co_await std::move(inner);
54 state->inner_result_.emplace(std::move(result));
55 }
56 catch(...)
57 {
58 state->inner_exception_ = std::current_exception();
59 }
60
61 int expected = -1;
62 if(state->winner_.compare_exchange_strong(
63 expected, 0, std::memory_order_relaxed))
64 state->core_.stop_source_.request_stop();
65 16x }
66
67 template<typename DelayAw, typename T>
68 when_all_runner<timeout_state<T>>
69 8x make_timeout_delay_runner(
70 DelayAw d, timeout_state<T>* state)
71 {
72 auto result = co_await std::move(d);
73
74 if(!result.ec)
75 {
76 int expected = -1;
77 if(state->winner_.compare_exchange_strong(
78 expected, 1, std::memory_order_relaxed))
79 state->core_.stop_source_.request_stop();
80 }
81 16x }
82
83 template<IoAwaitable Inner, typename DelayAw, typename T>
84 class timeout_launcher
85 {
86 Inner* inner_;
87 DelayAw* delay_;
88 timeout_state<T>* state_;
89
90 public:
91 8x timeout_launcher(
92 Inner* inner, DelayAw* delay,
93 timeout_state<T>* state)
94 8x : inner_(inner)
95 8x , delay_(delay)
96 8x , state_(state)
97 {
98 8x }
99
100 8x bool await_ready() const noexcept { return false; }
101
102 8x std::coroutine_handle<> await_suspend(
103 std::coroutine_handle<> continuation,
104 io_env const* caller_env)
105 {
106 8x state_->core_.continuation_ = continuation;
107 8x state_->core_.caller_env_ = caller_env;
108
109 8x if(caller_env->stop_token.stop_possible())
110 {
111 state_->core_.parent_stop_callback_.emplace(
112 caller_env->stop_token,
113 when_all_core::stop_callback_fn{
114 &state_->core_.stop_source_});
115
116 if(caller_env->stop_token.stop_requested())
117 state_->core_.stop_source_.request_stop();
118 }
119
120 8x auto token = state_->core_.stop_source_.get_token();
121
122 8x auto r0 = make_timeout_inner_runner(
123 8x std::move(*inner_), state_);
124 8x auto h0 = r0.release();
125 8x h0.promise().state_ = state_;
126 8x h0.promise().env_ = io_env{
127 caller_env->executor, token,
128 8x caller_env->frame_allocator};
129 8x state_->runner_handles_[0] =
130 std::coroutine_handle<>{h0};
131
132 8x auto r1 = make_timeout_delay_runner(
133 8x std::move(*delay_), state_);
134 8x auto h1 = r1.release();
135 8x h1.promise().state_ = state_;
136 8x h1.promise().env_ = io_env{
137 caller_env->executor, token,
138 8x caller_env->frame_allocator};
139 8x state_->runner_handles_[1] =
140 std::coroutine_handle<>{h1};
141
142 8x caller_env->executor.post(
143 8x state_->runner_handles_[0]);
144 8x caller_env->executor.post(
145 8x state_->runner_handles_[1]);
146
147 16x return std::noop_coroutine();
148 24x }
149
150 8x void await_resume() const noexcept {}
151 };
152
153 } // namespace detail
154
155 /** Race an io_result-returning awaitable against a deadline.
156
157 Starts the awaitable and a timer concurrently. The first to
158 complete wins and cancels the other. If the awaitable finishes
159 first, its result is returned as-is (success, error, or
160 exception). If the timer fires first, an `io_result` with
161 `ec == error::timeout` is produced.
162
163 Unlike @ref when_any, exceptions from the inner awaitable
164 are always propagated — they are never swallowed by the timer.
165
166 @par Return Type
167
168 Always returns `io_result<Ts...>` matching the inner
169 awaitable's result type. On timeout, `ec` is set to
170 `error::timeout` and payload values are default-initialized.
171
172 @par Precision
173
174 The timeout fires at or after the specified duration.
175
176 @par Cancellation
177
178 If the parent's stop token is activated, both children are
179 cancelled. The inner awaitable's cancellation result is
180 returned.
181
182 @par Example
183 @code
184 auto [ec, n] = co_await timeout(sock.read_some(buf), 50ms);
185 if (ec == cond::timeout) {
186 // handle timeout
187 }
188 @endcode
189
190 @tparam A An IoAwaitable returning `io_result<Ts...>`.
191
192 @param a The awaitable to race against the deadline.
193 @param dur The maximum duration to wait.
194
195 @return `task<awaitable_result_t<A>>`.
196
197 @throws Rethrows any exception from the inner awaitable,
198 regardless of whether the timer has fired.
199
200 @see delay, cond::timeout
201 */
202 template<IoAwaitable A, typename Rep, typename Period>
203 requires detail::is_io_result_v<awaitable_result_t<A>>
204 8x auto timeout(A a, std::chrono::duration<Rep, Period> dur)
205 -> task<awaitable_result_t<A>>
206 {
207 using T = awaitable_result_t<A>;
208
209 auto d = delay(dur);
210 detail::timeout_state<T> state;
211
212 co_await detail::timeout_launcher<
213 A, decltype(d), T>(&a, &d, &state);
214
215 if(state.core_.first_exception_)
216 std::rethrow_exception(state.core_.first_exception_);
217 if(state.inner_exception_)
218 std::rethrow_exception(state.inner_exception_);
219
220 if(state.winner_.load(std::memory_order_relaxed) == 0)
221 co_return std::move(*state.inner_result_);
222
223 // Delay fired first: timeout
224 T r{};
225 r.ec = make_error_code(error::timeout);
226 co_return r;
227 16x }
228
229 } // capy
230 } // boost
231
232 #endif
233