96.15% Lines (75/78) 96.43% Functions (27/28)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) 2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
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_TASK_HPP 10   #ifndef BOOST_CAPY_TASK_HPP
11   #define BOOST_CAPY_TASK_HPP 11   #define BOOST_CAPY_TASK_HPP
12   12  
13   #include <boost/capy/detail/config.hpp> 13   #include <boost/capy/detail/config.hpp>
14   #include <boost/capy/concept/executor.hpp> 14   #include <boost/capy/concept/executor.hpp>
15   #include <boost/capy/concept/io_awaitable.hpp> 15   #include <boost/capy/concept/io_awaitable.hpp>
16   #include <boost/capy/ex/io_awaitable_promise_base.hpp> 16   #include <boost/capy/ex/io_awaitable_promise_base.hpp>
17   #include <boost/capy/ex/io_env.hpp> 17   #include <boost/capy/ex/io_env.hpp>
18   #include <boost/capy/ex/frame_allocator.hpp> 18   #include <boost/capy/ex/frame_allocator.hpp>
19   #include <boost/capy/detail/await_suspend_helper.hpp> 19   #include <boost/capy/detail/await_suspend_helper.hpp>
20   20  
21   #include <exception> 21   #include <exception>
22   #include <optional> 22   #include <optional>
23   #include <type_traits> 23   #include <type_traits>
24   #include <utility> 24   #include <utility>
25   #include <variant> 25   #include <variant>
26   26  
27   namespace boost { 27   namespace boost {
28   namespace capy { 28   namespace capy {
29   29  
30   namespace detail { 30   namespace detail {
31   31  
32   // Helper base for result storage and return_void/return_value 32   // Helper base for result storage and return_void/return_value
33   template<typename T> 33   template<typename T>
34   struct task_return_base 34   struct task_return_base
35   { 35   {
36   std::optional<T> result_; 36   std::optional<T> result_;
37   37  
HITCBC 38   1275 void return_value(T value) 38   1275 void return_value(T value)
39   { 39   {
HITCBC 40   1275 result_ = std::move(value); 40   1275 result_ = std::move(value);
HITCBC 41   1275 } 41   1275 }
42   42  
HITCBC 43   148 T&& result() noexcept 43   148 T&& result() noexcept
44   { 44   {
HITCBC 45   148 return std::move(*result_); 45   148 return std::move(*result_);
46   } 46   }
47   }; 47   };
48   48  
49   template<> 49   template<>
50   struct task_return_base<void> 50   struct task_return_base<void>
51   { 51   {
HITCBC 52   1976 void return_void() 52   1994 void return_void()
53   { 53   {
HITCBC 54   1976 } 54   1994 }
55   }; 55   };
56   56  
57   } // namespace detail 57   } // namespace detail
58   58  
59   /** Lazy coroutine task satisfying @ref IoRunnable. 59   /** Lazy coroutine task satisfying @ref IoRunnable.
60   60  
61   Use `task<T>` as the return type for coroutines that perform I/O 61   Use `task<T>` as the return type for coroutines that perform I/O
62   and return a value of type `T`. The coroutine body does not start 62   and return a value of type `T`. The coroutine body does not start
63   executing until the task is awaited, enabling efficient composition 63   executing until the task is awaited, enabling efficient composition
64   without unnecessary eager execution. 64   without unnecessary eager execution.
65   65  
66   The task participates in the I/O awaitable protocol: when awaited, 66   The task participates in the I/O awaitable protocol: when awaited,
67   it receives the caller's executor and stop token, propagating them 67   it receives the caller's executor and stop token, propagating them
68   to nested `co_await` expressions. This enables cancellation and 68   to nested `co_await` expressions. This enables cancellation and
69   proper completion dispatch across executor boundaries. 69   proper completion dispatch across executor boundaries.
70   70  
71   @par Thread Safety 71   @par Thread Safety
72   Distinct objects: Safe. 72   Distinct objects: Safe.
73   Shared objects: Unsafe. 73   Shared objects: Unsafe.
74   74  
75   @par Example 75   @par Example
76   76  
77   @code 77   @code
78   task<int> compute_value() 78   task<int> compute_value()
79   { 79   {
80   auto [ec, n] = co_await stream.read_some( buf ); 80   auto [ec, n] = co_await stream.read_some( buf );
81   if( ec ) 81   if( ec )
82   co_return 0; 82   co_return 0;
83   co_return process( buf, n ); 83   co_return process( buf, n );
84   } 84   }
85   85  
86   task<> run_session( tcp_socket sock ) 86   task<> run_session( tcp_socket sock )
87   { 87   {
88   int result = co_await compute_value(); 88   int result = co_await compute_value();
89   // ... 89   // ...
90   } 90   }
91   @endcode 91   @endcode
92   92  
93   @tparam T The result type. Use `task<>` for `task<void>`. 93   @tparam T The result type. Use `task<>` for `task<void>`.
94   94  
95   @see IoRunnable, IoAwaitable, run, run_async 95   @see IoRunnable, IoAwaitable, run, run_async
96   */ 96   */
97   template<typename T = void> 97   template<typename T = void>
98   struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE 98   struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE
99   task 99   task
100   { 100   {
101   struct promise_type 101   struct promise_type
102   : io_awaitable_promise_base<promise_type> 102   : io_awaitable_promise_base<promise_type>
103   , detail::task_return_base<T> 103   , detail::task_return_base<T>
104   { 104   {
105   private: 105   private:
106   friend task; 106   friend task;
107   union { std::exception_ptr ep_; }; 107   union { std::exception_ptr ep_; };
108   bool has_ep_; 108   bool has_ep_;
109   109  
110   public: 110   public:
HITCBC 111   5005 promise_type() noexcept 111   5015 promise_type() noexcept
HITCBC 112   5005 : has_ep_(false) 112   5015 : has_ep_(false)
113   { 113   {
HITCBC 114   5005 } 114   5015 }
115   115  
HITCBC 116   5005 ~promise_type() 116   5015 ~promise_type()
117   { 117   {
HITCBC 118   5005 if(has_ep_) 118   5015 if(has_ep_)
HITCBC 119   1591 ep_.~exception_ptr(); 119   1591 ep_.~exception_ptr();
HITCBC 120   5005 } 120   5015 }
121   121  
HITCBC 122   4145 std::exception_ptr exception() const noexcept 122   4158 std::exception_ptr exception() const noexcept
123   { 123   {
HITCBC 124   4145 if(has_ep_) 124   4158 if(has_ep_)
HITCBC 125   2086 return ep_; 125   2086 return ep_;
HITCBC 126   2059 return {}; 126   2072 return {};
127   } 127   }
128   128  
HITCBC 129   5005 task get_return_object() 129   5015 task get_return_object()
130   { 130   {
HITCBC 131   5005 return task{std::coroutine_handle<promise_type>::from_promise(*this)}; 131   5015 return task{std::coroutine_handle<promise_type>::from_promise(*this)};
132   } 132   }
133   133  
HITCBC 134   5005 auto initial_suspend() noexcept 134   5015 auto initial_suspend() noexcept
135   { 135   {
136   struct awaiter 136   struct awaiter
137   { 137   {
138   promise_type* p_; 138   promise_type* p_;
139   139  
HITCBC 140   5005 bool await_ready() const noexcept 140   5015 bool await_ready() const noexcept
141   { 141   {
HITCBC 142   5005 return false; 142   5015 return false;
143   } 143   }
144   144  
HITCBC 145   5005 void await_suspend(std::coroutine_handle<>) const noexcept 145   5015 void await_suspend(std::coroutine_handle<>) const noexcept
146   { 146   {
HITCBC 147   5005 } 147   5015 }
148   148  
HITCBC 149   5002 void await_resume() const noexcept 149   5012 void await_resume() const noexcept
150   { 150   {
151   // Restore TLS when body starts executing 151   // Restore TLS when body starts executing
HITCBC 152   5002 set_current_frame_allocator(p_->environment()->frame_allocator); 152   5012 set_current_frame_allocator(p_->environment()->frame_allocator);
HITCBC 153   5002 } 153   5012 }
154   }; 154   };
HITCBC 155   5005 return awaiter{this}; 155   5015 return awaiter{this};
156   } 156   }
157   157  
HITCBC 158   4842 auto final_suspend() noexcept 158   4860 auto final_suspend() noexcept
159   { 159   {
160   struct awaiter 160   struct awaiter
161   { 161   {
162   promise_type* p_; 162   promise_type* p_;
163   163  
HITCBC 164   4842 bool await_ready() const noexcept 164   4860 bool await_ready() const noexcept
165   { 165   {
HITCBC 166   4842 return false; 166   4860 return false;
167   } 167   }
168   168  
HITCBC 169   4842 std::coroutine_handle<> await_suspend(std::coroutine_handle<>) const noexcept 169   4860 std::coroutine_handle<> await_suspend(std::coroutine_handle<>) const noexcept
170   { 170   {
HITCBC 171   4842 return p_->continuation(); 171   4860 return p_->continuation();
172   } 172   }
173   173  
MISUBC 174   void await_resume() const noexcept 174   void await_resume() const noexcept
175   { 175   {
MISUBC 176   } 176   }
177   }; 177   };
HITCBC 178   4842 return awaiter{this}; 178   4860 return awaiter{this};
179   } 179   }
180   180  
HITCBC 181   1591 void unhandled_exception() noexcept 181   1591 void unhandled_exception() noexcept
182   { 182   {
HITCBC 183   1591 new (&ep_) std::exception_ptr(std::current_exception()); 183   1591 new (&ep_) std::exception_ptr(std::current_exception());
HITCBC 184   1591 has_ep_ = true; 184   1591 has_ep_ = true;
HITCBC 185   1591 } 185   1591 }
186   186  
187   template<class Awaitable> 187   template<class Awaitable>
188   struct transform_awaiter 188   struct transform_awaiter
189   { 189   {
190   std::decay_t<Awaitable> a_; 190   std::decay_t<Awaitable> a_;
191   promise_type* p_; 191   promise_type* p_;
192   192  
HITCBC 193   9156 bool await_ready() noexcept 193   9161 bool await_ready() noexcept
194   { 194   {
HITCBC 195   9156 return a_.await_ready(); 195   9161 return a_.await_ready();
196   } 196   }
197   197  
HITCBC 198   8996 decltype(auto) await_resume() 198   9009 decltype(auto) await_resume()
199   { 199   {
200   // Restore TLS before body resumes 200   // Restore TLS before body resumes
HITCBC 201   8996 set_current_frame_allocator(p_->environment()->frame_allocator); 201   9009 set_current_frame_allocator(p_->environment()->frame_allocator);
HITCBC 202   8996 return a_.await_resume(); 202   9009 return a_.await_resume();
203   } 203   }
204   204  
205   template<class Promise> 205   template<class Promise>
HITCBC 206   2480 auto await_suspend(std::coroutine_handle<Promise> h) noexcept 206   2485 auto await_suspend(std::coroutine_handle<Promise> h) noexcept
207   { 207   {
208   using R = decltype(a_.await_suspend(h, p_->environment())); 208   using R = decltype(a_.await_suspend(h, p_->environment()));
209   if constexpr (std::is_same_v<R, std::coroutine_handle<>>) 209   if constexpr (std::is_same_v<R, std::coroutine_handle<>>)
HITCBC 210   2480 return detail::symmetric_transfer(a_.await_suspend(h, p_->environment())); 210   2485 return detail::symmetric_transfer(a_.await_suspend(h, p_->environment()));
211   else 211   else
MISUBC 212   return a_.await_suspend(h, p_->environment()); 212   return a_.await_suspend(h, p_->environment());
213   } 213   }
214   }; 214   };
215   215  
216   template<class Awaitable> 216   template<class Awaitable>
HITCBC 217   9156 auto transform_awaitable(Awaitable&& a) 217   9161 auto transform_awaitable(Awaitable&& a)
218   { 218   {
219   using A = std::decay_t<Awaitable>; 219   using A = std::decay_t<Awaitable>;
220   if constexpr (IoAwaitable<A>) 220   if constexpr (IoAwaitable<A>)
221   { 221   {
222   return transform_awaiter<Awaitable>{ 222   return transform_awaiter<Awaitable>{
HITCBC 223   11308 std::forward<Awaitable>(a), this}; 223   11318 std::forward<Awaitable>(a), this};
224   } 224   }
225   else 225   else
226   { 226   {
227   static_assert(sizeof(A) == 0, "requires IoAwaitable"); 227   static_assert(sizeof(A) == 0, "requires IoAwaitable");
228   } 228   }
HITCBC 229   2152 } 229   2157 }
230   }; 230   };
231   231  
232   std::coroutine_handle<promise_type> h_; 232   std::coroutine_handle<promise_type> h_;
233   233  
234   /// Destroy the task and its coroutine frame if owned. 234   /// Destroy the task and its coroutine frame if owned.
HITCBC 235   10342 ~task() 235   10369 ~task()
236   { 236   {
HITCBC 237   10342 if(h_) 237   10369 if(h_)
HITCBC 238   1686 h_.destroy(); 238   1691 h_.destroy();
HITCBC 239   10342 } 239   10369 }
240   240  
241   /// Return false; tasks are never immediately ready. 241   /// Return false; tasks are never immediately ready.
HITCBC 242   1558 bool await_ready() const noexcept 242   1563 bool await_ready() const noexcept
243   { 243   {
HITCBC 244   1558 return false; 244   1563 return false;
245   } 245   }
246   246  
247   /// Return the result or rethrow any stored exception. 247   /// Return the result or rethrow any stored exception.
HITCBC 248   1683 auto await_resume() 248   1688 auto await_resume()
249   { 249   {
HITCBC 250   1683 if(h_.promise().has_ep_) 250   1688 if(h_.promise().has_ep_)
HITCBC 251   547 std::rethrow_exception(h_.promise().ep_); 251   547 std::rethrow_exception(h_.promise().ep_);
252   if constexpr (! std::is_void_v<T>) 252   if constexpr (! std::is_void_v<T>)
HITCBC 253   1125 return std::move(*h_.promise().result_); 253   1125 return std::move(*h_.promise().result_);
254   else 254   else
HITCBC 255   11 return; 255   16 return;
256   } 256   }
257   257  
258   /// Start execution with the caller's context. 258   /// Start execution with the caller's context.
HITCBC 259   1666 std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, io_env const* env) 259   1666 std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, io_env const* env)
260   { 260   {
HITCBC 261   1666 h_.promise().set_continuation(cont); 261   1666 h_.promise().set_continuation(cont);
HITCBC 262   1666 h_.promise().set_environment(env); 262   1666 h_.promise().set_environment(env);
HITCBC 263   1666 return h_; 263   1666 return h_;
264   } 264   }
265   265  
266   /// Return the coroutine handle. 266   /// Return the coroutine handle.
HITCBC 267   3339 std::coroutine_handle<promise_type> handle() const noexcept 267   3349 std::coroutine_handle<promise_type> handle() const noexcept
268   { 268   {
HITCBC 269   3339 return h_; 269   3349 return h_;
270   } 270   }
271   271  
272   /** Release ownership of the coroutine frame. 272   /** Release ownership of the coroutine frame.
273   273  
274   After calling this, destroying the task does not destroy the 274   After calling this, destroying the task does not destroy the
275   coroutine frame. The caller becomes responsible for the frame's 275   coroutine frame. The caller becomes responsible for the frame's
276   lifetime. 276   lifetime.
277   277  
278   @par Postconditions 278   @par Postconditions
279   `handle()` returns the original handle, but the task no longer 279   `handle()` returns the original handle, but the task no longer
280   owns it. 280   owns it.
281   */ 281   */
HITCBC 282   3319 void release() noexcept 282   3324 void release() noexcept
283   { 283   {
HITCBC 284   3319 h_ = nullptr; 284   3324 h_ = nullptr;
HITCBC 285   3319 } 285   3324 }
286   286  
287   task(task const&) = delete; 287   task(task const&) = delete;
288   task& operator=(task const&) = delete; 288   task& operator=(task const&) = delete;
289   289  
290   /// Construct by moving, transferring ownership. 290   /// Construct by moving, transferring ownership.
HITCBC 291   5337 task(task&& other) noexcept 291   5354 task(task&& other) noexcept
HITCBC 292   5337 : h_(std::exchange(other.h_, nullptr)) 292   5354 : h_(std::exchange(other.h_, nullptr))
293   { 293   {
HITCBC 294   5337 } 294   5354 }
295   295  
296   /// Assign by moving, transferring ownership. 296   /// Assign by moving, transferring ownership.
297   task& operator=(task&& other) noexcept 297   task& operator=(task&& other) noexcept
298   { 298   {
299   if(this != &other) 299   if(this != &other)
300   { 300   {
301   if(h_) 301   if(h_)
302   h_.destroy(); 302   h_.destroy();
303   h_ = std::exchange(other.h_, nullptr); 303   h_ = std::exchange(other.h_, nullptr);
304   } 304   }
305   return *this; 305   return *this;
306   } 306   }
307   307  
308   private: 308   private:
HITCBC 309   5005 explicit task(std::coroutine_handle<promise_type> h) 309   5015 explicit task(std::coroutine_handle<promise_type> h)
HITCBC 310   5005 : h_(h) 310   5015 : h_(h)
311   { 311   {
HITCBC 312   5005 } 312   5015 }
313   }; 313   };
314   314  
315   } // namespace capy 315   } // namespace capy
316   } // namespace boost 316   } // namespace boost
317   317  
318   #endif 318   #endif