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// Qt-Security score:significant reason:default
5
6#include "qwaitcondition.h"
7
8#include "qatomic.h"
9#include "qdeadlinetimer.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 void qt_report_pthread_error(int code, const char *where, const char *what)
25{
26 if (code != 0)
27 qErrnoWarning(code, msg: "%s: %s failure", where, what);
28}
29
30static void qt_initialize_pthread_cond(pthread_cond_t *cond, const char *where)
31{
32 pthread_condattr_t *attrp = nullptr;
33
34#if QT_CONFIG(pthread_condattr_setclock)
35 pthread_condattr_t condattr;
36 attrp = &condattr;
37
38 pthread_condattr_init(attr: &condattr);
39 auto destroy = qScopeGuard(f: [&] { pthread_condattr_destroy(attr: &condattr); });
40 if (QWaitConditionClockId != CLOCK_REALTIME)
41 pthread_condattr_setclock(attr: &condattr, clock_id: QWaitConditionClockId);
42#endif
43
44 qt_report_pthread_error(code: pthread_cond_init(cond: cond, cond_attr: attrp), where, what: "cv init");
45}
46
47class QWaitConditionPrivate
48{
49public:
50 pthread_mutex_t mutex;
51 pthread_cond_t cond;
52 int waiters;
53 int wakeups;
54
55 int wait_relative(QDeadlineTimer deadline)
56 {
57 timespec ti = deadlineToAbstime<QWaitConditionClockId>(deadline);
58 return pthread_cond_timedwait(cond: &cond, mutex: &mutex, abstime: &ti);
59 }
60
61 bool wait(QDeadlineTimer deadline)
62 {
63 int code;
64 forever {
65 if (!deadline.isForever()) {
66 code = wait_relative(deadline);
67 } else {
68 code = pthread_cond_wait(cond: &cond, mutex: &mutex);
69 }
70 if (code == 0 && wakeups == 0) {
71 // spurious wakeup
72 continue;
73 }
74 break;
75 }
76
77 Q_ASSERT_X(waiters > 0, "QWaitCondition::wait", "internal error (waiters)");
78 --waiters;
79 if (code == 0) {
80 Q_ASSERT_X(wakeups > 0, "QWaitCondition::wait", "internal error (wakeups)");
81 --wakeups;
82 }
83 qt_report_pthread_error(code: pthread_mutex_unlock(mutex: &mutex), where: "QWaitCondition::wait()",
84 what: "mutex unlock");
85
86 if (code && code != ETIMEDOUT)
87 qt_report_pthread_error(code, where: "QWaitCondition::wait()", what: "cv wait");
88
89 return (code == 0);
90 }
91};
92
93QWaitCondition::QWaitCondition()
94{
95 d = new QWaitConditionPrivate;
96 qt_report_pthread_error(code: pthread_mutex_init(mutex: &d->mutex, mutexattr: nullptr), where: "QWaitCondition", what: "mutex init");
97 qt_initialize_pthread_cond(cond: &d->cond, where: "QWaitCondition");
98 d->waiters = d->wakeups = 0;
99}
100
101QWaitCondition::~QWaitCondition()
102{
103 qt_report_pthread_error(code: pthread_cond_destroy(cond: &d->cond), where: "QWaitCondition", what: "cv destroy");
104 qt_report_pthread_error(code: pthread_mutex_destroy(mutex: &d->mutex), where: "QWaitCondition", what: "mutex destroy");
105 delete d;
106}
107
108void QWaitCondition::wakeOne()
109{
110 qt_report_pthread_error(code: pthread_mutex_lock(mutex: &d->mutex), where: "QWaitCondition::wakeOne()",
111 what: "mutex lock");
112 d->wakeups = qMin(a: d->wakeups + 1, b: d->waiters);
113 qt_report_pthread_error(code: pthread_cond_signal(cond: &d->cond), where: "QWaitCondition::wakeOne()",
114 what: "cv signal");
115 qt_report_pthread_error(code: pthread_mutex_unlock(mutex: &d->mutex), where: "QWaitCondition::wakeOne()",
116 what: "mutex unlock");
117}
118
119void QWaitCondition::wakeAll()
120{
121 qt_report_pthread_error(code: pthread_mutex_lock(mutex: &d->mutex), where: "QWaitCondition::wakeAll()",
122 what: "mutex lock");
123 d->wakeups = d->waiters;
124 qt_report_pthread_error(code: pthread_cond_broadcast(cond: &d->cond), where: "QWaitCondition::wakeAll()",
125 what: "cv broadcast");
126 qt_report_pthread_error(code: pthread_mutex_unlock(mutex: &d->mutex), where: "QWaitCondition::wakeAll()",
127 what: "mutex unlock");
128}
129
130bool QWaitCondition::wait(QMutex *mutex, unsigned long time)
131{
132 if (time == std::numeric_limits<unsigned long>::max())
133 return wait(lockedMutex: mutex, deadline: QDeadlineTimer(QDeadlineTimer::Forever));
134 return wait(lockedMutex: mutex, deadline: QDeadlineTimer(time));
135}
136
137bool QWaitCondition::wait(QMutex *mutex, QDeadlineTimer deadline)
138{
139 if (!mutex)
140 return false;
141
142 qt_report_pthread_error(code: pthread_mutex_lock(mutex: &d->mutex), where: "QWaitCondition::wait()", what: "mutex lock");
143 ++d->waiters;
144 mutex->unlock();
145
146 bool returnValue = d->wait(deadline);
147
148 mutex->lock();
149
150 return returnValue;
151}
152
153bool QWaitCondition::wait(QReadWriteLock *readWriteLock, unsigned long time)
154{
155 if (time == std::numeric_limits<unsigned long>::max())
156 return wait(lockedReadWriteLock: readWriteLock, deadline: QDeadlineTimer(QDeadlineTimer::Forever));
157 return wait(lockedReadWriteLock: readWriteLock, deadline: QDeadlineTimer(time));
158}
159
160bool QWaitCondition::wait(QReadWriteLock *readWriteLock, QDeadlineTimer deadline)
161{
162 using namespace QReadWriteLockStates;
163
164 if (!readWriteLock)
165 return false;
166 auto previousState = QReadWriteLockPrivate::stateForWaitCondition(q: readWriteLock);
167 if (previousState == Unlocked)
168 return false;
169 if (previousState == RecursivelyLocked) {
170 qWarning(msg: "QWaitCondition: cannot wait on QReadWriteLocks with recursive lockForWrite()");
171 return false;
172 }
173
174 qt_report_pthread_error(code: pthread_mutex_lock(mutex: &d->mutex), where: "QWaitCondition::wait()", what: "mutex lock");
175 ++d->waiters;
176
177 readWriteLock->unlock();
178
179 bool returnValue = d->wait(deadline);
180
181 if (previousState == LockedForWrite)
182 readWriteLock->lockForWrite();
183 else
184 readWriteLock->lockForRead();
185
186 return returnValue;
187}
188
189QT_END_NAMESPACE
190

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