Home | History | Annotate | Line # | Download | only in experimental
      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 
     18 namespace std {
     19 namespace experimental {
     20 inline namespace coroutines_v1 {
     21 
     22   // 18.11.1 coroutine traits
     23 template <typename R, typename... ArgTypes>
     24 class coroutine_traits;
     25 // 18.11.2 coroutine handle
     26 template <typename Promise = void>
     27 class coroutine_handle;
     28 // 18.11.2.7 comparison operators:
     29 bool operator==(coroutine_handle<> x, coroutine_handle<> y) _NOEXCEPT;
     30 bool operator!=(coroutine_handle<> x, coroutine_handle<> y) _NOEXCEPT;
     31 bool operator<(coroutine_handle<> x, coroutine_handle<> y) _NOEXCEPT;
     32 bool operator<=(coroutine_handle<> x, coroutine_handle<> y) _NOEXCEPT;
     33 bool operator>=(coroutine_handle<> x, coroutine_handle<> y) _NOEXCEPT;
     34 bool operator>(coroutine_handle<> x, coroutine_handle<> y) _NOEXCEPT;
     35 // 18.11.3 trivial awaitables
     36 struct suspend_never;
     37 struct suspend_always;
     38 // 18.11.2.8 hash support:
     39 template <class T> struct hash;
     40 template <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 
     72 template <class _Tp, class = void>
     73 struct __coroutine_traits_sfinae {};
     74 
     75 template <class _Tp>
     76 struct __coroutine_traits_sfinae<
     77     _Tp, typename __void_t<typename _Tp::promise_type>::type>
     78 {
     79   using promise_type = typename _Tp::promise_type;
     80 };
     81 
     82 template <typename _Ret, typename... _Args>
     83 struct coroutine_traits
     84     : public __coroutine_traits_sfinae<_Ret>
     85 {
     86 };
     87 
     88 template <typename _Promise = void>
     89 class _LIBCPP_TEMPLATE_VIS coroutine_handle;
     90 
     91 template <>
     92 class _LIBCPP_TEMPLATE_VIS coroutine_handle<void> {
     93 public:
     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 
    138 public:
    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 
    159 private:
    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:
    170 inline _LIBCPP_INLINE_VISIBILITY
    171 bool operator==(coroutine_handle<> __x, coroutine_handle<> __y) _NOEXCEPT {
    172     return __x.address() == __y.address();
    173 }
    174 inline _LIBCPP_INLINE_VISIBILITY
    175 bool operator!=(coroutine_handle<> __x, coroutine_handle<> __y) _NOEXCEPT {
    176     return !(__x == __y);
    177 }
    178 inline _LIBCPP_INLINE_VISIBILITY
    179 bool operator<(coroutine_handle<> __x, coroutine_handle<> __y) _NOEXCEPT {
    180     return less<void*>()(__x.address(), __y.address());
    181 }
    182 inline _LIBCPP_INLINE_VISIBILITY
    183 bool operator>(coroutine_handle<> __x, coroutine_handle<> __y) _NOEXCEPT {
    184     return __y < __x;
    185 }
    186 inline _LIBCPP_INLINE_VISIBILITY
    187 bool operator<=(coroutine_handle<> __x, coroutine_handle<> __y) _NOEXCEPT {
    188     return !(__x > __y);
    189 }
    190 inline _LIBCPP_INLINE_VISIBILITY
    191 bool operator>=(coroutine_handle<> __x, coroutine_handle<> __y) _NOEXCEPT {
    192     return !(__x < __y);
    193 }
    194 
    195 template <typename _Promise>
    196 class _LIBCPP_TEMPLATE_VIS coroutine_handle : public coroutine_handle<> {
    197     using _Base = coroutine_handle<>;
    198 public:
    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 
    218 public:
    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)
    261 struct noop_coroutine_promise {};
    262 
    263 template <>
    264 class _LIBCPP_TEMPLATE_VIS coroutine_handle<noop_coroutine_promise>
    265     : public coroutine_handle<> {
    266   using _Base = coroutine_handle<>;
    267   using _Promise = noop_coroutine_promise;
    268 public:
    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 
    283 private:
    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 
    292 using noop_coroutine_handle = coroutine_handle<noop_coroutine_promise>;
    293 
    294 inline _LIBCPP_INLINE_VISIBILITY
    295 noop_coroutine_handle noop_coroutine() _NOEXCEPT {
    296   return noop_coroutine_handle();
    297 }
    298 #endif // __has_builtin(__builtin_coro_noop)
    299 
    300 struct 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 
    309 struct 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 
    322 template <class _Tp>
    323 struct 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