Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qwaitcondition_unix.cpp
Go to the documentation of this file.
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
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, "%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(&condattr);
64 auto destroy = qScopeGuard([&] { pthread_condattr_destroy(&condattr); });
65 if (SteadyClockClockId != CLOCK_REALTIME)
66 pthread_condattr_setclock(&condattr, SteadyClockClockId);
67#endif
68
69 qt_report_pthread_error(pthread_cond_init(cond, attrp), where, "cv init");
70}
71
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(timePoint.time_since_epoch());
79}
80
82{
83public:
84 pthread_mutex_t mutex;
85 pthread_cond_t cond;
88
90 {
91 timespec ti;
93 return pthread_cond_timedwait(&cond, &mutex, &ti);
94 }
95
97 {
98 int code;
99 forever {
100 if (!deadline.isForever()) {
101 code = wait_relative(deadline);
102 } else {
103 code = pthread_cond_wait(&cond, &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(pthread_mutex_unlock(&mutex), "QWaitCondition::wait()",
119 "mutex unlock");
120
121 if (code && code != ETIMEDOUT)
122 qt_report_pthread_error(code, "QWaitCondition::wait()", "cv wait");
123
124 return (code == 0);
125 }
126};
127
129{
131 qt_report_pthread_error(pthread_mutex_init(&d->mutex, nullptr), "QWaitCondition", "mutex init");
132 qt_initialize_pthread_cond(&d->cond, "QWaitCondition");
133 d->waiters = d->wakeups = 0;
134}
135
137{
138 qt_report_pthread_error(pthread_cond_destroy(&d->cond), "QWaitCondition", "cv destroy");
139 qt_report_pthread_error(pthread_mutex_destroy(&d->mutex), "QWaitCondition", "mutex destroy");
140 delete d;
141}
142
144{
145 qt_report_pthread_error(pthread_mutex_lock(&d->mutex), "QWaitCondition::wakeOne()",
146 "mutex lock");
147 d->wakeups = qMin(d->wakeups + 1, d->waiters);
148 qt_report_pthread_error(pthread_cond_signal(&d->cond), "QWaitCondition::wakeOne()",
149 "cv signal");
150 qt_report_pthread_error(pthread_mutex_unlock(&d->mutex), "QWaitCondition::wakeOne()",
151 "mutex unlock");
152}
153
155{
156 qt_report_pthread_error(pthread_mutex_lock(&d->mutex), "QWaitCondition::wakeAll()",
157 "mutex lock");
158 d->wakeups = d->waiters;
159 qt_report_pthread_error(pthread_cond_broadcast(&d->cond), "QWaitCondition::wakeAll()",
160 "cv broadcast");
161 qt_report_pthread_error(pthread_mutex_unlock(&d->mutex), "QWaitCondition::wakeAll()",
162 "mutex unlock");
163}
164
165bool QWaitCondition::wait(QMutex *mutex, unsigned long time)
166{
167 if (time == std::numeric_limits<unsigned long>::max())
169 return wait(mutex, QDeadlineTimer(time));
170}
171
173{
174 if (!mutex)
175 return false;
176
177 qt_report_pthread_error(pthread_mutex_lock(&d->mutex), "QWaitCondition::wait()", "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(readWriteLock, QDeadlineTimer(QDeadlineTimer::Forever));
192 return wait(readWriteLock, QDeadlineTimer(time));
193}
194
196{
197 using namespace QReadWriteLockStates;
198
199 if (!readWriteLock)
200 return false;
201 auto previousState = QReadWriteLockPrivate::stateForWaitCondition(readWriteLock);
202 if (previousState == Unlocked)
203 return false;
204 if (previousState == RecursivelyLocked) {
205 qWarning("QWaitCondition: cannot wait on QReadWriteLocks with recursive lockForWrite()");
206 return false;
207 }
208
209 qt_report_pthread_error(pthread_mutex_lock(&d->mutex), "QWaitCondition::wait()", "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
\inmodule QtCore
qint64 deadline() const noexcept Q_DECL_PURE_FUNCTION
Returns the absolute time point for the deadline stored in QDeadlineTimer object, calculated in milli...
constexpr bool isForever() const noexcept
Returns true if this QDeadlineTimer object never expires, false otherwise.
static constexpr ForeverConstant Forever
\inmodule QtCore
Definition qmutex.h:285
void unlock() noexcept
Unlocks the mutex.
Definition qmutex.h:293
void lock() noexcept
Locks the mutex.
Definition qmutex.h:290
static QReadWriteLockStates::StateForWaitCondition stateForWaitCondition(const QReadWriteLock *lock)
\inmodule QtCore
bool wait(QDeadlineTimer deadline)
int wait_relative(QDeadlineTimer deadline)
bool wait(QMutex *, QDeadlineTimer=QDeadlineTimer(QDeadlineTimer::Forever))
void qErrnoWarning(const char *msg,...)
Combined button and popup list for selecting options.
timespec durationToTimespec(std::chrono::nanoseconds timeout) noexcept
#define forever
Definition qforeach.h:78
#define qWarning
Definition qlogging.h:162
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
static void qt_abstime_for_timeout(timespec *ts, QDeadlineTimer deadline)
static void qt_initialize_pthread_cond(pthread_cond_t *cond, const char *where)
static void qt_report_pthread_error(int code, const char *where, const char *what)
static QT_BEGIN_NAMESPACE constexpr clockid_t SteadyClockClockId
QDeadlineTimer deadline(30s)
QMutex mutex
[2]