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/error.hpp>
 
17 -
#include <boost/capy/io_result.hpp>
 
18 -
#include <boost/capy/task.hpp>
 
19 -
#include <boost/capy/when_any.hpp>
 
20 -

 
21 -
#include <chrono>
 
22 -
#include <system_error>
 
23 -
#include <type_traits>
 
24 -

 
25 -
namespace boost {
 
26 -
namespace capy {
 
27 -

 
28 -
namespace detail {
 
29 -

 
30 -
template<typename T>
 
31 -
struct is_io_result : std::false_type {};
 
32 -

 
33 -
template<typename... Args>
 
34 -
struct is_io_result<io_result<Args...>> : std::true_type {};
 
35 -

 
36 -
template<typename T>
 
37 -
inline constexpr bool is_io_result_v = is_io_result<T>::value;
 
38 -

 
39 -
} // detail
 
40 -

 
41 -
/** Race an awaitable against a deadline.
 
42 -

 
43 -
    Starts the awaitable and a timer concurrently. If the
 
44 -
    awaitable completes first, its result is returned. If the
 
45 -
    timer fires first, stop is requested for the awaitable and
 
46 -
    a timeout error is produced.
 
47 -

 
48 -
    @par Return Type
 
49 -

 
50 -
    The return type matches the inner awaitable's result type:
 
51 -

 
52 -
    @li For `io_result<...>` types: returns `io_result` with
 
53 -
        `ec == error::timeout` and default-initialized values
 
54 -
    @li For non-void types: throws `std::system_error(error::timeout)`
 
55 -
    @li For void: throws `std::system_error(error::timeout)`
 
56 -

 
57 -
    @par Precision
 
58 -

 
59 -
    The timeout fires at or after the specified duration.
 
60 -

 
61 -
    @par Cancellation
 
62 -

 
63 -
    If the parent's stop token is activated, the inner awaitable
 
64 -
    is cancelled normally (not a timeout). The result reflects
 
65 -
    the inner awaitable's cancellation behavior.
 
66 -

 
67 -
    @par Example
 
68 -
    @code
 
69 -
    auto [ec, n] = co_await timeout(sock.read_some(buf), 50ms);
 
70 -
    if (ec == cond::timeout) {
 
71 -
        // handle timeout
 
72 -
    }
 
73 -
    @endcode
 
74 -

 
75 -
    @tparam A An IoAwaitable whose result type determines
 
76 -
        how timeouts are reported.
 
77 -

 
78 -
    @param a The awaitable to race against the deadline.
 
79 -
    @param dur The maximum duration to wait.
 
80 -

 
81 -
    @return `task<awaitable_result_t<A>>`.
 
82 -

 
83 -
    @throws std::system_error with `error::timeout` if the timer
 
84 -
        fires first and the result type is not `io_result`.
 
85 -
        Exceptions thrown by the inner awaitable propagate
 
86 -
        unchanged.
 
87 -

 
88 -
    @see delay, when_any, cond::timeout
 
89 -
*/
 
90 -
template<IoAwaitable A, typename Rep, typename Period>
 
91 -
auto timeout(A a, std::chrono::duration<Rep, Period> dur)
 
92 -
    -> task<awaitable_result_t<A>>
 
93 -
{
 
94 -
    using T = awaitable_result_t<A>;
 
95 -

 
96 -
    auto result = co_await when_any(
 
97 -
        std::move(a), delay(dur));
 
98 -

 
99 -
    if(result.index() == 0)
 
100 -
    {
 
101 -
        // Task completed first
 
102 -
        if constexpr (std::is_void_v<T>)
 
103 -
            co_return;
 
104 -
        else
 
105 -
            co_return std::get<0>(std::move(result));
 
106 -
    }
 
107 -
    else
 
108 -
    {
 
109 -
        // Timer won
 
110 -
        if constexpr (detail::is_io_result_v<T>)
 
111 -
        {
 
112 -
            T timeout_result{};
 
113 -
            timeout_result.ec = make_error_code(error::timeout);
 
114 -
            co_return timeout_result;
 
115 -
        }
 
116 -
        else if constexpr (std::is_void_v<T>)
 
117 -
        {
 
118 -
            throw std::system_error(
 
119 -
                make_error_code(error::timeout));
 
120 -
        }
 
121 -
        else
 
122 -
        {
 
123 -
            throw std::system_error(
 
124 -
                make_error_code(error::timeout));
 
125 -
        }
 
126 -
    }
 
127 -
}
 
128 -

 
129 -
} // capy
 
130 -
} // boost
 
131 -

 
132 -
#endif