Files
salmanoff/docs/prompts/post-to-and-back-in-invokables.md
T

3.3 KiB

Post-to and post-back in posting invokers

Two targets

Posting coroutines (TaggedPostingPromise / *ViralPostingInvoker / *NonViralPostingInvoker) use two different io_context targets:

Direction When Target
Post-TO initial_suspend Where the callee coroutine starts running
Post-BACK final_suspend Where the caller resumes after co_await

Post-back always uses callerIoContext, initialized from ComponentThread::getSelf()->getIoContext() at the co_await site. That behavior is unchanged.

Post-to defaults to the static ThreadTag::io_context() for the invoker alias (e.g. MrnttThreadTag, BodyThreadTag).

Static post-TO (default)

Existing coroutines need no signature change. TaggedPostingPromise::initial_suspend() posts the callee handle to ThreadTag::io_context().

Dynamic post-TO (ExplicitPostTarget)

Opt in by adding sscl::co::ExplicitPostTarget to the coroutine parameter list. The promise constructor extracts it into std::optional<ExplicitPostTarget> calleePostTarget; initial_suspend() posts to calleePostTarget->ioContext instead of ThreadTag::io_context().

// Viral member example: run on body thread, post back to mrntt caller
BodyViralPostingInvoker<bool> Body::initializeCReq(
    sscl::co::ExplicitPostTarget postTarget)
{
    co_await ...;
}

// Non-viral example
reattachKnownListCReq(
    sscl::co::ExplicitPostTarget{someIoContext},
    exceptionPtr,
    callback);

The caller must ensure the referenced io_context outlives the callee frame.

DeviceManager attach/detach (mrntt orchestrator, library on body/world)

DeviceManager::attachStimBuffDeviceCReq and detachStimBuffDeviceCReq stay MrnttViralPostingInvoker on the marionette thread. They co_await library attachDeviceCReq / detachDeviceCReq (DynamicViralPostingInvoker) with ExplicitPostTarget first:

  • edev (sensorType == 'e') → world thread
  • otherwise → body thread

Post-TO runs the library coroutine on that target; post-back resumes the DeviceManager coroutine on mrntt. Attach also passes componentThread for device/producer binding inside the library.

auto targetThread = threadForDeviceOp(*spec);
co_return co_await lib.stimBuffApiDesc.sal_mgmt_libOps.attachDeviceCReq(
    sscl::co::ExplicitPostTarget{targetThread->getIoContext()},
    spec,
    targetThread);

Detach uses the same threadForDeviceOp rule (not producer lookup).

Constructor discrimination

PostingPromise mirrors the existing implicit-this peel:

  • Member coroutines pass the object parameter first; it is discarded.
  • When present, ExplicitPostTarget is the next parameter (before non-viral exception_ptr& and completion callback).
  • Free non-viral dynamic: ExplicitPostTarget, then exception_ptr&, then callback.
  • ExplicitPostTarget is recognized by type (is_explicit_post_target_v) and stored in calleePostTarget.
  • ObjectArg requires !is_explicit_post_target_v so the wrapper is never mistaken for thisptr.

Timing invariant

Post-TO and post-BACK work must run from InitialSuspendPostingInvoker::await_suspend and FinalSuspendPostingInvoker::await_suspend, not synchronously inside initial_suspend() / final_suspend() bodies. See comments in postingPromise.h.