You're 100% correct.
The key bit of information is that emission of a signal connected to an object in a different thread via a queued or automatic connection results in posting a QMetaCallEvent
to the target object. It doesn't directly result in any calls at all.
The event loop running in the thread where the target object resides has toy deliver the event to the object - you can verify that by properly overriding the event
method and outputting a debug message when the event has the MetaCall
type. Remember to call the base class's method in your reimplementation. Since the event loop runs synchronously, it executes the calls serially. Thus no additional serialization-of-access means are necessary. It doesn't matter what thread the meta call event was posted from - the thread per se is not used for the posting, and the event queue will look the same whether a number of events was posted from one thread, or multiple threads.
It is the QObject::event
method that handles the QMetaCallEvent
and executes the call. The call may be to a slot, an invokable method, a constructor/destructor, or a functor that is to execute in a given object's thread context.