From 08ac5cce693a7634341db8f4da4d729a64b0af46 Mon Sep 17 00:00:00 2001 From: Michele Calgaro Date: Fri, 23 Aug 2024 23:40:14 +0900 Subject: [PATCH] Improve handling of the global post event list in order to minimize possible crashes on exit. Key points: 1. a TQPostEventList can now have an associated mutex, which is used in case of the global post event list (GPEL) 2. the lifetime of the GPEL is no longer associated to the lifetime of the TQApplication object, but rather extended to the lifetime of the main thread. The GPEL is a static local initialized on first access and destroyed when the global static object destructor is invoked 3. access to the GPEL after the TQApplication object has been destroyed has been minimized by protecting calls in ~TQObject() and ~TQWidget(). 4. special care was taken not to affect performances or unnecessarily create tons of unused TQMutexes This replaces PR #182. Technically it is still possibly unsafe due to the order of destruction of the globat static objects not being guaranteed across multiple compilation units, but the aforementioned changes should minimize (possible to zero) the chances of a SEGV happening. Signed-off-by: Michele Calgaro (cherry picked from commit ddce1c91f8582885497b463b24bac59f6fdfdf63) --- src/kernel/qapplication.cpp | 159 +++++++++++++++++++----------------- src/kernel/qobject.cpp | 5 +- src/kernel/qwidget.cpp | 5 +- 3 files changed, 92 insertions(+), 77 deletions(-) diff --git a/src/kernel/qapplication.cpp b/src/kernel/qapplication.cpp index 45016b04..fafb0fc0 100644 --- a/src/kernel/qapplication.cpp +++ b/src/kernel/qapplication.cpp @@ -379,10 +379,11 @@ int TQApplication::composedUnicode = 0; TQMutex *TQApplication::tqt_mutex = 0; TQMutex *tqt_sharedStringMutex = 0; TQ_EXPORT TQMutex * tqt_sharedMetaObjectMutex = 0; + #ifdef QT_USE_GLIBMAINLOOP TQMutex *tqt_timerListMutex = 0; #endif // QT_USE_GLIBMAINLOOP -static TQMutex *postevent_mutex = 0; + static TQt::HANDLE tqt_application_thread_id = 0; TQ_EXPORT TQt::HANDLE tqt_get_application_thread_id() { @@ -597,7 +598,7 @@ static TQCoreApplicationThread tqt_main_thread; // Definitions for posted events struct TQPostEvent { TQPostEvent( TQObject *r, TQEvent *e ): receiver( r ), event( e ) {} - ~TQPostEvent() { delete event; } + ~TQPostEvent() { delete event; } TQObject *receiver; TQEvent *event; }; @@ -605,36 +606,68 @@ struct TQPostEvent { class TQ_EXPORT TQPostEventList : public TQPtrList { public: - TQPostEventList() : TQPtrList() {} - TQPostEventList( const TQPostEventList &list ) : TQPtrList(list) {} - ~TQPostEventList() { clear(); } - TQPostEventList &operator=(const TQPostEventList &list) - { return (TQPostEventList&)TQPtrList::operator=(list); } + TQPostEventList(bool with_mutex = false) : TQPtrList(), m_mutex(nullptr) + { +#ifdef TQT_THREAD_SUPPORT + if (with_mutex) + { + m_mutex = new TQMutex(TRUE); + } +#endif + } + + ~TQPostEventList() + { + if (m_mutex) + { + delete m_mutex; + m_mutex = nullptr; + } + clear(); + } + + TQMutex* mutex() const { return m_mutex; } + +private: + TQMutex *m_mutex; + + TQPostEventList(const TQPostEventList &) = delete; + TQPostEventList &operator=(const TQPostEventList &) = delete; }; + class TQ_EXPORT TQPostEventListIt : public TQPtrListIterator { public: TQPostEventListIt( const TQPostEventList &l ) : TQPtrListIterator(l) {} TQPostEventListIt &operator=(const TQPostEventListIt &i) -{ return (TQPostEventListIt&)TQPtrListIterator::operator=(i); } +{ + return (TQPostEventListIt&)TQPtrListIterator::operator=(i); } }; -static TQPostEventList *globalPostedEvents = 0; // list of posted events +// The global list and its pointer are initialized in different functions +// to optimize access to the list pointer in normal usage +static TQPostEventList* InitGlobalPostedEventsList() +{ + static TQPostEventList _globalEventList(true); + _globalEventList.setAutoDelete(TRUE); + return &_globalEventList; +} + +static TQPostEventList* GlobalPostedEvents() +{ + static TQPostEventList *_globalPostedEvents = InitGlobalPostedEventsList(); + return _globalPostedEvents; +} uint qGlobalPostedEventsCount() { #ifdef TQT_THREAD_SUPPORT - TQMutexLocker locker( postevent_mutex ); + TQMutexLocker locker( GlobalPostedEvents()->mutex() ); #endif // TQT_THREAD_SUPPORT - if (!globalPostedEvents) { - return 0; - } - return globalPostedEvents->count(); + return GlobalPostedEvents()->count(); } -static TQSingleCleanupHandler qapp_cleanup_events; - #ifndef TQT_NO_PALETTE TQPalette *tqt_std_pal = 0; @@ -1094,7 +1127,6 @@ void TQApplication::initialize( int argc, char **argv, bool enable_sm ) #ifdef QT_USE_GLIBMAINLOOP tqt_timerListMutex = new TQMutex( TRUE ); #endif // QT_USE_GLIBMAINLOOP - postevent_mutex = new TQMutex( TRUE ); tqt_application_thread_id = TQThread::currentThread(); #endif // TQT_THREAD_SUPPORT @@ -1253,8 +1285,6 @@ TQApplication::~TQApplication() #ifdef TQT_THREAD_SUPPORT delete tqt_mutex; tqt_mutex = 0; - delete postevent_mutex; - postevent_mutex = 0; #endif // TQT_THREAD_SUPPORT if( tqApp == this ) { @@ -2546,33 +2576,31 @@ bool TQApplication::notify( TQObject *receiver, TQEvent *e ) if ( e->type() == TQEvent::ChildRemoved && receiver->postedEvents) { #ifdef TQT_THREAD_SUPPORT - TQMutexLocker locker( postevent_mutex ); + TQMutexLocker locker( GlobalPostedEvents()->mutex() ); #endif // TQT_THREAD_SUPPORT - if (globalPostedEvents) { - // the TQObject destructor calls TQObject::removeChild, which calls - // TQApplication::sendEvent() directly. this can happen while the event - // loop is in the middle of posting events, and when we get here, we may - // not have any more posted events for this object. - if ( receiver->postedEvents ) { - // if this is a child remove event and the child insert - // hasn't been dispatched yet, kill that insert - TQPostEventList * l = receiver->postedEvents; - TQObject * c = ((TQChildEvent*)e)->child(); - TQPostEvent * pe; - l->first(); - while( ( pe = l->current()) != 0 ) { - if ( pe->event && pe->receiver == receiver && - pe->event->type() == TQEvent::ChildInserted && - ((TQChildEvent*)pe->event)->child() == c ) { - pe->event->posted = FALSE; - delete pe->event; - pe->event = 0; - l->remove(); - continue; - } - l->next(); + // the TQObject destructor calls TQObject::removeChild, which calls + // TQApplication::sendEvent() directly. this can happen while the event + // loop is in the middle of posting events, and when we get here, we may + // not have any more posted events for this object. + if ( receiver->postedEvents ) { + // if this is a child remove event and the child insert + // hasn't been dispatched yet, kill that insert + TQPostEventList * l = receiver->postedEvents; + TQObject * c = ((TQChildEvent*)e)->child(); + TQPostEvent * pe; + l->first(); + while( ( pe = l->current()) != 0 ) { + if ( pe->event && pe->receiver == receiver && + pe->event->type() == TQEvent::ChildInserted && + ((TQChildEvent*)pe->event)->child() == c ) { + pe->event->posted = FALSE; + delete pe->event; + pe->event = 0; + l->remove(); + continue; } + l->next(); } } } @@ -3394,16 +3422,9 @@ void TQApplication::postEvent( TQObject *receiver, TQEvent *event ) } #ifdef TQT_THREAD_SUPPORT - TQMutexLocker locker( postevent_mutex ); + TQMutexLocker locker( GlobalPostedEvents()->mutex() ); #endif // TQT_THREAD_SUPPORT - if ( !globalPostedEvents ) { // create list - globalPostedEvents = new TQPostEventList; - TQ_CHECK_PTR( globalPostedEvents ); - globalPostedEvents->setAutoDelete( TRUE ); - qapp_cleanup_events.set( &globalPostedEvents ); - } - if ( !receiver->postedEvents ) { receiver->postedEvents = new TQPostEventList; } @@ -3487,7 +3508,7 @@ void TQApplication::postEvent( TQObject *receiver, TQEvent *event ) event->posted = TRUE; TQPostEvent * pe = new TQPostEvent( receiver, event ); l->append( pe ); - globalPostedEvents->append( pe ); + GlobalPostedEvents()->append( pe ); #ifdef TQT_THREAD_SUPPORT // Wake up the receiver thread event loop @@ -3543,24 +3564,20 @@ void TQApplication::sendPostedEvents( TQObject *receiver, int event_type ) } #ifdef TQT_THREAD_SUPPORT - TQMutexLocker locker( postevent_mutex ); + TQMutexLocker locker( GlobalPostedEvents()->mutex() ); #endif - if ( !globalPostedEvents || ( receiver && !receiver->postedEvents ) ) { - return; - } - bool sent = TRUE; while ( sent ) { sent = FALSE; - if ( !globalPostedEvents || ( receiver && !receiver->postedEvents ) ) { + if (receiver && !receiver->postedEvents) { return; } // if we have a receiver, use the local list. Otherwise, use the // global list - TQPostEventList * l = receiver ? receiver->postedEvents : globalPostedEvents; + TQPostEventList * l = receiver ? receiver->postedEvents : GlobalPostedEvents(); // okay. here is the tricky loop. be careful about optimizing // this, it looks the way it does for good reasons. @@ -3632,14 +3649,14 @@ void TQApplication::sendPostedEvents( TQObject *receiver, int event_type ) // clear the global list, i.e. remove everything that was // delivered. - if ( l == globalPostedEvents ) { - globalPostedEvents->first(); - while( (pe=globalPostedEvents->current()) != 0 ) { + if ( l == GlobalPostedEvents() ) { + GlobalPostedEvents()->first(); + while( (pe=GlobalPostedEvents()->current()) != 0 ) { if ( pe->event ) { - globalPostedEvents->next(); + GlobalPostedEvents()->next(); } else { - globalPostedEvents->remove(); + GlobalPostedEvents()->remove(); } } } @@ -3681,7 +3698,7 @@ void TQApplication::removePostedEvents( TQObject *receiver, int event_type ) } #ifdef TQT_THREAD_SUPPORT - TQMutexLocker locker( postevent_mutex ); + TQMutexLocker locker( GlobalPostedEvents()->mutex() ); #endif // TQT_THREAD_SUPPORT // the TQObject destructor calls this function directly. this can @@ -3734,18 +3751,10 @@ void TQApplication::removePostedEvent( TQEvent * event ) } #ifdef TQT_THREAD_SUPPORT - TQMutexLocker locker( postevent_mutex ); + TQMutexLocker locker( GlobalPostedEvents()->mutex() ); #endif // TQT_THREAD_SUPPORT - if ( !globalPostedEvents ) { -#if defined(QT_DEBUG) - tqDebug( "TQApplication::removePostedEvent: %p %d is posted: impossible", - (void*)event, event->type() ); - return; -#endif - } - - TQPostEventListIt it( *globalPostedEvents ); + TQPostEventListIt it( *GlobalPostedEvents() ); TQPostEvent * pe; while( (pe = it.current()) != 0 ) { ++it; diff --git a/src/kernel/qobject.cpp b/src/kernel/qobject.cpp index bb4c848b..7692e9c2 100644 --- a/src/kernel/qobject.cpp +++ b/src/kernel/qobject.cpp @@ -747,7 +747,10 @@ TQObject::~TQObject() if ( pendTimer ) { // might be pending timers qKillTimer( this ); } - TQApplication::removePostedEvents( this ); + if ( tqApp ) + { + TQApplication::removePostedEvents( this ); + } if ( isTree ) { remove_tree( this ); // remove from global root list isTree = FALSE; diff --git a/src/kernel/qwidget.cpp b/src/kernel/qwidget.cpp index 0c76bd3b..c865fbe9 100644 --- a/src/kernel/qwidget.cpp +++ b/src/kernel/qwidget.cpp @@ -1008,7 +1008,10 @@ TQWidget::~TQWidget() childObjects = 0; } - TQApplication::removePostedEvents( this ); + if ( tqApp ) + { + TQApplication::removePostedEvents( this ); + } destroy(); // platform-dependent cleanup if ( extra )