Skip to content

bridge: opt-in way for BridgeHandler::execute to wait for an in-flight async registration instead of failing "handler not bound" #841

Description

@Yaraslaut

Problem

With QtWebSocketBackendConfig::asyncRegistrationEnabled (required for a WASM client, whose main thread cannot spin the nested QEventLoop the blocking registration uses), a freshly constructed BridgeHandler is unbound until the server replies. BridgeHandler::execute() on a plain (non-keyed) action then fails fast with "handler not bound".

whenBound() (#60) makes this gateable, but every call site has to do it. In practice an embedder ends up writing the same wrapper around every handler it owns, because view models typically construct a handler and dispatch their first action in the same breath (e.g. on screen open):

template <typename Model>
class BoundHandler final : public morph::bridge::BridgeHandler<Model> {
    using Base = morph::bridge::BridgeHandler<Model>;
public:
    using Base::Base;
    template <typename Action>
    auto execute(Action action) -> morph::async::Completion<typename morph::model::ActionTraits<Action>::Result> {
        if (Base::isBound()) return Base::execute(std::move(action));
        // makeSettleable + whenBound().then(Base::execute(...)) + a liveness token
    }
};

The payload- and result-keyed branches of execute() already defer through attachHandlerAsync / ensureBoundAsync, so plain actions are the only route without a deferred path.

Proposal

An opt-in way for plain actions to wait for an in-flight registration instead of failing fast, e.g. one of:

  • a BridgeHandler constructor option / policy (UnboundPolicy::Defer vs the current FailFast), or
  • a separate executeWhenBound(action) on BridgeHandler.

Semantics: if isBound(), dispatch as today; if a registration is in flight, chain on whenBound() and dispatch on success; reject with the registration error (or a "not bound" error when whenBound() resolves false); drop the continuation if the handler is destroyed first.

The default stays fail-fast, so the documented trade in asyncRegistrationEnabled is unchanged for anyone relying on it.

Why in morph

The wrapper needs a liveness guard against the handler being destroyed before the bind settles, which morph can provide more cheaply and correctly from inside BridgeHandler (it already tracks binding lifetime) than each embedder can from outside.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: coreSubsystem: coreenhancementNew feature or requesttriage: validWell-framed; implement as written

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions