lockTry should not be callable on temporary lock

This commit is contained in:
matlabbe
2026-09-27 15:45:32 -07:00
parent dfcffc8d4c
commit 8b1994cc4f
2 changed files with 25 additions and 3 deletions
+14 -3
View File
@@ -177,6 +177,17 @@ public:
* } * }
* @endcode * @endcode
* *
* The object must be named: a temporary would be destroyed, and the mutex unlocked,
* at the end of the expression. lock(), lockTry() and unlock() can only be called on
* a named object, so that `if(UScopeMutex(m, false).lockTry() == 0)` doesn't compile.
* In C++17, the object can be scoped to an if statement instead:
* @code
* if(UScopeMutex sm(m, false); sm.lockTry() == 0)
* {
* // locked here
* } // unlocked here, only if lockTry() succeeded
* @endcode
*
* @see UMutex * @see UMutex
*/ */
class UScopeMutex class UScopeMutex
@@ -215,7 +226,7 @@ public:
* Lock the mutex, if this object doesn't hold it already. * Lock the mutex, if this object doesn't hold it already.
* @return 0 on success, an error code otherwise. * @return 0 on success, an error code otherwise.
*/ */
int lock() int lock() &
{ {
if(locked_) if(locked_)
{ {
@@ -232,7 +243,7 @@ public:
* @return 0 if the mutex is held by this object, EBUSY (or another * @return 0 if the mutex is held by this object, EBUSY (or another
* error code) otherwise. * error code) otherwise.
*/ */
int lockTry() int lockTry() &
{ {
if(locked_) if(locked_)
{ {
@@ -248,7 +259,7 @@ public:
* Unlock the mutex before this object goes out of scope, only if this object locked it. * Unlock the mutex before this object goes out of scope, only if this object locked it.
* @return 0 on success (or if this object didn't hold the mutex), an error code otherwise. * @return 0 on success (or if this object didn't hold the mutex), an error code otherwise.
*/ */
int unlock() int unlock() &
{ {
if(!locked_) if(!locked_)
{ {
+11
View File
@@ -3,6 +3,8 @@
#include <thread> #include <thread>
#include <chrono> #include <chrono>
#include <atomic> #include <atomic>
#include <type_traits>
#include <utility>
#include <vector> #include <vector>
TEST(UMutexTest, Constructor) TEST(UMutexTest, Constructor)
@@ -149,6 +151,15 @@ TEST(UMutexTest, UScopeMutexWithPointer)
t.join(); t.join();
} }
// lock(), lockTry() and unlock() can only be called on a named UScopeMutex: a temporary
// would unlock the mutex at the end of the expression, before the code it should protect.
template<typename T, typename = void>
struct CanLockTry : std::false_type {};
template<typename T>
struct CanLockTry<T, decltype(void(std::declval<T>().lockTry()))> : std::true_type {};
static_assert(CanLockTry<UScopeMutex &>::value, "lockTry() must be callable on a named UScopeMutex");
static_assert(!CanLockTry<UScopeMutex>::value, "lockTry() must not be callable on a temporary UScopeMutex");
TEST(UMutexTest, UScopeMutexDeferredIsNotLocked) TEST(UMutexTest, UScopeMutexDeferredIsNotLocked)
{ {
UMutex mutex; UMutex mutex;