mirror of
https://github.com/logos-co/logos-cpp-sdk.git
synced 2026-08-31 01:31:10 +00:00
fix: marshal provider events onto the source thread (#68)
* fix: marshal provider events onto the source thread 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> * docs: generalize the threading comment * fix: use AutoConnection so same-thread emits stay synchronous 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. --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
4e20614cda
commit
d77c3dd616
+14
-1
@@ -9,7 +9,20 @@ ModuleProxy::ModuleProxy(LogosProviderObject* provider, QObject* parent)
|
||||
if (m_provider) {
|
||||
m_provider->setEventListener([this](const QString& eventName, const QVariantList& data) {
|
||||
qDebug() << "[LogosProviderObject] ModuleProxy: forwarding event" << eventName << "as Qt signal";
|
||||
emit eventResponse(eventName, data);
|
||||
// Events may be fired from any thread (e.g. a module's worker/FFI
|
||||
// thread), but this object is the QtRemoteObjects source and must be
|
||||
// driven from its own thread. Emitting directly from a foreign
|
||||
// thread runs QtRO's source serialization there, racing the source
|
||||
// socket against a reply being sent from the source thread, which
|
||||
// can silently drop the reply. AutoConnection keeps same-thread
|
||||
// callers synchronous (the common case) and only queues the
|
||||
// emission when it arrives from another thread, so events and
|
||||
// replies stay serialized on the thread QtRO expects to own the
|
||||
// source. Passing `this` as the context also cancels a queued
|
||||
// emission if this object is destroyed first.
|
||||
QMetaObject::invokeMethod(this, [this, eventName, data]() {
|
||||
emit eventResponse(eventName, data);
|
||||
}, Qt::AutoConnection);
|
||||
});
|
||||
qDebug() << "[LogosProviderObject] ModuleProxy: created, wrapping LogosProviderObject"
|
||||
<< m_provider->providerName();
|
||||
|
||||
Reference in New Issue
Block a user