1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Copyright (C) 2016 Intel Corporation.
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the QtCore module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 3 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL3 included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 3 requirements
24** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25**
26** GNU General Public License Usage
27** Alternatively, this file may be used under the terms of the GNU
28** General Public License version 2.0 or (at your option) the GNU General
29** Public license version 3 or any later version approved by the KDE Free
30** Qt Foundation. The licenses are as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32** included in the packaging of this file. Please review the following
33** information to ensure the GNU General Public License requirements will
34** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35** https://www.gnu.org/licenses/gpl-3.0.html.
36**
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41#include "qplatformdefs.h"
42#include "qwaitcondition.h"
43#include "qmutex.h"
44#include "qreadwritelock.h"
45#include "qatomic.h"
46#include "qstring.h"
47#include "qdeadlinetimer.h"
48#include "private/qdeadlinetimer_p.h"
49#include "qelapsedtimer.h"
50#include "private/qcore_unix_p.h"
51
52#include "qmutex_p.h"
53#include "qreadwritelock_p.h"
54
55#include <errno.h>
56#include <sys/time.h>
57#include <time.h>
58
59QT_BEGIN_NAMESPACE
60
61#ifdef Q_OS_ANDROID
62// pthread_condattr_setclock is available only since Android 5.0. On older versions, there's
63// a private function for relative waits (hidden in 5.0).
64// Use weakref so we can determine at runtime whether each of them is present.
65static int local_condattr_setclock(pthread_condattr_t*, clockid_t)
66__attribute__((weakref("pthread_condattr_setclock")));
67
68static int local_cond_timedwait_relative(pthread_cond_t*, pthread_mutex_t *, const timespec *)
69__attribute__((weakref("__pthread_cond_timedwait_relative")));
70#endif
71
72static void report_error(int code, const char *where, const char *what)
73{
74 if (code != 0)
75 qErrnoWarning(code, msg: "%s: %s failure", where, what);
76}
77
78void qt_initialize_pthread_cond(pthread_cond_t *cond, const char *where)
79{
80 pthread_condattr_t condattr;
81
82 pthread_condattr_init(attr: &condattr);
83#if (_POSIX_MONOTONIC_CLOCK-0 >= 0)
84#if defined(Q_OS_ANDROID)
85 if (local_condattr_setclock && QElapsedTimer::clockType() == QElapsedTimer::MonotonicClock)
86 local_condattr_setclock(&condattr, CLOCK_MONOTONIC);
87#elif !defined(Q_OS_MAC)
88 if (QElapsedTimer::clockType() == QElapsedTimer::MonotonicClock)
89 pthread_condattr_setclock(attr: &condattr, CLOCK_MONOTONIC);
90#endif
91#endif
92 report_error(code: pthread_cond_init(cond: cond, cond_attr: &condattr), where, what: "cv init");
93 pthread_condattr_destroy(attr: &condattr);
94}
95
96void qt_abstime_for_timeout(timespec *ts, QDeadlineTimer deadline)
97{
98#ifdef Q_OS_MAC
99 // on Mac, qt_gettime() (on qelapsedtimer_mac.cpp) returns ticks related to the Mach absolute time
100 // that doesn't work with pthread
101 // Mac also doesn't have clock_gettime
102 struct timeval tv;
103 qint64 nsec = deadline.remainingTimeNSecs();
104 gettimeofday(&tv, 0);
105 ts->tv_sec = tv.tv_sec + nsec / (1000 * 1000 * 1000);
106 ts->tv_nsec = tv.tv_usec * 1000 + nsec % (1000 * 1000 * 1000);
107
108 normalizedTimespec(*ts);
109#else
110 // depends on QDeadlineTimer's internals!!
111 Q_STATIC_ASSERT(QDeadlineTimerNanosecondsInT2);
112 ts->tv_sec = deadline._q_data().first;
113 ts->tv_nsec = deadline._q_data().second;
114#endif
115}
116
117class QWaitConditionPrivate {
118public:
119 pthread_mutex_t mutex;
120 pthread_cond_t cond;
121 int waiters;
122 int wakeups;
123
124 int wait_relative(QDeadlineTimer deadline)
125 {
126 timespec ti;
127#ifdef Q_OS_ANDROID
128 if (!local_condattr_setclock && local_cond_timedwait_relative) {
129 qint64 nsec = deadline.remainingTimeNSecs();
130 ti.tv_sec = nsec / (1000 * 1000 * 1000);
131 ti.tv_nsec = nsec - ti.tv_sec * 1000 * 1000 * 1000;
132 return local_cond_timedwait_relative(&cond, &mutex, &ti);
133 }
134#endif
135 qt_abstime_for_timeout(ts: &ti, deadline);
136 return pthread_cond_timedwait(cond: &cond, mutex: &mutex, abstime: &ti);
137 }
138
139 bool wait(QDeadlineTimer deadline)
140 {
141 int code;
142 forever {
143 if (!deadline.isForever()) {
144 code = wait_relative(deadline);
145 } else {
146 code = pthread_cond_wait(cond: &cond, mutex: &mutex);
147 }
148 if (code == 0 && wakeups == 0) {
149 // many vendors warn of spurious wakeups from
150 // pthread_cond_wait(), especially after signal delivery,
151 // even though POSIX doesn't allow for it... sigh
152 continue;
153 }
154 break;
155 }
156
157 Q_ASSERT_X(waiters > 0, "QWaitCondition::wait", "internal error (waiters)");
158 --waiters;
159 if (code == 0) {
160 Q_ASSERT_X(wakeups > 0, "QWaitCondition::wait", "internal error (wakeups)");
161 --wakeups;
162 }
163 report_error(code: pthread_mutex_unlock(mutex: &mutex), where: "QWaitCondition::wait()", what: "mutex unlock");
164
165 if (code && code != ETIMEDOUT)
166 report_error(code, where: "QWaitCondition::wait()", what: "cv wait");
167
168 return (code == 0);
169 }
170};
171
172
173QWaitCondition::QWaitCondition()
174{
175 d = new QWaitConditionPrivate;
176 report_error(code: pthread_mutex_init(mutex: &d->mutex, mutexattr: nullptr), where: "QWaitCondition", what: "mutex init");
177 qt_initialize_pthread_cond(cond: &d->cond, where: "QWaitCondition");
178 d->waiters = d->wakeups = 0;
179}
180
181
182QWaitCondition::~QWaitCondition()
183{
184 report_error(code: pthread_cond_destroy(cond: &d->cond), where: "QWaitCondition", what: "cv destroy");
185 report_error(code: pthread_mutex_destroy(mutex: &d->mutex), where: "QWaitCondition", what: "mutex destroy");
186 delete d;
187}
188
189void QWaitCondition::wakeOne()
190{
191 report_error(code: pthread_mutex_lock(mutex: &d->mutex), where: "QWaitCondition::wakeOne()", what: "mutex lock");
192 d->wakeups = qMin(a: d->wakeups + 1, b: d->waiters);
193 report_error(code: pthread_cond_signal(cond: &d->cond), where: "QWaitCondition::wakeOne()", what: "cv signal");
194 report_error(code: pthread_mutex_unlock(mutex: &d->mutex), where: "QWaitCondition::wakeOne()", what: "mutex unlock");
195}
196
197void QWaitCondition::wakeAll()
198{
199 report_error(code: pthread_mutex_lock(mutex: &d->mutex), where: "QWaitCondition::wakeAll()", what: "mutex lock");
200 d->wakeups = d->waiters;
201 report_error(code: pthread_cond_broadcast(cond: &d->cond), where: "QWaitCondition::wakeAll()", what: "cv broadcast");
202 report_error(code: pthread_mutex_unlock(mutex: &d->mutex), where: "QWaitCondition::wakeAll()", what: "mutex unlock");
203}
204
205bool QWaitCondition::wait(QMutex *mutex, unsigned long time)
206{
207 if (time == std::numeric_limits<unsigned long>::max())
208 return wait(lockedMutex: mutex, deadline: QDeadlineTimer(QDeadlineTimer::Forever));
209 return wait(lockedMutex: mutex, deadline: QDeadlineTimer(time));
210}
211
212bool QWaitCondition::wait(QMutex *mutex, QDeadlineTimer deadline)
213{
214 if (! mutex)
215 return false;
216 if (mutex->isRecursive()) {
217 qWarning(msg: "QWaitCondition: cannot wait on recursive mutexes");
218 return false;
219 }
220
221 report_error(code: pthread_mutex_lock(mutex: &d->mutex), where: "QWaitCondition::wait()", what: "mutex lock");
222 ++d->waiters;
223 mutex->unlock();
224
225 bool returnValue = d->wait(deadline);
226
227 mutex->lock();
228
229 return returnValue;
230}
231
232bool QWaitCondition::wait(QReadWriteLock *readWriteLock, unsigned long time)
233{
234 if (time == std::numeric_limits<unsigned long>::max())
235 return wait(lockedReadWriteLock: readWriteLock, deadline: QDeadlineTimer(QDeadlineTimer::Forever));
236 return wait(lockedReadWriteLock: readWriteLock, deadline: QDeadlineTimer(time));
237}
238
239bool QWaitCondition::wait(QReadWriteLock *readWriteLock, QDeadlineTimer deadline)
240{
241 if (!readWriteLock)
242 return false;
243 auto previousState = readWriteLock->stateForWaitCondition();
244 if (previousState == QReadWriteLock::Unlocked)
245 return false;
246 if (previousState == QReadWriteLock::RecursivelyLocked) {
247 qWarning(msg: "QWaitCondition: cannot wait on QReadWriteLocks with recursive lockForWrite()");
248 return false;
249 }
250
251 report_error(code: pthread_mutex_lock(mutex: &d->mutex), where: "QWaitCondition::wait()", what: "mutex lock");
252 ++d->waiters;
253
254 readWriteLock->unlock();
255
256 bool returnValue = d->wait(deadline);
257
258 if (previousState == QReadWriteLock::LockedForWrite)
259 readWriteLock->lockForWrite();
260 else
261 readWriteLock->lockForRead();
262
263 return returnValue;
264}
265
266QT_END_NAMESPACE
267

source code of qtbase/src/corelib/thread/qwaitcondition_unix.cpp