1  
//
1  
//
2  
// Copyright (c) 2026 Michael Vandeberg
2  
// Copyright (c) 2026 Michael Vandeberg
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
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)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/cppalliance/capy
7  
// Official repository: https://github.com/cppalliance/capy
8  
//
8  
//
9  

9  

10  
#ifndef BOOST_CAPY_TIMEOUT_HPP
10  
#ifndef BOOST_CAPY_TIMEOUT_HPP
11  
#define BOOST_CAPY_TIMEOUT_HPP
11  
#define BOOST_CAPY_TIMEOUT_HPP
12  

12  

13  
#include <boost/capy/detail/config.hpp>
13  
#include <boost/capy/detail/config.hpp>
14  
#include <boost/capy/concept/io_awaitable.hpp>
14  
#include <boost/capy/concept/io_awaitable.hpp>
15  
#include <boost/capy/delay.hpp>
15  
#include <boost/capy/delay.hpp>
16  
#include <boost/capy/error.hpp>
16  
#include <boost/capy/error.hpp>
17  
#include <boost/capy/io_result.hpp>
17  
#include <boost/capy/io_result.hpp>
18  
#include <boost/capy/task.hpp>
18  
#include <boost/capy/task.hpp>
19  
#include <boost/capy/when_any.hpp>
19  
#include <boost/capy/when_any.hpp>
20  

20  

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

24  

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

27  

28  
namespace detail {
28  
namespace detail {
29  

29  

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

32  

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

35  

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

38  

39  
} // detail
39  
} // detail
40  

40  

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

42  

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

47  

48  
    @par Return Type
48  
    @par Return Type
49  

49  

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

51  

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

56  

57  
    @par Precision
57  
    @par Precision
58  

58  

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

60  

61  
    @par Cancellation
61  
    @par Cancellation
62  

62  

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

66  

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

74  

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

77  

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

80  

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

82  

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

87  

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

95  

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

98  

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

128  

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

131  

132  
#endif
132  
#endif