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_support.hpp>
16  
#include <boost/capy/ex/io_awaitable_support.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  

19  

20  
#include <exception>
20  
#include <exception>
21  
#include <optional>
21  
#include <optional>
22  
#include <type_traits>
22  
#include <type_traits>
23  
#include <utility>
23  
#include <utility>
24  
#include <variant>
24  
#include <variant>
25  

25  

26  
namespace boost {
26  
namespace boost {
27  
namespace capy {
27  
namespace capy {
28  

28  

29  
namespace detail {
29  
namespace detail {
30  

30  

31  
// Helper base for result storage and return_void/return_value
31  
// Helper base for result storage and return_void/return_value
32  
template<typename T>
32  
template<typename T>
33  
struct task_return_base
33  
struct task_return_base
34  
{
34  
{
35  
    std::optional<T> result_;
35  
    std::optional<T> result_;
36  

36  

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

41  

42  
    T&& result() noexcept
42  
    T&& result() noexcept
43  
    {
43  
    {
44  
        return std::move(*result_);
44  
        return std::move(*result_);
45  
    }
45  
    }
46  
};
46  
};
47  

47  

48  
template<>
48  
template<>
49  
struct task_return_base<void>
49  
struct task_return_base<void>
50  
{
50  
{
51  
    void return_void()
51  
    void return_void()
52  
    {
52  
    {
53  
    }
53  
    }
54  
};
54  
};
55  

55  

56  
} // namespace detail
56  
} // namespace detail
57  

57  

58  
/** Lazy coroutine task satisfying @ref IoRunnable.
58  
/** Lazy coroutine task satisfying @ref IoRunnable.
59  

59  

60  
    Use `task<T>` as the return type for coroutines that perform I/O
60  
    Use `task<T>` as the return type for coroutines that perform I/O
61  
    and return a value of type `T`. The coroutine body does not start
61  
    and return a value of type `T`. The coroutine body does not start
62  
    executing until the task is awaited, enabling efficient composition
62  
    executing until the task is awaited, enabling efficient composition
63  
    without unnecessary eager execution.
63  
    without unnecessary eager execution.
64  

64  

65  
    The task participates in the I/O awaitable protocol: when awaited,
65  
    The task participates in the I/O awaitable protocol: when awaited,
66  
    it receives the caller's executor and stop token, propagating them
66  
    it receives the caller's executor and stop token, propagating them
67  
    to nested `co_await` expressions. This enables cancellation and
67  
    to nested `co_await` expressions. This enables cancellation and
68  
    proper completion dispatch across executor boundaries.
68  
    proper completion dispatch across executor boundaries.
69  

69  

70  
    @tparam T The result type. Use `task<>` for `task<void>`.
70  
    @tparam T The result type. Use `task<>` for `task<void>`.
71  

71  

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

75  

76  
    @par Example
76  
    @par Example
77  

77  

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

86  

87  
    task<> run_session( tcp_socket sock )
87  
    task<> run_session( tcp_socket sock )
88  
    {
88  
    {
89  
        int result = co_await compute_value();
89  
        int result = co_await compute_value();
90  
        // ...
90  
        // ...
91  
    }
91  
    }
92  
    @endcode
92  
    @endcode
93  

93  

94  
    @see IoRunnable, IoAwaitable, run, run_async
94  
    @see IoRunnable, IoAwaitable, run, run_async
95  
*/
95  
*/
96  
template<typename T = void>
96  
template<typename T = void>
97  
struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE
97  
struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE
98  
    task
98  
    task
99  
{
99  
{
100  
    struct promise_type
100  
    struct promise_type
101  
        : io_awaitable_support<promise_type>
101  
        : io_awaitable_support<promise_type>
102  
        , detail::task_return_base<T>
102  
        , detail::task_return_base<T>
103  
    {
103  
    {
104  
    private:
104  
    private:
105  
        friend task;
105  
        friend task;
106  
        union { std::exception_ptr ep_; };
106  
        union { std::exception_ptr ep_; };
107  
        bool has_ep_;
107  
        bool has_ep_;
108  

108  

109  
    public:
109  
    public:
110  
        promise_type() noexcept
110  
        promise_type() noexcept
111  
            : has_ep_(false)
111  
            : has_ep_(false)
112  
        {
112  
        {
113  
        }
113  
        }
114  

114  

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

120  

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

127  

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

132  

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

138  

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

143  

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

147  

148  
                void await_resume() const noexcept
148  
                void await_resume() const noexcept
149  
                {
149  
                {
150  
                    // Restore TLS when body starts executing
150  
                    // Restore TLS when body starts executing
151  
                    auto* fa = p_->environment()->allocator;
151  
                    auto* fa = p_->environment()->allocator;
152  
                    if(fa && fa != current_frame_allocator())
152  
                    if(fa && fa != current_frame_allocator())
153  
                        current_frame_allocator() = fa;
153  
                        current_frame_allocator() = fa;
154  
                }
154  
                }
155  
            };
155  
            };
156  
            return awaiter{this};
156  
            return awaiter{this};
157  
        }
157  
        }
158  

158  

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

164  

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

169  

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

174  

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

181  

182  
        void unhandled_exception()
182  
        void unhandled_exception()
183  
        {
183  
        {
184  
            new (&ep_) std::exception_ptr(std::current_exception());
184  
            new (&ep_) std::exception_ptr(std::current_exception());
185  
            has_ep_ = true;
185  
            has_ep_ = true;
186  
        }
186  
        }
187  

187  

188  
        template<class Awaitable>
188  
        template<class Awaitable>
189  
        struct transform_awaiter
189  
        struct transform_awaiter
190  
        {
190  
        {
191  
            std::decay_t<Awaitable> a_;
191  
            std::decay_t<Awaitable> a_;
192  
            promise_type* p_;
192  
            promise_type* p_;
193  

193  

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

198  

199  
            decltype(auto) await_resume()
199  
            decltype(auto) await_resume()
200  
            {
200  
            {
201  
                // Restore TLS before body resumes
201  
                // Restore TLS before body resumes
202  
                auto* fa = p_->environment()->allocator;
202  
                auto* fa = p_->environment()->allocator;
203  
                if(fa && fa != current_frame_allocator())
203  
                if(fa && fa != current_frame_allocator())
204  
                    current_frame_allocator() = fa;
204  
                    current_frame_allocator() = fa;
205  
                return a_.await_resume();
205  
                return a_.await_resume();
206  
            }
206  
            }
207  

207  

208  
            template<class Promise>
208  
            template<class Promise>
209  
            auto await_suspend(std::coroutine_handle<Promise> h) noexcept
209  
            auto await_suspend(std::coroutine_handle<Promise> h) noexcept
210  
            {
210  
            {
211  
                return a_.await_suspend(h, p_->environment());
211  
                return a_.await_suspend(h, p_->environment());
212  
            }
212  
            }
213  
        };
213  
        };
214  

214  

215  
        template<class Awaitable>
215  
        template<class Awaitable>
216  
        auto transform_awaitable(Awaitable&& a)
216  
        auto transform_awaitable(Awaitable&& a)
217  
        {
217  
        {
218  
            using A = std::decay_t<Awaitable>;
218  
            using A = std::decay_t<Awaitable>;
219  
            if constexpr (IoAwaitable<A>)
219  
            if constexpr (IoAwaitable<A>)
220  
            {
220  
            {
221  
                return transform_awaiter<Awaitable>{
221  
                return transform_awaiter<Awaitable>{
222  
                    std::forward<Awaitable>(a), this};
222  
                    std::forward<Awaitable>(a), this};
223  
            }
223  
            }
224  
            else
224  
            else
225  
            {
225  
            {
226  
                static_assert(sizeof(A) == 0, "requires IoAwaitable");
226  
                static_assert(sizeof(A) == 0, "requires IoAwaitable");
227  
            }
227  
            }
228  
        }
228  
        }
229  
    };
229  
    };
230  

230  

231  
    std::coroutine_handle<promise_type> h_;
231  
    std::coroutine_handle<promise_type> h_;
232  

232  

233  
    /// Destroy the task and its coroutine frame if owned.
233  
    /// Destroy the task and its coroutine frame if owned.
234  
    ~task()
234  
    ~task()
235  
    {
235  
    {
236  
        if(h_)
236  
        if(h_)
237  
            h_.destroy();
237  
            h_.destroy();
238  
    }
238  
    }
239  

239  

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

245  

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

256  

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

264  

265  
    /// Return the coroutine handle.
265  
    /// Return the coroutine handle.
266  
    std::coroutine_handle<promise_type> handle() const noexcept
266  
    std::coroutine_handle<promise_type> handle() const noexcept
267  
    {
267  
    {
268  
        return h_;
268  
        return h_;
269  
    }
269  
    }
270  

270  

271  
    /** Release ownership of the coroutine frame.
271  
    /** Release ownership of the coroutine frame.
272  

272  

273  
        After calling this, destroying the task does not destroy the
273  
        After calling this, destroying the task does not destroy the
274  
        coroutine frame. The caller becomes responsible for the frame's
274  
        coroutine frame. The caller becomes responsible for the frame's
275  
        lifetime.
275  
        lifetime.
276  

276  

277  
        @par Postconditions
277  
        @par Postconditions
278  
        `handle()` returns the original handle, but the task no longer
278  
        `handle()` returns the original handle, but the task no longer
279  
        owns it.
279  
        owns it.
280  
    */
280  
    */
281  
    void release() noexcept
281  
    void release() noexcept
282  
    {
282  
    {
283  
        h_ = nullptr;
283  
        h_ = nullptr;
284  
    }
284  
    }
285  

285  

286  
    task(task const&) = delete;
286  
    task(task const&) = delete;
287  
    task& operator=(task const&) = delete;
287  
    task& operator=(task const&) = delete;
288  

288  

289  
    /// Move construct, transferring ownership.
289  
    /// Move construct, transferring ownership.
290  
    task(task&& other) noexcept
290  
    task(task&& other) noexcept
291  
        : h_(std::exchange(other.h_, nullptr))
291  
        : h_(std::exchange(other.h_, nullptr))
292  
    {
292  
    {
293  
    }
293  
    }
294  

294  

295  
    /// Move assign, transferring ownership.
295  
    /// Move assign, transferring ownership.
296  
    task& operator=(task&& other) noexcept
296  
    task& operator=(task&& other) noexcept
297  
    {
297  
    {
298  
        if(this != &other)
298  
        if(this != &other)
299  
        {
299  
        {
300  
            if(h_)
300  
            if(h_)
301  
                h_.destroy();
301  
                h_.destroy();
302  
            h_ = std::exchange(other.h_, nullptr);
302  
            h_ = std::exchange(other.h_, nullptr);
303  
        }
303  
        }
304  
        return *this;
304  
        return *this;
305  
    }
305  
    }
306  

306  

307  
private:
307  
private:
308  
    explicit task(std::coroutine_handle<promise_type> h)
308  
    explicit task(std::coroutine_handle<promise_type> h)
309  
        : h_(h)
309  
        : h_(h)
310  
    {
310  
    {
311  
    }
311  
    }
312  
};
312  
};
313  

313  

314  
} // namespace capy
314  
} // namespace capy
315  
} // namespace boost
315  
} // namespace boost
316  

316  

317  
#endif
317  
#endif