include/boost/capy/io_task.hpp

100.0% Lines (29/29) 100.0% List of functions (54/54)
io_task.hpp
f(x) Functions (54)
Function Calls Lines Blocks
boost::capy::io_task<>::promise_type::get_return_object() :45 70x 100.0% 100.0% boost::capy::io_task<int>::promise_type::get_return_object() :45 7x 100.0% 100.0% boost::capy::io_task<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::promise_type::get_return_object() :45 7x 100.0% 100.0% boost::capy::io_task<std::vector<unsigned long, std::allocator<unsigned long> > >::promise_type::get_return_object() :45 1x 100.0% 100.0% boost::capy::io_task<unsigned long, int>::promise_type::get_return_object() :45 1x 100.0% 100.0% boost::capy::io_task<unsigned long>::promise_type::get_return_object() :45 1528x 100.0% 100.0% auto boost::capy::io_task<>::promise_type::yield_value<>(boost::capy::io_result<>) :55 2x 100.0% 100.0% auto boost::capy::io_task<int>::promise_type::yield_value<int, int, int>(boost::capy::io_result<int, int, int>) :55 1x 100.0% 100.0% auto boost::capy::io_task<int>::promise_type::yield_value<int>(boost::capy::io_result<int>) :55 2x 100.0% 100.0% auto boost::capy::io_task<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::promise_type::yield_value<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> > >) :55 1x 100.0% 100.0% boost::capy::io_task<>::~io_task() :84 407x 100.0% 100.0% boost::capy::io_task<int>::~io_task() :84 26x 100.0% 100.0% boost::capy::io_task<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::~io_task() :84 37x 100.0% 100.0% boost::capy::io_task<std::vector<unsigned long, std::allocator<unsigned long> > >::~io_task() :84 6x 100.0% 100.0% boost::capy::io_task<unsigned long, int>::~io_task() :84 6x 100.0% 100.0% boost::capy::io_task<unsigned long>::~io_task() :84 3574x 100.0% 100.0% boost::capy::io_task<>::await_ready() const :90 68x 100.0% 100.0% boost::capy::io_task<int>::await_ready() const :90 4x 100.0% 100.0% boost::capy::io_task<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::await_ready() const :90 6x 100.0% 100.0% boost::capy::io_task<std::vector<unsigned long, std::allocator<unsigned long> > >::await_ready() const :90 1x 100.0% 100.0% boost::capy::io_task<unsigned long, int>::await_ready() const :90 1x 100.0% 100.0% boost::capy::io_task<unsigned long>::await_ready() const :90 1401x 100.0% 100.0% boost::capy::io_task<>::await_resume() :96 68x 100.0% 100.0% boost::capy::io_task<int>::await_resume() :96 4x 100.0% 100.0% boost::capy::io_task<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::await_resume() :96 6x 75.0% 62.0% boost::capy::io_task<std::vector<unsigned long, std::allocator<unsigned long> > >::await_resume() :96 1x 75.0% 62.0% boost::capy::io_task<unsigned long, int>::await_resume() :96 1x 75.0% 62.0% boost::capy::io_task<unsigned long>::await_resume() :96 1527x 100.0% 100.0% boost::capy::io_task<>::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :106 68x 100.0% 100.0% boost::capy::io_task<int>::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :106 4x 100.0% 100.0% boost::capy::io_task<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :106 6x 100.0% 100.0% boost::capy::io_task<std::vector<unsigned long, std::allocator<unsigned long> > >::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :106 1x 100.0% 100.0% boost::capy::io_task<unsigned long, int>::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :106 1x 100.0% 100.0% boost::capy::io_task<unsigned long>::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :106 1527x 100.0% 100.0% boost::capy::io_task<>::handle() const :124 2x 100.0% 100.0% boost::capy::io_task<int>::handle() const :124 3x 100.0% 100.0% boost::capy::io_task<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::handle() const :124 1x 100.0% 100.0% boost::capy::io_task<unsigned long>::handle() const :124 1x 100.0% 100.0% boost::capy::io_task<>::release() :144 2x 100.0% 100.0% boost::capy::io_task<int>::release() :144 3x 100.0% 100.0% boost::capy::io_task<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::release() :144 1x 100.0% 100.0% boost::capy::io_task<unsigned long>::release() :144 1x 100.0% 100.0% boost::capy::io_task<>::io_task(boost::capy::io_task<>&&) :153 337x 100.0% 100.0% boost::capy::io_task<int>::io_task(boost::capy::io_task<int>&&) :153 19x 100.0% 100.0% boost::capy::io_task<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::io_task(boost::capy::io_task<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >&&) :153 30x 100.0% 100.0% boost::capy::io_task<std::vector<unsigned long, std::allocator<unsigned long> > >::io_task(boost::capy::io_task<std::vector<unsigned long, std::allocator<unsigned long> > >&&) :153 5x 100.0% 100.0% boost::capy::io_task<unsigned long, int>::io_task(boost::capy::io_task<unsigned long, int>&&) :153 5x 100.0% 100.0% boost::capy::io_task<unsigned long>::io_task(boost::capy::io_task<unsigned long>&&) :153 2046x 100.0% 100.0% boost::capy::io_task<>::io_task(std::__n4861::coroutine_handle<boost::capy::io_task<>::promise_type>) :172 70x 100.0% 100.0% boost::capy::io_task<int>::io_task(std::__n4861::coroutine_handle<boost::capy::io_task<int>::promise_type>) :172 7x 100.0% 100.0% boost::capy::io_task<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::io_task(std::__n4861::coroutine_handle<boost::capy::io_task<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::promise_type>) :172 7x 100.0% 100.0% boost::capy::io_task<std::vector<unsigned long, std::allocator<unsigned long> > >::io_task(std::__n4861::coroutine_handle<boost::capy::io_task<std::vector<unsigned long, std::allocator<unsigned long> > >::promise_type>) :172 1x 100.0% 100.0% boost::capy::io_task<unsigned long, int>::io_task(std::__n4861::coroutine_handle<boost::capy::io_task<unsigned long, int>::promise_type>) :172 1x 100.0% 100.0% boost::capy::io_task<unsigned long>::io_task(std::__n4861::coroutine_handle<boost::capy::io_task<unsigned long>::promise_type>) :172 1528x 100.0% 100.0%
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
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_IO_TASK_HPP
11 #define BOOST_CAPY_IO_TASK_HPP
12
13 #include <boost/capy/io_result.hpp>
14 #include <boost/capy/task.hpp>
15 #include <coroutine>
16
17 namespace boost {
18 namespace capy {
19
20 /** A task type for I/O operations yielding io_result.
21
22 This is a convenience alias for `task<io_result<Ts...>>`.
23 The converting constructor on `io_result<>` allows direct
24 `co_return` of error codes:
25
26 @code
27 io_task<> connect_to_server(socket& s, endpoint ep)
28 {
29 co_return co_await s.connect(ep); // returns io_result<>
30 }
31
32 io_task<> handler(route_params& rp)
33 {
34 co_return route::next; // error_code converts to io_result<>
35 }
36 @endcode
37
38 @tparam Ts Additional value types beyond error_code.
39 */
40 template<class... Ts>
41 struct io_task
42 {
43 struct promise_type : task<io_result<Ts...>>::promise_type
44 {
45 1614x io_task get_return_object()
46 {
47 1614x return io_task{std::coroutine_handle<promise_type>::from_promise(*this)};
48 }
49
50 friend io_task;
51
52 // An io_task can yield an io_result value. That means the coroutie suspend if the error is set.
53 template<typename ... Us>
54 requires (std::constructible_from<Ts> && ...)
55 6x auto yield_value(io_result<Us...> res)
56 {
57 struct awaiter
58 {
59 io_result<Us...> res;
60 bool await_ready() {return !res.ec;}
61
62 std::coroutine_handle<> await_suspend(std::coroutine_handle<promise_type> h)
63 {
64 auto &p = h.promise();
65 p.return_value({res.ec, Ts()...});
66
67 return p.continuation();
68 }
69
70 std::tuple<Us...> await_resume()
71 {
72 return std::move(res.values);
73 }
74 };
75
76 6x return awaiter{std::move(res)};
77 }
78
79
80 };
81
82 /// Destroy the task and its coroutine frame if owned.
83
84 4056x ~io_task()
85 {
86 4056x if (h_)
87 1607x h_.destroy();
88 4056x }
89 /// Return false; tasks are never immediately ready.
90 1481x bool await_ready() const noexcept
91 {
92 1481x return false;
93 }
94
95 /// Return the result or rethrow any stored exception.
96 1607x auto await_resume()
97 {
98 1607x if(h_.promise().has_ep_)
99 533x std::rethrow_exception(h_.promise().ep_);
100 1074x return std::move(*h_.promise().result_);
101 }
102
103
104
105 /// Start execution with the caller's context.
106 1607x std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, io_env const* env)
107 {
108 1607x h_.promise().set_continuation(cont);
109 1607x h_.promise().set_environment(env);
110 1607x return h_;
111 }
112
113 /** Return the coroutine handle.
114
115 @note Do not call `destroy()` on the returned handle while the
116 task is being awaited. The task's lifetime is normally managed
117 by `run_async`, `run`, or the awaiting parent; manually
118 destroying a suspended task that another coroutine is awaiting
119 produces undefined behavior. For cooperative cancellation, use
120 `std::stop_token`.
121
122 @return The coroutine handle.
123 */
124 7x std::coroutine_handle<promise_type> handle() const noexcept
125 {
126 7x return h_;
127 }
128
129 /** Release ownership of the coroutine frame.
130
131 After calling this, destroying the task does not destroy the
132 coroutine frame. The caller becomes responsible for the frame's
133 lifetime.
134
135 @note If the caller intends to call `destroy()` on the
136 released handle, it must do so only when the task has not
137 started or has fully completed. Destroying a suspended task
138 that is being awaited produces undefined behavior.
139
140 @par Postconditions
141 `handle()` returns the original handle, but the task no longer
142 owns it.
143 */
144 7x void release() noexcept
145 {
146 7x h_ = nullptr;
147 7x }
148
149 io_task(io_task const&) = delete;
150 io_task& operator=(io_task const&) = delete;
151
152 /// Construct by moving, transferring ownership.
153 2442x io_task(io_task&& other) noexcept
154 2442x : h_(std::exchange(other.h_, nullptr))
155 {
156 2442x }
157
158 /// Assign by moving, transferring ownership.
159 io_task& operator=(io_task&& other) noexcept
160 {
161 if(this != &other)
162 {
163 if(h_)
164 h_.destroy();
165 h_ = std::exchange(other.h_, nullptr);
166 }
167 return *this;
168 }
169
170
171 private:
172 1614x explicit io_task(std::coroutine_handle<promise_type> h)
173 1614x : h_(h)
174 {
175 1614x }
176
177 std::coroutine_handle<promise_type> h_;
178
179
180 };
181
182 } // namespace capy
183 } // namespace boost
184
185 #endif
186