File indexing completed on 2025-12-16 09:44:26
0001
0002
0003
0004
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 , 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 , 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
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