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  

20  

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

26  

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

29  

29  
namespace detail {
30  
namespace detail {
30  

31  

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

37  

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

42  

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

48  

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

56  

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

58  

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

60  

60  
    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
61  
    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
62  
    executing until the task is awaited, enabling efficient composition
63  
    executing until the task is awaited, enabling efficient composition
63  
    without unnecessary eager execution.
64  
    without unnecessary eager execution.
64  

65  

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

70  

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

72  

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

76  

76  
    @par Example
77  
    @par Example
77  

78  

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

87  

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

94  

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

109  

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

115  

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

121  

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

128  

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

133  

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

139  

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

144  

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

148  

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

157  

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

163  

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

168  

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

173  

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

180  

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

186  

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

192  

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

197  

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

204  

204  
            template<class Promise>
205  
            template<class Promise>
205  
            auto await_suspend(std::coroutine_handle<Promise> h) noexcept
206  
            auto await_suspend(std::coroutine_handle<Promise> h) noexcept
206 -
#ifdef _MSC_VER
 
207 -
                // Workaround: MSVC stores the coroutine_handle<> return
 
208 -
                // value on the coroutine frame via hidden __$ReturnUdt$.
 
209 -
                // After await_suspend publishes the handle to another
 
210 -
                // thread, that thread can resume/destroy the frame before
 
211 -
                // __resume reads the handle back for the symmetric
 
212 -
                // transfer tail-call, causing a use-after-free.
 
213  
            {
207  
            {
214  
                using R = decltype(a_.await_suspend(h, p_->environment()));
208  
                using R = decltype(a_.await_suspend(h, p_->environment()));
215  
                if constexpr (std::is_same_v<R, std::coroutine_handle<>>)
209  
                if constexpr (std::is_same_v<R, std::coroutine_handle<>>)
216 -
                    a_.await_suspend(h, p_->environment()).resume();
210 +
                    return detail::symmetric_transfer(a_.await_suspend(h, p_->environment()));
217  
                else
211  
                else
218 -
#else
 
219 -
                return a_.await_suspend(h, p_->environment());
 
220 -
#endif
 
221  
                    return a_.await_suspend(h, p_->environment());
212  
                    return a_.await_suspend(h, p_->environment());
222  
            }
213  
            }
223  
        };
214  
        };
224  

215  

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

231  

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

233  

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

240  

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

246  

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

257  

267  
    /// Start execution with the caller's context.
258  
    /// Start execution with the caller's context.
268  
    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)
269  
    {
260  
    {
270  
        h_.promise().set_continuation(cont);
261  
        h_.promise().set_continuation(cont);
271  
        h_.promise().set_environment(env);
262  
        h_.promise().set_environment(env);
272  
        return h_;
263  
        return h_;
273  
    }
264  
    }
274  

265  

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

271  

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

273  

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

277  

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

286  

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

289  

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

295  

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

307  

317  
private:
308  
private:
318  
    explicit task(std::coroutine_handle<promise_type> h)
309  
    explicit task(std::coroutine_handle<promise_type> h)
319  
        : h_(h)
310  
        : h_(h)
320  
    {
311  
    {
321  
    }
312  
    }
322  
};
313  
};
323  

314  

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

317  

327  
#endif
318  
#endif