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
22QT_BEGIN_NAMESPACE
23
24static 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
49static 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
55static 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
72static 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
81class QWaitConditionPrivate
82{
83public:
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
128QWaitCondition::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
136QWaitCondition::~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
143void 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
154void 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
165bool 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
172bool 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
188bool 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
195bool 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
224QT_END_NAMESPACE
225

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