fix: marshal provider events onto the source thread#68
Merged
Conversation
ModuleProxy's event listener emitted eventResponse directly on whatever thread the module fired the event from (its worker/FFI thread). QtRemoteObjects then serialized and sent the event from that foreign thread, racing the source socket against a method reply being sent from the source thread, which silently dropped the reply. This is why a method that emits an event mid-call never returns to the caller (e.g. delivery_module start(), which emits connectionStateChanged as the node connects) while a method that emits nothing (createNode) returns fine. Marshal the emission onto the ModuleProxy's own thread via a queued invocation so events and method replies are serialized on the single thread QtRemoteObjects expects to own the source. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
QueuedConnection deferred every emission, breaking same-thread callers that emit-then-assert and crashing when a queued lambda outlived the object. AutoConnection invokes synchronously when already on the source thread and only queues cross-thread emissions (the actual fix); passing 'this' as context cancels a queued call if the object is destroyed first.
iurimatias
approved these changes
May 25, 2026
This was referenced May 25, 2026
iurimatias
pushed a commit
to logos-co/logos-view-module-runtime
that referenced
this pull request
May 25, 2026
Picks up logos-co/logos-cpp-sdk#68 — marshals provider event emission onto the remoting source thread. Part of propagating the fix to the host runtime.
iurimatias
pushed a commit
to logos-co/logos-liblogos
that referenced
this pull request
May 25, 2026
Picks up logos-co/logos-cpp-sdk#68 — marshals provider event emission onto the remoting source thread. Host-runtime step toward fixing the start hang.
iurimatias
pushed a commit
to logos-co/logos-standalone-app
that referenced
this pull request
May 25, 2026
…eading fix) Picks up logos-co/logos-cpp-sdk#68 across the host runtime (the ui-host + logoscore where ModuleProxy runs), via the merged view-module-runtime and liblogos bumps. Completes the host chain for the start hang.
This was referenced May 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Addresses logos-co/logos-delivery-module#44
Problem
A module method that emits an event mid-call never returns to the caller over remoting, while a method that emits nothing returns fine.
Concretely:
delivery_module.start()emitsconnectionStateChangedas the node connects; the module logsstart completed with success, but the caller's RPC times out.createNode()(no event) works.Cause
ModuleProxy's event listener emitseventResponsedirectly on whatever thread the module fired the event from — its worker/FFI thread, not the remoting source's thread. QtRemoteObjects then serializes and sends that event from the foreign thread, racing the source socket against the method reply being sent from the source thread, which silently drops the reply.Fix
Marshal the emission onto the
ModuleProxy's own thread (QMetaObject::invokeMethod(this, …, Qt::QueuedConnection)) so events and replies are serialized on the single thread QtRemoteObjects expects to own the source.Found by bisecting a downstream consumer hang to the universal-interface migration; verified the asymmetry (event-emitting call hangs, event-free call works) matches this race.