1 | // Copyright (C) 2022 The Qt Company Ltd. |
2 | // Copyright (C) 2016 Intel Corporation. |
3 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
4 | |
5 | #include <QtCore/private/qsingleshottimer_p.h> |
6 | |
7 | #include "qcoreapplication.h" |
8 | #include "qmetaobject_p.h" |
9 | #include "private/qnumeric_p.h" |
10 | |
11 | QT_BEGIN_NAMESPACE |
12 | |
13 | QSingleShotTimer::QSingleShotTimer(Duration interval, Qt::TimerType timerType, |
14 | const QObject *r, const char *member) |
15 | : QObject(QAbstractEventDispatcher::instance()) |
16 | { |
17 | connect(sender: this, SIGNAL(timeout()), receiver: r, member); |
18 | startTimerForReceiver(interval, timerType, receiver: r); |
19 | } |
20 | |
21 | QSingleShotTimer::QSingleShotTimer(Duration interval, Qt::TimerType timerType, |
22 | const QObject *r, QtPrivate::QSlotObjectBase *slotObj) |
23 | : QObject(QAbstractEventDispatcher::instance()) |
24 | { |
25 | int signal_index = QMetaObjectPrivate::signalOffset(m: &staticMetaObject); |
26 | Q_ASSERT(QMetaObjectPrivate::signal(&staticMetaObject, signal_index).name() == "timeout" ); |
27 | QObjectPrivate::connectImpl(sender: this, signal_index, receiver: r ? r : this, slot: nullptr, slotObj, |
28 | type: Qt::AutoConnection, types: nullptr, senderMetaObject: &staticMetaObject); |
29 | |
30 | startTimerForReceiver(interval, timerType, receiver: r); |
31 | } |
32 | |
33 | QSingleShotTimer::~QSingleShotTimer() |
34 | { |
35 | if (timerId > Qt::TimerId::Invalid) |
36 | killTimer(id: timerId); |
37 | } |
38 | |
39 | /* |
40 | Move the timer, and the dispatching and handling of the timer event, into |
41 | the same thread as where it will be handled, so that it fires reliably even |
42 | if the thread that set up the timer is busy. |
43 | */ |
44 | void QSingleShotTimer::startTimerForReceiver(Duration interval, Qt::TimerType timerType, |
45 | const QObject *receiver) |
46 | { |
47 | if (receiver && receiver->thread() != thread()) { |
48 | // Avoid leaking the QSingleShotTimer instance in case the application exits before the |
49 | // timer fires |
50 | connect(sender: QCoreApplication::instance(), signal: &QCoreApplication::aboutToQuit, context: this, |
51 | slot: &QObject::deleteLater); |
52 | setParent(nullptr); |
53 | moveToThread(thread: receiver->thread()); |
54 | |
55 | QDeadlineTimer deadline(interval, timerType); |
56 | auto invokable = [this, deadline, timerType] { |
57 | if (deadline.hasExpired()) { |
58 | Q_EMIT timeout(); |
59 | } else { |
60 | timerId = Qt::TimerId{startTimer(time: deadline.remainingTimeAsDuration(), timerType)}; |
61 | } |
62 | }; |
63 | QMetaObject::invokeMethod(object: this, function&: invokable, type: Qt::QueuedConnection); |
64 | } else { |
65 | timerId = Qt::TimerId{startTimer(time: interval, timerType)}; |
66 | } |
67 | } |
68 | |
69 | void QSingleShotTimer::timerEvent(QTimerEvent *) |
70 | { |
71 | // need to kill the timer _before_ we emit timeout() in case the |
72 | // slot connected to timeout calls processEvents() |
73 | if (timerId > Qt::TimerId::Invalid) |
74 | killTimer(id: std::exchange(obj&: timerId, new_val: Qt::TimerId::Invalid)); |
75 | |
76 | Q_EMIT timeout(); |
77 | |
78 | // we would like to use delete later here, but it feels like a |
79 | // waste to post a new event to handle this event, so we just unset the flag |
80 | // and explicitly delete... |
81 | delete this; |
82 | } |
83 | |
84 | QT_END_NAMESPACE |
85 | |
86 | #include "moc_qsingleshottimer_p.cpp" |
87 | |