mp-coro main
Coroutine support tools
task.h
Go to the documentation of this file.
1// The MIT License (MIT)
2//
3// Copyright (c) 2021 Mateusz Pusz
4//
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files (the "Software"), to deal
7// in the Software without restriction, including without limitation the rights
8// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9// copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11//
12// The above copyright notice and this permission notice shall be included in all
13// copies or substantial portions of the Software.
14//
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21// SOFTWARE.
22
23#pragma once
24
27#include <mp-coro/concepts.h>
28#include <mp-coro/coro_ptr.h>
29#include <mp-coro/trace.h>
30#include <mp-coro/type_traits.h>
31#include <concepts>
32#include <coroutine>
33
34namespace mp_coro {
35
36/// Task that produces a value of type `T`: to get that value, simply await the
37/// @ref task.
38///
39/// Tasks are lazy: they always suspend initially. Each task has an optional
40/// continuation (i.e. the coroutine that is waiting for the result of this
41/// task) that is resumed at the final suspend point.
42///
43/// @tparam T Type of the value returned.
44/// @tparam Allocator Has no effect.
45///
46/// @see https://lewissbaker.github.io/2020/05/11/understanding_symmetric_transfer
47///
48/// @par Example
49///
50/// ```cpp
51/// task<> foo() {
52/// co_return;
53/// }
54/// task<> bar() {
55/// task<> f = foo();
56/// co_await f;
57/// }
58/// ```
59/// @mermaid{task}
60///
61/// @todo Support custom allocators.
62///
63/// @ingroup coro_ret_types
64template <task_value_type T = void, typename Allocator = void>
65class [[nodiscard]] task {
66 public:
67 /// The type of the value produced by this @ref task.
68 using value_type = T;
69
70 /// Required promise type for coroutines returning a @ref task. Stores the
71 /// value produced by the @ref task, and a handle to an optional
72 /// continuation to execute
73 /// @todo when exactly?
75 std::coroutine_handle<> continuation = std::noop_coroutine();
76
77 /// Returns a @ref task that references this promise.
79 TRACE_FUNC();
80 return this;
81 }
82
83 /// Lazy: not started until awaited.
84 static std::suspend_always initial_suspend() noexcept {
85 TRACE_FUNC();
86 return {};
87 }
88
89 /// Awaiter returned by @ref final_suspend.
90 struct final_awaiter : std::suspend_always {
91 std::coroutine_handle<>
92 await_suspend(std::coroutine_handle<promise_type> this_coro) noexcept {
93 TRACE_FUNC();
94 return this_coro.promise().continuation;
95 }
96 };
97
98 /// Resumes the continuation of this @ref task when awaited. Beyond this
99 /// point, the task's coroutine will never be resumed.
100 static awaiter_of<void> auto final_suspend() noexcept {
101 TRACE_FUNC();
102 return final_awaiter {};
103 }
104 };
105
106 task(task &&) = default; ///< Move constructor.
107 task &operator=(task &&) = delete; ///< Move assignment not allowed.
108
109 private:
110 /// Awaiter type for awaiting the result of a @ref task.
111 struct awaiter {
112 /// Reference to the promise object of the @ref task in question.
114
115 /// Returns true if the @ref task's coroutine is already done
116 /// (suspended at its final suspension point).
117 bool await_ready() const noexcept {
118 TRACE_FUNC();
119 return std::coroutine_handle<promise_type>::from_promise(promise).done();
120 }
121
122 /// Set the current coroutine as this @ref task's continuation, and then
123 /// resume this @ref task's coroutine.
124 std::coroutine_handle<> await_suspend(std::coroutine_handle<> h) const noexcept {
125 TRACE_FUNC();
126 promise.continuation = h;
127 return std::coroutine_handle<promise_type>::from_promise(promise);
128 }
129
130 /// Return the value of the @ref task's promise.
131 decltype(auto) await_resume() const {
132 TRACE_FUNC();
133 return promise.get();
134 }
135 };
136
137 public:
138 /// When awaited, sets the current coroutine as this @ref task's
139 /// continuation, and then resumes this @ref task's coroutine.
140 awaiter_of<T> auto operator co_await() const &noexcept {
141 TRACE_FUNC();
142 return awaiter(*promise_);
143 }
144
146 operator co_await() const &noexcept requires std::move_constructible<T> {
147 TRACE_FUNC();
148 return awaiter(*promise_);
149 }
150
151 awaiter_of<T &&> auto operator co_await() const &&noexcept requires std::move_constructible<T> {
152 TRACE_FUNC();
153
154 struct rvalue_awaiter : awaiter {
155 T &&await_resume() {
156 TRACE_FUNC();
157 return std::move(this->promise).get();
158 }
159 };
160 return rvalue_awaiter({*promise_});
161 }
162
163 private:
164 /// An owning pointer to the promise object in the coroutine frame.
165 /// When the task is destructed, this will cause the coroutine frame to be
166 /// destroyed automatically.
168
169 /// Private constructor used in @ref promise_type::get_return_object.
170 task(promise_type *promise) : promise_(promise) { TRACE_FUNC(); }
171};
172
173/// @relates task
174template <awaitable A>
176 TRACE_FUNC();
177 co_return co_await std::forward<A>(awaitable);
178}
179
180} // namespace mp_coro
const T & get() const &
Definition: storage.h:51
Task that produces a value of type T: to get that value, simply await the task.
Definition: task.h:65
task(promise_type *promise)
Private constructor used in promise_type::get_return_object.
Definition: task.h:170
task(task &&)=default
Move constructor.
task< remove_rvalue_reference_t< await_result_t< A > > > make_task(A &&awaitable)
Definition: task.h:175
promise_ptr< promise_type > promise_
An owning pointer to the promise object in the coroutine frame.
Definition: task.h:167
task & operator=(task &&)=delete
Move assignment not allowed.
T value_type
The type of the value produced by this task.
Definition: task.h:68
Definition: async.h:31
std::unique_ptr< T, coro_deleter > promise_ptr
RAII wrapper that destroys promise object's associated coroutine.
Definition: coro_ptr.h:44
Base class with deleted copy constructor and copy assignment operator.
Definition: noncopyable.h:28
promise_type & promise
Reference to the promise object of the task in question.
Definition: task.h:113
bool await_ready() const noexcept
Returns true if the task's coroutine is already done (suspended at its final suspension point).
Definition: task.h:117
decltype(auto) await_resume() const
Return the value of the task's promise.
Definition: task.h:131
std::coroutine_handle await_suspend(std::coroutine_handle<> h) const noexcept
Set the current coroutine as this task's continuation, and then resume this task's coroutine.
Definition: task.h:124
Awaiter returned by final_suspend.
Definition: task.h:90
std::coroutine_handle await_suspend(std::coroutine_handle< promise_type > this_coro) noexcept
Definition: task.h:92
Required promise type for coroutines returning a task.
Definition: task.h:74
task get_return_object() noexcept
Returns a task that references this promise.
Definition: task.h:78
static awaiter_of< void > auto final_suspend() noexcept
Resumes the continuation of this task when awaited.
Definition: task.h:100
static std::suspend_always initial_suspend() noexcept
Lazy: not started until awaited.
Definition: task.h:84
std::coroutine_handle continuation
Definition: task.h:75
#define TRACE_FUNC()
Definition: trace.h:27