Is there any cases where QObject will not emit a destroyed signal?
-
I have a situation where it's absolutely critical that I known when an object is destroyed. No matter how or when it happens. Since it's so critical that I know, I wanted to know if there are any edge cases where QObject will not emit the destroyed signal upon being destroyed. Has anyone encountered any edge cases like this?
Edit: [VRonin] removed links
-
@MalachiCole
there shouldn't be. If you take a look at the source code:935 QObject::~QObject() 936 { 937 Q_D(QObject); 938 d->wasDeleted = true; 939 d->blockSig = 0; // unblock signals so we always emit destroyed() 940 941 QtSharedPointer::ExternalRefCountData *sharedRefcount = d->sharedRefcount.loadRelaxed(); 942 if (sharedRefcount) { 943 if (sharedRefcount->strongref.loadRelaxed() > 0) { 944 qWarning("QObject: shared QObject was deleted directly. The program is malformed and may crash."); 945 // but continue deleting, it's too late to stop anyway 946 } 947 948 // indicate to all QWeakPointers that this QObject has now been deleted 949 sharedRefcount->strongref.storeRelaxed(0); 950 if (!sharedRefcount->weakref.deref()) 951 delete sharedRefcount; 952 } 953 954 if (!d->isWidget && d->isSignalConnected(0)) { 955 emit destroyed(this); 956 } 957 958 if (d->declarativeData) { 959 if (static_cast<QAbstractDeclarativeDataImpl*>(d->declarativeData)->ownedByQml1) { 960 if (QAbstractDeclarativeData::destroyed_qml1) 961 QAbstractDeclarativeData::destroyed_qml1(d->declarativeData, this); 962 } else { 963 if (QAbstractDeclarativeData::destroyed) 964 QAbstractDeclarativeData::destroyed(d->declarativeData, this); 965 } 966 } 967 968 QObjectPrivate::ConnectionData *cd = d->connections.loadRelaxed(); 969 if (cd) { 970 if (cd->currentSender) { 971 cd->currentSender->receiverDeleted(); 972 cd->currentSender = nullptr; 973 } 974 975 QBasicMutex *signalSlotMutex = signalSlotLock(this); 976 QBasicMutexLocker locker(signalSlotMutex); 977 978 // disconnect all receivers 979 int receiverCount = cd->signalVectorCount(); 980 for (int signal = -1; signal < receiverCount; ++signal) { 981 QObjectPrivate::ConnectionList &connectionList = cd->connectionsForSignal(signal); 982 983 while (QObjectPrivate::Connection *c = connectionList.first.loadRelaxed()) { 984 Q_ASSERT(c->receiver.loadAcquire()); 985 986 QBasicMutex *m = signalSlotLock(c->receiver.loadRelaxed()); 987 bool needToUnlock = QOrderedMutexLocker::relock(signalSlotMutex, m); 988 if (c->receiver.loadAcquire()) { 989 cd->removeConnection(c); 990 Q_ASSERT(connectionList.first.loadRelaxed() != c); 991 } 992 if (needToUnlock) 993 m->unlock(); 994 } 995 } 996 997 /* Disconnect all senders: 998 */ 999 while (QObjectPrivate::Connection *node = cd->senders) { 1000 Q_ASSERT(node->receiver.loadAcquire()); 1001 QObject *sender = node->sender; 1002 // Send disconnectNotify before removing the connection from sender's connection list. 1003 // This ensures any eventual destructor of sender will block on getting receiver's lock 1004 // and not finish until we release it. 1005 sender->disconnectNotify(QMetaObjectPrivate::signal(sender->metaObject(), node->signal_index)); 1006 QBasicMutex *m = signalSlotLock(sender); 1007 bool needToUnlock = QOrderedMutexLocker::relock(signalSlotMutex, m); 1008 //the node has maybe been removed while the mutex was unlocked in relock? 1009 if (node != cd->senders) { 1010 // We hold the wrong mutex 1011 Q_ASSERT(needToUnlock); 1012 m->unlock(); 1013 continue; 1014 } 1015 1016 QObjectPrivate::ConnectionData *senderData = sender->d_func()->connections.loadRelaxed(); 1017 Q_ASSERT(senderData); 1018 1019 QtPrivate::QSlotObjectBase *slotObj = nullptr; 1020 if (node->isSlotObject) { 1021 slotObj = node->slotObj; 1022 node->isSlotObject = false; 1023 } 1024 1025 senderData->removeConnection(node); 1026 if (needToUnlock) 1027 m->unlock(); 1028 1029 if (slotObj) { 1030 locker.unlock(); 1031 slotObj->destroyIfLastRef(); 1032 locker.relock(); 1033 } 1034 } 1035 1036 // invalidate all connections on the object and make sure 1037 // activate() will skip them 1038 cd->currentConnectionId.storeRelaxed(0); 1039 } 1040 if (cd && !cd->ref.deref()) 1041 delete cd; 1042 d->connections.storeRelaxed(nullptr); 1043 1044 if (!d->children.isEmpty()) 1045 d->deleteChildren(); 1046 1047 #if QT_VERSION < 0x60000 1048 qt_removeObject(this); 1049 #endif 1050 if (Q_UNLIKELY(qtHookData[QHooks::RemoveQObject])) 1051 reinterpret_cast<QHooks::RemoveQObjectCallback>(qtHookData[QHooks::RemoveQObject])(this); 1052 1053 Q_TRACE(QObject_dtor, this); 1054 1055 if (d->parent) // remove it from parent object 1056 d->setParent_helper(0); 1057 }
you will see, that even if you have set blockSignals to true, the destroyed signal will be emitted.
-
@J-Hilk , @MalachiCole
I haven't read through the code, and don't know what exactly it calls, but since the OP asks about "edge case" and "absolutely critical", is there any kind of indirect dynamic memory allocation going on here at all? Just that it always possible that the application runs out of memory while executing the code, a slight possibility to be aware of. No more than that.