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()->frame_allocator);
151  
                    set_current_frame_allocator(p_->environment()->frame_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()->frame_allocator);
200  
                set_current_frame_allocator(p_->environment()->frame_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  
#ifdef _MSC_VER
207  
#ifdef _MSC_VER
208  
                // Workaround: MSVC stores the coroutine_handle<> return
208  
                // Workaround: MSVC stores the coroutine_handle<> return
209  
                // value on the coroutine frame via hidden __$ReturnUdt$.
209  
                // value on the coroutine frame via hidden __$ReturnUdt$.
210  
                // After await_suspend publishes the handle to another
210  
                // After await_suspend publishes the handle to another
211  
                // thread, that thread can resume/destroy the frame before
211  
                // thread, that thread can resume/destroy the frame before
212  
                // __resume reads the handle back for the symmetric
212  
                // __resume reads the handle back for the symmetric
213  
                // transfer tail-call, causing a use-after-free.
213  
                // transfer tail-call, causing a use-after-free.
214  
                using R = decltype(a_.await_suspend(h, p_->environment()));
214  
                using R = decltype(a_.await_suspend(h, p_->environment()));
215  
                if constexpr (std::is_same_v<R, std::coroutine_handle<>>)
215  
                if constexpr (std::is_same_v<R, std::coroutine_handle<>>)
216  
                    a_.await_suspend(h, p_->environment()).resume();
216  
                    a_.await_suspend(h, p_->environment()).resume();
217  
                else
217  
                else
218  
                    return a_.await_suspend(h, p_->environment());
218  
                    return a_.await_suspend(h, p_->environment());
219  
#else
219  
#else
220  
                return a_.await_suspend(h, p_->environment());
220  
                return a_.await_suspend(h, p_->environment());
221  
#endif
221  
#endif
222  
            }
222  
            }
223  
        };
223  
        };
224  

224  

225  
        template<class Awaitable>
225  
        template<class Awaitable>
226  
        auto transform_awaitable(Awaitable&& a)
226  
        auto transform_awaitable(Awaitable&& a)
227  
        {
227  
        {
228  
            using A = std::decay_t<Awaitable>;
228  
            using A = std::decay_t<Awaitable>;
229  
            if constexpr (IoAwaitable<A>)
229  
            if constexpr (IoAwaitable<A>)
230  
            {
230  
            {
231  
                return transform_awaiter<Awaitable>{
231  
                return transform_awaiter<Awaitable>{
232  
                    std::forward<Awaitable>(a), this};
232  
                    std::forward<Awaitable>(a), this};
233  
            }
233  
            }
234  
            else
234  
            else
235  
            {
235  
            {
236  
                static_assert(sizeof(A) == 0, "requires IoAwaitable");
236  
                static_assert(sizeof(A) == 0, "requires IoAwaitable");
237  
            }
237  
            }
238  
        }
238  
        }
239  
    };
239  
    };
240  

240  

241  
    std::coroutine_handle<promise_type> h_;
241  
    std::coroutine_handle<promise_type> h_;
242  

242  

243  
    /// Destroy the task and its coroutine frame if owned.
243  
    /// Destroy the task and its coroutine frame if owned.
244  
    ~task()
244  
    ~task()
245  
    {
245  
    {
246  
        if(h_)
246  
        if(h_)
247  
            h_.destroy();
247  
            h_.destroy();
248  
    }
248  
    }
249  

249  

250  
    /// Return false; tasks are never immediately ready.
250  
    /// Return false; tasks are never immediately ready.
251  
    bool await_ready() const noexcept
251  
    bool await_ready() const noexcept
252  
    {
252  
    {
253  
        return false;
253  
        return false;
254  
    }
254  
    }
255  

255  

256  
    /// Return the result or rethrow any stored exception.
256  
    /// Return the result or rethrow any stored exception.
257  
    auto await_resume()
257  
    auto await_resume()
258  
    {
258  
    {
259  
        if(h_.promise().has_ep_)
259  
        if(h_.promise().has_ep_)
260  
            std::rethrow_exception(h_.promise().ep_);
260  
            std::rethrow_exception(h_.promise().ep_);
261  
        if constexpr (! std::is_void_v<T>)
261  
        if constexpr (! std::is_void_v<T>)
262  
            return std::move(*h_.promise().result_);
262  
            return std::move(*h_.promise().result_);
263  
        else
263  
        else
264  
            return;
264  
            return;
265  
    }
265  
    }
266  

266  

267  
    /// Start execution with the caller's context.
267  
    /// Start execution with the caller's context.
268  
    std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, io_env const* env)
268  
    std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, io_env const* env)
269  
    {
269  
    {
270  
        h_.promise().set_continuation(cont);
270  
        h_.promise().set_continuation(cont);
271  
        h_.promise().set_environment(env);
271  
        h_.promise().set_environment(env);
272  
        return h_;
272  
        return h_;
273  
    }
273  
    }
274  

274  

275  
    /// Return the coroutine handle.
275  
    /// Return the coroutine handle.
276  
    std::coroutine_handle<promise_type> handle() const noexcept
276  
    std::coroutine_handle<promise_type> handle() const noexcept
277  
    {
277  
    {
278  
        return h_;
278  
        return h_;
279  
    }
279  
    }
280  

280  

281  
    /** Release ownership of the coroutine frame.
281  
    /** Release ownership of the coroutine frame.
282  

282  

283  
        After calling this, destroying the task does not destroy the
283  
        After calling this, destroying the task does not destroy the
284  
        coroutine frame. The caller becomes responsible for the frame's
284  
        coroutine frame. The caller becomes responsible for the frame's
285  
        lifetime.
285  
        lifetime.
286  

286  

287  
        @par Postconditions
287  
        @par Postconditions
288  
        `handle()` returns the original handle, but the task no longer
288  
        `handle()` returns the original handle, but the task no longer
289  
        owns it.
289  
        owns it.
290  
    */
290  
    */
291  
    void release() noexcept
291  
    void release() noexcept
292  
    {
292  
    {
293  
        h_ = nullptr;
293  
        h_ = nullptr;
294  
    }
294  
    }
295  

295  

296  
    task(task const&) = delete;
296  
    task(task const&) = delete;
297  
    task& operator=(task const&) = delete;
297  
    task& operator=(task const&) = delete;
298  

298  

299  
    /// Move construct, transferring ownership.
299  
    /// Move construct, transferring ownership.
300  
    task(task&& other) noexcept
300  
    task(task&& other) noexcept
301  
        : h_(std::exchange(other.h_, nullptr))
301  
        : h_(std::exchange(other.h_, nullptr))
302  
    {
302  
    {
303  
    }
303  
    }
304  

304  

305  
    /// Move assign, transferring ownership.
305  
    /// Move assign, transferring ownership.
306  
    task& operator=(task&& other) noexcept
306  
    task& operator=(task&& other) noexcept
307  
    {
307  
    {
308  
        if(this != &other)
308  
        if(this != &other)
309  
        {
309  
        {
310  
            if(h_)
310  
            if(h_)
311  
                h_.destroy();
311  
                h_.destroy();
312  
            h_ = std::exchange(other.h_, nullptr);
312  
            h_ = std::exchange(other.h_, nullptr);
313  
        }
313  
        }
314  
        return *this;
314  
        return *this;
315  
    }
315  
    }
316  

316  

317  
private:
317  
private:
318  
    explicit task(std::coroutine_handle<promise_type> h)
318  
    explicit task(std::coroutine_handle<promise_type> h)
319  
        : h_(h)
319  
        : h_(h)
320  
    {
320  
    {
321  
    }
321  
    }
322  
};
322  
};
323  

323  

324  
} // namespace capy
324  
} // namespace capy
325  
} // namespace boost
325  
} // namespace boost
326  

326  

327  
#endif
327  
#endif