100.00% Lines (9/9) 100.00% Functions (2/2)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2026 Steve Gerbino 2   // Copyright (c) 2026 Steve Gerbino
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/corosio 7   // Official repository: https://github.com/cppalliance/corosio
8   // 8   //
9   9  
10   #ifndef BOOST_COROSIO_TIMEOUT_HPP 10   #ifndef BOOST_COROSIO_TIMEOUT_HPP
11   #define BOOST_COROSIO_TIMEOUT_HPP 11   #define BOOST_COROSIO_TIMEOUT_HPP
12   12  
13   #include <boost/corosio/detail/config.hpp> 13   #include <boost/corosio/detail/config.hpp>
14   #include <boost/corosio/detail/timeout_awaitable.hpp> 14   #include <boost/corosio/detail/timeout_awaitable.hpp>
15   #include <boost/capy/concept/io_awaitable.hpp> 15   #include <boost/capy/concept/io_awaitable.hpp>
16   16  
17   #include <chrono> 17   #include <chrono>
18   #include <type_traits> 18   #include <type_traits>
19   #include <utility> 19   #include <utility>
20   20  
21   namespace boost::corosio { 21   namespace boost::corosio {
22   22  
23   /** Race an io_result-returning awaitable against a deadline. 23   /** Race an io_result-returning awaitable against a deadline.
24   24  
25   Starts the awaitable with an interposed stop token and arms a 25   Starts the awaitable with an interposed stop token and arms a
26   timer. If the awaitable finishes first, its result is returned 26   timer. If the awaitable finishes first, its result is returned
27   as-is (success, error, or exception). If the deadline passes 27   as-is (success, error, or exception). If the deadline passes
28   first, the awaitable is cancelled and an `io_result` whose 28   first, the awaitable is cancelled and an `io_result` whose
29   `ec` compares equal to `capy::cond::timeout` (with a 29   `ec` compares equal to `capy::cond::timeout` (with a
30   default-initialized payload) is produced. 30   default-initialized payload) is produced.
31   31  
32   Exceptions from the inner awaitable always propagate; they are 32   Exceptions from the inner awaitable always propagate; they are
33   never swallowed by the timer. 33   never swallowed by the timer.
34   34  
35   @par Preconditions 35   @par Preconditions
36   The awaiting coroutine's executor must belong to an 36   The awaiting coroutine's executor must belong to an
37   `io_context`; any other execution context terminates with a 37   `io_context`; any other execution context terminates with a
38   diagnostic. 38   diagnostic.
39   39  
40   @par Cancellation 40   @par Cancellation
41   If the parent's stop token is activated, the inner awaitable 41   If the parent's stop token is activated, the inner awaitable
42   is cancelled and its cancellation result is returned. Requesting 42   is cancelled and its cancellation result is returned. Requesting
43   stop from another thread requires a multi-threaded-capable 43   stop from another thread requires a multi-threaded-capable
44   io_context; a context running in single_threaded mode 44   io_context; a context running in single_threaded mode
45   (auto-enabled at concurrency_hint == 1) does not permit 45   (auto-enabled at concurrency_hint == 1) does not permit
46   cross-thread cancellation. 46   cross-thread cancellation.
47   47  
48   @par Example 48   @par Example
49   @code 49   @code
50   auto [ec, n] = co_await timeout(sock.read_some(buf), 50ms); 50   auto [ec, n] = co_await timeout(sock.read_some(buf), 50ms);
51   if (ec == capy::cond::timeout) { 51   if (ec == capy::cond::timeout) {
52   // handle timeout 52   // handle timeout
53   } 53   }
54   @endcode 54   @endcode
55   55  
56   @param a The awaitable to race against the deadline. 56   @param a The awaitable to race against the deadline.
57   @param dur The maximum duration to wait, measured from 57   @param dur The maximum duration to wait, measured from
58   suspension. 58   suspension.
59   59  
60   @return An awaitable yielding `io_result` matching the inner 60   @return An awaitable yielding `io_result` matching the inner
61   awaitable's result type. 61   awaitable's result type.
62   62  
63   @see delay 63   @see delay
64   */ 64   */
65   template<capy::IoAwaitable A, typename Rep, typename Period> 65   template<capy::IoAwaitable A, typename Rep, typename Period>
66   requires detail::is_io_result_v< 66   requires detail::is_io_result_v<
67   std::remove_cvref_t<capy::awaitable_result_t<A>>> && 67   std::remove_cvref_t<capy::awaitable_result_t<A>>> &&
68   std::is_default_constructible_v< 68   std::is_default_constructible_v<
69   std::remove_cvref_t<capy::awaitable_result_t<A>>> 69   std::remove_cvref_t<capy::awaitable_result_t<A>>>
HITCBC 70   2038 [[nodiscard]] auto timeout(A a, std::chrono::duration<Rep, Period> dur) 70   2038 [[nodiscard]] auto timeout(A a, std::chrono::duration<Rep, Period> dur)
71   { 71   {
72   using namespace std::chrono; 72   using namespace std::chrono;
73   // Narrow reps wrap if nanoseconds::max() is converted into them; 73   // Narrow reps wrap if nanoseconds::max() is converted into them;
74   // a double comparison clamps safely in both directions. 74   // a double comparison clamps safely in both directions.
75   using dsec = duration<double>; 75   using dsec = duration<double>;
HITCBC 76   4062 auto ns = dsec(dur) >= dsec((nanoseconds::max)()) 76   4062 auto ns = dsec(dur) >= dsec((nanoseconds::max)())
HITCBC 77   4062 ? (nanoseconds::max)() 77   4062 ? (nanoseconds::max)()
HITCBC 78   2036 : dsec(dur) <= dsec((nanoseconds::min)()) 78   2036 : dsec(dur) <= dsec((nanoseconds::min)())
HITCBC 79   2036 ? (nanoseconds::min)() 79   2036 ? (nanoseconds::min)()
HITCBC 80   2034 : duration_cast<nanoseconds>(dur); 80   2034 : duration_cast<nanoseconds>(dur);
HITCBC 81   4076 return detail::timeout_awaitable<A>(std::move(a), ns); 81   4076 return detail::timeout_awaitable<A>(std::move(a), ns);
82   } 82   }
83   83  
84   /** Race an io_result-returning awaitable against an absolute deadline. 84   /** Race an io_result-returning awaitable against an absolute deadline.
85   85  
86   Behaves as the duration overload with the deadline fixed at 86   Behaves as the duration overload with the deadline fixed at
87   `tp` instead of measured from suspension. 87   `tp` instead of measured from suspension.
88   88  
89   @param a The awaitable to race against the deadline. 89   @param a The awaitable to race against the deadline.
90   @param tp The steady-clock time point at which the awaitable 90   @param tp The steady-clock time point at which the awaitable
91   is cancelled. 91   is cancelled.
92   92  
93   @return An awaitable yielding `io_result` matching the inner 93   @return An awaitable yielding `io_result` matching the inner
94   awaitable's result type. 94   awaitable's result type.
95   95  
96   @see delay 96   @see delay
97   */ 97   */
98   template<capy::IoAwaitable A> 98   template<capy::IoAwaitable A>
99   requires detail::is_io_result_v< 99   requires detail::is_io_result_v<
100   std::remove_cvref_t<capy::awaitable_result_t<A>>> && 100   std::remove_cvref_t<capy::awaitable_result_t<A>>> &&
101   std::is_default_constructible_v< 101   std::is_default_constructible_v<
102   std::remove_cvref_t<capy::awaitable_result_t<A>>> 102   std::remove_cvref_t<capy::awaitable_result_t<A>>>
HITCBC 103   4 [[nodiscard]] auto timeout(A a, std::chrono::steady_clock::time_point tp) 103   4 [[nodiscard]] auto timeout(A a, std::chrono::steady_clock::time_point tp)
104   { 104   {
HITCBC 105   4 return detail::timeout_awaitable<A>(std::move(a), tp); 105   4 return detail::timeout_awaitable<A>(std::move(a), tp);
106   } 106   }
107   107  
108   } // namespace boost::corosio 108   } // namespace boost::corosio
109   109  
110   #endif 110   #endif