On Friday, 5 January 2024 12:56:03 -03 Alexander Dyagilev wrote:
> Hello,
> 
> Let's suppose we have an object A with slot AA living in thread AAA.
> 
> Let's suppose we have an object B with signal BB living in thread BBB.
> 
> Let's suppose we've created a direct signal/slot connection from BB to AA.
> 
> Question: is it possible that BB can call AA after it was destroyed? I mean
> race condition bug (we have 2 threads). I.e. is connection 100% always
> destroyed before A is destroyed?

The connection is, but the answer is more complicated than that.

The signal-slot connection mechanism is thread-safe. You can connect and 
disconnect signals to slots while emitting them. Because the signal activation 
mechanism locks the receiver's connection list, it will either find the 
connection intact or it will find the connection torn down. So if you race 
connections and disconnections with emissions, you will find that some slots 
may be called or not, depending on timing. This is the answer to the question 
asked above, but not what you wanted to know.

This protection offers a modicum of protection against deletion too, but that 
is not guaranteed. So my recommendation is that you invest in a mutex and stop 
reading this reply.

This lock does apply in QObject's destructor, so again there's a 
synchronisation between the emission in thread BBV and the deletion in thread 
AAA. If the connection were Auto or QueuedConnection, that would result in an 
event being posted to the object, which would either be delivered *before* the 
deletion began or would be discarded by the deletion so no delivery happens. 
This means with Auto or QueuedConnections, you can't really race the deletion.

> Well, it seems I just understood. BBB see the connection and starts calling
> AA. Then, CPU switch occurs. Thread BBB suspended and AAA resuming. AAA
> destroys A. Then BBB resuming and continues to call AA. And we get a crash.
> Am I right here?

In the case of DirectConnection, no such protection occurs because QObject 
must drop the locks before invoking your slot. It's the same as your getting 
the pointer address for AA from somewhere else and directly calling it. YOU 
must ensure that the object stays alive until your signal emission has 
finished. That's a mutex.

Just so we're clear, there's also the case of the deletion happening while the 
execution is happening, with no context switches. It's possible the signal 
activation code blocked the QObject destructor from proceeding... but at that 
point, object AA is no longer of type A, but of type QObject. This is already 
wrong.

That means you must prevent object BB from starting its deletion in the first 
place, not from completing it. That is, a mutex inside the object is usually 
wrong: it must be outside.

-- 
Thiago Macieira - thiago.macieira (AT) intel.com
  Cloud Software Architect - Intel DCAI Cloud Engineering

Attachment: smime.p7s
Description: S/MIME cryptographic signature

_______________________________________________
Interest mailing list
Interest@qt-project.org
https://lists.qt-project.org/listinfo/interest

Reply via email to