Skip to content

Model registration is synchronous only; blocks on WASM main thread #26

Description

@Yaraslaut

Summary

Bridge registers a model by calling IBackend::registerModel, which is synchronous — it
must have the server-assigned ModelId before it returns. QtWebSocketBackend implements
that by parking a nested QEventLoop until the reply arrives (sendSync).

On a WASM main thread Qt refuses to do this. The nested loop trips

WaitForMoreEvents is not supported on the main thread without asyncify

and aborts the module. So a Qt/QML client compiled to WebAssembly cannot register a single
model against a remote backend — the first registerModel kills the page.

Why the existing escape hatches don't cover it

  • waitForConnected() has the same problem: blocking the browser event loop hangs the page.
  • Building with asyncify is a heavy, whole-program cost (size and speed) to work around one
    blocking call on one code path.
  • Deferring registration until "later" doesn't help — the call is synchronous whenever it runs.

Suggested fix

Give IBackend an optional asynchronous registration path, defaulting to "unsupported" so
every current backend is unaffected:

/// Returns false if this backend has no async path; Bridge then uses registerModel().
virtual bool registerModelAsync(
    const std::string& typeId,
    std::function<std::unique_ptr<::morph::model::detail::IModelHolder>()> factory,
    const std::string& contextKey,
    std::function<void(::morph::exec::detail::ModelId)> onRegistered,
    std::function<void(const std::string&)> onError)
{
    return false;   // default: no async path here
}

A socket backend overrides it to send the register envelope and settle later. No protocol
change is needed — the server already echoes callId on register replies, so the reply can
be matched through the same pending-call map that execute uses.

Bridge then prefers the async path when the backend offers one and falls back to the
synchronous call otherwise, which keeps every existing embedder on today's behaviour.

Exactly one of onRegistered / onError should be invoked, on the backend's own thread,
unless the backend is destroyed first.

I have this working locally against a WASM client and am happy to open a PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions