QAbstractEventDispatcher: prevent too late unregistering of timers
Timers can't unregister when the dispatcher object is no longer of a dispatcher type. It's too late even at ~QAbstractEventDispatcher, because unregisterTimer() is a pure virtual. To prevent their attempting to unregister, we set the thread's dispatcher to nullptr if it is this object. This has been a latent bug, so it's worth fixing. This started happening for me with an un-pushed change that changed the order of how QCoreApplication and QGuiApplication destroy the main thread event dispatcher (namely, in their destructors, not waiting for ~QObject to deleteChildren()). Drive-by relax the store in QThread::setEventDispatcher(). Fixes: QTBUG-137130 Pick-to: 6.9 6.8 Change-Id: I8845736c38a931af62e3fffdfd3554874df89e8e Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
parent
08325952ee
commit
c8d3d7a7af
@ -175,7 +175,11 @@ QAbstractEventDispatcher::QAbstractEventDispatcher(QAbstractEventDispatcherPriva
|
|||||||
Destroys the event dispatcher.
|
Destroys the event dispatcher.
|
||||||
*/
|
*/
|
||||||
QAbstractEventDispatcher::~QAbstractEventDispatcher()
|
QAbstractEventDispatcher::~QAbstractEventDispatcher()
|
||||||
{ }
|
{
|
||||||
|
QThreadData *data = QThreadData::current();
|
||||||
|
if (data->eventDispatcher.loadRelaxed() == this)
|
||||||
|
data->eventDispatcher.storeRelaxed(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns a pointer to the event dispatcher object for the specified
|
Returns a pointer to the event dispatcher object for the specified
|
||||||
|
@ -1236,7 +1236,7 @@ void QThread::setEventDispatcher(QAbstractEventDispatcher *eventDispatcher)
|
|||||||
} else {
|
} else {
|
||||||
eventDispatcher->moveToThread(this);
|
eventDispatcher->moveToThread(this);
|
||||||
if (eventDispatcher->thread() == this) // was the move successful?
|
if (eventDispatcher->thread() == this) // was the move successful?
|
||||||
d->data->eventDispatcher = eventDispatcher;
|
d->data->eventDispatcher.storeRelaxed(eventDispatcher);
|
||||||
else
|
else
|
||||||
qWarning("QThread::setEventDispatcher: Could not move event dispatcher to target thread");
|
qWarning("QThread::setEventDispatcher: Could not move event dispatcher to target thread");
|
||||||
}
|
}
|
||||||
|
@ -83,6 +83,7 @@ private slots:
|
|||||||
void singleShotToFunctors();
|
void singleShotToFunctors();
|
||||||
void singleShot_chrono();
|
void singleShot_chrono();
|
||||||
void singleShot_static();
|
void singleShot_static();
|
||||||
|
void singleShotDestructionBeforeEventDispatcher();
|
||||||
void crossThreadSingleShotToFunctor_data();
|
void crossThreadSingleShotToFunctor_data();
|
||||||
void crossThreadSingleShotToFunctor();
|
void crossThreadSingleShotToFunctor();
|
||||||
#ifdef QT_BUILD_INTERNAL
|
#ifdef QT_BUILD_INTERNAL
|
||||||
@ -1193,6 +1194,22 @@ void tst_QTimer::postedEventsShouldNotStarveTimers()
|
|||||||
QTRY_VERIFY_WITH_TIMEOUT(timeoutSpy.size() > 5, 100);
|
QTRY_VERIFY_WITH_TIMEOUT(timeoutSpy.size() > 5, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_QTimer::singleShotDestructionBeforeEventDispatcher()
|
||||||
|
{
|
||||||
|
// This makes sure the QSingleShotTimer doesn't cause a crash when the
|
||||||
|
// event dispatcher is deleted. As of the time of this test's writing, the
|
||||||
|
// QSST was parented to the dispatcher, so if it hadn't yet expired, it
|
||||||
|
// would be deleted by the QObject destructor, which is too late to
|
||||||
|
// unregister the timer.
|
||||||
|
|
||||||
|
auto thr = QThread::create([] {
|
||||||
|
QObject o;
|
||||||
|
QTimer::singleShot(300s, &o, [] {});
|
||||||
|
});
|
||||||
|
thr->start();
|
||||||
|
thr->wait();
|
||||||
|
}
|
||||||
|
|
||||||
struct DummyFunctor {
|
struct DummyFunctor {
|
||||||
static QThread *callThread;
|
static QThread *callThread;
|
||||||
void operator()() {
|
void operator()() {
|
||||||
@ -1673,6 +1690,10 @@ void tst_QTimer::initMain()
|
|||||||
void tst_QTimer::cleanupTestCase()
|
void tst_QTimer::cleanupTestCase()
|
||||||
{
|
{
|
||||||
delete s_staticSingleShotUser;
|
delete s_staticSingleShotUser;
|
||||||
|
|
||||||
|
// Same as singleShotDestructionBeforeEventDispatcher() above, but for the
|
||||||
|
// main thread.
|
||||||
|
QTimer::singleShot(300s, this, [] {});
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_QTimer::singleShot_static()
|
void tst_QTimer::singleShot_static()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user