From 6250349b0b0a48a23b6cd847e70330e90c812bcc Mon Sep 17 00:00:00 2001 From: Alexander Golubev Date: Sat, 16 Mar 2024 08:14:54 +0300 Subject: Make TQConnectionList shared This commit fixes crashes caused by modifications of TQobject connections from the slots invoked by its signals. Up to recent time those were hidden by implementation specificities in TQObject::activate_signal(), so they would appear only under quite peculiar circumstances, but after attempts to de-duplicate the code they surfaced up. Besides that the patch de-duplicates some code in TQObject::disconnectInternal(). Signed-off-by: Alexander Golubev --- src/kernel/ntqsignalslotimp.h | 19 ++++++- src/kernel/qobject.cpp | 113 ++++++++++++++++++++++-------------------- 2 files changed, 77 insertions(+), 55 deletions(-) diff --git a/src/kernel/ntqsignalslotimp.h b/src/kernel/ntqsignalslotimp.h index 375f8eaa..7e698919 100644 --- a/src/kernel/ntqsignalslotimp.h +++ b/src/kernel/ntqsignalslotimp.h @@ -47,7 +47,7 @@ #include "ntqptrvector.h" #endif // QT_H -class TQ_EXPORT TQConnectionList : public TQPtrList +class TQ_EXPORT TQConnectionList : public TQPtrList, public TQShared { public: TQConnectionList() : TQPtrList() {} @@ -55,6 +55,23 @@ public: ~TQConnectionList() { clear(); } TQConnectionList &operator=(const TQConnectionList &list) { return (TQConnectionList&)TQPtrList::operator=(list); } + + TQConnectionList *deepCopy() const { + TQConnectionList *rv = new TQConnectionList; + TQ_CHECK_PTR( rv ); + for( TQConnectionList::ConstIterator it = this->constBegin(), + end = this->constEnd(); + it!=end;++it + ) { + TQConnection *c = + new TQConnection((*it)->object(), (*it)->member(), + (*it)->memberName(), (*it)->memberType()); + + TQ_CHECK_PTR( c ); + rv->append(c); + } + return rv; + } }; class TQ_EXPORT TQConnectionListIt : public TQPtrListIterator diff --git a/src/kernel/qobject.cpp b/src/kernel/qobject.cpp index c2907d55..9dc8f36b 100644 --- a/src/kernel/qobject.cpp +++ b/src/kernel/qobject.cpp @@ -775,6 +775,9 @@ TQObject::~TQObject() removeObjFromList( obj->senderObjects, this ); } } + if( clist->deref() ) { + delete clist; + } } delete connections; connections = 0; @@ -1610,7 +1613,6 @@ TQConnectionList *TQObject::receivers( int signal ) const if ( !connections ) { TQObject* that = (TQObject*) this; that->connections = new TQSignalVec( signal+1 ); - that->connections->setAutoDelete( TRUE ); } if ( !connections->at( signal ) ) { TQConnectionList* clist = new TQConnectionList; @@ -2247,7 +2249,6 @@ void TQObject::connectInternal( const TQObject *sender, int signal_index, const if ( !s->connections ) { // create connections lookup table s->connections = new TQSignalVec( signal_index+1 ); TQ_CHECK_PTR( s->connections ); - s->connections->setAutoDelete( TRUE ); } TQConnectionList *clist = s->connections->at( signal_index ); @@ -2256,6 +2257,13 @@ void TQObject::connectInternal( const TQObject *sender, int signal_index, const TQ_CHECK_PTR( clist ); clist->setAutoDelete( TRUE ); s->connections->insert( signal_index, clist ); + } else if( clist->TQShared::count != 1 ) { // if we are not the sole owner, make a deep copy of the list + TQConnectionList *clistNew = clist->deepCopy(); + TQ_CHECK_PTR( clistNew ); + clistNew->setAutoDelete( TRUE ); + clist->deref(); + clist = clistNew; + s->connections->insert( signal_index , clist ); } TQMetaObject *rmeta = r->metaObject(); @@ -2273,6 +2281,7 @@ void TQObject::connectInternal( const TQObject *sender, int signal_index, const TQConnection *c = new TQConnection( r, member_index, rm ? rm->name : "qt_invoke", membcode ); TQ_CHECK_PTR( c ); clist->append( c ); + if ( !r->senderObjects ) { // create list of senders #ifdef TQT_THREAD_SUPPORT r->d->senderObjectListMutex->lock(); @@ -2487,69 +2496,48 @@ bool TQObject::disconnectInternal( const TQObject *sender, int signal_index, return FALSE; bool success = FALSE; - TQConnectionList *clist; - TQConnection *c; - if ( signal_index == -1 ) { - for ( int i = 0; i < (int) s->connections->size(); i++ ) { - clist = (*s->connections)[i]; // for all signals... - if ( !clist ) - continue; - c = clist->first(); - while ( c ) { // for all receivers... - if ( r == 0 ) { // remove all receivers - if (c->object()->senderObjects) { -#ifdef TQT_THREAD_SUPPORT - TQMutexLocker locker(c->object()->senderObjects->listMutex); -#endif // TQT_THREAD_SUPPORT - removeObjFromList( c->object()->senderObjects, s ); - } - success = TRUE; - c = clist->next(); - } else if ( r == c->object() && - ( (member_index == -1) || - ((member_index == c->member()) && (c->memberType() == membcode)) ) ) { - if (c->object()->senderObjects) { -#ifdef TQT_THREAD_SUPPORT - TQMutexLocker locker(c->object()->senderObjects->listMutex); -#endif // TQT_THREAD_SUPPORT - removeObjFromList( c->object()->senderObjects, s, TRUE ); - } - success = TRUE; - clist->remove(); - c = clist->current(); - } else { - c = clist->next(); - } - } - if ( r == 0 ) // disconnect all receivers - s->connections->insert( i, 0 ); - } - } else { - clist = s->connections->at( signal_index ); + + // A helper lambdata to disconnect the given signal + auto disconnecSignal = [s, r, membcode, member_index](int signal_index) -> bool { + bool rv = FALSE; + TQConnectionList *clist = s->connections->at( signal_index ); if ( !clist ) return FALSE; - c = clist->first(); + TQConnection *c = clist->first(); while ( c ) { // for all receivers... if ( r == 0 ) { // remove all receivers - if (c->object()->senderObjects) { + if (c->object()->senderObjects) { #ifdef TQT_THREAD_SUPPORT - TQMutexLocker locker(c->object()->senderObjects->listMutex); + TQMutexLocker locker(c->object()->senderObjects->listMutex); #endif // TQT_THREAD_SUPPORT - removeObjFromList( c->object()->senderObjects, s, TRUE ); - } - success = TRUE; + removeObjFromList( c->object()->senderObjects, s, TRUE ); + } + rv = TRUE; c = clist->next(); } else if ( r == c->object() && ( (member_index == -1) || - ((member_index == c->member()) && (c->memberType() == membcode)) ) ) { - if (c->object()->senderObjects) { + ((member_index == c->member()) && (c->memberType() == membcode)) ) ) + { + if (c->object()->senderObjects) { #ifdef TQT_THREAD_SUPPORT - TQMutexLocker locker(c->object()->senderObjects->listMutex); + TQMutexLocker locker(c->object()->senderObjects->listMutex); #endif // TQT_THREAD_SUPPORT - removeObjFromList( c->object()->senderObjects, s, TRUE ); - } - success = TRUE; + removeObjFromList( c->object()->senderObjects, s, TRUE ); + } + rv = TRUE; + + // if we are not the sole owner, deep-copy the list and use it instead of rhe + // current one. This needed in case we are already inside a slot which uses it. + if( clist->TQShared::count != 1 ) { + TQConnectionList *clistNew = clist->deepCopy(); + TQ_CHECK_PTR( clistNew ); + clistNew->setAutoDelete( TRUE ); + clistNew->at(clist->at()); // make current point to the same element + clist->deref(); + clist = clistNew; + s->connections->insert( signal_index , clist ); + } clist->remove(); c = clist->current(); } else { @@ -2557,8 +2545,22 @@ bool TQObject::disconnectInternal( const TQObject *sender, int signal_index, } } if ( r == 0 ) { // disconnect all receivers + if( clist->deref() ) { + delete clist; + } s->connections->insert( signal_index, 0 ); } + return rv; + }; + + if ( signal_index == -1 ) { + for ( int i = 0; i < (int) s->connections->size(); i++ ) { + if( disconnecSignal( i ) ) { + success = TRUE; + } + } + } else { + success = disconnecSignal( signal_index ); } return success; } @@ -2772,7 +2774,7 @@ void TQObject::activate_signal( TQConnectionList *clist, TQUObject *o ) if ( !clist ) { return; } - + clist->ref(); #ifndef TQT_NO_PRELIMINARY_SIGNAL_SPY if ( tqt_preliminary_signal_spy ) { qt_spy_signal( this, connections->findRef( clist), o ); @@ -2861,6 +2863,9 @@ void TQObject::activate_signal( TQConnectionList *clist, TQUObject *o ) if (sol) sol->listMutex->unlock(); #endif // TQT_THREAD_SUPPORT } + if( clist->deref() ) { + delete clist; + } } /*! -- cgit v1.2.1