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
21QT_BEGIN_NAMESPACE
22
23static 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
48static 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
54static 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
71static 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
83class QWaitConditionPrivate
84{
85public:
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
130QWaitCondition::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
138QWaitCondition::~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
145void 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
156void 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
167bool 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
174bool 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
190bool 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
197bool 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
226QT_END_NAMESPACE
227

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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