blob: 13e32625079cd62c1f2468ced1896827524b3162 [file] [log] [blame]
Yi Kong7bf2a682019-04-18 17:30:49 -07001// -*- C++ -*-
2//===----------------------------- coroutine -----------------------------===//
3//
4// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5// See https://llvm.org/LICENSE.txt for license information.
6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7//
8//===----------------------------------------------------------------------===//
9
10#ifndef _LIBCPP_EXPERIMENTAL_COROUTINE
11#define _LIBCPP_EXPERIMENTAL_COROUTINE
12
13/**
14 experimental/coroutine synopsis
15
16// C++next
17
18namespace std {
19namespace experimental {
20inline namespace coroutines_v1 {
21
22 // 18.11.1 coroutine traits
23template <typename R, typename... ArgTypes>
24class coroutine_traits;
25// 18.11.2 coroutine handle
26template <typename Promise = void>
27class coroutine_handle;
28// 18.11.2.7 comparison operators:
29bool operator==(coroutine_handle<> x, coroutine_handle<> y) _NOEXCEPT;
30bool operator!=(coroutine_handle<> x, coroutine_handle<> y) _NOEXCEPT;
31bool operator<(coroutine_handle<> x, coroutine_handle<> y) _NOEXCEPT;
32bool operator<=(coroutine_handle<> x, coroutine_handle<> y) _NOEXCEPT;
33bool operator>=(coroutine_handle<> x, coroutine_handle<> y) _NOEXCEPT;
34bool operator>(coroutine_handle<> x, coroutine_handle<> y) _NOEXCEPT;
35// 18.11.3 trivial awaitables
36struct suspend_never;
37struct suspend_always;
38// 18.11.2.8 hash support:
39template <class T> struct hash;
40template <class P> struct hash<coroutine_handle<P>>;
41
42} // namespace coroutines_v1
43} // namespace experimental
44} // namespace std
45
46 */
47
48#include <experimental/__config>
49#include <new>
50#include <type_traits>
51#include <functional>
52#include <memory> // for hash<T*>
53#include <cstddef>
54#include <cassert>
55#include <__debug>
56
57#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
58#pragma GCC system_header
59#endif
60
61#ifdef _LIBCPP_HAS_NO_COROUTINES
62# if defined(_LIBCPP_WARNING)
63 _LIBCPP_WARNING("<experimental/coroutine> cannot be used with this compiler")
64# else
65# warning <experimental/coroutine> cannot be used with this compiler
66# endif
67#endif
68
69#ifndef _LIBCPP_HAS_NO_COROUTINES
70
71_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_COROUTINES
72
73template <class _Tp, class = void>
74struct __coroutine_traits_sfinae {};
75
76template <class _Tp>
77struct __coroutine_traits_sfinae<
78 _Tp, typename __void_t<typename _Tp::promise_type>::type>
79{
80 using promise_type = typename _Tp::promise_type;
81};
82
83template <typename _Ret, typename... _Args>
84struct _LIBCPP_TEMPLATE_VIS coroutine_traits
85 : public __coroutine_traits_sfinae<_Ret>
86{
87};
88
89template <typename _Promise = void>
90class _LIBCPP_TEMPLATE_VIS coroutine_handle;
91
92template <>
93class _LIBCPP_TEMPLATE_VIS coroutine_handle<void> {
94public:
95 _LIBCPP_INLINE_VISIBILITY
96 _LIBCPP_CONSTEXPR coroutine_handle() _NOEXCEPT : __handle_(nullptr) {}
97
98 _LIBCPP_INLINE_VISIBILITY
99 _LIBCPP_CONSTEXPR coroutine_handle(nullptr_t) _NOEXCEPT : __handle_(nullptr) {}
100
101 _LIBCPP_INLINE_VISIBILITY
102 coroutine_handle& operator=(nullptr_t) _NOEXCEPT {
103 __handle_ = nullptr;
104 return *this;
105 }
106
107 _LIBCPP_INLINE_VISIBILITY
108 _LIBCPP_CONSTEXPR void* address() const _NOEXCEPT { return __handle_; }
109
110 _LIBCPP_INLINE_VISIBILITY
111 _LIBCPP_CONSTEXPR explicit operator bool() const _NOEXCEPT { return __handle_; }
112
113 _LIBCPP_INLINE_VISIBILITY
114 void operator()() { resume(); }
115
116 _LIBCPP_INLINE_VISIBILITY
117 void resume() {
118 _LIBCPP_ASSERT(__is_suspended(),
119 "resume() can only be called on suspended coroutines");
120 _LIBCPP_ASSERT(!done(),
121 "resume() has undefined behavior when the coroutine is done");
122 __builtin_coro_resume(__handle_);
123 }
124
125 _LIBCPP_INLINE_VISIBILITY
126 void destroy() {
127 _LIBCPP_ASSERT(__is_suspended(),
128 "destroy() can only be called on suspended coroutines");
129 __builtin_coro_destroy(__handle_);
130 }
131
132 _LIBCPP_INLINE_VISIBILITY
133 bool done() const {
134 _LIBCPP_ASSERT(__is_suspended(),
135 "done() can only be called on suspended coroutines");
136 return __builtin_coro_done(__handle_);
137 }
138
139public:
140 _LIBCPP_INLINE_VISIBILITY
141 static coroutine_handle from_address(void* __addr) _NOEXCEPT {
142 coroutine_handle __tmp;
143 __tmp.__handle_ = __addr;
144 return __tmp;
145 }
146
147 // FIXME: Should from_address(nullptr) be allowed?
148 _LIBCPP_INLINE_VISIBILITY
149 static coroutine_handle from_address(nullptr_t) _NOEXCEPT {
150 return coroutine_handle(nullptr);
151 }
152
153 template <class _Tp, bool _CallIsValid = false>
154 static coroutine_handle from_address(_Tp*) {
155 static_assert(_CallIsValid,
156 "coroutine_handle<void>::from_address cannot be called with "
157 "non-void pointers");
158 }
159
160private:
161 bool __is_suspended() const _NOEXCEPT {
162 // FIXME actually implement a check for if the coro is suspended.
163 return __handle_;
164 }
165
166 template <class _PromiseT> friend class coroutine_handle;
167 void* __handle_;
168};
169
170// 18.11.2.7 comparison operators:
171inline _LIBCPP_INLINE_VISIBILITY
172bool operator==(coroutine_handle<> __x, coroutine_handle<> __y) _NOEXCEPT {
173 return __x.address() == __y.address();
174}
175inline _LIBCPP_INLINE_VISIBILITY
176bool operator!=(coroutine_handle<> __x, coroutine_handle<> __y) _NOEXCEPT {
177 return !(__x == __y);
178}
179inline _LIBCPP_INLINE_VISIBILITY
180bool operator<(coroutine_handle<> __x, coroutine_handle<> __y) _NOEXCEPT {
181 return less<void*>()(__x.address(), __y.address());
182}
183inline _LIBCPP_INLINE_VISIBILITY
184bool operator>(coroutine_handle<> __x, coroutine_handle<> __y) _NOEXCEPT {
185 return __y < __x;
186}
187inline _LIBCPP_INLINE_VISIBILITY
188bool operator<=(coroutine_handle<> __x, coroutine_handle<> __y) _NOEXCEPT {
189 return !(__x > __y);
190}
191inline _LIBCPP_INLINE_VISIBILITY
192bool operator>=(coroutine_handle<> __x, coroutine_handle<> __y) _NOEXCEPT {
193 return !(__x < __y);
194}
195
196template <typename _Promise>
197class _LIBCPP_TEMPLATE_VIS coroutine_handle : public coroutine_handle<> {
198 using _Base = coroutine_handle<>;
199public:
200#ifndef _LIBCPP_CXX03_LANG
201 // 18.11.2.1 construct/reset
202 using coroutine_handle<>::coroutine_handle;
203#else
204 _LIBCPP_INLINE_VISIBILITY coroutine_handle() _NOEXCEPT : _Base() {}
205 _LIBCPP_INLINE_VISIBILITY coroutine_handle(nullptr_t) _NOEXCEPT : _Base(nullptr) {}
206#endif
207 _LIBCPP_INLINE_VISIBILITY
208 coroutine_handle& operator=(nullptr_t) _NOEXCEPT {
209 _Base::operator=(nullptr);
210 return *this;
211 }
212
213 _LIBCPP_INLINE_VISIBILITY
214 _Promise& promise() const {
215 return *static_cast<_Promise*>(
216 __builtin_coro_promise(this->__handle_, _LIBCPP_ALIGNOF(_Promise), false));
217 }
218
219public:
220 _LIBCPP_INLINE_VISIBILITY
221 static coroutine_handle from_address(void* __addr) _NOEXCEPT {
222 coroutine_handle __tmp;
223 __tmp.__handle_ = __addr;
224 return __tmp;
225 }
226
227 // NOTE: this overload isn't required by the standard but is needed so
228 // the deleted _Promise* overload doesn't make from_address(nullptr)
229 // ambiguous.
230 // FIXME: should from_address work with nullptr?
231 _LIBCPP_INLINE_VISIBILITY
232 static coroutine_handle from_address(nullptr_t) _NOEXCEPT {
233 return coroutine_handle(nullptr);
234 }
235
236 template <class _Tp, bool _CallIsValid = false>
237 static coroutine_handle from_address(_Tp*) {
238 static_assert(_CallIsValid,
239 "coroutine_handle<promise_type>::from_address cannot be called with "
240 "non-void pointers");
241 }
242
243 template <bool _CallIsValid = false>
244 static coroutine_handle from_address(_Promise*) {
245 static_assert(_CallIsValid,
246 "coroutine_handle<promise_type>::from_address cannot be used with "
247 "pointers to the coroutine's promise type; use 'from_promise' instead");
248 }
249
250 _LIBCPP_INLINE_VISIBILITY
251 static coroutine_handle from_promise(_Promise& __promise) _NOEXCEPT {
252 typedef typename remove_cv<_Promise>::type _RawPromise;
253 coroutine_handle __tmp;
254 __tmp.__handle_ = __builtin_coro_promise(
255 _VSTD::addressof(const_cast<_RawPromise&>(__promise)),
256 _LIBCPP_ALIGNOF(_Promise), true);
257 return __tmp;
258 }
259};
260
261#if __has_builtin(__builtin_coro_noop)
262struct noop_coroutine_promise {};
263
264template <>
265class _LIBCPP_TEMPLATE_VIS coroutine_handle<noop_coroutine_promise>
266 : public coroutine_handle<> {
267 using _Base = coroutine_handle<>;
268 using _Promise = noop_coroutine_promise;
269public:
270
271 _LIBCPP_INLINE_VISIBILITY
272 _Promise& promise() const {
273 return *static_cast<_Promise*>(
274 __builtin_coro_promise(this->__handle_, _LIBCPP_ALIGNOF(_Promise), false));
275 }
276
277 _LIBCPP_CONSTEXPR explicit operator bool() const _NOEXCEPT { return true; }
278 _LIBCPP_CONSTEXPR bool done() const _NOEXCEPT { return false; }
279
280 _LIBCPP_CONSTEXPR_AFTER_CXX17 void operator()() const _NOEXCEPT {}
281 _LIBCPP_CONSTEXPR_AFTER_CXX17 void resume() const _NOEXCEPT {}
282 _LIBCPP_CONSTEXPR_AFTER_CXX17 void destroy() const _NOEXCEPT {}
283
284private:
285 _LIBCPP_INLINE_VISIBILITY
286 friend coroutine_handle<noop_coroutine_promise> noop_coroutine() _NOEXCEPT;
287
288 _LIBCPP_INLINE_VISIBILITY coroutine_handle() _NOEXCEPT {
289 this->__handle_ = __builtin_coro_noop();
290 }
291};
292
293using noop_coroutine_handle = coroutine_handle<noop_coroutine_promise>;
294
295inline _LIBCPP_INLINE_VISIBILITY
296noop_coroutine_handle noop_coroutine() _NOEXCEPT {
297 return noop_coroutine_handle();
298}
299#endif // __has_builtin(__builtin_coro_noop)
300
301struct _LIBCPP_TYPE_VIS suspend_never {
302 _LIBCPP_INLINE_VISIBILITY
303 bool await_ready() const _NOEXCEPT { return true; }
304 _LIBCPP_INLINE_VISIBILITY
305 void await_suspend(coroutine_handle<>) const _NOEXCEPT {}
306 _LIBCPP_INLINE_VISIBILITY
307 void await_resume() const _NOEXCEPT {}
308};
309
310struct _LIBCPP_TYPE_VIS suspend_always {
311 _LIBCPP_INLINE_VISIBILITY
312 bool await_ready() const _NOEXCEPT { return false; }
313 _LIBCPP_INLINE_VISIBILITY
314 void await_suspend(coroutine_handle<>) const _NOEXCEPT {}
315 _LIBCPP_INLINE_VISIBILITY
316 void await_resume() const _NOEXCEPT {}
317};
318
319_LIBCPP_END_NAMESPACE_EXPERIMENTAL_COROUTINES
320
321_LIBCPP_BEGIN_NAMESPACE_STD
322
323template <class _Tp>
324struct hash<_VSTD_CORO::coroutine_handle<_Tp> > {
325 using __arg_type = _VSTD_CORO::coroutine_handle<_Tp>;
326 _LIBCPP_INLINE_VISIBILITY
327 size_t operator()(__arg_type const& __v) const _NOEXCEPT
328 {return hash<void*>()(__v.address());}
329};
330
331_LIBCPP_END_NAMESPACE_STD
332
333#endif // !defined(_LIBCPP_HAS_NO_COROUTINES)
334
335#endif /* _LIBCPP_EXPERIMENTAL_COROUTINE */