Back to home page

EIC code displayed by LXR

 
 

    


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

0001 // Copyright (c) 2024 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_EXPERIMENTAL_CONTEXT_HPP
0006 #define BOOST_COBALT_EXPERIMENTAL_CONTEXT_HPP
0007 
0008 #include <boost/callable_traits/args.hpp>
0009 #include <boost/context/fiber.hpp>
0010 #include <boost/context/fixedsize_stack.hpp>
0011 
0012 #include <boost/cobalt/concepts.hpp>
0013 #include <boost/cobalt/experimental/frame.hpp>
0014 #include <boost/cobalt/config.hpp>
0015 #include <coroutine>
0016 #include <new>
0017 
0018 namespace boost::cobalt::experimental
0019 {
0020 
0021 template<typename, typename ...>
0022 struct context;
0023 
0024 namespace detail
0025 {
0026 
0027 template<typename Promise>
0028 struct context_frame : frame<context_frame<Promise>,  Promise>
0029 {
0030   boost::context::fiber caller, callee;
0031 
0032   void (*after_resume)(context_frame *, void *) = nullptr;
0033   void * after_resume_p;
0034 
0035   template<typename ... Args>
0036     requires std::constructible_from<Promise, Args...>
0037   context_frame(Args && ... args) : frame<context_frame, Promise>(args...) {}
0038 
0039   template<typename ... Args>
0040     requires (!std::constructible_from<Promise, Args...> && std::is_default_constructible_v<Promise>)
0041   context_frame(Args && ...) {}
0042 
0043   void resume()
0044   {
0045     callee = std::move(callee).resume();
0046     if (auto af = std::exchange(after_resume, nullptr))
0047       af(this, after_resume_p);
0048   }
0049   void destroy()
0050   {
0051     auto c = std::exchange(callee, {});
0052     this->~context_frame();
0053   }
0054 
0055   template<typename Awaitable>
0056   auto do_resume(void * )
0057   {
0058     return +[](context_frame * this_, void * p)
0059     {
0060       auto aw_ = static_cast<Awaitable*>(p);
0061       auto h = std::coroutine_handle<Promise>::from_address(this_) ;
0062       aw_->await_suspend(h);
0063     };
0064   }
0065 
0066   template<typename Awaitable>
0067   auto do_resume(bool * )
0068   {
0069     return +[](context_frame * this_, void * p)
0070     {
0071       auto aw_ = static_cast<Awaitable*>(p);
0072       auto h = std::coroutine_handle<Promise>::from_address(this_) ;
0073       if (!aw_->await_suspend(h))
0074         h.resume();
0075     };
0076   }
0077 
0078   template<typename Awaitable, typename Promise_>
0079   auto do_resume(std::coroutine_handle<Promise_> * )
0080   {
0081     return +[](context_frame * this_, void * p)
0082     {
0083       auto aw_ = static_cast<Awaitable*>(p);
0084       auto h = std::coroutine_handle<Promise>::from_address(this_) ;
0085       aw_->await_suspend(h).resume();
0086     };
0087   }
0088 
0089 
0090   template<typename Awaitable>
0091   auto do_await(Awaitable aw)
0092   {
0093     if (!aw.await_ready())
0094     {
0095       after_resume_p = & aw;
0096       after_resume = do_resume<Awaitable>(
0097           static_cast<decltype(aw.await_suspend(std::declval<std::coroutine_handle<Promise>>()))*>(nullptr)
0098           );
0099       caller = std::move(caller).resume();
0100     }
0101     return aw.await_resume();
0102   }
0103 
0104   template<typename Handle, typename ... Args>
0105   context<Handle, Args...> get_context()
0106   {
0107     return context<Handle, Args...>{this};
0108   }
0109 
0110 };
0111 
0112 
0113 template<typename Traits, typename Promise, typename ... Args>
0114 struct stack_allocator : boost::context::fixedsize_stack {};
0115 
0116 template<typename Traits, typename Promise, typename ... Args>
0117 requires requires {Promise::operator new(std::size_t{});}
0118 struct stack_allocator<Traits, Promise, Args...>
0119 {
0120   boost::context::stack_context allocate()
0121   {
0122     const auto size = Traits::default_size();
0123     const auto p = Promise::operator new(size);
0124 
0125     boost::context::stack_context sctx;
0126     sctx.size = size;
0127     sctx.sp = static_cast< char * >( p) + sctx.size;
0128     return sctx;
0129   }
0130 
0131   void deallocate( boost::context::stack_context & sctx) noexcept
0132   {
0133     void * vp = static_cast< char * >( sctx.sp) - sctx.size;
0134     Promise::operator delete(vp, sctx.size);
0135   }
0136 };
0137 
0138 template<typename Traits, typename Promise, typename ... Args>
0139   requires requires {Promise::operator new(std::size_t{}, std::decay_t<Args&>()...);}
0140 struct stack_allocator<Traits, Promise, Args...>
0141 {
0142   std::tuple<Args&...> args;
0143 
0144   boost::context::stack_context allocate()
0145   {
0146     const auto size = Traits::default_size();
0147     const auto p = std::apply(
0148         [size](auto & ... args_)
0149         {
0150           return Promise::operator new(size, args_...);
0151         }, args);
0152 
0153     boost::context::stack_context sctx;
0154     sctx.size = size;
0155     sctx.sp = static_cast< char * >( p) + sctx.size;
0156     return sctx;
0157   }
0158 
0159   void deallocate( boost::context::stack_context & sctx) noexcept
0160   {
0161     void * vp = static_cast< char * >( sctx.sp) - sctx.size;
0162     Promise::operator delete(vp, sctx.size);
0163   }
0164 };
0165 
0166 
0167 struct await_transform_base
0168 {
0169   struct dummy {};
0170   void await_transform(dummy);
0171 };
0172 
0173 template<typename T>
0174 struct await_transform_impl : await_transform_base, T
0175 {
0176 };
0177 
0178 template<typename T>
0179 concept has_await_transform = ! requires (await_transform_impl<T> & p) {p.await_transform(await_transform_base::dummy{});};
0180 
0181 template<typename Promise, typename Context, typename Func, typename ... Args>
0182 void do_return(std::true_type /* is_void */, Promise& promise, Context ctx, Func && func, Args && ... args)
0183 {
0184   std::forward<Func>(func)(ctx, std::forward<Args>(args)...);
0185   promise.return_void();
0186 }
0187 
0188 template<typename Promise, typename Context, typename Func, typename ... Args>
0189 void do_return(std::false_type /* is_void */, Promise& promise, Context ctx, Func && func, Args && ... args)
0190 {
0191   promise.return_value(std::forward<Func>(func)(ctx, std::forward<Args>(args)...));
0192 }
0193 
0194 
0195 }
0196 
0197 template<typename Return, typename ... Args>
0198 struct context
0199 {
0200   using return_type = Return;
0201   using promise_type = typename std::coroutine_traits<Return, Args...>::promise_type;
0202 
0203         promise_type & promise()       {return frame_->promise;}
0204   const promise_type & promise() const {return frame_->promise;}
0205   template<typename Return_, typename ... Args_>
0206     requires std::same_as<promise_type, typename context<Return_, Args_...>::promise_type>
0207   constexpr operator context<Return_, Args_...>() const
0208   {
0209     return {frame_};
0210   }
0211 
0212   template<typename Awaitable>
0213     requires (detail::has_await_transform<promise_type> &&
0214         requires (promise_type & pro, Awaitable && aw)
0215         {
0216           {pro.await_transform(std::forward<Awaitable>(aw))} -> awaitable_type<promise_type>;
0217         })
0218   auto await(Awaitable && aw)
0219   {
0220     return frame_->do_await(frame_->promise.await_transform(std::forward<Awaitable>(aw)));
0221   }
0222 
0223   template<typename Awaitable>
0224     requires (detail::has_await_transform<promise_type> &&
0225         requires (promise_type & pro, Awaitable && aw)
0226         {
0227           {pro.await_transform(std::forward<Awaitable>(aw).operator co_await())} -> awaitable_type<promise_type>;
0228         })
0229   auto await(Awaitable && aw)
0230   {
0231     return frame_->do_await(frame_->promise.await_transform(std::forward<Awaitable>(aw)).operator co_await());
0232   }
0233   template<typename Awaitable>
0234     requires (detail::has_await_transform<promise_type> &&
0235         requires (promise_type & pro, Awaitable && aw)
0236         {
0237           {operator co_await(pro.await_transform(std::forward<Awaitable>(aw)))} -> awaitable_type<promise_type>;
0238         })
0239   auto await(Awaitable && aw)
0240   {
0241     return frame_->do_await(operator co_await(frame_->promise.await_transform(std::forward<Awaitable>(aw))));
0242   }
0243   template<awaitable_type<promise_type> Awaitable>
0244     requires (!detail::has_await_transform<promise_type> )
0245   auto await(Awaitable && aw)
0246   {
0247     return frame_->do_await(std::forward<Awaitable>(aw));
0248   }
0249 
0250   template<typename Awaitable>
0251     requires (!detail::has_await_transform<promise_type>
0252           && requires (Awaitable && aw) {{operator co_await(std::forward<Awaitable>(aw))} -> awaitable_type<promise_type>;})
0253   auto await(Awaitable && aw)
0254   {
0255     return frame_->do_await(operator co_await(std::forward<Awaitable>(aw)));
0256   }
0257 
0258   template<typename Awaitable>
0259     requires (!detail::has_await_transform<promise_type>
0260             && requires (Awaitable && aw) {{std::forward<Awaitable>(aw).operator co_await()} -> awaitable_type<promise_type>;})
0261   auto await(Awaitable && aw)
0262   {
0263     return frame_->do_await(std::forward<Awaitable>(aw).operator co_await());
0264   }
0265 
0266   template<typename Yield>
0267     requires requires (promise_type & pro, Yield && value) {{pro.yield_value(std::forward<Yield>(value))} -> awaitable_type<promise_type>;}
0268   auto yield(Yield && value)
0269   {
0270     frame_->do_await(frame_->promise.yield_value(std::forward<Yield>(value)));
0271   }
0272 
0273  private:
0274 
0275   context(detail::context_frame<promise_type> * frame) : frame_(frame) {}
0276   template<typename, typename ...>
0277   friend struct context;
0278 
0279   //template<typename >
0280   friend struct detail::context_frame<promise_type>;
0281 
0282   detail::context_frame<promise_type> * frame_;
0283 };
0284 
0285 template<typename Return, typename ... Args, std::invocable<context<Return, Args...>, Args...> Func, typename StackAlloc>
0286 auto make_context(Func && func, std::allocator_arg_t, StackAlloc  && salloc, Args && ... args)
0287 {
0288   auto sctx_ = salloc.allocate();
0289 
0290   using promise_type = typename std::coroutine_traits<Return, Args...>::promise_type;
0291   void * p = static_cast<char*>(sctx_.sp) - sizeof(detail::context_frame<promise_type>);
0292   auto sz = sctx_.size - sizeof(detail::context_frame<promise_type>);
0293 
0294   if (auto diff = reinterpret_cast<std::uintptr_t>(p) % alignof(detail::context_frame<promise_type>); diff != 0u)
0295   {
0296     p = static_cast<char*>(p) - diff;
0297     sz -= diff;
0298   }
0299 
0300   boost::context::preallocated psc{p, sz, sctx_};
0301   auto f = new (p) detail::context_frame<promise_type>(args...);
0302 
0303   auto res = f->promise.get_return_object();
0304 
0305   constexpr auto is_always_lazy =
0306         requires (promise_type & pro) {{pro.initial_suspend()} -> std::same_as<std::suspend_always>;}
0307       && noexcept(f->promise.initial_suspend());
0308 
0309   struct invoker
0310   {
0311     detail::context_frame<promise_type> * frame;
0312     mutable Func func;
0313     mutable std::tuple<Args...> args;
0314 
0315     invoker(detail::context_frame<promise_type> * frame, Func && func, Args && ...  args)
0316         : frame(frame), func(std::forward<Func>(func)), args(std::forward<Args>(args)...)
0317     {
0318     }
0319 
0320     boost::context::fiber operator()(boost::context::fiber && f) const
0321     {
0322       auto & promise = frame->promise;
0323       frame->caller = std::move(f);
0324 
0325       try
0326       {
0327         if (!is_always_lazy)
0328           frame->do_await(promise.initial_suspend());
0329 
0330         std::apply(
0331             [&](auto && ... args_)
0332             {
0333               auto ctx = frame->template get_context<Return, Args...>();
0334               using return_type = decltype(std::forward<Func>(func)(ctx, std::forward<Args>(args_)...));
0335 
0336               detail::do_return(std::is_void<return_type>{}, frame->promise, ctx,
0337                                 std::forward<Func>(func), std::forward<Args>(args_)...);
0338             },
0339             std::move(args));
0340       }
0341       catch (boost::context::detail::forced_unwind &) { throw; }
0342       catch (...) {promise.unhandled_exception();}
0343 
0344       static_assert(noexcept(promise.final_suspend()));
0345       frame->do_await(promise.final_suspend());
0346       return std::move(frame->caller);
0347     }
0348 
0349   };
0350 
0351   f->callee = boost::context::fiber{
0352     std::allocator_arg, psc, std::forward<StackAlloc>(salloc),
0353     invoker(f, std::forward<Func>(func), std::forward<Args>(args)...)};
0354 
0355   if constexpr (is_always_lazy)
0356     f->promise.initial_suspend();
0357   else
0358     f->resume();
0359 
0360   return res;
0361 }
0362 
0363 template<typename Return, typename ... Args, std::invocable<context<Return, Args...>, Args...> Func>
0364 auto make_context(Func && func, Args && ... args)
0365 {
0366   return make_context<Return>(std::forward<Func>(func), std::allocator_arg,
0367                       boost::context::fixedsize_stack(), std::forward<Args>(args)...);
0368 }
0369 
0370 
0371 template<typename ... Args, typename Func, typename StackAlloc>
0372 auto make_context(Func && func, std::allocator_arg_t, StackAlloc  && salloc, Args && ... args)
0373 {
0374   return make_context<typename std::tuple_element_t<0u, callable_traits::args_t<Func>>::return_type>(
0375       std::forward<Func>(func), std::allocator_arg, std::forward<StackAlloc>(salloc), std::forward<Args>(args)...
0376       );
0377 }
0378 
0379 
0380 template<typename ... Args, typename Func>
0381 auto make_context(Func && func, Args && ... args)
0382 {
0383   return make_context<typename std::tuple_element_t<0u, callable_traits::args_t<Func>>::return_type>(
0384       std::forward<Func>(func), std::forward<Args>(args)...
0385   );
0386 }
0387 
0388 }
0389 
0390 #endif //BOOST_COBALT_EXPERIMENTAL_CONTEXT_HPP