Skip to content

Commit 313d388

Browse files
committed
Fix dispatching when app (or thread) terminated
Make sure to **not** notify handlers if the captured thread doesn't exist anymore, which would potentially result in dispatching to the wrong thread (ie. nullptr == current thread). This also applies when the app is shutting down and the even loop is not anymore available. In both cases, we should not trigger any error and skip notifications.
1 parent f794916 commit 313d388

File tree

2 files changed

+30
-7
lines changed

2 files changed

+30
-7
lines changed

src/qtpromise/qpromise_p.h

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ namespace QtPromisePrivate {
3232

3333
// https://stackoverflow.com/a/21653558
3434
template <typename F>
35-
static void qtpromise_defer(F&& f, QThread* thread = nullptr)
35+
static void qtpromise_defer(F&& f, const QPointer<QThread>& thread)
3636
{
3737
struct Event : public QEvent
3838
{
@@ -43,11 +43,34 @@ static void qtpromise_defer(F&& f, QThread* thread = nullptr)
4343
FType m_f;
4444
};
4545

46+
if (!thread || thread->isFinished()) {
47+
// Make sure to not call `f` if the captured thread doesn't exist anymore,
48+
// which would potentially result in dispatching to the wrong thread (ie.
49+
// nullptr == current thread). Since the target thread is gone, it should
50+
// be safe to simply skip that notification.
51+
return;
52+
}
53+
4654
QObject* target = QAbstractEventDispatcher::instance(thread);
55+
if (!target && QCoreApplication::closingDown()) {
56+
// When the app is shutting down, the even loop is not anymore available
57+
// so we don't have any way to dispatch `f`. This case can happen when a
58+
// promise is resolved after the app is requested to close, in which case
59+
// we should not trigger any error and skip that notification.
60+
return;
61+
}
62+
4763
Q_ASSERT_X(target, "postMetaCall", "Target thread must have an event loop");
4864
QCoreApplication::postEvent(target, new Event(std::forward<F>(f)));
4965
}
5066

67+
template <typename F>
68+
static void qtpromise_defer(F&& f)
69+
{
70+
Q_ASSERT(QThread::currentThread());
71+
qtpromise_defer(std::forward<F>(f), QThread::currentThread());
72+
}
73+
5174
template <typename T>
5275
struct PromiseDeduce
5376
{

tests/auto/qtpromise/future/tst_future.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -261,9 +261,9 @@ void tst_future::fail()
261261
QString result;
262262
auto input = QPromise<QString>::reject(MyException("bar"));
263263
auto output = input.fail([](const MyException& e) {
264-
return QtConcurrent::run([=]() {
265-
return QString("foo%1").arg(e.error());
266-
});
264+
return QtConcurrent::run([](const QString& error) {
265+
return QString("foo%1").arg(error);
266+
}, e.error());
267267
});
268268

269269
QCOMPARE(input.isRejected(), true);
@@ -282,9 +282,9 @@ void tst_future::fail_void()
282282
QString result;
283283
auto input = QPromise<void>::reject(MyException("bar"));
284284
auto output = input.fail([&](const MyException& e) {
285-
return QtConcurrent::run([&]() {
286-
result = e.error();
287-
});
285+
return QtConcurrent::run([&](const QString& error) {
286+
result = error;
287+
}, e.error());
288288
});
289289

290290
QCOMPARE(input.isRejected(), true);

0 commit comments

Comments
 (0)