-
Notifications
You must be signed in to change notification settings - Fork 4
/
coroutine.h
314 lines (265 loc) · 10.4 KB
/
coroutine.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
#pragma once
/**
* @file coroutine/frame.h
* @author github.com/luncliff ([email protected])
* @brief `<coroutine>` header with `std::` namespace/
* @note The implementation adjusts difference of coroutine frame between compilers
*
* @see <experimental/resumable> from Microsoft VC++ (since 2017 Feb.)
* @see <experimental/coroutine> from LLVM libcxx (since 6.0)
* @see
* https://github.com/iains/gcc-cxx-coroutines/tree/c%2B%2B-coroutines/gcc/testsuite/g%2B%2B.dg/coroutines
* @see 17.12 Coroutines [support.coroutine]
* @see https://en.cppreference.com/w/cpp/header
* @see http://www.open-std.org/jtc1/sc22/wg21/docs/papers
*
* @copyright CC BY 4.0
*/
#pragma once
#include <version>
#if defined(__cpp_lib_coroutine)
# include <coroutine>
#else
// suppress <experimental/resumable>
# define _EXPERIMENTAL_RESUMABLE_
// enforced by MSVC, but be explicit
# ifndef _RESUMABLE_FUNCTIONS_SUPPORTED
# define _RESUMABLE_FUNCTIONS_SUPPORTED
# endif
# if _STL_COMPILER_PREPROCESSOR
# include <memory>
# include <new>
# endif // _STL_COMPILER_PREPROCESSOR
# include <cstdint>
# include <exception> // std::current_exception
# include <functional> // std::hash
# include <stddef.h>
# include <type_traits>
struct portable_coro_prefix;
bool portable_coro_done(portable_coro_prefix *_Handle);
void portable_coro_resume(portable_coro_prefix *_Handle);
void portable_coro_destroy(portable_coro_prefix *_Handle);
auto portable_coro_from_promise(void *_PromAddr, ptrdiff_t _PromSize) -> portable_coro_prefix *;
void *portable_coro_get_promise(portable_coro_prefix *_Handle, ptrdiff_t _PromSize);
namespace std {
// 17.12.3, coroutine handle
template<typename _PromiseT = void>
struct coroutine_handle;
// STRUCT TEMPLATE coroutine_handle
template<>
struct coroutine_handle<void> {
// 17.12.3.1, construct
constexpr coroutine_handle() noexcept {}
// 17.12.3.1, reset
constexpr coroutine_handle(std::nullptr_t) noexcept {}
coroutine_handle &operator=(nullptr_t) noexcept {
_Ptr = nullptr;
return *this;
}
// 17.12.3.2, export
constexpr void *address() const noexcept { return _Ptr; }
// 17.12.3.2, import
static /*constexpr*/ coroutine_handle from_address(void *_Addr) {
coroutine_handle _Result{};
_Result._Ptr = reinterpret_cast<portable_coro_prefix *>(_Addr);
return _Result;
}
// 17.12.3.3, observers
constexpr explicit operator bool() const noexcept { return _Ptr != nullptr; }
bool done() const { return portable_coro_done(_Ptr); }
// 17.12.3.4, resumption
void resume() const { return portable_coro_resume(_Ptr); }
void operator()() const { return portable_coro_resume(_Ptr); }
void destroy() const { return portable_coro_destroy(_Ptr); }
protected: // this is `private` in the standard
portable_coro_prefix *_Ptr = nullptr;
};
template<typename _PromiseT>
struct coroutine_handle : public coroutine_handle<void> {
// 17.12.3.1, construct
using coroutine_handle<void>::coroutine_handle;
static coroutine_handle from_promise(_PromiseT &_Prom) {
auto *_Addr = portable_coro_from_promise(&_Prom, sizeof(_PromiseT));
return coroutine_handle::from_address(_Addr);
}
// 17.12.3.1, reset
coroutine_handle &operator=(nullptr_t) noexcept {
this->_Ptr = nullptr;
return *this;
}
// 17.12.3.2, export/import
static /*constexpr*/ coroutine_handle from_address(void *_Addr) noexcept {
coroutine_handle _Result{};
_Result._Ptr = reinterpret_cast<portable_coro_prefix *>(_Addr);
return _Result;
}
// 17.12.3.5, promise access
_PromiseT &promise() const {
auto *_Prefix = reinterpret_cast<portable_coro_prefix *>(this->address());
void *_Addr = portable_coro_get_promise(_Prefix, sizeof(_PromiseT));
_PromiseT *_Prom = reinterpret_cast<_PromiseT *>(_Addr);
return *_Prom;
}
};
// 17.12.3.6, comparison operators
constexpr bool operator==(const coroutine_handle<void> _Left,
const coroutine_handle<void> _Right) noexcept {
return _Left.address() == _Right.address();
}
/// @todo apply standard spaceship operator
/// ```
/// constexpr strong_ordering operator<=>(coroutine_handle<> x, coroutine_handle<> y) noexcept;
/// ```
constexpr bool operator!=(const coroutine_handle<void> _Left,
const coroutine_handle<void> _Right) noexcept {
return !(_Left == _Right);
}
constexpr bool operator<(const coroutine_handle<void> _Left,
const coroutine_handle<void> _Right) noexcept {
return _Left.address() < _Right.address();
}
constexpr bool operator>(const coroutine_handle<void> _Left,
const coroutine_handle<void> _Right) noexcept {
return _Right < _Left;
}
constexpr bool operator<=(const coroutine_handle<void> _Left,
const coroutine_handle<void> _Right) noexcept {
return !(_Left > _Right);
}
constexpr bool operator>=(const coroutine_handle<void> _Left,
const coroutine_handle<void> _Right) noexcept {
return !(_Left < _Right);
}
// 17.12.4, no-op coroutines
struct noop_coroutine_promise {};
// STRUCT noop_coroutine_handle
using noop_coroutine_handle = coroutine_handle<noop_coroutine_promise>;
// 17.12.4.3
noop_coroutine_handle noop_coroutine() noexcept;
// STRUCT coroutine_handle<noop_coroutine_promise>
template<>
struct coroutine_handle<noop_coroutine_promise> : public coroutine_handle<void> {
// 17.12.4.2.1, observers
constexpr explicit operator bool() const noexcept { return true; }
constexpr bool done() const noexcept { return false; }
// 17.12.4.2.2, resumption
constexpr void operator()() const noexcept {}
constexpr void resume() const noexcept {}
constexpr void destroy() const noexcept {}
# if defined(__clang__)
# if __has_builtin(__builtin_coro_noop)
/**
* @see libcxx release_90 include/experimental/coroutine
*/
noop_coroutine_promise &promise() const noexcept {
void *_Prom = __builtin_coro_promise(__builtin_coro_noop(), //
alignof(noop_coroutine_promise), false);
return *(noop_coroutine_promise *)(_Prom);
}
private:
coroutine_handle() noexcept
: coroutine_handle<void>{from_address(__builtin_coro_noop())} {}
# else
# error "requires higher clang version to use __builtin_coro_noop"
# endif
# elif defined(_MSC_VER)
// 17.12.4.2.4, address
// C3615: cannot result in a constant expression
constexpr void *address() const noexcept {
/// @todo: work safely for `portable_coro_` functions
return (noop_coroutine_promise *)(UINTPTR_MAX - 0x170704);
}
// 17.12.4.2.3, promise access
noop_coroutine_promise &promise() const noexcept {
return *(noop_coroutine_promise *)(this->address());
}
private:
coroutine_handle() noexcept
: coroutine_handle<void>{from_address(&this->promise())} {
// A noop_coroutine_handle's ptr is always a non-null pointer
}
# endif
private:
friend noop_coroutine_handle noop_coroutine() noexcept;
};
inline noop_coroutine_handle noop_coroutine() noexcept { return {}; }
// 17.12.5, trivial awaitables
// STRUCT suspend_never
class suspend_never {
public:
constexpr bool await_ready() const noexcept { return true; }
constexpr void await_resume() const noexcept {}
constexpr void await_suspend(coroutine_handle<void>) const noexcept {}
};
// STRUCT suspend_always
class suspend_always {
public:
constexpr bool await_ready() const noexcept { return false; }
constexpr void await_resume() const noexcept {}
constexpr void await_suspend(coroutine_handle<void>) const noexcept {}
};
// 17.12.2, coroutine traits
template<class _Ret, class = void>
struct coro_traits_sfinae {};
template<class _Ret>
struct coro_traits_sfinae<_Ret, void_t<typename _Ret::promise_type>> {
using promise_type = typename _Ret::promise_type;
};
// there is no way but to define in `std::experimental` since compilers are checking it
namespace experimental {
// STRUCT TEMPLATE coroutine_traits
template<typename _Ret, typename... _Ts>
struct coroutine_traits : coro_traits_sfinae<_Ret> {};
# if defined(__clang__)
// clang: std::experimental::coroutine_handle must be a class template
template<typename P>
struct coroutine_handle : public std::coroutine_handle<P> {};
# elif defined(_MSC_VER)
// msvc: compatibility with existing `experimental::coroutine_handle` identifiers.
using std::coroutine_handle;
// _Resumable_helper_traits class isolates front-end from public surface naming changes
// The original code is in <experimental/resumable>
template<typename _Ret, typename... _Ts>
struct _Resumable_helper_traits {
using _Traits = coroutine_traits<_Ret, _Ts...>;
using _PromiseT = typename _Traits::promise_type;
using _Handle_type = coroutine_handle<_PromiseT>;
static _PromiseT *_Promise_from_frame(void *_Addr) noexcept {
auto &prom = _Handle_type::from_address(_Addr).promise();
return &prom;
}
static _Handle_type _Handle_from_frame(void *_Addr) noexcept {
return _Handle_type::from_promise(*_Promise_from_frame(_Addr));
}
static void _Set_exception(void *_Addr) {
_Promise_from_frame(_Addr)->set_exception(std::current_exception());
}
static void _ConstructPromise(void *_Addr, void *_Resume_addr, int _HeapElision) {
*reinterpret_cast<void **>(_Addr) = _Resume_addr;
*reinterpret_cast<uint32_t *>(reinterpret_cast<uintptr_t>(_Addr) + sizeof(void *)) =
2u + (_HeapElision ? 0u : 0x10000u);
auto _Prom = _Promise_from_frame(_Addr);
::new (static_cast<void *>(_Prom)) _PromiseT();
}
static void _DestructPromise(void *_Addr) { _Promise_from_frame(_Addr)->~_PromiseT(); }
};
# endif
} // namespace experimental
// STRUCT TEMPLATE coroutine_traits
template<typename _Ret, typename... _Param>
using coroutine_traits = std::experimental::coroutine_traits<_Ret, _Param...>;
// 17.12.3.7, hash support
template<typename _PromiseT>
struct hash<coroutine_handle<_PromiseT>> {
// deprecated in C++17
using argument_type = coroutine_handle<_PromiseT>;
// deprecated in C++17
using result_type = size_t;
[[nodiscard]] //
result_type
operator()(argument_type const &_Handle) const noexcept {
return hash<void *>()(_Handle.address());
}
};
} // namespace std
#endif