1// Copyright (C) 2016 Intel Corporation.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QDEADLINETIMER_H
5#define QDEADLINETIMER_H
6
7#include <QtCore/qelapsedtimer.h>
8#include <QtCore/qmetatype.h>
9#include <QtCore/qnamespace.h>
10
11#ifdef max
12// un-pollute the namespace. We need std::numeric_limits::max() and std::chrono::duration::max()
13# undef max
14#endif
15
16#include <limits>
17
18#include <chrono>
19
20QT_BEGIN_NAMESPACE
21
22class Q_CORE_EXPORT QDeadlineTimer
23{
24public:
25 enum class ForeverConstant { Forever };
26 static constexpr ForeverConstant Forever = ForeverConstant::Forever;
27
28 constexpr QDeadlineTimer() noexcept = default;
29 constexpr explicit QDeadlineTimer(Qt::TimerType type_) noexcept
30 : type(type_) {}
31 constexpr QDeadlineTimer(ForeverConstant, Qt::TimerType type_ = Qt::CoarseTimer) noexcept
32 : t1((std::numeric_limits<qint64>::max)()), type(type_) {}
33 explicit QDeadlineTimer(qint64 msecs, Qt::TimerType type = Qt::CoarseTimer) noexcept;
34
35 void swap(QDeadlineTimer &other) noexcept
36 { std::swap(a&: t1, b&: other.t1); std::swap(a&: type, b&: other.type); }
37
38 constexpr bool isForever() const noexcept
39 { return t1 == (std::numeric_limits<qint64>::max)(); }
40 bool hasExpired() const noexcept;
41
42 Qt::TimerType timerType() const noexcept
43 { return Qt::TimerType(type & 0xff); }
44 void setTimerType(Qt::TimerType type);
45
46 qint64 remainingTime() const noexcept;
47 qint64 remainingTimeNSecs() const noexcept;
48 void setRemainingTime(qint64 msecs, Qt::TimerType type = Qt::CoarseTimer) noexcept;
49 void setPreciseRemainingTime(qint64 secs, qint64 nsecs = 0,
50 Qt::TimerType type = Qt::CoarseTimer) noexcept;
51
52 qint64 deadline() const noexcept Q_DECL_PURE_FUNCTION;
53 qint64 deadlineNSecs() const noexcept Q_DECL_PURE_FUNCTION;
54 void setDeadline(qint64 msecs, Qt::TimerType timerType = Qt::CoarseTimer) noexcept;
55 void setPreciseDeadline(qint64 secs, qint64 nsecs = 0,
56 Qt::TimerType type = Qt::CoarseTimer) noexcept;
57
58 static QDeadlineTimer addNSecs(QDeadlineTimer dt, qint64 nsecs) noexcept Q_DECL_PURE_FUNCTION;
59 static QDeadlineTimer current(Qt::TimerType timerType = Qt::CoarseTimer) noexcept;
60
61 friend bool operator==(QDeadlineTimer d1, QDeadlineTimer d2) noexcept
62 { return d1.t1 == d2.t1; }
63 friend bool operator!=(QDeadlineTimer d1, QDeadlineTimer d2) noexcept
64 { return !(d1 == d2); }
65 friend bool operator<(QDeadlineTimer d1, QDeadlineTimer d2) noexcept
66 { return d1.t1 < d2.t1; }
67 friend bool operator<=(QDeadlineTimer d1, QDeadlineTimer d2) noexcept
68 { return d1 == d2 || d1 < d2; }
69 friend bool operator>(QDeadlineTimer d1, QDeadlineTimer d2) noexcept
70 { return d2 < d1; }
71 friend bool operator>=(QDeadlineTimer d1, QDeadlineTimer d2) noexcept
72 { return !(d1 < d2); }
73
74 friend Q_CORE_EXPORT QDeadlineTimer operator+(QDeadlineTimer dt, qint64 msecs);
75 friend QDeadlineTimer operator+(qint64 msecs, QDeadlineTimer dt)
76 { return dt + msecs; }
77 friend QDeadlineTimer operator-(QDeadlineTimer dt, qint64 msecs)
78 { return dt + (-msecs); }
79 friend qint64 operator-(QDeadlineTimer dt1, QDeadlineTimer dt2)
80 { return (dt1.deadlineNSecs() - dt2.deadlineNSecs()) / (1000 * 1000); }
81 QDeadlineTimer &operator+=(qint64 msecs)
82 { *this = *this + msecs; return *this; }
83 QDeadlineTimer &operator-=(qint64 msecs)
84 { *this = *this + (-msecs); return *this; }
85
86 template <class Clock, class Duration = typename Clock::duration>
87 QDeadlineTimer(std::chrono::time_point<Clock, Duration> deadline_,
88 Qt::TimerType type_ = Qt::CoarseTimer) : t2(0)
89 { setDeadline(deadline_, type_); }
90 template <class Clock, class Duration = typename Clock::duration>
91 QDeadlineTimer &operator=(std::chrono::time_point<Clock, Duration> deadline_)
92 { setDeadline(deadline_); return *this; }
93
94 template <class Clock, class Duration = typename Clock::duration>
95 void setDeadline(std::chrono::time_point<Clock, Duration> tp,
96 Qt::TimerType type_ = Qt::CoarseTimer);
97
98 template <class Clock, class Duration = typename Clock::duration>
99 std::chrono::time_point<Clock, Duration> deadline() const;
100
101 template <class Rep, class Period>
102 QDeadlineTimer(std::chrono::duration<Rep, Period> remaining, Qt::TimerType type_ = Qt::CoarseTimer)
103 : t2(0)
104 { setRemainingTime(remaining, type_); }
105
106 template <class Rep, class Period>
107 QDeadlineTimer &operator=(std::chrono::duration<Rep, Period> remaining)
108 { setRemainingTime(remaining); return *this; }
109
110 template <class Rep, class Period>
111 void setRemainingTime(std::chrono::duration<Rep, Period> remaining, Qt::TimerType type_ = Qt::CoarseTimer)
112 {
113 using namespace std::chrono;
114 if (remaining == remaining.max())
115 *this = QDeadlineTimer(Forever, type_);
116 else
117 setPreciseRemainingTime(secs: 0, nsecs: ceil<nanoseconds>(remaining).count(), type: type_);
118 }
119
120 std::chrono::nanoseconds remainingTimeAsDuration() const noexcept
121 {
122 if (isForever())
123 return std::chrono::nanoseconds::max();
124 qint64 nsecs = rawRemainingTimeNSecs();
125 if (nsecs <= 0)
126 return std::chrono::nanoseconds::zero();
127 return std::chrono::nanoseconds(nsecs);
128 }
129
130 template <class Rep, class Period>
131 friend QDeadlineTimer operator+(QDeadlineTimer dt, std::chrono::duration<Rep, Period> value)
132 { return QDeadlineTimer::addNSecs(dt, nsecs: std::chrono::duration_cast<std::chrono::nanoseconds>(value).count()); }
133 template <class Rep, class Period>
134 friend QDeadlineTimer operator+(std::chrono::duration<Rep, Period> value, QDeadlineTimer dt)
135 { return dt + value; }
136 template <class Rep, class Period>
137 friend QDeadlineTimer operator+=(QDeadlineTimer &dt, std::chrono::duration<Rep, Period> value)
138 { return dt = dt + value; }
139
140private:
141 qint64 t1 = 0;
142#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
143 unsigned t2 = 0;
144#endif
145 unsigned type = Qt::CoarseTimer;
146
147 qint64 rawRemainingTimeNSecs() const noexcept;
148};
149
150template<class Clock, class Duration>
151std::chrono::time_point<Clock, Duration> QDeadlineTimer::deadline() const
152{
153 using namespace std::chrono;
154 if constexpr (std::is_same_v<Clock, steady_clock>) {
155 auto val = duration_cast<Duration>(nanoseconds(deadlineNSecs()));
156 return time_point<Clock, Duration>(val);
157 } else {
158 auto val = nanoseconds(rawRemainingTimeNSecs()) + Clock::now();
159 return time_point_cast<Duration>(val);
160 }
161}
162
163template<class Clock, class Duration>
164void QDeadlineTimer::setDeadline(std::chrono::time_point<Clock, Duration> tp, Qt::TimerType type_)
165{
166 using namespace std::chrono;
167 if (tp == tp.max()) {
168 *this = Forever;
169 type = type_;
170 } else if constexpr (std::is_same_v<Clock, steady_clock>) {
171 setPreciseDeadline(secs: 0,
172 nsecs: duration_cast<nanoseconds>(tp.time_since_epoch()).count(),
173 type: type_);
174 } else {
175 setPreciseRemainingTime(secs: 0, nsecs: duration_cast<nanoseconds>(tp - Clock::now()).count(), type: type_);
176 }
177}
178
179Q_DECLARE_SHARED(QDeadlineTimer)
180
181QT_END_NAMESPACE
182
183QT_DECL_METATYPE_EXTERN(QDeadlineTimer, Q_CORE_EXPORT)
184
185#endif // QDEADLINETIMER_H
186