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  

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_promise_base<promise_type>
101  
        : io_awaitable_promise_base<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  
                    set_current_frame_allocator(p_->environment()->allocator);
151  
                    set_current_frame_allocator(p_->environment()->allocator);
152  
                }
152  
                }
153  
            };
153  
            };
154  
            return awaiter{this};
154  
            return awaiter{this};
155  
        }
155  
        }
156  

156  

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

162  

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

167  

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

172  

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

179  

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

185  

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

191  

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

196  

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

203  

204  
            template<class Promise>
204  
            template<class Promise>
205  
            auto await_suspend(std::coroutine_handle<Promise> h) noexcept
205  
            auto await_suspend(std::coroutine_handle<Promise> h) noexcept
206  
            {
206  
            {
207  
                return a_.await_suspend(h, p_->environment());
207  
                return a_.await_suspend(h, p_->environment());
208  
            }
208  
            }
209  
        };
209  
        };
210  

210  

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

226  

227  
    std::coroutine_handle<promise_type> h_;
227  
    std::coroutine_handle<promise_type> h_;
228  

228  

229  
    /// Destroy the task and its coroutine frame if owned.
229  
    /// Destroy the task and its coroutine frame if owned.
230  
    ~task()
230  
    ~task()
231  
    {
231  
    {
232  
        if(h_)
232  
        if(h_)
233  
            h_.destroy();
233  
            h_.destroy();
234  
    }
234  
    }
235  

235  

236  
    /// Return false; tasks are never immediately ready.
236  
    /// Return false; tasks are never immediately ready.
237  
    bool await_ready() const noexcept
237  
    bool await_ready() const noexcept
238  
    {
238  
    {
239  
        return false;
239  
        return false;
240  
    }
240  
    }
241  

241  

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

252  

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

260  

261  
    /// Return the coroutine handle.
261  
    /// Return the coroutine handle.
262  
    std::coroutine_handle<promise_type> handle() const noexcept
262  
    std::coroutine_handle<promise_type> handle() const noexcept
263  
    {
263  
    {
264  
        return h_;
264  
        return h_;
265  
    }
265  
    }
266  

266  

267  
    /** Release ownership of the coroutine frame.
267  
    /** Release ownership of the coroutine frame.
268  

268  

269  
        After calling this, destroying the task does not destroy the
269  
        After calling this, destroying the task does not destroy the
270  
        coroutine frame. The caller becomes responsible for the frame's
270  
        coroutine frame. The caller becomes responsible for the frame's
271  
        lifetime.
271  
        lifetime.
272  

272  

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

281  

282  
    task(task const&) = delete;
282  
    task(task const&) = delete;
283  
    task& operator=(task const&) = delete;
283  
    task& operator=(task const&) = delete;
284  

284  

285  
    /// Move construct, transferring ownership.
285  
    /// Move construct, transferring ownership.
286  
    task(task&& other) noexcept
286  
    task(task&& other) noexcept
287  
        : h_(std::exchange(other.h_, nullptr))
287  
        : h_(std::exchange(other.h_, nullptr))
288  
    {
288  
    {
289  
    }
289  
    }
290  

290  

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

302  

303  
private:
303  
private:
304  
    explicit task(std::coroutine_handle<promise_type> h)
304  
    explicit task(std::coroutine_handle<promise_type> h)
305  
        : h_(h)
305  
        : h_(h)
306  
    {
306  
    {
307  
    }
307  
    }
308  
};
308  
};
309  

309  

310  
} // namespace capy
310  
} // namespace capy
311  
} // namespace boost
311  
} // namespace boost
312  

312  

313  
#endif
313  
#endif