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/detail/await_suspend_helper.hpp>
 
19  
#include <boost/capy/ex/frame_allocator.hpp>
18  
#include <boost/capy/ex/frame_allocator.hpp>
20  

19  

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

25  

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

28  

30  
namespace detail {
29  
namespace detail {
31  

30  

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

36  

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

41  

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

47  

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

55  

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

57  

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

59  

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

64  

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

69  

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

71  

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

75  

77  
    @par Example
76  
    @par Example
78  

77  

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

86  

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

93  

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

108  

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

114  

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

120  

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

127  

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

132  

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

138  

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

143  

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

147  

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

156  

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

162  

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

167  

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

172  

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

179  

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

185  

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

191  

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

196  

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

203  

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

224  

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

240  

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

242  

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

249  

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

255  

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

266  

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

274  

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

280  

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

282  

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

286  

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

295  

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

298  

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

304  

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

316  

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

323  

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

326  

318  
#endif
327  
#endif