summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2023-04-27 21:29:46 -0700
committerThiago Macieira <thiago.macieira@intel.com>2023-05-16 19:13:03 -0700
commit37f1fb78eeb107d593f9a7bf0491466a1c60e068 (patch)
treeea73ba33386ec06039fcfcccbbe0e3738bbeda43
parentfda4da6df96f336d03ac1d018cecea03cbf31627 (diff)
downloadqtbase-37f1fb78eeb107d593f9a7bf0491466a1c60e068.tar.gz
QSemaphore: add QDeadlineTimer API
This removes the last use of QtPrivate::convertToMilliseconds(). Change-Id: I6f518d59e63249ddbf43fffd1759fee2e00d36f4 Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io> Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
-rw-r--r--src/corelib/compat/removed_api.cpp1
-rw-r--r--src/corelib/doc/snippets/code/src_corelib_thread_qsemaphore.cpp6
-rw-r--r--src/corelib/thread/qmutex.h30
-rw-r--r--src/corelib/thread/qsemaphore.cpp52
-rw-r--r--src/corelib/thread/qsemaphore.h23
-rw-r--r--tests/auto/corelib/thread/qmutex/tst_qmutex.cpp100
6 files changed, 64 insertions, 148 deletions
diff --git a/src/corelib/compat/removed_api.cpp b/src/corelib/compat/removed_api.cpp
index 1212aef1cb..13a162135a 100644
--- a/src/corelib/compat/removed_api.cpp
+++ b/src/corelib/compat/removed_api.cpp
@@ -605,6 +605,7 @@ QStringView QXmlStreamAttributes::value(QLatin1StringView qualifiedName) const
#if QT_CONFIG(thread)
#include "qmutex.h"
#include "qreadwritelock.h"
+#include "qsemaphore.h"
#endif
// #include "qotherheader.h"
diff --git a/src/corelib/doc/snippets/code/src_corelib_thread_qsemaphore.cpp b/src/corelib/doc/snippets/code/src_corelib_thread_qsemaphore.cpp
index a46e10c7b0..d7c9b900af 100644
--- a/src/corelib/doc/snippets/code/src_corelib_thread_qsemaphore.cpp
+++ b/src/corelib/doc/snippets/code/src_corelib_thread_qsemaphore.cpp
@@ -35,6 +35,12 @@ sem.tryAcquire(250, 1000); // sem.available() == 5, waits 1000 milliseconds a
sem.tryAcquire(3, 30000); // sem.available() == 2, returns true without waiting
//! [3]
+//! [tryAcquire-QDeadlineTimer]
+QSemaphore sem(5); // sem.available() == 5
+sem.tryAcquire(250, QDeadlineTimer(1000)); // sem.available() == 5, waits 1000 milliseconds and returns false
+sem.tryAcquire(3, QDeadlineTimer(30s)); // sem.available() == 2, returns true without waiting
+//! [tryAcquire-QDeadlineTimer]
+
//! [4]
// ... do something that may throw or return early
sem.release();
diff --git a/src/corelib/thread/qmutex.h b/src/corelib/thread/qmutex.h
index 18ffdd2cf4..8a75db012d 100644
--- a/src/corelib/thread/qmutex.h
+++ b/src/corelib/thread/qmutex.h
@@ -8,10 +8,6 @@
#include <QtCore/qatomic.h>
#include <QtCore/qdeadlinetimer.h>
#include <QtCore/qtsan_impl.h>
-#include <new>
-
-#include <chrono>
-#include <limits>
QT_BEGIN_NAMESPACE
@@ -27,32 +23,6 @@ class QMutex;
class QRecursiveMutex;
class QMutexPrivate;
-namespace QtPrivate
-{
- template<class Rep, class Period>
- static int convertToMilliseconds(std::chrono::duration<Rep, Period> duration)
- {
- // N4606 § 30.4.1.3.5 [thread.timedmutex.requirements] specifies that a
- // duration less than or equal to duration.zero() shall result in a
- // try_lock, unlike QMutex's tryLock with a negative duration which
- // results in a lock.
-
- if (duration <= duration.zero())
- return 0;
-
- // when converting from 'duration' to milliseconds, make sure that
- // the result is not shorter than 'duration':
- std::chrono::milliseconds wait = std::chrono::duration_cast<std::chrono::milliseconds>(duration);
- if (wait < duration)
- wait += std::chrono::milliseconds(1);
- Q_ASSERT(wait >= duration);
- const auto ms = wait.count();
- const auto maxInt = (std::numeric_limits<int>::max)();
-
- return ms < maxInt ? int(ms) : maxInt;
- }
-}
-
class Q_CORE_EXPORT QBasicMutex
{
Q_DISABLE_COPY_MOVE(QBasicMutex)
diff --git a/src/corelib/thread/qsemaphore.cpp b/src/corelib/thread/qsemaphore.cpp
index 803aa30bef..af34a7bb39 100644
--- a/src/corelib/thread/qsemaphore.cpp
+++ b/src/corelib/thread/qsemaphore.cpp
@@ -145,10 +145,10 @@ static QBasicAtomicInteger<quint32> *futexHigh32(QBasicAtomicInteger<quintptr> *
}
template <bool IsTimed> bool
-futexSemaphoreTryAcquire_loop(QBasicAtomicInteger<quintptr> &u, quintptr curValue, quintptr nn, int timeout)
+futexSemaphoreTryAcquire_loop(QBasicAtomicInteger<quintptr> &u, quintptr curValue, quintptr nn,
+ QDeadlineTimer timer)
{
- QDeadlineTimer timer(IsTimed ? QDeadlineTimer(timeout) : QDeadlineTimer());
- qint64 remainingTime = timeout * Q_INT64_C(1000) * 1000;
+ qint64 remainingTime = IsTimed ? timer.remainingTimeNSecs() : -1;
int n = int(unsigned(nn));
// we're called after one testAndSet, so start by waiting first
@@ -190,8 +190,13 @@ futexSemaphoreTryAcquire_loop(QBasicAtomicInteger<quintptr> &u, quintptr curValu
}
}
-template <bool IsTimed> bool futexSemaphoreTryAcquire(QBasicAtomicInteger<quintptr> &u, int n, int timeout)
+static constexpr QDeadlineTimer::ForeverConstant Expired =
+ QDeadlineTimer::ForeverConstant(1);
+
+template <typename T> bool
+futexSemaphoreTryAcquire(QBasicAtomicInteger<quintptr> &u, int n, T timeout)
{
+ constexpr bool IsTimed = std::is_same_v<QDeadlineTimer, T>;
// Try to acquire without waiting (we still loop because the testAndSet
// call can fail).
quintptr nn = unsigned(n);
@@ -205,8 +210,13 @@ template <bool IsTimed> bool futexSemaphoreTryAcquire(QBasicAtomicInteger<quintp
if (u.testAndSetOrdered(curValue, newValue, curValue))
return true; // succeeded!
}
- if (timeout == 0)
- return false;
+ if constexpr (IsTimed) {
+ if (timeout.hasExpired())
+ return false;
+ } else {
+ if (timeout == Expired)
+ return false;
+ }
// we need to wait
constexpr quintptr oneWaiter = quintptr(Q_UINT64_C(1) << 32); // zero on 32-bit
@@ -293,7 +303,7 @@ void QSemaphore::acquire(int n)
Q_ASSERT_X(n >= 0, "QSemaphore::acquire", "parameter 'n' must be non-negative");
if (futexAvailable()) {
- futexSemaphoreTryAcquire<false>(u, n, -1);
+ futexSemaphoreTryAcquire(u, n, QDeadlineTimer::Forever);
return;
}
@@ -409,7 +419,7 @@ bool QSemaphore::tryAcquire(int n)
Q_ASSERT_X(n >= 0, "QSemaphore::tryAcquire", "parameter 'n' must be non-negative");
if (futexAvailable())
- return futexSemaphoreTryAcquire<false>(u, n, 0);
+ return futexSemaphoreTryAcquire(u, n, Expired);
const auto locker = qt_scoped_lock(d->mutex);
if (n > d->avail)
@@ -419,6 +429,8 @@ bool QSemaphore::tryAcquire(int n)
}
/*!
+ \fn QSemaphore::tryAcquire(int n, int timeout)
+
Tries to acquire \c n resources guarded by the semaphore and
returns \c true on success. If available() < \a n, this call will
wait for at most \a timeout milliseconds for resources to become
@@ -434,26 +446,40 @@ bool QSemaphore::tryAcquire(int n)
\sa acquire()
*/
-bool QSemaphore::tryAcquire(int n, int timeout)
+
+/*!
+ \since 6.6
+
+ Tries to acquire \c n resources guarded by the semaphore and returns \c
+ true on success. If available() < \a n, this call will wait until \a timer
+ expires for resources to become available.
+
+ Example:
+
+ \snippet code/src_corelib_thread_qsemaphore.cpp tryAcquire-QDeadlineTimer
+
+ \sa acquire()
+*/
+bool QSemaphore::tryAcquire(int n, QDeadlineTimer timer)
{
- if (timeout < 0) {
+ if (timer.isForever()) {
acquire(n);
return true;
}
- if (timeout == 0)
+ if (timer.hasExpired())
return tryAcquire(n);
Q_ASSERT_X(n >= 0, "QSemaphore::tryAcquire", "parameter 'n' must be non-negative");
if (futexAvailable())
- return futexSemaphoreTryAcquire<true>(u, n, timeout);
+ return futexSemaphoreTryAcquire(u, n, timer);
using namespace std::chrono;
const auto sufficientResourcesAvailable = [this, n] { return d->avail >= n; };
auto locker = qt_unique_lock(d->mutex);
- if (!d->cond.wait_for(locker, milliseconds{timeout}, sufficientResourcesAvailable))
+ if (!d->cond.wait_until(locker, timer.deadline<steady_clock>(), sufficientResourcesAvailable))
return false;
d->avail -= n;
return true;
diff --git a/src/corelib/thread/qsemaphore.h b/src/corelib/thread/qsemaphore.h
index 8823468dbc..f20bef4288 100644
--- a/src/corelib/thread/qsemaphore.h
+++ b/src/corelib/thread/qsemaphore.h
@@ -5,16 +5,13 @@
#define QSEMAPHORE_H
#include <QtCore/qglobal.h>
-#include <QtCore/qmutex.h> // for convertToMilliseconds()
-
-#include <chrono>
+#include <QtCore/qdeadlinetimer.h>
QT_REQUIRE_CONFIG(thread);
QT_BEGIN_NAMESPACE
class QSemaphorePrivate;
-
class Q_CORE_EXPORT QSemaphore
{
public:
@@ -23,10 +20,19 @@ public:
void acquire(int n = 1);
bool tryAcquire(int n = 1);
+ QT_CORE_INLINE_SINCE(6, 6)
bool tryAcquire(int n, int timeout);
+ bool tryAcquire(int n, QDeadlineTimer timeout);
+#ifndef Q_QDOC
+ // because tryAcquire(n, QDeadlineTimer::Forever) is tryLock(n, 0)
+ bool tryAcquire(int n, QDeadlineTimer::ForeverConstant)
+ { acquire(n); return true; }
+#endif
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
template <typename Rep, typename Period>
bool tryAcquire(int n, std::chrono::duration<Rep, Period> timeout)
- { return tryAcquire(n, QtPrivate::convertToMilliseconds(timeout)); }
+ { return tryAcquire(n, QDeadlineTimer(timeout)); }
+#endif
void release(int n = 1);
@@ -53,6 +59,13 @@ private:
};
};
+#if QT_CORE_INLINE_IMPL_SINCE(6, 6)
+bool QSemaphore::tryAcquire(int n, int timeout)
+{
+ return tryAcquire(n, QDeadlineTimer(timeout));
+}
+#endif
+
class QSemaphoreReleaser
{
public:
diff --git a/tests/auto/corelib/thread/qmutex/tst_qmutex.cpp b/tests/auto/corelib/thread/qmutex/tst_qmutex.cpp
index 121af38ec6..d2c0a38fd0 100644
--- a/tests/auto/corelib/thread/qmutex/tst_qmutex.cpp
+++ b/tests/auto/corelib/thread/qmutex/tst_qmutex.cpp
@@ -29,8 +29,6 @@ public:
Q_ENUM(TimeUnit)
private slots:
- void convertToMilliseconds_data();
- void convertToMilliseconds();
void tryLock_non_recursive();
void try_lock_for_non_recursive();
void try_lock_until_non_recursive();
@@ -71,104 +69,6 @@ enum {
static constexpr std::chrono::milliseconds waitTimeAsDuration(waitTime);
-void tst_QMutex::convertToMilliseconds_data()
-{
- QTest::addColumn<TimeUnit>("unit");
- QTest::addColumn<double>("doubleValue");
- QTest::addColumn<qint64>("intValue");
- QTest::addColumn<qint64>("expected");
-
- auto add = [](TimeUnit unit, double d, long long i, qint64 expected) {
- const QScopedArrayPointer<char> enumName(QTest::toString(unit));
- QTest::addRow("%s:%f:%lld", enumName.data(), d, i)
- << unit << d << qint64(i) << expected;
- };
-
- auto forAllUnitsAdd = [=](double d, long long i, qint64 expected) {
- for (auto unit : {TimeUnit::Nanoseconds, TimeUnit::Microseconds, TimeUnit::Milliseconds, TimeUnit::Seconds})
- add(unit, d, i, expected);
- };
-
- forAllUnitsAdd(-0.5, -1, 0); // all negative values result in 0
-
- forAllUnitsAdd(0, 0, 0);
-
- add(TimeUnit::Nanoseconds, 1, 1, 1);
- add(TimeUnit::Nanoseconds, 1000 * 1000, 1000 * 1000, 1);
- add(TimeUnit::Nanoseconds, 1000 * 1000 + 0.5, 1000 * 1000 + 1, 2);
-
- add(TimeUnit::Microseconds, 1, 1, 1);
- add(TimeUnit::Microseconds, 1000, 1000, 1);
- add(TimeUnit::Microseconds, 1000 + 0.5, 1000 + 1, 2);
-
- add(TimeUnit::Milliseconds, 1, 1, 1);
- add(TimeUnit::Milliseconds, 1.5, 2, 2);
-
- add(TimeUnit::Seconds, 0.9991, 1, 1000);
-
- //
- // overflowing int results in INT_MAX (equivalent to a spurious wakeup after ~24 days); check it:
- //
-
- // spot on:
- add(TimeUnit::Nanoseconds, INT_MAX * 1000. * 1000, INT_MAX * Q_INT64_C(1000) * 1000, INT_MAX);
- add(TimeUnit::Microseconds, INT_MAX * 1000., INT_MAX * Q_INT64_C(1000), INT_MAX);
- add(TimeUnit::Milliseconds, INT_MAX, INT_MAX, INT_MAX);
-
- // minimally above:
- add(TimeUnit::Nanoseconds, INT_MAX * 1000. * 1000 + 1, INT_MAX * Q_INT64_C(1000) * 1000 + 1, INT_MAX);
- add(TimeUnit::Microseconds, INT_MAX * 1000. + 1, INT_MAX * Q_INT64_C(1000) + 1, INT_MAX);
- add(TimeUnit::Milliseconds, INT_MAX + 1., INT_MAX + Q_INT64_C(1), INT_MAX);
- add(TimeUnit::Seconds, INT_MAX / 1000. + 1, INT_MAX / 1000 + 1, INT_MAX);
-
- // minimally below:
- add(TimeUnit::Nanoseconds, INT_MAX * 1000. * 1000 - 1, INT_MAX * Q_INT64_C(1000) * 1000 - 1, INT_MAX);
- add(TimeUnit::Microseconds, INT_MAX * 1000. - 1, INT_MAX * Q_INT64_C(1000) - 1, INT_MAX);
- add(TimeUnit::Milliseconds, INT_MAX - 0.1, INT_MAX , INT_MAX);
-
-}
-
-void tst_QMutex::convertToMilliseconds()
-{
- QFETCH(TimeUnit, unit);
- QFETCH(double, doubleValue);
- QFETCH(qint64, intValue);
- QFETCH(qint64, expected);
-
- constexpr qint64 maxShort = std::numeric_limits<short>::max();
- constexpr qint64 maxInt = std::numeric_limits<int>::max();
- constexpr qint64 maxUInt = std::numeric_limits<uint>::max();
-
- switch (unit) {
-#define CASE(Unit, Period) \
- case TimeUnit::Unit: \
- DO(double, Period, doubleValue); \
- if (intValue < maxShort) \
- DO(short, Period, short(intValue)); \
- if (intValue < maxInt) \
- DO(int, Period, int(intValue)); \
- DO(qint64, Period, intValue); \
- if (intValue >= 0) { \
- if (intValue < maxUInt) \
- DO(uint, Period, uint(intValue)); \
- DO(quint64, Period, quint64(intValue)); \
- } \
- break
-#define DO(Rep, Period, val) \
- do { \
- const std::chrono::duration<Rep, Period> wait((val)); \
- QCOMPARE(QtPrivate::convertToMilliseconds(wait), expected); \
- } while (0)
-
- CASE(Nanoseconds, std::nano);
- CASE(Microseconds, std::micro);
- CASE(Milliseconds, std::milli);
- CASE(Seconds, std::ratio<1>);
-#undef DO
-#undef CASE
- }
-}
-
void tst_QMutex::tryLock_non_recursive()
{
class Thread : public QThread