# 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 calleePostTarget`; `initial_suspend()` posts to `calleePostTarget->ioContext` instead of `ThreadTag::io_context()`. ```cpp // Viral member example: run on body thread, post back to mrntt caller BodyViralPostingInvoker 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. ```cpp 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`.