• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <__debug>
55
56#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
57#pragma GCC system_header
58#endif
59
60#ifdef _LIBCPP_HAS_NO_COROUTINES
61# if defined(_LIBCPP_WARNING)
62    _LIBCPP_WARNING("<experimental/coroutine> cannot be used with this compiler")
63# else
64#   warning <experimental/coroutine> cannot be used with this compiler
65# endif
66#endif
67
68#ifndef _LIBCPP_HAS_NO_COROUTINES
69
70_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_COROUTINES
71
72template <class _Tp, class = void>
73struct __coroutine_traits_sfinae {};
74
75template <class _Tp>
76struct __coroutine_traits_sfinae<
77    _Tp, typename __void_t<typename _Tp::promise_type>::type>
78{
79  using promise_type = typename _Tp::promise_type;
80};
81
82template <typename _Ret, typename... _Args>
83struct coroutine_traits
84    : public __coroutine_traits_sfinae<_Ret>
85{
86};
87
88template <typename _Promise = void>
89class _LIBCPP_TEMPLATE_VIS coroutine_handle;
90
91template <>
92class _LIBCPP_TEMPLATE_VIS coroutine_handle<void> {
93public:
94    _LIBCPP_INLINE_VISIBILITY
95    _LIBCPP_CONSTEXPR coroutine_handle() _NOEXCEPT : __handle_(nullptr) {}
96
97    _LIBCPP_INLINE_VISIBILITY
98    _LIBCPP_CONSTEXPR coroutine_handle(nullptr_t) _NOEXCEPT : __handle_(nullptr) {}
99
100    _LIBCPP_INLINE_VISIBILITY
101    coroutine_handle& operator=(nullptr_t) _NOEXCEPT {
102        __handle_ = nullptr;
103        return *this;
104    }
105
106    _LIBCPP_INLINE_VISIBILITY
107    _LIBCPP_CONSTEXPR void* address() const _NOEXCEPT { return __handle_; }
108
109    _LIBCPP_INLINE_VISIBILITY
110    _LIBCPP_CONSTEXPR explicit operator bool() const _NOEXCEPT { return __handle_; }
111
112    _LIBCPP_INLINE_VISIBILITY
113    void operator()() { resume(); }
114
115    _LIBCPP_INLINE_VISIBILITY
116    void resume() {
117      _LIBCPP_ASSERT(__is_suspended(),
118                     "resume() can only be called on suspended coroutines");
119      _LIBCPP_ASSERT(!done(),
120                "resume() has undefined behavior when the coroutine is done");
121      __builtin_coro_resume(__handle_);
122    }
123
124    _LIBCPP_INLINE_VISIBILITY
125    void destroy() {
126      _LIBCPP_ASSERT(__is_suspended(),
127                     "destroy() can only be called on suspended coroutines");
128      __builtin_coro_destroy(__handle_);
129    }
130
131    _LIBCPP_INLINE_VISIBILITY
132    bool done() const {
133      _LIBCPP_ASSERT(__is_suspended(),
134                     "done() can only be called on suspended coroutines");
135      return __builtin_coro_done(__handle_);
136    }
137
138public:
139    _LIBCPP_INLINE_VISIBILITY
140    static coroutine_handle from_address(void* __addr) _NOEXCEPT {
141        coroutine_handle __tmp;
142        __tmp.__handle_ = __addr;
143        return __tmp;
144    }
145
146    // FIXME: Should from_address(nullptr) be allowed?
147    _LIBCPP_INLINE_VISIBILITY
148    static coroutine_handle from_address(nullptr_t) _NOEXCEPT {
149      return coroutine_handle(nullptr);
150    }
151
152    template <class _Tp, bool _CallIsValid = false>
153    static coroutine_handle from_address(_Tp*) {
154      static_assert(_CallIsValid,
155       "coroutine_handle<void>::from_address cannot be called with "
156        "non-void pointers");
157    }
158
159private:
160  bool __is_suspended() const _NOEXCEPT  {
161    // FIXME actually implement a check for if the coro is suspended.
162    return __handle_;
163  }
164
165  template <class _PromiseT> friend class coroutine_handle;
166  void* __handle_;
167};
168
169// 18.11.2.7 comparison operators:
170inline _LIBCPP_INLINE_VISIBILITY
171bool operator==(coroutine_handle<> __x, coroutine_handle<> __y) _NOEXCEPT {
172    return __x.address() == __y.address();
173}
174inline _LIBCPP_INLINE_VISIBILITY
175bool operator!=(coroutine_handle<> __x, coroutine_handle<> __y) _NOEXCEPT {
176    return !(__x == __y);
177}
178inline _LIBCPP_INLINE_VISIBILITY
179bool operator<(coroutine_handle<> __x, coroutine_handle<> __y) _NOEXCEPT {
180    return less<void*>()(__x.address(), __y.address());
181}
182inline _LIBCPP_INLINE_VISIBILITY
183bool operator>(coroutine_handle<> __x, coroutine_handle<> __y) _NOEXCEPT {
184    return __y < __x;
185}
186inline _LIBCPP_INLINE_VISIBILITY
187bool operator<=(coroutine_handle<> __x, coroutine_handle<> __y) _NOEXCEPT {
188    return !(__x > __y);
189}
190inline _LIBCPP_INLINE_VISIBILITY
191bool operator>=(coroutine_handle<> __x, coroutine_handle<> __y) _NOEXCEPT {
192    return !(__x < __y);
193}
194
195template <typename _Promise>
196class _LIBCPP_TEMPLATE_VIS coroutine_handle : public coroutine_handle<> {
197    using _Base = coroutine_handle<>;
198public:
199#ifndef _LIBCPP_CXX03_LANG
200    // 18.11.2.1 construct/reset
201    using coroutine_handle<>::coroutine_handle;
202#else
203    _LIBCPP_INLINE_VISIBILITY coroutine_handle() _NOEXCEPT : _Base() {}
204    _LIBCPP_INLINE_VISIBILITY coroutine_handle(nullptr_t) _NOEXCEPT : _Base(nullptr) {}
205#endif
206    _LIBCPP_INLINE_VISIBILITY
207    coroutine_handle& operator=(nullptr_t) _NOEXCEPT {
208        _Base::operator=(nullptr);
209        return *this;
210    }
211
212    _LIBCPP_INLINE_VISIBILITY
213    _Promise& promise() const {
214        return *static_cast<_Promise*>(
215            __builtin_coro_promise(this->__handle_, _LIBCPP_ALIGNOF(_Promise), false));
216    }
217
218public:
219    _LIBCPP_INLINE_VISIBILITY
220    static coroutine_handle from_address(void* __addr) _NOEXCEPT {
221        coroutine_handle __tmp;
222        __tmp.__handle_ = __addr;
223        return __tmp;
224    }
225
226    // NOTE: this overload isn't required by the standard but is needed so
227    // the deleted _Promise* overload doesn't make from_address(nullptr)
228    // ambiguous.
229    // FIXME: should from_address work with nullptr?
230    _LIBCPP_INLINE_VISIBILITY
231    static coroutine_handle from_address(nullptr_t) _NOEXCEPT {
232      return coroutine_handle(nullptr);
233    }
234
235    template <class _Tp, bool _CallIsValid = false>
236    static coroutine_handle from_address(_Tp*) {
237      static_assert(_CallIsValid,
238       "coroutine_handle<promise_type>::from_address cannot be called with "
239        "non-void pointers");
240    }
241
242    template <bool _CallIsValid = false>
243    static coroutine_handle from_address(_Promise*) {
244      static_assert(_CallIsValid,
245       "coroutine_handle<promise_type>::from_address cannot be used with "
246        "pointers to the coroutine's promise type; use 'from_promise' instead");
247    }
248
249    _LIBCPP_INLINE_VISIBILITY
250    static coroutine_handle from_promise(_Promise& __promise) _NOEXCEPT {
251        typedef typename remove_cv<_Promise>::type _RawPromise;
252        coroutine_handle __tmp;
253        __tmp.__handle_ = __builtin_coro_promise(
254            _VSTD::addressof(const_cast<_RawPromise&>(__promise)),
255             _LIBCPP_ALIGNOF(_Promise), true);
256        return __tmp;
257    }
258};
259
260#if __has_builtin(__builtin_coro_noop)
261struct noop_coroutine_promise {};
262
263template <>
264class _LIBCPP_TEMPLATE_VIS coroutine_handle<noop_coroutine_promise>
265    : public coroutine_handle<> {
266  using _Base = coroutine_handle<>;
267  using _Promise = noop_coroutine_promise;
268public:
269
270  _LIBCPP_INLINE_VISIBILITY
271  _Promise& promise() const {
272    return *static_cast<_Promise*>(
273      __builtin_coro_promise(this->__handle_, _LIBCPP_ALIGNOF(_Promise), false));
274  }
275
276  _LIBCPP_CONSTEXPR explicit operator bool() const _NOEXCEPT { return true; }
277  _LIBCPP_CONSTEXPR bool done() const _NOEXCEPT { return false; }
278
279  _LIBCPP_CONSTEXPR_AFTER_CXX17 void operator()() const _NOEXCEPT {}
280  _LIBCPP_CONSTEXPR_AFTER_CXX17 void resume() const _NOEXCEPT {}
281  _LIBCPP_CONSTEXPR_AFTER_CXX17 void destroy() const _NOEXCEPT {}
282
283private:
284  _LIBCPP_INLINE_VISIBILITY
285  friend coroutine_handle<noop_coroutine_promise> noop_coroutine() _NOEXCEPT;
286
287  _LIBCPP_INLINE_VISIBILITY coroutine_handle() _NOEXCEPT {
288    this->__handle_ = __builtin_coro_noop();
289  }
290};
291
292using noop_coroutine_handle = coroutine_handle<noop_coroutine_promise>;
293
294inline _LIBCPP_INLINE_VISIBILITY
295noop_coroutine_handle noop_coroutine() _NOEXCEPT {
296  return noop_coroutine_handle();
297}
298#endif // __has_builtin(__builtin_coro_noop)
299
300struct suspend_never {
301  _LIBCPP_INLINE_VISIBILITY
302  bool await_ready() const _NOEXCEPT { return true; }
303  _LIBCPP_INLINE_VISIBILITY
304  void await_suspend(coroutine_handle<>) const _NOEXCEPT {}
305  _LIBCPP_INLINE_VISIBILITY
306  void await_resume() const _NOEXCEPT {}
307};
308
309struct suspend_always {
310  _LIBCPP_INLINE_VISIBILITY
311  bool await_ready() const _NOEXCEPT { return false; }
312  _LIBCPP_INLINE_VISIBILITY
313  void await_suspend(coroutine_handle<>) const _NOEXCEPT {}
314  _LIBCPP_INLINE_VISIBILITY
315  void await_resume() const _NOEXCEPT {}
316};
317
318_LIBCPP_END_NAMESPACE_EXPERIMENTAL_COROUTINES
319
320_LIBCPP_BEGIN_NAMESPACE_STD
321
322template <class _Tp>
323struct hash<_VSTD_CORO::coroutine_handle<_Tp> > {
324    using __arg_type = _VSTD_CORO::coroutine_handle<_Tp>;
325    _LIBCPP_INLINE_VISIBILITY
326    size_t operator()(__arg_type const& __v) const _NOEXCEPT
327    {return hash<void*>()(__v.address());}
328};
329
330_LIBCPP_END_NAMESPACE_STD
331
332#endif // !defined(_LIBCPP_HAS_NO_COROUTINES)
333
334#endif /* _LIBCPP_EXPERIMENTAL_COROUTINE */
335