diff --git a/include/spinscale/co/postTarget.h b/include/spinscale/co/postTarget.h new file mode 100644 index 0000000..813de18 --- /dev/null +++ b/include/spinscale/co/postTarget.h @@ -0,0 +1,29 @@ +#ifndef POST_TARGET_H +#define POST_TARGET_H + +#include + +#include + +namespace sscl::co { + +/** Opt-in dynamic post-TO target for TaggedPostingPromise coroutines. + * When omitted, initial_suspend posts to ThreadTag::io_context(). + * Post-back still uses callerIoContext (getSelf() at co_await site). + */ +struct ExplicitPostTarget +{ + boost::asio::io_context& ioContext; + + explicit ExplicitPostTarget(boost::asio::io_context& ctx) noexcept + : ioContext(ctx) + {} +}; + +template +inline constexpr bool is_explicit_post_target_v = + std::same_as, ExplicitPostTarget>; + +} // namespace sscl::co + +#endif // POST_TARGET_H diff --git a/include/spinscale/co/postingPromise.h b/include/spinscale/co/postingPromise.h index 18617fe..3b93a84 100644 --- a/include/spinscale/co/postingPromise.h +++ b/include/spinscale/co/postingPromise.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -16,6 +17,7 @@ #include #include +#include #include #include #include @@ -191,6 +193,7 @@ struct PostingPromise : returnValues(), postBackStatus(*this) {} + /** Non-viral entry: post-TO uses ThreadTag default (via TaggedPostingPromise). */ template PostingPromise( std::exception_ptr &_callerExceptionPtr, @@ -201,12 +204,41 @@ struct PostingPromise postBackStatus(*this) {} - /** Member coroutines pass the implicit object parameter before explicit - * (exceptionPtr, callback, ...) args. Discard the object and delegate to - * the free-function constructor shape. - */ + /** Non-viral entry with explicit post-TO target. */ + template + PostingPromise( + std::exception_ptr &_callerExceptionPtr, + std::function _callerLambda, + ExplicitPostTarget _calleePostTarget, + TailArgs &&...) noexcept + : returnValues(_callerExceptionPtr), + callerLambda(std::move(_callerLambda)), + postBackStatus(*this), + calleePostTarget(std::move(_calleePostTarget)) + {} + + /** Viral / free-function entry with explicit post-TO target. */ + template + PostingPromise( + ExplicitPostTarget _calleePostTarget, + TailArgs &&...) noexcept + : returnValues(), + postBackStatus(*this), + calleePostTarget(std::move(_calleePostTarget)) + {} + + /** Viral / free-function entry: post-TO uses ThreadTag default. */ + template + requires (!is_explicit_post_target_v>) + PostingPromise(FirstArg &&, TailArgs &&...) noexcept + : PostingPromise() + {} + + /** Member non-viral: peel implicit object parameter. */ template - requires (!std::same_as, std::exception_ptr>) + requires ( + !std::same_as, std::exception_ptr> + && !is_explicit_post_target_v>) PostingPromise( ObjectArg &&, std::exception_ptr &_callerExceptionPtr, @@ -217,6 +249,48 @@ struct PostingPromise std::move(_callerLambda)) {} + /** Member non-viral with explicit post-TO target. */ + template + requires ( + !std::same_as, std::exception_ptr> + && !is_explicit_post_target_v>) + PostingPromise( + ObjectArg &&, + std::exception_ptr &_callerExceptionPtr, + std::function _callerLambda, + ExplicitPostTarget _calleePostTarget, + TailArgs &&...) noexcept + : PostingPromise( + _callerExceptionPtr, + std::move(_callerLambda), + std::move(_calleePostTarget)) + {} + + /** Member viral with explicit post-TO target. */ + template + requires ( + !std::same_as, std::exception_ptr> + && !is_explicit_post_target_v>) + PostingPromise( + ObjectArg &&, + ExplicitPostTarget _calleePostTarget, + TailArgs &&...) noexcept + : PostingPromise(std::move(_calleePostTarget)) + {} + + /** Member viral: peel implicit object parameter. */ + template + requires ( + !std::same_as, std::exception_ptr> + && !is_explicit_post_target_v> + && !is_explicit_post_target_v>) + PostingPromise( + ObjectArg &&, + FirstArg &&, + TailArgs &&...) noexcept + : PostingPromise() + {} + ~PostingPromise() noexcept { #ifdef CONFIG_LIBSSCL_DEBUG_CO @@ -255,6 +329,7 @@ struct PostingPromise std::function callerLambda; boost::asio::io_context &callerIoContext = sscl::ComponentThread::getSelf()->getIoContext(); + std::optional calleePostTarget; std::coroutine_handle<> selfSchedHandle; std::coroutine_handle callerSchedHandle; PromiseChainLink *callerChainLink = nullptr; @@ -280,33 +355,7 @@ struct TaggedPostingPromise : public PostingPromise, public PromiseReturnOps, T> { - TaggedPostingPromise() noexcept - : PostingPromise() - {} - - template - TaggedPostingPromise( - std::exception_ptr &_exceptionPtr, - std::function _callerLambda, - TailArgs &&... tailArgs) noexcept - : PostingPromise( - _exceptionPtr, - std::move(_callerLambda), - std::forward(tailArgs)...) - {} - - template - requires (!std::same_as, std::exception_ptr>) - TaggedPostingPromise( - ObjectArg &&, - std::exception_ptr &_exceptionPtr, - std::function _callerLambda, - TailArgs &&... tailArgs) noexcept - : PostingPromise( - _exceptionPtr, - std::move(_callerLambda), - std::forward(tailArgs)...) - {} + using PostingPromise::PostingPromise; auto initial_suspend() noexcept { @@ -314,8 +363,13 @@ struct TaggedPostingPromise std::cout << __func__ << ": " << std::this_thread::get_id() << " About to post selfSchedHandle to " << typeid(ThreadTag).name() << ".\n"; std::cout << __func__ << ": " << std::this_thread::get_id() << " Returning InitialSuspendPostingInvoker.\n"; #endif + boost::asio::io_context &postToIoContext = + this->calleePostTarget + ? this->calleePostTarget->ioContext + : ThreadTag::io_context(); + return typename PostingPromise::InitialSuspendPostingInvoker( - ThreadTag::io_context(), + postToIoContext, this->selfSchedHandle); } };