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/corosio
7  
// Official repository: https://github.com/cppalliance/corosio
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  

38  
    void return_value(T value)
38  
    void return_value(T value)
39  
    {
39  
    {
40  
        result_ = std::move(value);
40  
        result_ = std::move(value);
41  
    }
41  
    }
42  

42  

43  
    T&& result() noexcept
43  
    T&& result() noexcept
44  
    {
44  
    {
45  
        return std::move(*result_);
45  
        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  
{
52  
    void return_void()
52  
    void return_void()
53  
    {
53  
    {
54  
    }
54  
    }
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  
    @tparam T The result type. Use `task<>` for `task<void>`.
71  
    @tparam T The result type. Use `task<>` for `task<void>`.
72  

72  

73  
    @par Thread Safety
73  
    @par Thread Safety
74  
    Distinct objects: Safe.
74  
    Distinct objects: Safe.
75  
    Shared objects: Unsafe.
75  
    Shared objects: Unsafe.
76  

76  

77  
    @par Example
77  
    @par Example
78  

78  

79  
    @code
79  
    @code
80  
    task<int> compute_value()
80  
    task<int> compute_value()
81  
    {
81  
    {
82  
        auto [ec, n] = co_await stream.read_some( buf );
82  
        auto [ec, n] = co_await stream.read_some( buf );
83  
        if( ec )
83  
        if( ec )
84  
            co_return 0;
84  
            co_return 0;
85  
        co_return process( buf, n );
85  
        co_return process( buf, n );
86  
    }
86  
    }
87  

87  

88  
    task<> run_session( tcp_socket sock )
88  
    task<> run_session( tcp_socket sock )
89  
    {
89  
    {
90  
        int result = co_await compute_value();
90  
        int result = co_await compute_value();
91  
        // ...
91  
        // ...
92  
    }
92  
    }
93  
    @endcode
93  
    @endcode
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:
111  
        promise_type() noexcept
111  
        promise_type() noexcept
112  
            : has_ep_(false)
112  
            : has_ep_(false)
113  
        {
113  
        {
114  
        }
114  
        }
115  

115  

116  
        ~promise_type()
116  
        ~promise_type()
117  
        {
117  
        {
118  
            if(has_ep_)
118  
            if(has_ep_)
119  
                ep_.~exception_ptr();
119  
                ep_.~exception_ptr();
120  
        }
120  
        }
121  

121  

122  
        std::exception_ptr exception() const noexcept
122  
        std::exception_ptr exception() const noexcept
123  
        {
123  
        {
124  
            if(has_ep_)
124  
            if(has_ep_)
125  
                return ep_;
125  
                return ep_;
126  
            return {};
126  
            return {};
127  
        }
127  
        }
128  

128  

129  
        task get_return_object()
129  
        task get_return_object()
130  
        {
130  
        {
131  
            return task{std::coroutine_handle<promise_type>::from_promise(*this)};
131  
            return task{std::coroutine_handle<promise_type>::from_promise(*this)};
132  
        }
132  
        }
133  

133  

134  
        auto initial_suspend() noexcept
134  
        auto initial_suspend() noexcept
135  
        {
135  
        {
136  
            struct awaiter
136  
            struct awaiter
137  
            {
137  
            {
138  
                promise_type* p_;
138  
                promise_type* p_;
139  

139  

140  
                bool await_ready() const noexcept
140  
                bool await_ready() const noexcept
141  
                {
141  
                {
142  
                    return false;
142  
                    return false;
143  
                }
143  
                }
144  

144  

145  
                void await_suspend(std::coroutine_handle<>) const noexcept
145  
                void await_suspend(std::coroutine_handle<>) const noexcept
146  
                {
146  
                {
147  
                }
147  
                }
148  

148  

149  
                void await_resume() const noexcept
149  
                void await_resume() const noexcept
150  
                {
150  
                {
151  
                    // Restore TLS when body starts executing
151  
                    // Restore TLS when body starts executing
152  
                    set_current_frame_allocator(p_->environment()->frame_allocator);
152  
                    set_current_frame_allocator(p_->environment()->frame_allocator);
153  
                }
153  
                }
154  
            };
154  
            };
155  
            return awaiter{this};
155  
            return awaiter{this};
156  
        }
156  
        }
157  

157  

158  
        auto final_suspend() noexcept
158  
        auto final_suspend() noexcept
159  
        {
159  
        {
160  
            struct awaiter
160  
            struct awaiter
161  
            {
161  
            {
162  
                promise_type* p_;
162  
                promise_type* p_;
163  

163  

164  
                bool await_ready() const noexcept
164  
                bool await_ready() const noexcept
165  
                {
165  
                {
166  
                    return false;
166  
                    return false;
167  
                }
167  
                }
168  

168  

169  
                std::coroutine_handle<> await_suspend(std::coroutine_handle<>) const noexcept
169  
                std::coroutine_handle<> await_suspend(std::coroutine_handle<>) const noexcept
170  
                {
170  
                {
171  
                    return p_->continuation();
171  
                    return p_->continuation();
172  
                }
172  
                }
173  

173  

174  
                void await_resume() const noexcept
174  
                void await_resume() const noexcept
175  
                {
175  
                {
176  
                }
176  
                }
177  
            };
177  
            };
178  
            return awaiter{this};
178  
            return awaiter{this};
179  
        }
179  
        }
180  

180  

181  
        void unhandled_exception()
181  
        void unhandled_exception()
182  
        {
182  
        {
183  
            new (&ep_) std::exception_ptr(std::current_exception());
183  
            new (&ep_) std::exception_ptr(std::current_exception());
184  
            has_ep_ = true;
184  
            has_ep_ = true;
185  
        }
185  
        }
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  

193  
            bool await_ready() noexcept
193  
            bool await_ready() noexcept
194  
            {
194  
            {
195  
                return a_.await_ready();
195  
                return a_.await_ready();
196  
            }
196  
            }
197  

197  

198  
            decltype(auto) await_resume()
198  
            decltype(auto) await_resume()
199  
            {
199  
            {
200  
                // Restore TLS before body resumes
200  
                // Restore TLS before body resumes
201  
                set_current_frame_allocator(p_->environment()->frame_allocator);
201  
                set_current_frame_allocator(p_->environment()->frame_allocator);
202  
                return a_.await_resume();
202  
                return a_.await_resume();
203  
            }
203  
            }
204  

204  

205  
            template<class Promise>
205  
            template<class Promise>
206  
            auto await_suspend(std::coroutine_handle<Promise> h) noexcept
206  
            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<>>)
210  
                    return detail::symmetric_transfer(a_.await_suspend(h, p_->environment()));
210  
                    return detail::symmetric_transfer(a_.await_suspend(h, p_->environment()));
211  
                else
211  
                else
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>
217  
        auto transform_awaitable(Awaitable&& a)
217  
        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>{
223  
                    std::forward<Awaitable>(a), this};
223  
                    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  
            }
229  
        }
229  
        }
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.
235  
    ~task()
235  
    ~task()
236  
    {
236  
    {
237  
        if(h_)
237  
        if(h_)
238  
            h_.destroy();
238  
            h_.destroy();
239  
    }
239  
    }
240  

240  

241  
    /// Return false; tasks are never immediately ready.
241  
    /// Return false; tasks are never immediately ready.
242  
    bool await_ready() const noexcept
242  
    bool await_ready() const noexcept
243  
    {
243  
    {
244  
        return false;
244  
        return false;
245  
    }
245  
    }
246  

246  

247  
    /// Return the result or rethrow any stored exception.
247  
    /// Return the result or rethrow any stored exception.
248  
    auto await_resume()
248  
    auto await_resume()
249  
    {
249  
    {
250  
        if(h_.promise().has_ep_)
250  
        if(h_.promise().has_ep_)
251  
            std::rethrow_exception(h_.promise().ep_);
251  
            std::rethrow_exception(h_.promise().ep_);
252  
        if constexpr (! std::is_void_v<T>)
252  
        if constexpr (! std::is_void_v<T>)
253  
            return std::move(*h_.promise().result_);
253  
            return std::move(*h_.promise().result_);
254  
        else
254  
        else
255  
            return;
255  
            return;
256  
    }
256  
    }
257  

257  

258  
    /// Start execution with the caller's context.
258  
    /// Start execution with the caller's context.
259  
    std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, io_env const* env)
259  
    std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, io_env const* env)
260  
    {
260  
    {
261  
        h_.promise().set_continuation(cont);
261  
        h_.promise().set_continuation(cont);
262  
        h_.promise().set_environment(env);
262  
        h_.promise().set_environment(env);
263  
        return h_;
263  
        return h_;
264  
    }
264  
    }
265  

265  

266  
    /// Return the coroutine handle.
266  
    /// Return the coroutine handle.
267  
    std::coroutine_handle<promise_type> handle() const noexcept
267  
    std::coroutine_handle<promise_type> handle() const noexcept
268  
    {
268  
    {
269  
        return h_;
269  
        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  
    */
282  
    void release() noexcept
282  
    void release() noexcept
283  
    {
283  
    {
284  
        h_ = nullptr;
284  
        h_ = nullptr;
285  
    }
285  
    }
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  
    /// Move construct, transferring ownership.
290  
    /// Move construct, transferring ownership.
291  
    task(task&& other) noexcept
291  
    task(task&& other) noexcept
292  
        : h_(std::exchange(other.h_, nullptr))
292  
        : h_(std::exchange(other.h_, nullptr))
293  
    {
293  
    {
294  
    }
294  
    }
295  

295  

296  
    /// Move assign, transferring ownership.
296  
    /// Move assign, 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:
309  
    explicit task(std::coroutine_handle<promise_type> h)
309  
    explicit task(std::coroutine_handle<promise_type> h)
310  
        : h_(h)
310  
        : h_(h)
311  
    {
311  
    {
312  
    }
312  
    }
313  
};
313  
};
314  

314  

315  
} // namespace capy
315  
} // namespace capy
316  
} // namespace boost
316  
} // namespace boost
317  

317  

318  
#endif
318  
#endif