1// -*- 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 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 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 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 */
336