Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-16 09:44:27

0001 // Copyright (c) 2022 Klemens D. Morgenstern
0002 //
0003 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0004 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
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 /* tag::outline[]
0029 
0030 // Awaitable type that returns the executor of the current coroutine.
0031 struct executor_t {}
0032 constexpr executor_t executor;
0033 
0034 // Awaitable type that returns the cancellation state of the current coroutine.
0035 struct cancellation_state_t {};
0036 constexpr cancellation_state_t cancellation_state;
0037 
0038 // Reset the cancellation state with custom or default filters.
0039 constexpr __unspecified__ reset_cancellation_state();
0040 template<typename Filter>
0041 constexpr __unspecified__ reset_cancellation_state(
0042     Filter && filter);
0043 template<typename InFilter, typename OutFilter>
0044 constexpr __unspecified__ reset_cancellation_state(
0045     InFilter && in_filter,
0046     OutFilter && out_filter);
0047 
0048 // get & set the throw_if_cancelled setting.
0049 __unspecified__ throw_if_cancelled();
0050 __unspecified__ throw_if_cancelled(bool value);
0051 
0052 // Set the cancellation source in a detached.
0053 __unspecified__ reset_cancellation_source();
0054 __unspecified__ reset_cancellation_source(asio::cancellation_slot slot);
0055 
0056 end::outline[]
0057  */
0058 
0059 using namespace asio::this_coro;
0060 //tag::outline[]
0061 
0062 // get the allocator the promise
0063 struct allocator_t {};
0064 constexpr allocator_t allocator;
0065 
0066 // get the current cancellation state-type
0067 struct cancelled_t {};
0068 constexpr cancelled_t cancelled;
0069 
0070 // set the over-eager mode of a generator
0071 struct initial_t {};
0072 constexpr initial_t initial;
0073 //end::outline[]
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   // This await transformation resets the associated cancellation state.
0102   auto await_transform(cobalt::this_coro::cancelled_t) noexcept
0103   {
0104     return cancelled_t_awaitable{state_.cancelled()};
0105   }
0106 
0107   // This await transformation resets the associated cancellation state.
0108   auto await_transform(asio::this_coro::cancellation_state_t) noexcept
0109   {
0110     return cancellation_state_t_awaitable{state_};
0111   }
0112 
0113   // This await transformation resets the associated cancellation state.
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   // This await transformation resets the associated cancellation state.
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   // This await transformation resets the associated cancellation state.
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   // This await transformation determines whether cancellation is propagated as
0275   // an exception.
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   // This await transformation sets whether cancellation is propagated as an
0283   // exception.
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     // | memory_resource | size_t | <padding> | coroutine.
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     // | memory_resource | size_t | <padding> | coroutine.
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 /// Allocate the memory and put the allocator behind the cobalt memory
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 /// Deallocate the memory and destroy the allocator in the cobalt memory.
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 /// Allocate the memory and put the allocator behind the cobalt memory
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 /// Deallocate the memory and destroy the allocator in the cobalt memory.
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 //BOOST_COBALT_THIS_CORO_HPP