summaryrefslogtreecommitdiffstats
path: root/src/corelib/thread/qsemaphore.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/thread/qsemaphore.cpp')
-rw-r--r--src/corelib/thread/qsemaphore.cpp53
1 files changed, 34 insertions, 19 deletions
diff --git a/src/corelib/thread/qsemaphore.cpp b/src/corelib/thread/qsemaphore.cpp
index a7b423e4e7..72781942ac 100644
--- a/src/corelib/thread/qsemaphore.cpp
+++ b/src/corelib/thread/qsemaphore.cpp
@@ -50,7 +50,7 @@ using namespace QtFutex;
A typical application of semaphores is for controlling access to
a circular buffer shared by a producer thread and a consumer
- thread. The \l{Semaphores Example} shows how
+ thread. The \l{Producer and Consumer using Semaphores} example shows how
to use QSemaphore to solve that problem.
A non-computing example of a semaphore would be dining at a
@@ -63,7 +63,8 @@ using namespace QtFutex;
seated (taking the available seats to 5, making the party of 10
people wait longer).
- \sa QSemaphoreReleaser, QMutex, QWaitCondition, QThread, {Semaphores Example}
+ \sa QSemaphoreReleaser, QMutex, QWaitCondition, QThread,
+ {Producer and Consumer using Semaphores}
*/
/*
@@ -148,7 +149,7 @@ template <bool IsTimed> bool
futexSemaphoreTryAcquire_loop(QBasicAtomicInteger<quintptr> &u, quintptr curValue, quintptr nn,
QDeadlineTimer timer)
{
- qint64 remainingTime = IsTimed ? timer.remainingTimeNSecs() : -1;
+ using namespace std::chrono;
int n = int(unsigned(nn));
// we're called after one testAndSet, so start by waiting first
@@ -165,8 +166,8 @@ futexSemaphoreTryAcquire_loop(QBasicAtomicInteger<quintptr> &u, quintptr curValu
}
}
- if (IsTimed && remainingTime > 0) {
- bool timedout = !futexWait(*ptr, curValue, remainingTime);
+ if (IsTimed) {
+ bool timedout = !futexWait(*ptr, curValue, timer);
if (timedout)
return false;
} else {
@@ -174,8 +175,6 @@ futexSemaphoreTryAcquire_loop(QBasicAtomicInteger<quintptr> &u, quintptr curValu
}
curValue = u.loadAcquire();
- if (IsTimed)
- remainingTime = timer.remainingTimeNSecs();
// try to acquire
while (futexAvailCounter(curValue) >= n) {
@@ -185,7 +184,7 @@ futexSemaphoreTryAcquire_loop(QBasicAtomicInteger<quintptr> &u, quintptr curValu
}
// not enough tokens available, put us to wait
- if (remainingTime == 0)
+ if (IsTimed && timer.hasExpired())
return false;
}
}
@@ -250,14 +249,31 @@ futexSemaphoreTryAcquire(QBasicAtomicInteger<quintptr> &u, int n, T timeout)
return false;
}
-class QSemaphorePrivate {
-public:
- explicit QSemaphorePrivate(int n) : avail(n) { }
+namespace QtSemaphorePrivate {
+using namespace QtPrivate;
+struct Layout1
+{
+ alignas(IdealMutexAlignment) std::mutex mutex;
+ qsizetype avail = 0;
+ alignas(IdealMutexAlignment) std::condition_variable cond;
+};
- QtPrivate::mutex mutex;
- QtPrivate::condition_variable cond;
+struct Layout2
+{
+ alignas(IdealMutexAlignment) std::mutex mutex;
+ alignas(IdealMutexAlignment) std::condition_variable cond;
+ qsizetype avail = 0;
+};
- int avail;
+// Choose Layout1 if it is smaller than Layout2. That happens for platforms
+// where sizeof(mutex) is 64.
+using Members = std::conditional_t<sizeof(Layout1) <= sizeof(Layout2), Layout1, Layout2>;
+} // namespace QtSemaphorePrivate
+
+class QSemaphorePrivate : public QtSemaphorePrivate::Members
+{
+public:
+ explicit QSemaphorePrivate(qsizetype n) { avail = n; }
};
/*!
@@ -378,16 +394,15 @@ void QSemaphore::release(int n)
// its acquisition anyway, so it has to wait;
// 2) it did not see the new counter value, in which case its
// futexWait will fail.
- if (futexHasWaiterCount) {
- futexWakeAll(*futexLow32(&u));
+ futexWakeAll(*futexLow32(&u));
+ if (futexHasWaiterCount)
futexWakeAll(*futexHigh32(&u));
- } else {
- futexWakeAll(u);
- }
}
return;
}
+ // Keep mutex locked until after notify_all() lest another thread acquire()s
+ // the semaphore once d->avail == 0 and then destroys it, leaving `d` dangling.
const auto locker = qt_scoped_lock(d->mutex);
d->avail += n;
d->cond.notify_all();