Archive for July, 2007

Beyond Signal Safety

I’d like to expand on the topic of Signal Safety (or SS, part of the Delta Object Rules).  SS states that an object should be safe to delete within a slot connected to one of its signals.  However, this definition should be expanded to include other object operations besides deleting.  For example, suppose an object has a reset() function which brings it back to a default state (I tend to write objects with such a function).  It should be safe to call reset() as the result of a signal.

I’ve known about this variant of SS for awhile, but I’ve never written about it until now.  It comes to my mind tonight because I’m seeing a couple of possible problem spots in QCA during my final code cleaning, and I recall just recently fixing a problem like this in Psi.  Revision 731 of the Psi SVN states: “We can’t call read() while in a QTcpSocket error slot.”  Now that’s an odd one, isn’t it?  Calling read() doesn’t seem so harmful.  It’s not like we’re deleting the object or doing something overly destructive.  Trolltech’s position is that you should not assume an object can be deleted during a signal, and that you should use deleteLater() to be safe.  However, what can their position be for this problem?  Call readLater instead of read?  There is no such thing as readLater.  It seems the only sane answers are that Trolltech should either make it possible to call read() during the error() signal, or they should document that you cannot make that call at that time.

Like the Signal Retraction (SR) problem, SS of non-delete methods borders on theoretical. I say that because I think most Qt-based code is absolutely infested with the problem, but we manage to get by anyway. Sure, it may crop up on rare occasions (the QTcpSocket one), but for the most part we simply get lucky and avoid it.  Like SR, it is also a royal pain to fix in some cases, such as when you emit two signals in a row:

emit sig1();
emit sig2();

Every so often I may use QPointer (or QGuardedPtr in Qt 3) to solve the problem like this:

QPointer<QObject*> self = this;
emit sig1();
if(!self)
    return; // return or otherwise get the heck out of the call stack
emit sig2();

Okay, classical SS solved now. But what happens if instead of deleting the object during sig1(), the user calls some other method? Depending on the state changes caused by that method, it may not be appropriate to emit sig2() anymore. According to my expanded definition of SS, we may still have a problem here. Especially if that method is a reset() function, this may introduce an SR problem (sig2() being emitted when the object is expected to be in an inactive state).

There’s always the queued approach:

QMetaObject::invokeMethod(this, "sig1", Qt::QueuedConnection);
QMetaObject::invokeMethod(this, "sig2", Qt::QueuedConnection);

Now SS is solved, but you’re going to have SR problems.

Lately I’m becoming more fond of queued signals, because they solve all SS and DS (Delayed Signals) problems. However, they introduce an SR mess. One solution to that is to bounce your signals off of an intermediate “relay” QObject. You queue your signals to the relay object, and the relay object emits direct. This way you get the queued effect, but you can destroy the relay object at any time to retract your signals.

Comments

Bad Behavior has blocked 557 access attempts in the last 7 days.