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