1 | // Copyright (C) 2016 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 "qwaitcondition.h" |
6 | |
7 | #include "qatomic.h" |
8 | #include "qdeadlinetimer.h" |
9 | #include "qelapsedtimer.h" |
10 | #include "qmutex.h" |
11 | #include "qplatformdefs.h" |
12 | #include "qreadwritelock.h" |
13 | #include "qstring.h" |
14 | |
15 | #include "private/qcore_unix_p.h" |
16 | #include "qreadwritelock_p.h" |
17 | |
18 | #include <errno.h> |
19 | #include <sys/time.h> |
20 | #include <time.h> |
21 | |
22 | QT_BEGIN_NAMESPACE |
23 | |
24 | static constexpr clockid_t SteadyClockClockId = |
25 | #if !defined(CLOCK_MONOTONIC) |
26 | // we don't know how to set the monotonic clock |
27 | CLOCK_REALTIME |
28 | #elif defined(_LIBCPP_VERSION) && defined(_LIBCPP_HAS_NO_MONOTONIC_CLOCK) |
29 | // libc++ falling back to system_clock |
30 | CLOCK_REALTIME |
31 | #elif defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_CLOCK_MONOTONIC) |
32 | // libstdc++ falling back to system_clock |
33 | CLOCK_REALTIME |
34 | #elif defined(Q_OS_DARWIN) |
35 | // Darwin lacks pthread_condattr_setclock() |
36 | CLOCK_REALTIME |
37 | #elif defined(Q_OS_QNX) |
38 | // unknown why |
39 | CLOCK_REALTIME |
40 | #elif defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) |
41 | // both libstdc++ and libc++ do use CLOCK_MONOTONIC |
42 | CLOCK_MONOTONIC |
43 | #else |
44 | # warning "Unknown C++ Standard Library implementation - code may be sub-optimal" |
45 | CLOCK_REALTIME |
46 | #endif |
47 | ; |
48 | |
49 | static void qt_report_pthread_error(int code, const char *where, const char *what) |
50 | { |
51 | if (code != 0) |
52 | qErrnoWarning(code, msg: "%s: %s failure" , where, what); |
53 | } |
54 | |
55 | static void qt_initialize_pthread_cond(pthread_cond_t *cond, const char *where) |
56 | { |
57 | pthread_condattr_t *attrp = nullptr; |
58 | |
59 | #if defined(CLOCK_MONOTONIC) && !defined(Q_OS_DARWIN) |
60 | pthread_condattr_t condattr; |
61 | attrp = &condattr; |
62 | |
63 | pthread_condattr_init(attr: &condattr); |
64 | auto destroy = qScopeGuard(f: [&] { pthread_condattr_destroy(attr: &condattr); }); |
65 | if (SteadyClockClockId != CLOCK_REALTIME) |
66 | pthread_condattr_setclock(attr: &condattr, clock_id: SteadyClockClockId); |
67 | #endif |
68 | |
69 | qt_report_pthread_error(code: pthread_cond_init(cond: cond, cond_attr: attrp), where, what: "cv init" ); |
70 | } |
71 | |
72 | static void qt_abstime_for_timeout(timespec *ts, QDeadlineTimer deadline) |
73 | { |
74 | using namespace std::chrono; |
75 | using Clock = |
76 | std::conditional_t<SteadyClockClockId == CLOCK_REALTIME, system_clock, steady_clock>; |
77 | auto timePoint = deadline.deadline<Clock>(); |
78 | *ts = durationToTimespec(timeout: timePoint.time_since_epoch()); |
79 | } |
80 | |
81 | class QWaitConditionPrivate |
82 | { |
83 | public: |
84 | pthread_mutex_t mutex; |
85 | pthread_cond_t cond; |
86 | int waiters; |
87 | int wakeups; |
88 | |
89 | int wait_relative(QDeadlineTimer deadline) |
90 | { |
91 | timespec ti; |
92 | qt_abstime_for_timeout(ts: &ti, deadline); |
93 | return pthread_cond_timedwait(cond: &cond, mutex: &mutex, abstime: &ti); |
94 | } |
95 | |
96 | bool wait(QDeadlineTimer deadline) |
97 | { |
98 | int code; |
99 | forever { |
100 | if (!deadline.isForever()) { |
101 | code = wait_relative(deadline); |
102 | } else { |
103 | code = pthread_cond_wait(cond: &cond, mutex: &mutex); |
104 | } |
105 | if (code == 0 && wakeups == 0) { |
106 | // spurious wakeup |
107 | continue; |
108 | } |
109 | break; |
110 | } |
111 | |
112 | Q_ASSERT_X(waiters > 0, "QWaitCondition::wait" , "internal error (waiters)" ); |
113 | --waiters; |
114 | if (code == 0) { |
115 | Q_ASSERT_X(wakeups > 0, "QWaitCondition::wait" , "internal error (wakeups)" ); |
116 | --wakeups; |
117 | } |
118 | qt_report_pthread_error(code: pthread_mutex_unlock(mutex: &mutex), where: "QWaitCondition::wait()" , |
119 | what: "mutex unlock" ); |
120 | |
121 | if (code && code != ETIMEDOUT) |
122 | qt_report_pthread_error(code, where: "QWaitCondition::wait()" , what: "cv wait" ); |
123 | |
124 | return (code == 0); |
125 | } |
126 | }; |
127 | |
128 | QWaitCondition::QWaitCondition() |
129 | { |
130 | d = new QWaitConditionPrivate; |
131 | qt_report_pthread_error(code: pthread_mutex_init(mutex: &d->mutex, mutexattr: nullptr), where: "QWaitCondition" , what: "mutex init" ); |
132 | qt_initialize_pthread_cond(cond: &d->cond, where: "QWaitCondition" ); |
133 | d->waiters = d->wakeups = 0; |
134 | } |
135 | |
136 | QWaitCondition::~QWaitCondition() |
137 | { |
138 | qt_report_pthread_error(code: pthread_cond_destroy(cond: &d->cond), where: "QWaitCondition" , what: "cv destroy" ); |
139 | qt_report_pthread_error(code: pthread_mutex_destroy(mutex: &d->mutex), where: "QWaitCondition" , what: "mutex destroy" ); |
140 | delete d; |
141 | } |
142 | |
143 | void QWaitCondition::wakeOne() |
144 | { |
145 | qt_report_pthread_error(code: pthread_mutex_lock(mutex: &d->mutex), where: "QWaitCondition::wakeOne()" , |
146 | what: "mutex lock" ); |
147 | d->wakeups = qMin(a: d->wakeups + 1, b: d->waiters); |
148 | qt_report_pthread_error(code: pthread_cond_signal(cond: &d->cond), where: "QWaitCondition::wakeOne()" , |
149 | what: "cv signal" ); |
150 | qt_report_pthread_error(code: pthread_mutex_unlock(mutex: &d->mutex), where: "QWaitCondition::wakeOne()" , |
151 | what: "mutex unlock" ); |
152 | } |
153 | |
154 | void QWaitCondition::wakeAll() |
155 | { |
156 | qt_report_pthread_error(code: pthread_mutex_lock(mutex: &d->mutex), where: "QWaitCondition::wakeAll()" , |
157 | what: "mutex lock" ); |
158 | d->wakeups = d->waiters; |
159 | qt_report_pthread_error(code: pthread_cond_broadcast(cond: &d->cond), where: "QWaitCondition::wakeAll()" , |
160 | what: "cv broadcast" ); |
161 | qt_report_pthread_error(code: pthread_mutex_unlock(mutex: &d->mutex), where: "QWaitCondition::wakeAll()" , |
162 | what: "mutex unlock" ); |
163 | } |
164 | |
165 | bool QWaitCondition::wait(QMutex *mutex, unsigned long time) |
166 | { |
167 | if (time == std::numeric_limits<unsigned long>::max()) |
168 | return wait(lockedMutex: mutex, deadline: QDeadlineTimer(QDeadlineTimer::Forever)); |
169 | return wait(lockedMutex: mutex, deadline: QDeadlineTimer(time)); |
170 | } |
171 | |
172 | bool QWaitCondition::wait(QMutex *mutex, QDeadlineTimer deadline) |
173 | { |
174 | if (!mutex) |
175 | return false; |
176 | |
177 | qt_report_pthread_error(code: pthread_mutex_lock(mutex: &d->mutex), where: "QWaitCondition::wait()" , what: "mutex lock" ); |
178 | ++d->waiters; |
179 | mutex->unlock(); |
180 | |
181 | bool returnValue = d->wait(deadline); |
182 | |
183 | mutex->lock(); |
184 | |
185 | return returnValue; |
186 | } |
187 | |
188 | bool QWaitCondition::wait(QReadWriteLock *readWriteLock, unsigned long time) |
189 | { |
190 | if (time == std::numeric_limits<unsigned long>::max()) |
191 | return wait(lockedReadWriteLock: readWriteLock, deadline: QDeadlineTimer(QDeadlineTimer::Forever)); |
192 | return wait(lockedReadWriteLock: readWriteLock, deadline: QDeadlineTimer(time)); |
193 | } |
194 | |
195 | bool QWaitCondition::wait(QReadWriteLock *readWriteLock, QDeadlineTimer deadline) |
196 | { |
197 | using namespace QReadWriteLockStates; |
198 | |
199 | if (!readWriteLock) |
200 | return false; |
201 | auto previousState = QReadWriteLockPrivate::stateForWaitCondition(q: readWriteLock); |
202 | if (previousState == Unlocked) |
203 | return false; |
204 | if (previousState == RecursivelyLocked) { |
205 | qWarning(msg: "QWaitCondition: cannot wait on QReadWriteLocks with recursive lockForWrite()" ); |
206 | return false; |
207 | } |
208 | |
209 | qt_report_pthread_error(code: pthread_mutex_lock(mutex: &d->mutex), where: "QWaitCondition::wait()" , what: "mutex lock" ); |
210 | ++d->waiters; |
211 | |
212 | readWriteLock->unlock(); |
213 | |
214 | bool returnValue = d->wait(deadline); |
215 | |
216 | if (previousState == LockedForWrite) |
217 | readWriteLock->lockForWrite(); |
218 | else |
219 | readWriteLock->lockForRead(); |
220 | |
221 | return returnValue; |
222 | } |
223 | |
224 | QT_END_NAMESPACE |
225 | |