Makes a blocking backend reach its caller without blocking it — and without touching the backend.
More...
|
| | SynchronousBackendAdapter (std::shared_ptr< detail::IBackend > inner, ::morph::exec::IExecutor &blockingExec) |
| | Wraps inner, running every one of its verbs on blockingExec.
|
| | ~SynchronousBackendAdapter () override |
| | Waits for every queued and in-flight call; see "Ordering" above.
|
|
| SynchronousBackendAdapter (const SynchronousBackendAdapter &)=delete |
|
SynchronousBackendAdapter & | operator= (const SynchronousBackendAdapter &)=delete |
|
| SynchronousBackendAdapter (SynchronousBackendAdapter &&)=delete |
|
SynchronousBackendAdapter & | operator= (SynchronousBackendAdapter &&)=delete |
| detail::IBackend & | wrapped () const noexcept |
| | The wrapped backend.
|
| ::morph::async::Completion<::morph::exec::detail::ModelId > | bindModel (detail::BindRequest request, ::morph::exec::IExecutor &cbExec) override |
| | Acquires a model instance without blocking the calling thread.
|
| ::morph::async::Completion<::morph::exec::detail::ModelId > | promoteModel (detail::PromoteRequest request, ::morph::exec::IExecutor &cbExec) override |
| | Files an already-live instance under a key without blocking the caller.
|
| void | setOwner (const ::morph::exec::detail::OwnerAffinity &owner) override |
| | Records the caller's owner. Not forwarded: the wrapped backend's owner is this adapter's control strand.
|
| ::morph::exec::detail::ModelId | registerModel (const std::string &typeId, std::function< std::unique_ptr<::morph::model::detail::IModelHolder >()> factory) override |
| | Runs the wrapped backend's registerModel on the strand and waits for it.
|
| ::morph::exec::detail::ModelId | registerModelWithContext (const std::string &typeId, std::function< std::unique_ptr<::morph::model::detail::IModelHolder >()> factory, std::string_view contextKey) override |
| | Runs the wrapped backend's registerModelWithContext on the strand and waits for it.
|
| void | assignPrimary (::morph::exec::detail::ModelId mid, const std::string &typeId, std::string_view primary) override |
| | Runs the wrapped backend's assignPrimary on the strand and waits for it.
|
| std::vector< std::string > | listInstances (const std::string &typeId) override |
| | Runs the wrapped backend's listInstances on the strand and waits for it.
|
| void | deregisterModel (::morph::exec::detail::ModelId mid) override |
| | Queues the wrapped backend's deregisterModel.
|
| ::morph::async::Completion< std::shared_ptr< void > > | execute (::morph::exec::detail::ModelId mid, detail::ActionCall call, ::morph::exec::IExecutor *cbExec) override |
| | Queues the dispatch on the strand and returns its Completion at once.
|
| void | executeInto (::morph::exec::detail::ModelId mid, detail::ActionCall call, ::morph::exec::IExecutor *cbExec, std::shared_ptr<::morph::async::detail::ISettleSink > sink) override |
| | Queues the wrapped backend's executeInto on the strand.
|
| void | notifyBackendChanged () override |
| | Queues the wrapped backend's notifyBackendChanged.
|
| void | cancelPending (const std::exception_ptr &exc) override |
| | Rejects the binds this adapter produced, then queues the wrapped backend's own cancelPending.
|
| void | setReconnectHandler (std::function< void()> handler, ::morph::exec::IExecutor *exec) override |
| | Queues the installation on the wrapped backend.
|
| void | setEventHandler (std::function< void(::morph::wire::EventBody)> handler, ::morph::exec::IExecutor *exec) override |
| | Queues the installation on the wrapped backend.
|
| void | setConnectHandler (const std::function< void()> &handler) override |
| | Queues the installation on the wrapped backend.
|
| void | setDisconnectHandler (const std::function< void()> &handler) override |
| | Queues the installation on the wrapped backend.
|
| void | setSession (::morph::session::Context session) override |
| | Queues the session on the wrapped backend.
|
Makes a blocking backend reach its caller without blocking it — and without touching the backend.
A decorator, not a base class: it wraps an existing IBackend and runs every one of its verbs on one control strand over an executor this adapter names. That strand is the wrapped backend's one owner, so a backend that keeps its state without a lock (LocalBackend) stays correct behind it, and the caller's thread never pays for a blocking control call.
- What it does and does not change
- The wrapped backend still blocks — nothing here makes a nested event loop or a socket round trip non-blocking. What changes is which thread pays: bindModel returns at once with an unsettled Completion, settled from the strand when the wrapped backend's bind has run there, and execute returns at once too. A single-threaded WASM main thread has no such executor to offer and is therefore not what this adapter is for; that case needs a backend with a genuinely non-blocking path.
- Why the executor is required rather than optional
- "Where does the blocking happen" is the only question this class exists to answer, so it is a constructor parameter with no default.
- Ordering
- Every verb is queued on the one strand, so the wrapped backend sees them one at a time and in the order they were issued. ~SynchronousBackendAdapter waits for every queued and in-flight call, so a reply can never land in a destroyed adapter; the executor must therefore still be running tasks when this adapter is destroyed (see docs/spec/concurrency_and_lifetimes.md, "Destruction ordering"). The single-threaded WebAssembly build has no thread to wait for: there the calls still queued are dropped.
- Owner
- The adapter itself belongs to its caller's owner (setOwner): bindModel, promoteModel and cancelPending keep the list of pending binds there, without a lock. The synchronous verbs (registerModel, registerModelWithContext, assignPrimary, listInstances) wait for the strand, so they must not be called from the executor's only thread.
Definition at line 694 of file backend.hpp.
| void morph::backend::SynchronousBackendAdapter::cancelPending |
( |
const std::exception_ptr & | exc | ) |
|
|
inlineoverride |
Rejects the binds this adapter produced, then queues the wrapped backend's own cancelPending.
A bindModel here settles from a task on the strand, holding a promise the wrapped backend never sees, so the wrapped backend's cancelPending reaches nothing of it: without this a bind cancelled by ~Bridge or by switchBackend would go on to resolve successfully afterwards.
Each queued bind carries a cancellation flag beside its promise, set here before the promise is rejected: a bind still queued sees it when it reaches the head of the strand and never reaches the wrapped backend, so no instance is created that nothing will deregister. A bind already running on the strand cannot be recalled; its completion stays rejected. The wrapped backend's own pending calls are cancelled on the strand, in order with everything queued before this call.
- Parameters
-
| exc | Exception delivered to every still-pending completion, this adapter's own and then the wrapped backend's. |
Definition at line 878 of file backend.hpp.