Seastar
High performance C++ framework for concurrent servers
coroutine.hh
1/*
2 * This file is open source software, licensed to you under the terms
3 * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
4 * distributed with this work for additional information regarding copyright
5 * ownership. You may not use this file except in compliance with the License.
6 *
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing,
12 * software distributed under the License is distributed on an
13 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 * KIND, either express or implied. See the License for the
15 * specific language governing permissions and limitations
16 * under the License.
17 */
18/*
19 * Copyright (C) 2019 ScyllaDB Ltd.
20 */
21
22#pragma once
23
24
25#include <seastar/core/future.hh>
26#include <seastar/coroutine/exception.hh>
27#include <seastar/util/modules.hh>
28#include <seastar/util/std-compat.hh>
29
30
31#ifndef SEASTAR_MODULE
32#include <coroutine>
33#endif
34
35namespace seastar {
36
37namespace internal {
38
39template <typename T = void>
40class coroutine_traits_base {
41public:
42 class promise_type final : public seastar::task {
43 seastar::promise<T> _promise;
44 public:
45 promise_type() = default;
46 promise_type(promise_type&&) = delete;
47 promise_type(const promise_type&) = delete;
48
49 template<typename... U>
50 void return_value(U&&... value) {
51 _promise.set_value(std::forward<U>(value)...);
52 }
53
54 void return_value(coroutine::exception ce) noexcept {
55 _promise.set_exception(std::move(ce.eptr));
56 }
57
58 void set_exception(std::exception_ptr&& eptr) noexcept {
59 _promise.set_exception(std::move(eptr));
60 }
61
62 [[deprecated("Forwarding coroutine returns are deprecated as too dangerous. Use 'co_return co_await ...' until explicit syntax is available.")]]
63 void return_value(future<T>&& fut) noexcept {
64 fut.forward_to(std::move(_promise));
65 }
66
67 void unhandled_exception() noexcept {
68 _promise.set_exception(std::current_exception());
69 }
70
71 seastar::future<T> get_return_object() noexcept {
72 return _promise.get_future();
73 }
74
75 std::suspend_never initial_suspend() noexcept { return { }; }
76 std::suspend_never final_suspend() noexcept { return { }; }
77
78 virtual void run_and_dispose() noexcept override {
79 auto handle = std::coroutine_handle<promise_type>::from_promise(*this);
80 handle.resume();
81 }
82
83 task* waiting_task() noexcept override { return _promise.waiting_task(); }
84
85 scheduling_group set_scheduling_group(scheduling_group sg) noexcept {
86 return std::exchange(this->_sg, sg);
87 }
88 };
89};
90
91template <>
92class coroutine_traits_base<> {
93public:
94 class promise_type final : public seastar::task {
95 seastar::promise<> _promise;
96 public:
97 promise_type() = default;
98 promise_type(promise_type&&) = delete;
99 promise_type(const promise_type&) = delete;
100
101 void return_void() noexcept {
102 _promise.set_value();
103 }
104
105 void set_exception(std::exception_ptr&& eptr) noexcept {
106 _promise.set_exception(std::move(eptr));
107 }
108
109 void unhandled_exception() noexcept {
110 _promise.set_exception(std::current_exception());
111 }
112
113 seastar::future<> get_return_object() noexcept {
114 return _promise.get_future();
115 }
116
117 std::suspend_never initial_suspend() noexcept { return { }; }
118 std::suspend_never final_suspend() noexcept { return { }; }
119
120 virtual void run_and_dispose() noexcept override {
121 auto handle = std::coroutine_handle<promise_type>::from_promise(*this);
122 handle.resume();
123 }
124
125 task* waiting_task() noexcept override { return _promise.waiting_task(); }
126
127 scheduling_group set_scheduling_group(scheduling_group new_sg) noexcept {
128 return task::set_scheduling_group(new_sg);
129 }
130 };
131};
132
133template<bool CheckPreempt, typename T>
134struct awaiter {
135 seastar::future<T> _future;
136public:
137 explicit awaiter(seastar::future<T>&& f) noexcept : _future(std::move(f)) { }
138
139 awaiter(const awaiter&) = delete;
140 awaiter(awaiter&&) = delete;
141
142 bool await_ready() const noexcept {
143 return _future.available() && (!CheckPreempt || !need_preempt());
144 }
145
146 template<typename U>
147 void await_suspend(std::coroutine_handle<U> hndl) noexcept {
148 if (!CheckPreempt || !_future.available()) {
149 _future.set_coroutine(hndl.promise());
150 } else {
151 schedule(&hndl.promise());
152 }
153 }
154
155 T await_resume() { return _future.get(); }
156};
157
158template<bool CheckPreempt>
159struct awaiter<CheckPreempt, void> {
160 seastar::future<> _future;
161public:
162 explicit awaiter(seastar::future<>&& f) noexcept : _future(std::move(f)) { }
163
164 awaiter(const awaiter&) = delete;
165 awaiter(awaiter&&) = delete;
166
167 bool await_ready() const noexcept {
168 return _future.available() && (!CheckPreempt || !need_preempt());
169 }
170
171 template<typename U>
172 void await_suspend(std::coroutine_handle<U> hndl) noexcept {
173 if (!CheckPreempt || !_future.available()) {
174 _future.set_coroutine(hndl.promise());
175 } else {
176 schedule(&hndl.promise());
177 }
178 }
179
180 void await_resume() { _future.get(); }
181};
182
183} // seastar::internal
184
185SEASTAR_MODULE_EXPORT_BEGIN
186
187template<typename T>
188auto operator co_await(future<T> f) noexcept {
189 return internal::awaiter<true, T>(std::move(f));
190}
191
192namespace coroutine {
198template<typename T> struct [[nodiscard]] without_preemption_check : public seastar::future<T> {
199 explicit without_preemption_check(seastar::future<T>&& f) noexcept : seastar::future<T>(std::move(f)) {}
200};
201
222template <typename Func>
223class lambda {
224 Func* _func;
225public:
228 explicit lambda(Func&& func) : _func(&func) {}
230 template <typename... Args>
231 decltype(auto) operator()(Args&&... args) const {
232 return std::invoke(*_func, std::forward<Args>(args)...);
233 }
234};
235
236}
237
241template<typename T>
242auto operator co_await(coroutine::without_preemption_check<T> f) noexcept {
243 return internal::awaiter<false, T>(std::move(f));
244}
245
246SEASTAR_MODULE_EXPORT_END
247
248} // seastar
249
250
251namespace std {
252
253SEASTAR_MODULE_EXPORT
254template<typename T, typename... Args>
255class coroutine_traits<seastar::future<T>, Args...> : public seastar::internal::coroutine_traits_base<T> {
256};
257
258} // std
259
Definition: coroutine.hh:223
lambda(Func &&func)
Definition: coroutine.hh:228
A representation of a possibly not-yet-computed value.
Definition: future.hh:1240
bool available() const noexcept
Checks whether the future is available.
Definition: future.hh:1394
promise - allows a future value to be made available at a later time.
Definition: future.hh:934
void set_value(A &&... a) noexcept
Sets the promises value.
Definition: future.hh:990
Definition: task.hh:34
virtual task * waiting_task() noexcept=0
Returns the next task which is waiting for this task to complete execution, or nullptr.
Seastar API namespace.
Definition: abort_on_ebadf.hh:26
STL namespace.