1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot 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/executor_ref.hpp>
17  
#include <boost/capy/ex/executor_ref.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 IoLaunchableTask.
58  
/** Lazy coroutine task satisfying @ref IoLaunchableTask.
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.failed() )
82  
        if( ec.failed() )
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 IoLaunchableTask, IoAwaitableTask, run, run_async
94  
    @see IoLaunchableTask, IoAwaitableTask, 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  
        std::exception_ptr ep_;
104  
        std::exception_ptr ep_;
105  

105  

106  
        std::exception_ptr exception() const noexcept
106  
        std::exception_ptr exception() const noexcept
107  
        {
107  
        {
108  
            return ep_;
108  
            return ep_;
109  
        }
109  
        }
110  

110  

111  
        task get_return_object()
111  
        task get_return_object()
112  
        {
112  
        {
113  
            return task{std::coroutine_handle<promise_type>::from_promise(*this)};
113  
            return task{std::coroutine_handle<promise_type>::from_promise(*this)};
114  
        }
114  
        }
115  

115  

116  
        auto initial_suspend() noexcept
116  
        auto initial_suspend() noexcept
117  
        {
117  
        {
118  
            struct awaiter
118  
            struct awaiter
119  
            {
119  
            {
120  
                promise_type* p_;
120  
                promise_type* p_;
121  

121  

122  
                bool await_ready() const noexcept
122  
                bool await_ready() const noexcept
123  
                {
123  
                {
124  
                    return false;
124  
                    return false;
125  
                }
125  
                }
126  

126  

127  
                void await_suspend(coro) const noexcept
127  
                void await_suspend(coro) const noexcept
128  
                {
128  
                {
129  
                    // Capture TLS allocator while it's still valid
129  
                    // Capture TLS allocator while it's still valid
130  
                    p_->set_frame_allocator(current_frame_allocator());
130  
                    p_->set_frame_allocator(current_frame_allocator());
131  
                }
131  
                }
132  

132  

133  
                void await_resume() const noexcept
133  
                void await_resume() const noexcept
134  
                {
134  
                {
135  
                    // Restore TLS when body starts executing
135  
                    // Restore TLS when body starts executing
136  
                    if(p_->frame_allocator())
136  
                    if(p_->frame_allocator())
137  
                        current_frame_allocator() = p_->frame_allocator();
137  
                        current_frame_allocator() = p_->frame_allocator();
138  
                }
138  
                }
139  
            };
139  
            };
140  
            return awaiter{this};
140  
            return awaiter{this};
141  
        }
141  
        }
142  

142  

143  
        auto final_suspend() noexcept
143  
        auto final_suspend() noexcept
144  
        {
144  
        {
145  
            struct awaiter
145  
            struct awaiter
146  
            {
146  
            {
147  
                promise_type* p_;
147  
                promise_type* p_;
148  

148  

149  
                bool await_ready() const noexcept
149  
                bool await_ready() const noexcept
150  
                {
150  
                {
151  
                    return false;
151  
                    return false;
152  
                }
152  
                }
153  

153  

154  
                coro await_suspend(coro) const noexcept
154  
                coro await_suspend(coro) const noexcept
155  
                {
155  
                {
156  
                    return p_->complete();
156  
                    return p_->complete();
157  
                }
157  
                }
158  

158  

159  
                void await_resume() const noexcept
159  
                void await_resume() const noexcept
160  
                {
160  
                {
161  
                }
161  
                }
162  
            };
162  
            };
163  
            return awaiter{this};
163  
            return awaiter{this};
164  
        }
164  
        }
165  

165  

166  
        void unhandled_exception()
166  
        void unhandled_exception()
167  
        {
167  
        {
168  
            ep_ = std::current_exception();
168  
            ep_ = std::current_exception();
169  
        }
169  
        }
170  

170  

171  
        template<class Awaitable>
171  
        template<class Awaitable>
172  
        struct transform_awaiter
172  
        struct transform_awaiter
173  
        {
173  
        {
174  
            std::decay_t<Awaitable> a_;
174  
            std::decay_t<Awaitable> a_;
175  
            promise_type* p_;
175  
            promise_type* p_;
176  

176  

177  
            bool await_ready()
177  
            bool await_ready()
178  
            {
178  
            {
179  
                return a_.await_ready();
179  
                return a_.await_ready();
180  
            }
180  
            }
181  

181  

182  
            decltype(auto) await_resume()
182  
            decltype(auto) await_resume()
183  
            {
183  
            {
184  
                // Restore TLS before body resumes
184  
                // Restore TLS before body resumes
185  
                if(p_->frame_allocator())
185  
                if(p_->frame_allocator())
186  
                    current_frame_allocator() = p_->frame_allocator();
186  
                    current_frame_allocator() = p_->frame_allocator();
187  
                return a_.await_resume();
187  
                return a_.await_resume();
188  
            }
188  
            }
189  

189  

190  
            template<class Promise>
190  
            template<class Promise>
191  
            auto await_suspend(std::coroutine_handle<Promise> h)
191  
            auto await_suspend(std::coroutine_handle<Promise> h)
192  
            {
192  
            {
193  
                return a_.await_suspend(h, p_->executor(), p_->stop_token());
193  
                return a_.await_suspend(h, p_->executor(), p_->stop_token());
194  
            }
194  
            }
195  
        };
195  
        };
196  

196  

197  
        template<class Awaitable>
197  
        template<class Awaitable>
198  
        auto transform_awaitable(Awaitable&& a)
198  
        auto transform_awaitable(Awaitable&& a)
199  
        {
199  
        {
200  
            using A = std::decay_t<Awaitable>;
200  
            using A = std::decay_t<Awaitable>;
201  
            if constexpr (IoAwaitable<A>)
201  
            if constexpr (IoAwaitable<A>)
202  
            {
202  
            {
203  
                return transform_awaiter<Awaitable>{
203  
                return transform_awaiter<Awaitable>{
204  
                    std::forward<Awaitable>(a), this};
204  
                    std::forward<Awaitable>(a), this};
205  
            }
205  
            }
206  
            else
206  
            else
207  
            {
207  
            {
208  
                static_assert(sizeof(A) == 0, "requires IoAwaitable");
208  
                static_assert(sizeof(A) == 0, "requires IoAwaitable");
209  
            }
209  
            }
210  
        }
210  
        }
211  
    };
211  
    };
212  

212  

213  
    std::coroutine_handle<promise_type> h_;
213  
    std::coroutine_handle<promise_type> h_;
214  

214  

215  
    /// Destroy the task and its coroutine frame if owned.
215  
    /// Destroy the task and its coroutine frame if owned.
216  
    ~task()
216  
    ~task()
217  
    {
217  
    {
218  
        if(h_)
218  
        if(h_)
219  
            h_.destroy();
219  
            h_.destroy();
220  
    }
220  
    }
221  

221  

222  
    /// Return false; tasks are never immediately ready.
222  
    /// Return false; tasks are never immediately ready.
223  
    bool await_ready() const noexcept
223  
    bool await_ready() const noexcept
224  
    {
224  
    {
225  
        return false;
225  
        return false;
226  
    }
226  
    }
227  

227  

228  
    /// Return the result or rethrow any stored exception.
228  
    /// Return the result or rethrow any stored exception.
229  
    auto await_resume()
229  
    auto await_resume()
230  
    {
230  
    {
231  
        if(h_.promise().ep_)
231  
        if(h_.promise().ep_)
232  
            std::rethrow_exception(h_.promise().ep_);
232  
            std::rethrow_exception(h_.promise().ep_);
233  
        if constexpr (! std::is_void_v<T>)
233  
        if constexpr (! std::is_void_v<T>)
234  
            return std::move(*h_.promise().result_);
234  
            return std::move(*h_.promise().result_);
235  
        else
235  
        else
236  
            return;
236  
            return;
237  
    }
237  
    }
238  

238  

239  
    /// Start execution with the caller's context.
239  
    /// Start execution with the caller's context.
240  
    coro await_suspend(coro cont, executor_ref caller_ex, std::stop_token token)
240  
    coro await_suspend(coro cont, executor_ref caller_ex, std::stop_token token)
241  
    {
241  
    {
242  
        h_.promise().set_continuation(cont, caller_ex);
242  
        h_.promise().set_continuation(cont, caller_ex);
243  
        h_.promise().set_executor(caller_ex);
243  
        h_.promise().set_executor(caller_ex);
244  
        h_.promise().set_stop_token(token);
244  
        h_.promise().set_stop_token(token);
245  
        return h_;
245  
        return h_;
246  
    }
246  
    }
247  

247  

248  
    /// Return the coroutine handle.
248  
    /// Return the coroutine handle.
249  
    std::coroutine_handle<promise_type> handle() const noexcept
249  
    std::coroutine_handle<promise_type> handle() const noexcept
250  
    {
250  
    {
251  
        return h_;
251  
        return h_;
252  
    }
252  
    }
253  

253  

254  
    /** Release ownership of the coroutine frame.
254  
    /** Release ownership of the coroutine frame.
255  

255  

256  
        After calling this, destroying the task does not destroy the
256  
        After calling this, destroying the task does not destroy the
257  
        coroutine frame. The caller becomes responsible for the frame's
257  
        coroutine frame. The caller becomes responsible for the frame's
258  
        lifetime.
258  
        lifetime.
259  

259  

260  
        @par Postconditions
260  
        @par Postconditions
261  
        `handle()` returns the original handle, but the task no longer
261  
        `handle()` returns the original handle, but the task no longer
262  
        owns it.
262  
        owns it.
263  
    */
263  
    */
264  
    void release() noexcept
264  
    void release() noexcept
265  
    {
265  
    {
266  
        h_ = nullptr;
266  
        h_ = nullptr;
267  
    }
267  
    }
268  

268  

269  
    task(task const&) = delete;
269  
    task(task const&) = delete;
270  
    task& operator=(task const&) = delete;
270  
    task& operator=(task const&) = delete;
271  

271  

272  
    /// Move construct, transferring ownership.
272  
    /// Move construct, transferring ownership.
273  
    task(task&& other) noexcept
273  
    task(task&& other) noexcept
274  
        : h_(std::exchange(other.h_, nullptr))
274  
        : h_(std::exchange(other.h_, nullptr))
275  
    {
275  
    {
276  
    }
276  
    }
277  

277  

278  
    /// Move assign, transferring ownership.
278  
    /// Move assign, transferring ownership.
279  
    task& operator=(task&& other) noexcept
279  
    task& operator=(task&& other) noexcept
280  
    {
280  
    {
281  
        if(this != &other)
281  
        if(this != &other)
282  
        {
282  
        {
283  
            if(h_)
283  
            if(h_)
284  
                h_.destroy();
284  
                h_.destroy();
285  
            h_ = std::exchange(other.h_, nullptr);
285  
            h_ = std::exchange(other.h_, nullptr);
286  
        }
286  
        }
287  
        return *this;
287  
        return *this;
288  
    }
288  
    }
289  

289  

290  
private:
290  
private:
291  
    explicit task(std::coroutine_handle<promise_type> h)
291  
    explicit task(std::coroutine_handle<promise_type> h)
292  
        : h_(h)
292  
        : h_(h)
293  
    {
293  
    {
294  
    }
294  
    }
295  
};
295  
};
296  

296  

297  
} // namespace capy
297  
} // namespace capy
298  
} // namespace boost
298  
} // namespace boost
299  

299  

300  
#endif
300  
#endif