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 #ifndef SEASTAR_COROUTINES_ENABLED
33 #error Coroutines support disabled.
34 #endif
35 #include <coroutine>
36 #endif
37 
38 namespace seastar {
39 
40 namespace internal {
41 
42 template <typename T = void>
43 class coroutine_traits_base {
44 public:
45  class promise_type final : public seastar::task {
46  seastar::promise<T> _promise;
47  public:
48  promise_type() = default;
49  promise_type(promise_type&&) = delete;
50  promise_type(const promise_type&) = delete;
51 
52  template<typename... U>
53  void return_value(U&&... value) {
54  _promise.set_value(std::forward<U>(value)...);
55  }
56 
57  void return_value(coroutine::exception ce) noexcept {
58  _promise.set_exception(std::move(ce.eptr));
59  }
60 
61  void set_exception(std::exception_ptr&& eptr) noexcept {
62  _promise.set_exception(std::move(eptr));
63  }
64 
65  [[deprecated("Forwarding coroutine returns are deprecated as too dangerous. Use 'co_return co_await ...' until explicit syntax is available.")]]
66  void return_value(future<T>&& fut) noexcept {
67  fut.forward_to(std::move(_promise));
68  }
69 
70  void unhandled_exception() noexcept {
71  _promise.set_exception(std::current_exception());
72  }
73 
74  seastar::future<T> get_return_object() noexcept {
75  return _promise.get_future();
76  }
77 
78  std::suspend_never initial_suspend() noexcept { return { }; }
79  std::suspend_never final_suspend() noexcept { return { }; }
80 
81  virtual void run_and_dispose() noexcept override {
82  auto handle = std::coroutine_handle<promise_type>::from_promise(*this);
83  handle.resume();
84  }
85 
86  task* waiting_task() noexcept override { return _promise.waiting_task(); }
87 
88  scheduling_group set_scheduling_group(scheduling_group sg) noexcept {
89  return std::exchange(this->_sg, sg);
90  }
91  };
92 };
93 
94 template <>
95 class coroutine_traits_base<> {
96 public:
97  class promise_type final : public seastar::task {
98  seastar::promise<> _promise;
99  public:
100  promise_type() = default;
101  promise_type(promise_type&&) = delete;
102  promise_type(const promise_type&) = delete;
103 
104  void return_void() noexcept {
105  _promise.set_value();
106  }
107 
108  void set_exception(std::exception_ptr&& eptr) noexcept {
109  _promise.set_exception(std::move(eptr));
110  }
111 
112  void unhandled_exception() noexcept {
113  _promise.set_exception(std::current_exception());
114  }
115 
116  seastar::future<> get_return_object() noexcept {
117  return _promise.get_future();
118  }
119 
120  std::suspend_never initial_suspend() noexcept { return { }; }
121  std::suspend_never final_suspend() noexcept { return { }; }
122 
123  virtual void run_and_dispose() noexcept override {
124  auto handle = std::coroutine_handle<promise_type>::from_promise(*this);
125  handle.resume();
126  }
127 
128  task* waiting_task() noexcept override { return _promise.waiting_task(); }
129 
130  scheduling_group set_scheduling_group(scheduling_group new_sg) noexcept {
131  return task::set_scheduling_group(new_sg);
132  }
133  };
134 };
135 
136 template<bool CheckPreempt, typename... T>
137 struct awaiter {
138  seastar::future<T...> _future;
139 public:
140  explicit awaiter(seastar::future<T...>&& f) noexcept : _future(std::move(f)) { }
141 
142  awaiter(const awaiter&) = delete;
143  awaiter(awaiter&&) = delete;
144 
145  bool await_ready() const noexcept {
146  return _future.available() && (!CheckPreempt || !need_preempt());
147  }
148 
149  template<typename U>
150  void await_suspend(std::coroutine_handle<U> hndl) noexcept {
151  if (!CheckPreempt || !_future.available()) {
152  _future.set_coroutine(hndl.promise());
153  } else {
154  schedule(&hndl.promise());
155  }
156  }
157 
158  std::tuple<T...> await_resume() { return _future.get(); }
159 };
160 
161 template<bool CheckPreempt, typename T>
162 struct awaiter<CheckPreempt, T> {
163  seastar::future<T> _future;
164 public:
165  explicit awaiter(seastar::future<T>&& f) noexcept : _future(std::move(f)) { }
166 
167  awaiter(const awaiter&) = delete;
168  awaiter(awaiter&&) = delete;
169 
170  bool await_ready() const noexcept {
171  return _future.available() && (!CheckPreempt || !need_preempt());
172  }
173 
174  template<typename U>
175  void await_suspend(std::coroutine_handle<U> hndl) noexcept {
176  if (!CheckPreempt || !_future.available()) {
177  _future.set_coroutine(hndl.promise());
178  } else {
179  schedule(&hndl.promise());
180  }
181  }
182 
183  T await_resume() { return _future.get0(); }
184 };
185 
186 template<bool CheckPreempt>
187 struct awaiter<CheckPreempt> {
188  seastar::future<> _future;
189 public:
190  explicit awaiter(seastar::future<>&& f) noexcept : _future(std::move(f)) { }
191 
192  awaiter(const awaiter&) = delete;
193  awaiter(awaiter&&) = delete;
194 
195  bool await_ready() const noexcept {
196  return _future.available() && (!CheckPreempt || !need_preempt());
197  }
198 
199  template<typename U>
200  void await_suspend(std::coroutine_handle<U> hndl) noexcept {
201  if (!CheckPreempt || !_future.available()) {
202  _future.set_coroutine(hndl.promise());
203  } else {
204  schedule(&hndl.promise());
205  }
206  }
207 
208  void await_resume() { _future.get(); }
209 };
210 
211 } // seastar::internal
212 
213 SEASTAR_MODULE_EXPORT_BEGIN
214 
215 template<typename... T>
216 auto operator co_await(future<T...> f) noexcept {
217  return internal::awaiter<true, T...>(std::move(f));
218 }
219 
220 namespace coroutine {
226 template<typename... T> struct [[nodiscard]] without_preemption_check : public seastar::future<T...> {
227  explicit without_preemption_check(seastar::future<T...>&& f) noexcept : seastar::future<T...>(std::move(f)) {}
228 };
229 template<typename T> struct [[nodiscard]] without_preemption_check<T> : public seastar::future<T> {
230  explicit without_preemption_check(seastar::future<T>&& f) noexcept : seastar::future<T>(std::move(f)) {}
231 };
232 template<> struct [[nodiscard]] without_preemption_check<> : public seastar::future<> {
233  explicit without_preemption_check(seastar::future<>&& f) noexcept : seastar::future<>(std::move(f)) {}
234 };
235 
256 template <typename Func>
257 class lambda {
258  Func* _func;
259 public:
262  explicit lambda(Func&& func) : _func(&func) {}
264  template <typename... Args>
265  decltype(auto) operator()(Args&&... args) const {
266  return std::invoke(*_func, std::forward<Args>(args)...);
267  }
268 };
269 
270 }
271 
275 template<typename... T>
276 auto operator co_await(coroutine::without_preemption_check<T...> f) noexcept {
277  return internal::awaiter<false, T...>(std::move(f));
278 }
279 
280 SEASTAR_MODULE_EXPORT_END
281 
282 } // seastar
283 
284 
285 namespace std {
286 
287 SEASTAR_MODULE_EXPORT
288 template<typename... T, typename... Args>
289 class coroutine_traits<seastar::future<T...>, Args...> : public seastar::internal::coroutine_traits_base<T...> {
290 };
291 
292 } // std
293 
Definition: coroutine.hh:257
lambda(Func &&func)
Definition: coroutine.hh:262
A representation of a possibly not-yet-computed value.
Definition: future.hh:1238
bool available() const noexcept
Checks whether the future is available.
Definition: future.hh:1379
promise - allows a future value to be made available at a later time.
Definition: future.hh:926
void set_value(A &&... a) noexcept
Sets the promises value.
Definition: future.hh:982
Definition: task.hh:35
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