1// Copyright (c) 2022 Klemens D. Morgenstern
2//
3// Distributed under the Boost Software License, Version 1.0. (See accompanying
4// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5#ifndef BOOST_COBALT_THIS_CORO_HPP
6#define BOOST_COBALT_THIS_CORO_HPP
7
8#include <boost/cobalt/this_thread.hpp>
9#include <boost/cobalt/detail/this_thread.hpp>
10
11#include <boost/asio/associated_allocator.hpp>
12#include <boost/asio/associated_cancellation_slot.hpp>
13#include <boost/asio/associated_executor.hpp>
14#include <boost/asio/this_coro.hpp>
15#include <boost/asio/cancellation_state.hpp>
16
17
18#include <coroutine>
19#include <optional>
20#include <tuple>
21
22namespace boost::cobalt
23{
24
25namespace this_coro
26{
27
28/* tag::outline[]
29
30// Awaitable type that returns the executor of the current coroutine.
31struct executor_t {}
32constexpr executor_t executor;
33
34// Awaitable type that returns the cancellation state of the current coroutine.
35struct cancellation_state_t {};
36constexpr cancellation_state_t cancellation_state;
37
38// Reset the cancellation state with custom or default filters.
39constexpr __unspecified__ reset_cancellation_state();
40template<typename Filter>
41constexpr __unspecified__ reset_cancellation_state(
42 Filter && filter);
43template<typename InFilter, typename OutFilter>
44constexpr __unspecified__ reset_cancellation_state(
45 InFilter && in_filter,
46 OutFilter && out_filter);
47
48// get & set the throw_if_cancelled setting.
49__unspecified__ throw_if_cancelled();
50__unspecified__ throw_if_cancelled(bool value);
51
52// Set the cancellation source in a detached.
53__unspecified__ reset_cancellation_source();
54__unspecified__ reset_cancellation_source(asio::cancellation_slot slot);
55
56end::outline[]
57 */
58
59using namespace asio::this_coro;
60//tag::outline[]
61
62// get the allocator the promise
63struct allocator_t {};
64constexpr allocator_t allocator;
65
66// get the current cancellation state-type
67struct cancelled_t {};
68constexpr cancelled_t cancelled;
69
70// set the over-eager mode of a generator
71struct initial_t {};
72constexpr initial_t initial;
73//end::outline[]
74
75template<typename CancellationSlot = asio::cancellation_slot>
76struct reset_cancellation_source_t
77{
78 CancellationSlot source;
79};
80
81template<typename CancellationSlot= asio::cancellation_slot>
82reset_cancellation_source_t<CancellationSlot> reset_cancellation_source(CancellationSlot slot = {})
83{
84 return reset_cancellation_source_t<CancellationSlot>{std::move(slot)};
85}
86
87}
88
89template<typename CancellationSlot = asio::cancellation_slot,
90 typename DefaultFilter = asio::enable_terminal_cancellation>
91struct promise_cancellation_base
92{
93 using cancellation_slot_type = asio::cancellation_slot;
94 cancellation_slot_type get_cancellation_slot() const {return state_.slot();}
95
96 template<typename InitialFilter = asio::enable_terminal_cancellation>
97 promise_cancellation_base(CancellationSlot slot = {}, InitialFilter filter = {})
98 : source_(slot), state_{source_, filter} {}
99
100
101 // This await transformation resets the associated cancellation state.
102 auto await_transform(cobalt::this_coro::cancelled_t) noexcept
103 {
104 return cancelled_t_awaitable{state_.cancelled()};
105 }
106
107 // This await transformation resets the associated cancellation state.
108 auto await_transform(asio::this_coro::cancellation_state_t) noexcept
109 {
110 return cancellation_state_t_awaitable{state_};
111 }
112
113 // This await transformation resets the associated cancellation state.
114 auto await_transform(asio::this_coro::reset_cancellation_state_0_t) noexcept
115 {
116 return reset_cancellation_state_0_t_awaitable{state_, source_};
117 }
118
119 // This await transformation resets the associated cancellation state.
120 template <typename Filter>
121 auto await_transform(
122 asio::this_coro::reset_cancellation_state_1_t<Filter> reset) noexcept
123 {
124 return reset_cancellation_state_1_t_awaitable<Filter>{state_, BOOST_ASIO_MOVE_CAST(Filter)(reset.filter), source_};
125 }
126
127 // This await transformation resets the associated cancellation state.
128 template <typename InFilter, typename OutFilter>
129 auto await_transform(
130 asio::this_coro::reset_cancellation_state_2_t<InFilter, OutFilter> reset)
131 noexcept
132 {
133 return reset_cancellation_state_2_t_awaitable<InFilter, OutFilter>{state_,
134 BOOST_ASIO_MOVE_CAST(InFilter)(reset.in_filter),
135 BOOST_ASIO_MOVE_CAST(OutFilter)(reset.out_filter),
136 source_};
137 }
138 const asio::cancellation_state & cancellation_state() const {return state_;}
139 asio::cancellation_state & cancellation_state() {return state_;}
140 asio::cancellation_type cancelled() const
141 {
142 return state_.cancelled();
143 }
144
145 cancellation_slot_type get_cancellation_slot() {return state_.slot();}
146
147 void reset_cancellation_source(CancellationSlot source = CancellationSlot())
148 {
149 source_ = source;
150 state_ = asio::cancellation_state{source, DefaultFilter()};
151 state_.clear();
152 }
153
154 CancellationSlot & source() {return source_;}
155 const CancellationSlot & source() const {return source_;}
156 private:
157 CancellationSlot source_;
158 asio::cancellation_state state_{source_, DefaultFilter() };
159
160 struct cancelled_t_awaitable
161 {
162 asio::cancellation_type state;
163
164 bool await_ready() const noexcept
165 {
166 return true;
167 }
168
169 void await_suspend(std::coroutine_handle<void>) noexcept
170 {
171 }
172
173 auto await_resume() const
174 {
175 return state;
176 }
177 };
178
179 struct cancellation_state_t_awaitable
180 {
181 asio::cancellation_state &state;
182
183 bool await_ready() const noexcept
184 {
185 return true;
186 }
187
188 void await_suspend(std::coroutine_handle<void>) noexcept
189 {
190 }
191
192 auto await_resume() const
193 {
194 return state;
195 }
196 };
197
198 struct reset_cancellation_state_0_t_awaitable
199 {
200 asio::cancellation_state &state;
201 CancellationSlot &source;
202
203 bool await_ready() const noexcept
204 {
205 return true;
206 }
207
208 void await_suspend(std::coroutine_handle<void>) noexcept
209 {
210 }
211
212 auto await_resume() const
213 {
214 state = asio::cancellation_state(source, DefaultFilter());
215 }
216 };
217
218 template<typename Filter>
219 struct reset_cancellation_state_1_t_awaitable
220 {
221 asio::cancellation_state & state;
222 Filter filter_;
223 CancellationSlot &source;
224
225 bool await_ready() const noexcept
226 {
227 return true;
228 }
229
230 void await_suspend(std::coroutine_handle<void>) noexcept
231 {
232 }
233
234 auto await_resume()
235 {
236 state = asio::cancellation_state(
237 source,
238 BOOST_ASIO_MOVE_CAST(Filter)(filter_));
239 }
240 };
241
242 template<typename InFilter, typename OutFilter>
243 struct reset_cancellation_state_2_t_awaitable
244 {
245 asio::cancellation_state & state;
246 InFilter in_filter_;
247 OutFilter out_filter_;
248 CancellationSlot &source;
249
250
251 bool await_ready() const noexcept
252 {
253 return true;
254 }
255
256 void await_suspend(std::coroutine_handle<void>) noexcept
257 {
258 }
259
260 auto await_resume()
261 {
262 state = asio::cancellation_state(
263 source,
264 BOOST_ASIO_MOVE_CAST(InFilter)(in_filter_),
265 BOOST_ASIO_MOVE_CAST(OutFilter)(out_filter_));
266 }
267 };
268};
269
270struct promise_throw_if_cancelled_base
271{
272 promise_throw_if_cancelled_base(bool throw_if_cancelled = true) : throw_if_cancelled_(throw_if_cancelled) {}
273
274 // This await transformation determines whether cancellation is propagated as
275 // an exception.
276 auto await_transform(this_coro::throw_if_cancelled_0_t)
277 noexcept
278 {
279 return throw_if_cancelled_0_awaitable_{.value_: throw_if_cancelled_};
280 }
281
282 // This await transformation sets whether cancellation is propagated as an
283 // exception.
284 auto await_transform(this_coro::throw_if_cancelled_1_t throw_if_cancelled)
285 noexcept
286 {
287 return throw_if_cancelled_1_awaitable_{.this_: this, .value_: throw_if_cancelled.value};
288 }
289 bool throw_if_cancelled() const {return throw_if_cancelled_;}
290 protected:
291 bool throw_if_cancelled_{true};
292
293 struct throw_if_cancelled_0_awaitable_
294 {
295 bool value_;
296
297 bool await_ready() const noexcept
298 {
299 return true;
300 }
301
302 void await_suspend(std::coroutine_handle<void>) noexcept
303 {
304 }
305
306 auto await_resume()
307 {
308 return value_;
309 }
310 };
311
312 struct throw_if_cancelled_1_awaitable_
313 {
314 promise_throw_if_cancelled_base* this_;
315 bool value_;
316
317 bool await_ready() const noexcept
318 {
319 return true;
320 }
321
322 void await_suspend(std::coroutine_handle<void>) noexcept
323 {
324 }
325
326 auto await_resume()
327 {
328 this_->throw_if_cancelled_ = value_;
329 }
330 };
331};
332
333struct promise_memory_resource_base
334{
335#if !defined(BOOST_COBALT_NO_PMR)
336 using allocator_type = pmr::polymorphic_allocator<void>;
337 allocator_type get_allocator() const {return allocator_type{resource};}
338
339 template<typename ... Args>
340 static void * operator new(const std::size_t size, Args & ... args)
341 {
342 auto res = detail::get_memory_resource_from_args(args...);
343 const auto p = res->allocate(size + sizeof(pmr::memory_resource *), alignof(pmr::memory_resource *));
344 auto pp = static_cast<pmr::memory_resource**>(p);
345 *pp = res;
346 return pp + 1;
347 }
348
349 static void operator delete(void * raw, const std::size_t size) noexcept
350 {
351 const auto p = static_cast<pmr::memory_resource**>(raw) - 1;
352 pmr::memory_resource * res = *p;
353 res->deallocate(p: p, bytes: size + sizeof(pmr::memory_resource *), alignment: alignof(pmr::memory_resource *));
354 }
355 promise_memory_resource_base(pmr::memory_resource * resource = this_thread::get_default_resource()) : resource(resource) {}
356
357private:
358 pmr::memory_resource * resource = this_thread::get_default_resource();
359#endif
360};
361
362/// Allocate the memory and put the allocator behind the cobalt memory
363template<typename AllocatorType>
364void *allocate_coroutine(const std::size_t size, AllocatorType alloc_)
365{
366 using alloc_type = typename std::allocator_traits<AllocatorType>::template rebind_alloc<unsigned char>;
367 alloc_type alloc{alloc_};
368
369 const std::size_t align_needed = size % alignof(alloc_type);
370 const std::size_t align_offset = align_needed != 0 ? alignof(alloc_type) - align_needed : 0ull;
371 const std::size_t alloc_size = size + sizeof(alloc_type) + align_offset;
372 const auto raw = std::allocator_traits<alloc_type>::allocate(alloc, alloc_size);
373 new(raw + size + align_offset) alloc_type(std::move(alloc));
374
375 return raw;
376}
377
378/// Deallocate the memory and destroy the allocator in the cobalt memory.
379template<typename AllocatorType>
380void deallocate_coroutine(void *raw_, const std::size_t size)
381{
382 using alloc_type = typename std::allocator_traits<AllocatorType>::template rebind_alloc<unsigned char>;
383 const auto raw = static_cast<unsigned char *>(raw_);
384
385 const std::size_t align_needed = size % alignof(alloc_type);
386 const std::size_t align_offset = align_needed != 0 ? alignof(alloc_type) - align_needed : 0ull;
387 const std::size_t alloc_size = size + sizeof(alloc_type) + align_offset;
388 auto alloc_p = reinterpret_cast<alloc_type *>(raw + size + align_offset);
389
390 auto alloc = std::move(*alloc_p);
391 alloc_p->~alloc_type();
392 using size_type = typename std::allocator_traits<alloc_type>::size_type;
393 std::allocator_traits<alloc_type>::deallocate(alloc, raw, static_cast<size_type>(alloc_size));
394}
395
396
397template<typename Promise>
398struct enable_await_allocator
399{
400 auto await_transform(this_coro::allocator_t)
401 {
402 return allocator_awaitable_{static_cast<Promise*>(this)->get_allocator()};
403
404 }
405 private:
406 struct allocator_awaitable_
407 {
408 using allocator_type = typename Promise::allocator_type;
409
410 allocator_type alloc;
411 constexpr static bool await_ready() { return true; }
412
413 bool await_suspend( std::coroutine_handle<void> ) { return false; }
414 allocator_type await_resume()
415 {
416 return alloc;
417 }
418 };
419};
420
421template<typename Promise>
422struct enable_await_executor
423{
424 auto await_transform(this_coro::executor_t)
425 {
426 return executor_awaitable_{static_cast<Promise*>(this)->get_executor()};
427 }
428 private:
429 struct executor_awaitable_
430 {
431 using executor_type = typename Promise::executor_type;
432
433 executor_type exec;
434 constexpr static bool await_ready() { return true; }
435
436 bool await_suspend( std::coroutine_handle<void> ) { return false; }
437 executor_type await_resume()
438 {
439 return exec;
440 }
441 };
442};
443
444}
445
446#endif //BOOST_COBALT_THIS_CORO_HPP
447

source code of boost/libs/cobalt/include/boost/cobalt/this_coro.hpp