diff options
author | Thiago Macieira <thiago.macieira@intel.com> | 2023-04-27 21:29:46 -0700 |
---|---|---|
committer | Thiago Macieira <thiago.macieira@intel.com> | 2023-05-16 19:13:03 -0700 |
commit | 37f1fb78eeb107d593f9a7bf0491466a1c60e068 (patch) | |
tree | ea73ba33386ec06039fcfcccbbe0e3738bbeda43 | |
parent | fda4da6df96f336d03ac1d018cecea03cbf31627 (diff) | |
download | qtbase-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.cpp | 1 | ||||
-rw-r--r-- | src/corelib/doc/snippets/code/src_corelib_thread_qsemaphore.cpp | 6 | ||||
-rw-r--r-- | src/corelib/thread/qmutex.h | 30 | ||||
-rw-r--r-- | src/corelib/thread/qsemaphore.cpp | 52 | ||||
-rw-r--r-- | src/corelib/thread/qsemaphore.h | 23 | ||||
-rw-r--r-- | tests/auto/corelib/thread/qmutex/tst_qmutex.cpp | 100 |
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 |