Q(Basic)Mutex: add try_lock{,_for,_until} for STL compatibility

Now QBasicMutex is Lockable and QMutex is TimedLockable, which means they can
be used in std::lock_guard, std::unique_lock, std::lock, etc.

[ChangeLog][QtCore][QMutex] QMutex now fully models the TimedLockable
concept by providing the try_lock, try_lock_for and try_lock_until
functions, therefore making it usable in Standard Library lock
management classes and functions.

Change-Id: I7c691481a5781a696701e1ab78186b5cefbd6a87
Reviewed-by: Olivier Goffart (Woboq GmbH) <ogoffart@woboq.com>
This commit is contained in:
Giuseppe D'Angelo 2016-07-14 15:37:55 +01:00
parent a8cf3b0257
commit a594f85d54
3 changed files with 846 additions and 213 deletions

View File

@ -264,6 +264,61 @@ bool QMutex::tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT
return lockInternal(timeout);
}
/*! \fn bool QMutex::try_lock()
\since 5.8
This function is provided for compatibility with the Standard Library
concept \c Lockable. It is equivalent to tryLock().
*/
/*! \fn bool QMutex::try_lock_for(std::chrono::duration<Rep, Period> duration)
\since 5.8
Attempts to lock the mutex. This function returns \c true if the lock
was obtained; otherwise it returns \c false. If another thread has
locked the mutex, this function will wait for at most \a duration
for the mutex to become available.
Note: Passing a negative duration as the \a duration is equivalent to
calling try_lock(). This behavior is different from tryLock.
If the lock was obtained, the mutex must be unlocked with unlock()
before another thread can successfully lock it.
Calling this function multiple times on the same mutex from the
same thread is allowed if this mutex is a
\l{QMutex::Recursive}{recursive mutex}. If this mutex is a
\l{QMutex::NonRecursive}{non-recursive mutex}, this function will
\e always return false when attempting to lock the mutex
recursively.
\sa lock(), unlock()
*/
/*! \fn bool QMutex::try_lock_until(std::chrono::time_point<Clock, Duration> timePoint)
\since 5.8
Attempts to lock the mutex. This function returns \c true if the lock
was obtained; otherwise it returns \c false. If another thread has
locked the mutex, this function will wait at most until \a timePoint
for the mutex to become available.
Note: Passing a \a timePoint which has already passed is equivalent
to calling try_lock. This behavior is different from tryLock.
If the lock was obtained, the mutex must be unlocked with unlock()
before another thread can successfully lock it.
Calling this function multiple times on the same mutex from the
same thread is allowed if this mutex is a
\l{QMutex::Recursive}{recursive mutex}. If this mutex is a
\l{QMutex::NonRecursive}{non-recursive mutex}, this function will
\e always return false when attempting to lock the mutex
recursively.
\sa lock(), unlock()
*/
/*! \fn void QMutex::unlock()
Unlocks the mutex. Attempting to unlock a mutex in a different
thread to the one that locked it results in an error. Unlocking a

View File

@ -44,6 +44,10 @@
#include <QtCore/qatomic.h>
#include <new>
#if QT_HAS_INCLUDE(<chrono>)
# include <chrono>
#endif
QT_BEGIN_NAMESPACE
@ -60,11 +64,13 @@ class QMutexData;
class Q_CORE_EXPORT QBasicMutex
{
public:
// BasicLockable concept
inline void lock() QT_MUTEX_LOCK_NOEXCEPT {
if (!fastTryLock())
lockInternal();
}
// BasicLockable concept
inline void unlock() Q_DECL_NOTHROW {
Q_ASSERT(d_ptr.load()); //mutex must be locked
if (!fastTryUnlock())
@ -75,6 +81,9 @@ public:
return fastTryLock();
}
// Lockable concept
bool try_lock() Q_DECL_NOTHROW { return tryLock(); }
bool isRecursive() Q_DECL_NOTHROW; //### Qt6: remove me
bool isRecursive() const Q_DECL_NOTHROW;
@ -112,10 +121,41 @@ public:
explicit QMutex(RecursionMode mode = NonRecursive);
~QMutex();
// BasicLockable concept
void lock() QT_MUTEX_LOCK_NOEXCEPT;
bool tryLock(int timeout = 0) QT_MUTEX_LOCK_NOEXCEPT;
// BasicLockable concept
void unlock() Q_DECL_NOTHROW;
// Lockable concept
bool try_lock() QT_MUTEX_LOCK_NOEXCEPT { return tryLock(); }
#if QT_HAS_INCLUDE(<chrono>)
// TimedLockable concept
template <class Rep, class Period>
bool try_lock_for(std::chrono::duration<Rep, Period> duration)
{
// § 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 tryLock(0);
return tryLock(std::chrono::duration_cast<std::chrono::milliseconds>(duration).count());
}
// TimedLockable concept
template<class Clock, class Duration>
bool try_lock_until(std::chrono::time_point<Clock, Duration> timePoint)
{
// Implemented in terms of try_lock_for to honor the similar
// requirement in § 30.4.1.3.12 [thread.timedmutex.requirements]
return try_lock_for(timePoint - Clock::now());
}
#endif
bool isRecursive() const Q_DECL_NOTHROW
{ return QBasicMutex::isRecursive(); }
@ -189,9 +229,26 @@ public:
inline void lock() Q_DECL_NOTHROW {}
inline bool tryLock(int timeout = 0) Q_DECL_NOTHROW { Q_UNUSED(timeout); return true; }
inline bool try_lock() Q_DECL_NOTHROW { return true; }
inline void unlock() Q_DECL_NOTHROW {}
inline bool isRecursive() const Q_DECL_NOTHROW { return true; }
#if QT_HAS_INCLUDE(<chrono>) || defined(Q_QDOC)
template <class Rep, class Period>
inline bool try_lock_for(std::chrono::duration<Rep, Period> duration) Q_DECL_NOTHROW
{
Q_UNUSED(duration);
return true;
}
template<class Clock, class Duration>
inline bool try_lock_until(std::chrono::time_point<Clock, Duration> timePoint) Q_DECL_NOTHROW
{
Q_UNUSED(timePoint);
return true;
}
#endif
private:
Q_DISABLE_COPY(QMutex)
};

File diff suppressed because it is too large Load Diff