File indexing completed on 2025-12-16 09:44:27
0001
0002
0003
0004
0005 #ifndef BOOST_COBALT_THIS_CORO_HPP
0006 #define BOOST_COBALT_THIS_CORO_HPP
0007
0008 #include <boost/cobalt/this_thread.hpp>
0009 #include <boost/cobalt/detail/this_thread.hpp>
0010
0011 #include <boost/asio/associated_allocator.hpp>
0012 #include <boost/asio/associated_cancellation_slot.hpp>
0013 #include <boost/asio/associated_executor.hpp>
0014 #include <boost/asio/this_coro.hpp>
0015 #include <boost/asio/cancellation_state.hpp>
0016
0017
0018 #include <coroutine>
0019 #include <optional>
0020 #include <tuple>
0021
0022 namespace boost::cobalt
0023 {
0024
0025 namespace this_coro
0026 {
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059 using namespace asio::this_coro;
0060
0061
0062
0063 struct allocator_t {};
0064 constexpr allocator_t allocator;
0065
0066
0067 struct cancelled_t {};
0068 constexpr cancelled_t cancelled;
0069
0070
0071 struct initial_t {};
0072 constexpr initial_t initial;
0073
0074
0075 template<typename CancellationSlot = asio::cancellation_slot>
0076 struct reset_cancellation_source_t
0077 {
0078 CancellationSlot source;
0079 };
0080
0081 template<typename CancellationSlot= asio::cancellation_slot>
0082 reset_cancellation_source_t<CancellationSlot> reset_cancellation_source(CancellationSlot slot = {})
0083 {
0084 return reset_cancellation_source_t<CancellationSlot>{std::move(slot)};
0085 }
0086
0087 }
0088
0089 template<typename CancellationSlot = asio::cancellation_slot,
0090 typename DefaultFilter = asio::enable_terminal_cancellation>
0091 struct promise_cancellation_base
0092 {
0093 using cancellation_slot_type = asio::cancellation_slot;
0094 cancellation_slot_type get_cancellation_slot() const {return state_.slot();}
0095
0096 template<typename InitialFilter = asio::enable_terminal_cancellation>
0097 promise_cancellation_base(CancellationSlot slot = {}, InitialFilter filter = {})
0098 : source_(slot), state_{source_, filter} {}
0099
0100
0101
0102 auto await_transform(cobalt::this_coro::cancelled_t) noexcept
0103 {
0104 return cancelled_t_awaitable{state_.cancelled()};
0105 }
0106
0107
0108 auto await_transform(asio::this_coro::cancellation_state_t) noexcept
0109 {
0110 return cancellation_state_t_awaitable{state_};
0111 }
0112
0113
0114 auto await_transform(asio::this_coro::reset_cancellation_state_0_t) noexcept
0115 {
0116 return reset_cancellation_state_0_t_awaitable{state_, source_};
0117 }
0118
0119
0120 template <typename Filter>
0121 auto await_transform(
0122 asio::this_coro::reset_cancellation_state_1_t<Filter> reset) noexcept
0123 {
0124 return reset_cancellation_state_1_t_awaitable<Filter>{state_, BOOST_ASIO_MOVE_CAST(Filter)(reset.filter), source_};
0125 }
0126
0127
0128 template <typename InFilter, typename OutFilter>
0129 auto await_transform(
0130 asio::this_coro::reset_cancellation_state_2_t<InFilter, OutFilter> reset)
0131 noexcept
0132 {
0133 return reset_cancellation_state_2_t_awaitable<InFilter, OutFilter>{state_,
0134 BOOST_ASIO_MOVE_CAST(InFilter)(reset.in_filter),
0135 BOOST_ASIO_MOVE_CAST(OutFilter)(reset.out_filter),
0136 source_};
0137 }
0138 const asio::cancellation_state & cancellation_state() const {return state_;}
0139 asio::cancellation_state & cancellation_state() {return state_;}
0140 asio::cancellation_type cancelled() const
0141 {
0142 return state_.cancelled();
0143 }
0144
0145 cancellation_slot_type get_cancellation_slot() {return state_.slot();}
0146
0147 void reset_cancellation_source(CancellationSlot source = CancellationSlot())
0148 {
0149 source_ = source;
0150 state_ = asio::cancellation_state{source, DefaultFilter()};
0151 state_.clear();
0152 }
0153
0154 CancellationSlot & source() {return source_;}
0155 const CancellationSlot & source() const {return source_;}
0156 private:
0157 CancellationSlot source_;
0158 asio::cancellation_state state_{source_, DefaultFilter() };
0159
0160 struct cancelled_t_awaitable
0161 {
0162 asio::cancellation_type state;
0163
0164 bool await_ready() const noexcept
0165 {
0166 return true;
0167 }
0168
0169 void await_suspend(std::coroutine_handle<void>) noexcept
0170 {
0171 }
0172
0173 auto await_resume() const
0174 {
0175 return state;
0176 }
0177 };
0178
0179 struct cancellation_state_t_awaitable
0180 {
0181 asio::cancellation_state &state;
0182
0183 bool await_ready() const noexcept
0184 {
0185 return true;
0186 }
0187
0188 void await_suspend(std::coroutine_handle<void>) noexcept
0189 {
0190 }
0191
0192 auto await_resume() const
0193 {
0194 return state;
0195 }
0196 };
0197
0198 struct reset_cancellation_state_0_t_awaitable
0199 {
0200 asio::cancellation_state &state;
0201 CancellationSlot &source;
0202
0203 bool await_ready() const noexcept
0204 {
0205 return true;
0206 }
0207
0208 void await_suspend(std::coroutine_handle<void>) noexcept
0209 {
0210 }
0211
0212 auto await_resume() const
0213 {
0214 state = asio::cancellation_state(source, DefaultFilter());
0215 }
0216 };
0217
0218 template<typename Filter>
0219 struct reset_cancellation_state_1_t_awaitable
0220 {
0221 asio::cancellation_state & state;
0222 Filter filter_;
0223 CancellationSlot &source;
0224
0225 bool await_ready() const noexcept
0226 {
0227 return true;
0228 }
0229
0230 void await_suspend(std::coroutine_handle<void>) noexcept
0231 {
0232 }
0233
0234 auto await_resume()
0235 {
0236 state = asio::cancellation_state(
0237 source,
0238 BOOST_ASIO_MOVE_CAST(Filter)(filter_));
0239 }
0240 };
0241
0242 template<typename InFilter, typename OutFilter>
0243 struct reset_cancellation_state_2_t_awaitable
0244 {
0245 asio::cancellation_state & state;
0246 InFilter in_filter_;
0247 OutFilter out_filter_;
0248 CancellationSlot &source;
0249
0250
0251 bool await_ready() const noexcept
0252 {
0253 return true;
0254 }
0255
0256 void await_suspend(std::coroutine_handle<void>) noexcept
0257 {
0258 }
0259
0260 auto await_resume()
0261 {
0262 state = asio::cancellation_state(
0263 source,
0264 BOOST_ASIO_MOVE_CAST(InFilter)(in_filter_),
0265 BOOST_ASIO_MOVE_CAST(OutFilter)(out_filter_));
0266 }
0267 };
0268 };
0269
0270 struct promise_throw_if_cancelled_base
0271 {
0272 promise_throw_if_cancelled_base(bool throw_if_cancelled = true) : throw_if_cancelled_(throw_if_cancelled) {}
0273
0274
0275
0276 auto await_transform(this_coro::throw_if_cancelled_0_t)
0277 noexcept
0278 {
0279 return throw_if_cancelled_0_awaitable_{throw_if_cancelled_};
0280 }
0281
0282
0283
0284 auto await_transform(this_coro::throw_if_cancelled_1_t throw_if_cancelled)
0285 noexcept
0286 {
0287 return throw_if_cancelled_1_awaitable_{this, throw_if_cancelled.value};
0288 }
0289 bool throw_if_cancelled() const {return throw_if_cancelled_;}
0290 protected:
0291 bool throw_if_cancelled_{true};
0292
0293 struct throw_if_cancelled_0_awaitable_
0294 {
0295 bool value_;
0296
0297 bool await_ready() const noexcept
0298 {
0299 return true;
0300 }
0301
0302 void await_suspend(std::coroutine_handle<void>) noexcept
0303 {
0304 }
0305
0306 auto await_resume()
0307 {
0308 return value_;
0309 }
0310 };
0311
0312 struct throw_if_cancelled_1_awaitable_
0313 {
0314 promise_throw_if_cancelled_base* this_;
0315 bool value_;
0316
0317 bool await_ready() const noexcept
0318 {
0319 return true;
0320 }
0321
0322 void await_suspend(std::coroutine_handle<void>) noexcept
0323 {
0324 }
0325
0326 auto await_resume()
0327 {
0328 this_->throw_if_cancelled_ = value_;
0329 }
0330 };
0331 };
0332
0333 struct promise_memory_resource_base
0334 {
0335 #if !defined(BOOST_COBALT_NO_PMR)
0336 using allocator_type = pmr::polymorphic_allocator<void>;
0337 allocator_type get_allocator() const {return allocator_type{resource};}
0338
0339 #if defined(__cpp_sized_deallocation)
0340 template<typename ... Args>
0341 static void * operator new(const std::size_t size, Args & ... args)
0342 {
0343 auto res = detail::get_memory_resource_from_args(args...);
0344 const auto p = res->allocate(size + sizeof(std::max_align_t));
0345 auto pp = static_cast<pmr::memory_resource**>(p);
0346 *pp = res;
0347 return static_cast<std::max_align_t *>(p) + 1;
0348 }
0349
0350 static void operator delete(void * raw, const std::size_t size) noexcept
0351 {
0352 const auto p = static_cast<std::max_align_t *>(raw) - 1;
0353 pmr::memory_resource * res = *reinterpret_cast<pmr::memory_resource**>(p);
0354 res->deallocate(p, size + sizeof(std::max_align_t));
0355 }
0356 #else
0357 template<typename ... Args>
0358 static void * operator new(const std::size_t size, Args & ... args)
0359 {
0360 using tt = std::pair<pmr::memory_resource *, std::size_t>;
0361
0362
0363 constexpr auto block_size = sizeof(tt) / sizeof(std::max_align_t)
0364 + (sizeof(tt) % sizeof(std::max_align_t) ? 1 : 0);
0365
0366
0367 auto res = detail::get_memory_resource_from_args(args...);
0368 const auto p = res->allocate(size + (block_size * sizeof(std::max_align_t)));
0369 new (p) tt(res, size);
0370 return static_cast<std::max_align_t*>(p) + block_size;
0371 }
0372
0373 static void operator delete(void * raw) noexcept
0374 {
0375 using tt = std::pair<pmr::memory_resource *, std::size_t>;
0376
0377
0378 constexpr auto block_size = sizeof(tt) / sizeof(std::max_align_t)
0379 + (sizeof(tt) % sizeof(std::max_align_t) ? 1 : 0);
0380
0381 const auto p = static_cast<std::max_align_t*>(raw) - block_size;
0382
0383 const auto tp = *reinterpret_cast<tt*>(p);
0384 const auto res = tp.first;
0385 const auto size = tp.second;
0386
0387 res->deallocate(p, size + (block_size * sizeof(std::max_align_t)));
0388 }
0389 #endif
0390
0391 promise_memory_resource_base(pmr::memory_resource * resource = this_thread::get_default_resource()) : resource(resource) {}
0392
0393 private:
0394 pmr::memory_resource * resource = this_thread::get_default_resource();
0395 #endif
0396 };
0397
0398 #if defined(__cpp_sized_deallocation)
0399
0400 template<typename AllocatorType>
0401 void *allocate_coroutine(const std::size_t size, AllocatorType alloc_)
0402 {
0403 using alloc_type = typename std::allocator_traits<AllocatorType>::template rebind_alloc<std::max_align_t>;
0404 alloc_type alloc{alloc_};
0405
0406 const std::size_t aligned_size = size / sizeof(std::max_align_t)
0407 + (size % sizeof(std::max_align_t) > 0 ? 1 : 0);
0408
0409 const std::size_t alloc_size = sizeof(AllocatorType) / sizeof(std::max_align_t)
0410 + (sizeof(AllocatorType) % sizeof(std::max_align_t) > 0 ? 1 : 0);
0411
0412 const auto raw = std::allocator_traits<alloc_type>::allocate(alloc, alloc_size + aligned_size);
0413 new(raw + aligned_size) alloc_type(std::move(alloc));
0414 return raw;
0415 }
0416
0417
0418 template<typename AllocatorType>
0419 void deallocate_coroutine(void *raw_, const std::size_t size)
0420 {
0421 using alloc_type = typename std::allocator_traits<AllocatorType>::template rebind_alloc<std::max_align_t>;
0422 const auto raw = static_cast<std::max_align_t*>(raw_);
0423
0424 const std::size_t aligned_size = size / sizeof(std::max_align_t)
0425 + (size % sizeof(std::max_align_t) > 0 ? 1 : 0);
0426
0427 const std::size_t alloc_size = sizeof(AllocatorType) / sizeof(std::max_align_t)
0428 + (sizeof(AllocatorType) % sizeof(std::max_align_t) > 0 ? 1 : 0);
0429
0430 auto alloc_p = reinterpret_cast<alloc_type *>(raw + aligned_size);
0431 auto alloc = std::move(*alloc_p);
0432 alloc_p->~alloc_type();
0433 std::allocator_traits<alloc_type>::deallocate(alloc, raw, aligned_size + alloc_size);
0434 }
0435 #else
0436
0437
0438
0439 template<typename AllocatorType>
0440 void *allocate_coroutine(const std::size_t size, AllocatorType alloc_)
0441 {
0442 using alloc_type = typename std::allocator_traits<AllocatorType>::template rebind_alloc<std::max_align_t>;
0443 alloc_type alloc{alloc_};
0444
0445 const std::size_t aligned_size = size / sizeof(std::max_align_t)
0446 + (size % sizeof(std::max_align_t) > 0 ? 1 : 0);
0447
0448 const std::size_t alloc_size = sizeof(AllocatorType) / sizeof(std::max_align_t)
0449 + (sizeof(AllocatorType) % sizeof(std::max_align_t) > 0 ? 1 : 0);
0450
0451 const std::size_t size_size = sizeof(std::size_t) / sizeof(std::max_align_t)
0452 + (sizeof(std::size_t) % sizeof(std::max_align_t) > 0 ? 1 : 0);
0453
0454
0455 static_assert(alignof(std::max_align_t) >= sizeof(std::size_t));
0456 const auto raw = std::allocator_traits<alloc_type>::allocate(alloc, alloc_size + aligned_size + size_size);
0457
0458 new(raw) alloc_type(std::move(alloc));
0459 new(raw + alloc_size) std::size_t(aligned_size);
0460 return raw + alloc_size + size_size;
0461 }
0462
0463
0464 template<typename AllocatorType>
0465 void deallocate_coroutine(void *raw_)
0466 {
0467 using alloc_type = typename std::allocator_traits<AllocatorType>::template rebind_alloc<std::max_align_t>;
0468 const auto raw = static_cast<std::max_align_t*>(raw_);
0469
0470 const std::size_t size_size = sizeof(std::size_t) / sizeof(std::max_align_t)
0471 + (sizeof(std::size_t) % sizeof(std::max_align_t) > 0 ? 1 : 0);
0472 const std::size_t aligned_size = *reinterpret_cast<std::size_t *>(raw - size_size);
0473 const std::size_t alloc_size = sizeof(AllocatorType) / sizeof(std::max_align_t)
0474 + (sizeof(AllocatorType) % sizeof(std::max_align_t) > 0 ? 1 : 0);
0475
0476 auto alloc_p = reinterpret_cast<alloc_type *>(raw - alloc_size - size_size);
0477 auto alloc = std::move(*alloc_p);
0478 alloc_p->~alloc_type();
0479 std::allocator_traits<alloc_type>::deallocate(alloc, raw - alloc_size - size_size, aligned_size + alloc_size + size_size);
0480 }
0481
0482
0483 #endif
0484
0485 template<typename Promise>
0486 struct enable_await_allocator
0487 {
0488 auto await_transform(this_coro::allocator_t)
0489 {
0490 return allocator_awaitable_{static_cast<Promise*>(this)->get_allocator()};
0491
0492 }
0493 private:
0494 struct allocator_awaitable_
0495 {
0496 using allocator_type = typename Promise::allocator_type;
0497
0498 allocator_type alloc;
0499 constexpr static bool await_ready() { return true; }
0500
0501 bool await_suspend( std::coroutine_handle<void> ) { return false; }
0502 allocator_type await_resume()
0503 {
0504 return alloc;
0505 }
0506 };
0507 };
0508
0509 template<typename Promise>
0510 struct enable_await_executor
0511 {
0512 auto await_transform(this_coro::executor_t)
0513 {
0514 return executor_awaitable_{static_cast<Promise*>(this)->get_executor()};
0515 }
0516 private:
0517 struct executor_awaitable_
0518 {
0519 using executor_type = typename Promise::executor_type;
0520
0521 executor_type exec;
0522 constexpr static bool await_ready() { return true; }
0523
0524 bool await_suspend( std::coroutine_handle<void> ) { return false; }
0525 executor_type await_resume()
0526 {
0527 return exec;
0528 }
0529 };
0530 };
0531
0532 }
0533
0534 #endif