Commit b805d853 authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook GitHub Bot

revise Synchronized LockedPtr to use lock types

Summary:
Use `std::unique_lock`, `std::shared_lock`, and `folly::upgrade_lock`. There are two reasons:

* Makes generic the use of `std::unique_lock` with `std::mutex`, which is currently special-cased.
* Permits specializations of `std::unique_lock` and the other lock types to be found automatically.

In particular, this permits the use of `Synchronized<T, DistributedMutex>`, which is only proxy-lockable and not lockable.

Reviewed By: simpkins

Differential Revision: D28705607

fbshipit-source-id: 48daa2910ce16ee4fde6f5ea629a41d9768f3c87
parent 424e569f
...@@ -25,9 +25,12 @@ ...@@ -25,9 +25,12 @@
#pragma once #pragma once
#include <chrono> #include <chrono>
#include <mutex>
#include <shared_mutex>
#include <type_traits> #include <type_traits>
#include <folly/functional/Invoke.h> #include <folly/functional/Invoke.h>
#include <folly/synchronization/Lock.h>
// Android, OSX, and Cygwin don't have timed mutexes // Android, OSX, and Cygwin don't have timed mutexes
#if defined(ANDROID) || defined(__ANDROID__) || defined(__APPLE__) || \ #if defined(ANDROID) || defined(__ANDROID__) || defined(__APPLE__) || \
...@@ -244,6 +247,8 @@ struct LockTraitsImpl<Mutex, MutexLevel::UPGRADE, false> ...@@ -244,6 +247,8 @@ struct LockTraitsImpl<Mutex, MutexLevel::UPGRADE, false>
template <class Mutex> template <class Mutex>
struct LockTraitsImpl<Mutex, MutexLevel::UNIQUE, true> struct LockTraitsImpl<Mutex, MutexLevel::UNIQUE, true>
: public LockTraitsImpl<Mutex, MutexLevel::UNIQUE, false> { : public LockTraitsImpl<Mutex, MutexLevel::UNIQUE, false> {
using lock_type = std::unique_lock<Mutex>;
static constexpr bool is_timed{true}; static constexpr bool is_timed{true};
static constexpr bool is_shared{false}; static constexpr bool is_shared{false};
static constexpr bool is_upgrade{false}; static constexpr bool is_upgrade{false};
...@@ -269,6 +274,8 @@ template <class Mutex> ...@@ -269,6 +274,8 @@ template <class Mutex>
struct LockTraitsImpl<Mutex, MutexLevel::SHARED, true> struct LockTraitsImpl<Mutex, MutexLevel::SHARED, true>
: public LockTraitsImpl<Mutex, MutexLevel::SHARED, false>, : public LockTraitsImpl<Mutex, MutexLevel::SHARED, false>,
public LockTraitsImpl<Mutex, MutexLevel::UNIQUE, true> { public LockTraitsImpl<Mutex, MutexLevel::UNIQUE, true> {
using lock_type = std::shared_lock<Mutex>;
static constexpr bool is_timed{true}; static constexpr bool is_timed{true};
static constexpr bool is_shared{true}; static constexpr bool is_shared{true};
static constexpr bool is_upgrade{false}; static constexpr bool is_upgrade{false};
...@@ -300,6 +307,8 @@ template <class Mutex> ...@@ -300,6 +307,8 @@ template <class Mutex>
struct LockTraitsImpl<Mutex, MutexLevel::UPGRADE, true> struct LockTraitsImpl<Mutex, MutexLevel::UPGRADE, true>
: public LockTraitsImpl<Mutex, MutexLevel::UPGRADE, false>, : public LockTraitsImpl<Mutex, MutexLevel::UPGRADE, false>,
public LockTraitsImpl<Mutex, MutexLevel::SHARED, true> { public LockTraitsImpl<Mutex, MutexLevel::SHARED, true> {
using lock_type = upgrade_lock<Mutex>;
static constexpr bool is_timed{true}; static constexpr bool is_timed{true};
static constexpr bool is_shared{true}; static constexpr bool is_shared{true};
static constexpr bool is_upgrade{true}; static constexpr bool is_upgrade{true};
...@@ -349,6 +358,9 @@ struct LockTraitsImpl<Mutex, MutexLevel::UPGRADE, true> ...@@ -349,6 +358,9 @@ struct LockTraitsImpl<Mutex, MutexLevel::UPGRADE, true>
*/ */
template <template <typename...> class LockTraits> template <template <typename...> class LockTraits>
struct UnlockPolicyExclusive { struct UnlockPolicyExclusive {
template <typename Mutex>
using lock_type = std::unique_lock<Mutex>;
constexpr static bool allows_concurrent_access = false; constexpr static bool allows_concurrent_access = false;
template <typename Mutex> template <typename Mutex>
...@@ -358,6 +370,9 @@ struct UnlockPolicyExclusive { ...@@ -358,6 +370,9 @@ struct UnlockPolicyExclusive {
}; };
template <template <typename...> class LockTraits> template <template <typename...> class LockTraits>
struct UnlockPolicyShared { struct UnlockPolicyShared {
template <typename Mutex>
using lock_type = std::shared_lock<Mutex>;
constexpr static bool allows_concurrent_access = true; constexpr static bool allows_concurrent_access = true;
template <typename Mutex> template <typename Mutex>
...@@ -367,6 +382,9 @@ struct UnlockPolicyShared { ...@@ -367,6 +382,9 @@ struct UnlockPolicyShared {
}; };
template <template <typename...> class LockTraits> template <template <typename...> class LockTraits>
struct UnlockPolicyUpgrade { struct UnlockPolicyUpgrade {
template <typename Mutex>
using lock_type = upgrade_lock<Mutex>;
constexpr static bool allows_concurrent_access = true; constexpr static bool allows_concurrent_access = true;
template <typename Mutex> template <typename Mutex>
...@@ -453,6 +471,8 @@ struct LockTraits : public LockTraitsBase<Mutex> {}; ...@@ -453,6 +471,8 @@ struct LockTraits : public LockTraitsBase<Mutex> {};
struct LockPolicyExclusive : detail::UnlockPolicyExclusive<LockTraits> { struct LockPolicyExclusive : detail::UnlockPolicyExclusive<LockTraits> {
using UnlockPolicy = detail::UnlockPolicyExclusive<LockTraits>; using UnlockPolicy = detail::UnlockPolicyExclusive<LockTraits>;
static constexpr bool is_try = false;
template <class Mutex> template <class Mutex>
static std::true_type lock(Mutex& mutex) { static std::true_type lock(Mutex& mutex) {
LockTraits<Mutex>::lock(mutex); LockTraits<Mutex>::lock(mutex);
...@@ -472,6 +492,8 @@ struct LockPolicyExclusive : detail::UnlockPolicyExclusive<LockTraits> { ...@@ -472,6 +492,8 @@ struct LockPolicyExclusive : detail::UnlockPolicyExclusive<LockTraits> {
struct LockPolicyShared : detail::UnlockPolicyShared<LockTraits> { struct LockPolicyShared : detail::UnlockPolicyShared<LockTraits> {
using UnlockPolicy = detail::UnlockPolicyShared<LockTraits>; using UnlockPolicy = detail::UnlockPolicyShared<LockTraits>;
static constexpr bool is_try = false;
template <class Mutex> template <class Mutex>
static std::true_type lock(Mutex& mutex) { static std::true_type lock(Mutex& mutex) {
LockTraits<Mutex>::lock_shared(mutex); LockTraits<Mutex>::lock_shared(mutex);
...@@ -494,6 +516,8 @@ struct LockPolicyShared : detail::UnlockPolicyShared<LockTraits> { ...@@ -494,6 +516,8 @@ struct LockPolicyShared : detail::UnlockPolicyShared<LockTraits> {
struct LockPolicyUpgrade : detail::UnlockPolicyUpgrade<LockTraits> { struct LockPolicyUpgrade : detail::UnlockPolicyUpgrade<LockTraits> {
using UnlockPolicy = detail::UnlockPolicyUpgrade<LockTraits>; using UnlockPolicy = detail::UnlockPolicyUpgrade<LockTraits>;
static constexpr bool is_try = false;
template <class Mutex> template <class Mutex>
static std::true_type lock(Mutex& mutex) { static std::true_type lock(Mutex& mutex) {
LockTraits<Mutex>::lock_upgrade(mutex); LockTraits<Mutex>::lock_upgrade(mutex);
...@@ -516,6 +540,8 @@ struct LockPolicyUpgrade : detail::UnlockPolicyUpgrade<LockTraits> { ...@@ -516,6 +540,8 @@ struct LockPolicyUpgrade : detail::UnlockPolicyUpgrade<LockTraits> {
struct LockPolicyTryExclusive : detail::UnlockPolicyExclusive<LockTraits> { struct LockPolicyTryExclusive : detail::UnlockPolicyExclusive<LockTraits> {
using UnlockPolicy = detail::UnlockPolicyExclusive<LockTraits>; using UnlockPolicy = detail::UnlockPolicyExclusive<LockTraits>;
static constexpr bool is_try = true;
template <class Mutex> template <class Mutex>
static bool lock(Mutex& mutex) { static bool lock(Mutex& mutex) {
return LockTraits<Mutex>::try_lock(mutex); return LockTraits<Mutex>::try_lock(mutex);
...@@ -529,6 +555,8 @@ struct LockPolicyTryExclusive : detail::UnlockPolicyExclusive<LockTraits> { ...@@ -529,6 +555,8 @@ struct LockPolicyTryExclusive : detail::UnlockPolicyExclusive<LockTraits> {
struct LockPolicyTryShared : detail::UnlockPolicyShared<LockTraits> { struct LockPolicyTryShared : detail::UnlockPolicyShared<LockTraits> {
using UnlockPolicy = detail::UnlockPolicyShared<LockTraits>; using UnlockPolicy = detail::UnlockPolicyShared<LockTraits>;
static constexpr bool is_try = true;
template <class Mutex> template <class Mutex>
static bool lock(Mutex& mutex) { static bool lock(Mutex& mutex) {
return LockTraits<Mutex>::try_lock_shared(mutex); return LockTraits<Mutex>::try_lock_shared(mutex);
...@@ -542,102 +570,12 @@ struct LockPolicyTryShared : detail::UnlockPolicyShared<LockTraits> { ...@@ -542,102 +570,12 @@ struct LockPolicyTryShared : detail::UnlockPolicyShared<LockTraits> {
struct LockPolicyTryUpgrade : detail::UnlockPolicyUpgrade<LockTraits> { struct LockPolicyTryUpgrade : detail::UnlockPolicyUpgrade<LockTraits> {
using UnlockPolicy = detail::UnlockPolicyUpgrade<LockTraits>; using UnlockPolicy = detail::UnlockPolicyUpgrade<LockTraits>;
static constexpr bool is_try = true;
template <class Mutex> template <class Mutex>
static bool lock(Mutex& mutex) { static bool lock(Mutex& mutex) {
return LockTraits<Mutex>::try_lock_upgrade(mutex); return LockTraits<Mutex>::try_lock_upgrade(mutex);
} }
}; };
/*****************************************************************************
* Policies for all the transitions from possible mutex levels
****************************************************************************/
/**
* A lock policy with the following mapping
*
* lock() -> unlock_upgrade_and_lock()
* unlock() -> unlock()
* try_lock_for -> try_unlock_upgrade_and_lock_for()
*/
struct LockPolicyFromUpgradeToExclusive : LockPolicyExclusive {
template <class Mutex>
static std::true_type lock(Mutex& mutex) {
LockTraits<Mutex>::unlock_upgrade_and_lock(mutex);
return std::true_type{};
}
template <class Mutex, class Rep, class Period>
static bool try_lock_for(
Mutex& mutex, const std::chrono::duration<Rep, Period>& timeout) {
return LockTraits<Mutex>::try_unlock_upgrade_and_lock_for(mutex, timeout);
}
};
/**
* A lock policy with the following mapping
*
* lock() -> unlock_and_lock_upgrade()
* unlock() -> unlock_upgrade()
* try_lock_for -> unlock_and_lock_upgrade()
*/
struct LockPolicyFromExclusiveToUpgrade : LockPolicyUpgrade {
template <class Mutex>
static std::true_type lock(Mutex& mutex) {
LockTraits<Mutex>::unlock_and_lock_upgrade(mutex);
return std::true_type{};
}
template <class Mutex, class Rep, class Period>
static bool try_lock_for(
Mutex& mutex, const std::chrono::duration<Rep, Period>&) {
LockTraits<Mutex>::unlock_and_lock_upgrade(mutex);
// downgrade should be non blocking and should succeed
return true;
}
};
/**
* A lock policy with the following mapping
*
* lock() -> unlock_upgrade_and_lock_shared()
* unlock() -> unlock_shared()
* try_lock_for -> unlock_upgrade_and_lock_shared()
*/
struct LockPolicyFromUpgradeToShared : LockPolicyShared {
template <class Mutex>
static std::true_type lock(Mutex& mutex) {
LockTraits<Mutex>::unlock_upgrade_and_lock_shared(mutex);
return std::true_type{};
}
template <class Mutex, class Rep, class Period>
static bool try_lock_for(
Mutex& mutex, const std::chrono::duration<Rep, Period>&) {
LockTraits<Mutex>::unlock_upgrade_and_lock_shared(mutex);
// downgrade should be non blocking and should succeed
return true;
}
};
/**
* A lock policy with the following mapping
*
* lock() -> unlock_and_lock_shared()
* unlock() -> unlock_shared()
* try_lock_for() -> unlock_and_lock_shared()
*/
struct LockPolicyFromExclusiveToShared : LockPolicyShared {
template <class Mutex>
static std::true_type lock(Mutex& mutex) {
LockTraits<Mutex>::unlock_and_lock_shared(mutex);
return std::true_type{};
}
template <class Mutex, class Rep, class Period>
static bool try_lock_for(
Mutex& mutex, const std::chrono::duration<Rep, Period>&) {
LockTraits<Mutex>::unlock_and_lock_shared(mutex);
// downgrade should be non blocking and should succeed
return true;
}
};
} // namespace folly } // namespace folly
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include <folly/Utility.h> #include <folly/Utility.h>
#include <folly/container/Foreach.h> #include <folly/container/Foreach.h>
#include <folly/functional/ApplyTuple.h> #include <folly/functional/ApplyTuple.h>
#include <folly/synchronization/Lock.h>
#include <glog/logging.h> #include <glog/logging.h>
...@@ -821,7 +822,15 @@ struct Synchronized : public SynchronizedBase< ...@@ -821,7 +822,15 @@ struct Synchronized : public SynchronizedBase<
: datum_{std::get<IndicesOne>(std::move(datumArgs))...}, : datum_{std::get<IndicesOne>(std::move(datumArgs))...},
mutex_{std::get<IndicesTwo>(std::move(mutexArgs))...} {} mutex_{std::get<IndicesTwo>(std::move(mutexArgs))...} {}
// Synchronized data members // simulacrum of data members - keep data members in sync!
// LockedPtr needs offsetof() which is specified only for standard-layout
// types which Synchronized is not so we define a simulacrum for offsetof
struct Simulacrum {
aligned_storage_for_t<DataType> datum_;
aligned_storage_for_t<MutexType> mutex_;
};
// data members - keep simulacrum of data members in sync!
T datum_; T datum_;
mutable Mutex mutex_; mutable Mutex mutex_;
}; };
...@@ -1018,311 +1027,32 @@ auto lock(Synchronized& synchronized, Args&&... args) { ...@@ -1018,311 +1027,32 @@ auto lock(Synchronized& synchronized, Args&&... args) {
} // namespace detail } // namespace detail
/**
* A helper base class for implementing LockedPtr.
*
* The main reason for having this as a separate class is so we can specialize
* it for std::mutex, so we can expose a std::unique_lock to the caller
* when std::mutex is being used. This allows callers to use a
* std::condition_variable with the mutex from a Synchronized<T, std::mutex>.
*
* We don't use std::unique_lock with other Mutex types since it makes the
* LockedPtr class slightly larger, and it makes the logic to support
* ScopedUnlocker slightly more complicated. std::mutex is the only one that
* really seems to benefit from the unique_lock. std::condition_variable
* itself only supports std::unique_lock<std::mutex>, so there doesn't seem to
* be any real benefit to exposing the unique_lock with other mutex types.
*
* Note that the SynchronizedType template parameter may or may not be const
* qualified.
*/
template <class SynchronizedType, class Mutex, class LockPolicy>
class LockedPtrBase {
public:
using MutexType = Mutex;
friend class folly::ScopedUnlocker<SynchronizedType, LockPolicy>;
/**
* Friend all instantiations of LockedPtr and LockedPtrBase
*/
template <typename S, typename L>
friend class folly::LockedPtr;
template <typename S, typename M, typename L>
friend class LockedPtrBase;
/**
* Destructor releases.
*/
~LockedPtrBase() {
if (parent_) {
LockPolicy::unlock(parent_->mutex_);
}
}
/**
* Unlock the synchronized data.
*
* The LockedPtr can no longer be dereferenced after unlock() has been
* called. isValid() will return false on an unlocked LockedPtr.
*
* unlock() can only be called on a LockedPtr that is valid.
*/
void unlock() {
DCHECK(parent_ != nullptr);
LockPolicy::unlock(parent_->mutex_);
parent_ = nullptr;
}
protected:
LockedPtrBase() {}
explicit LockedPtrBase(SynchronizedType* parent) : parent_(parent) {
DCHECK(parent);
if (!LockPolicy::lock(parent_->mutex_)) {
parent_ = nullptr;
}
}
template <class Rep, class Period>
LockedPtrBase(
SynchronizedType* parent,
const std::chrono::duration<Rep, Period>& timeout) {
if (LockPolicy::try_lock_for(parent->mutex_, timeout)) {
this->parent_ = parent;
}
}
LockedPtrBase(LockedPtrBase&& rhs) noexcept
: parent_{std::exchange(rhs.parent_, nullptr)} {}
LockedPtrBase& operator=(LockedPtrBase&& rhs) noexcept {
assignImpl(*this, rhs);
return *this;
}
/**
* Templated move construct and assignment operators
*
* These allow converting LockedPtr types that have the same unlocking
* policy to each other. This allows us to write code like
*
* auto wlock = sync.wlock();
* wlock.unlock();
*
* auto ulock = sync.ulock();
* wlock = ulock.moveFromUpgradeToWrite();
*/
template <typename LockPolicyType>
LockedPtrBase(
LockedPtrBase<SynchronizedType, Mutex, LockPolicyType>&& rhs) noexcept
: parent_{std::exchange(rhs.parent_, nullptr)} {}
template <typename LockPolicyType>
LockedPtrBase& operator=(
LockedPtrBase<SynchronizedType, Mutex, LockPolicyType>&& rhs) noexcept {
assignImpl(*this, rhs);
return *this;
}
/**
* Implementation for the assignment operator
*/
template <typename LockPolicyLhs, typename LockPolicyRhs>
void assignImpl(
LockedPtrBase<SynchronizedType, Mutex, LockPolicyLhs>& lhs,
LockedPtrBase<SynchronizedType, Mutex, LockPolicyRhs>& rhs) noexcept {
if (lhs.parent_) {
LockPolicy::unlock(lhs.parent_->mutex_);
}
lhs.parent_ = std::exchange(rhs.parent_, nullptr);
}
using UnlockerData = SynchronizedType*;
/**
* Get a pointer to the Synchronized object from the UnlockerData.
*
* In the generic case UnlockerData is just the Synchronized pointer,
* so we return it as is. (This function is more interesting in the
* std::mutex specialization below.)
*/
static SynchronizedType* getSynchronized(UnlockerData data) { return data; }
UnlockerData releaseLock() {
DCHECK(parent_ != nullptr);
auto current = parent_;
parent_ = nullptr;
LockPolicy::unlock(current->mutex_);
return current;
}
void reacquireLock(UnlockerData&& data) {
DCHECK(parent_ == nullptr);
parent_ = data;
LockPolicy::lock(parent_->mutex_);
}
SynchronizedType* parent_ = nullptr;
};
/**
* LockedPtrBase specialization for use with std::mutex.
*
* When std::mutex is used we use a std::unique_lock to hold the mutex.
* This makes it possible to use std::condition_variable with a
* Synchronized<T, std::mutex>.
*/
template <class SynchronizedType, class LockPolicy>
class LockedPtrBase<SynchronizedType, std::mutex, LockPolicy> {
public:
using MutexType = std::mutex;
friend class folly::ScopedUnlocker<SynchronizedType, LockPolicy>;
/**
* Friend all instantiations of LockedPtr and LockedPtrBase
*/
template <typename S, typename L>
friend class folly::LockedPtr;
template <typename S, typename M, typename L>
friend class LockedPtrBase;
/**
* Destructor releases.
*/
~LockedPtrBase() {
// The std::unique_lock will automatically release the lock when it is
// destroyed, so we don't need to do anything extra here.
}
LockedPtrBase(LockedPtrBase&& rhs) noexcept
: lock_{std::move(rhs.lock_)},
parent_{std::exchange(rhs.parent_, nullptr)} {}
LockedPtrBase& operator=(LockedPtrBase&& rhs) noexcept {
assignImpl(*this, rhs);
return *this;
}
/**
* Templated move construct and assignment operators
*
* These allow converting LockedPtr types that have the same unlocking
* policy to each other.
*/
template <typename LockPolicyType>
LockedPtrBase(LockedPtrBase<SynchronizedType, std::mutex, LockPolicyType>&&
other) noexcept
: lock_{std::move(other.lock_)},
parent_{std::exchange(other.parent_, nullptr)} {}
template <typename LockPolicyType>
LockedPtrBase& operator=(
LockedPtrBase<SynchronizedType, std::mutex, LockPolicyType>&&
rhs) noexcept {
assignImpl(*this, rhs);
return *this;
}
/**
* Implementation for the assignment operator
*/
template <typename LockPolicyLhs, typename LockPolicyRhs>
void assignImpl(
LockedPtrBase<SynchronizedType, std::mutex, LockPolicyLhs>& lhs,
LockedPtrBase<SynchronizedType, std::mutex, LockPolicyRhs>&
rhs) noexcept {
lhs.lock_ = std::move(rhs.lock_);
lhs.parent_ = std::exchange(rhs.parent_, nullptr);
}
/**
* Get a reference to the std::unique_lock.
*
* This is provided so that callers can use Synchronized<T, std::mutex>
* with a std::condition_variable.
*
* While this API could be used to bypass the normal Synchronized APIs and
* manually interact with the underlying unique_lock, this is strongly
* discouraged.
*/
std::unique_lock<std::mutex>& getUniqueLock() { return lock_; }
/**
* Unlock the synchronized data.
*
* The LockedPtr can no longer be dereferenced after unlock() has been
* called. isValid() will return false on an unlocked LockedPtr.
*
* unlock() can only be called on a LockedPtr that is valid.
*/
void unlock() {
DCHECK(parent_ != nullptr);
lock_.unlock();
parent_ = nullptr;
}
protected:
LockedPtrBase() {}
explicit LockedPtrBase(SynchronizedType* parent)
: lock_{parent->mutex_, std::adopt_lock}, parent_{parent} {
DCHECK(parent);
if (!LockPolicy::lock(parent_->mutex_)) {
parent_ = nullptr;
lock_.release();
}
}
using UnlockerData =
std::pair<std::unique_lock<std::mutex>, SynchronizedType*>;
static SynchronizedType* getSynchronized(const UnlockerData& data) {
return data.second;
}
UnlockerData releaseLock() {
DCHECK(parent_ != nullptr);
UnlockerData data(std::move(lock_), parent_);
parent_ = nullptr;
data.first.unlock();
return data;
}
void reacquireLock(UnlockerData&& data) {
lock_ = std::move(data.first);
lock_.lock();
parent_ = data.second;
}
// The specialization for std::mutex does have to store slightly more
// state than the default implementation.
std::unique_lock<std::mutex> lock_;
SynchronizedType* parent_ = nullptr;
};
/** /**
* This class temporarily unlocks a LockedPtr in a scoped manner. * This class temporarily unlocks a LockedPtr in a scoped manner.
*/ */
template <class SynchronizedType, class LockPolicy> template <class SynchronizedType, class LockPolicy>
class ScopedUnlocker { class ScopedUnlocker {
public: public:
explicit ScopedUnlocker(LockedPtr<SynchronizedType, LockPolicy>* p) explicit ScopedUnlocker(LockedPtr<SynchronizedType, LockPolicy>* p) noexcept
: ptr_(p), data_(ptr_->releaseLock()) {} : ptr_(p), parent_(p->parent()) {
ptr_->releaseLock();
}
ScopedUnlocker(const ScopedUnlocker&) = delete; ScopedUnlocker(const ScopedUnlocker&) = delete;
ScopedUnlocker& operator=(const ScopedUnlocker&) = delete; ScopedUnlocker& operator=(const ScopedUnlocker&) = delete;
ScopedUnlocker(ScopedUnlocker&& other) noexcept ScopedUnlocker(ScopedUnlocker&& other) noexcept
: ptr_(std::exchange(other.ptr_, nullptr)), : ptr_(std::exchange(other.ptr_, nullptr)),
data_(std::move(other.data_)) {} parent_(std::exchange(other.parent_, nullptr)) {}
ScopedUnlocker& operator=(ScopedUnlocker&& other) = delete; ScopedUnlocker& operator=(ScopedUnlocker&& other) = delete;
~ScopedUnlocker() { ~ScopedUnlocker() noexcept(false) {
if (ptr_) { if (ptr_) {
ptr_->reacquireLock(std::move(data_)); ptr_->reacquireLock(parent_);
} }
} }
/**
* Return a pointer to the Synchronized object used by this ScopedUnlocker.
*/
SynchronizedType* getSynchronized() const {
return LockedPtr<SynchronizedType, LockPolicy>::getSynchronized(data_);
}
private: private:
using Data = typename LockedPtr<SynchronizedType, LockPolicy>::UnlockerData;
LockedPtr<SynchronizedType, LockPolicy>* ptr_{nullptr}; LockedPtr<SynchronizedType, LockPolicy>* ptr_{nullptr};
Data data_; SynchronizedType* parent_{nullptr};
}; };
/** /**
...@@ -1336,49 +1066,42 @@ class ScopedUnlocker { ...@@ -1336,49 +1066,42 @@ class ScopedUnlocker {
* exclusive or shared mode. * exclusive or shared mode.
*/ */
template <class SynchronizedType, class LockPolicy> template <class SynchronizedType, class LockPolicy>
class LockedPtr : public LockedPtrBase< class LockedPtr {
SynchronizedType,
typename SynchronizedType::MutexType,
LockPolicy> {
private: private:
using Base = LockedPtrBase<
SynchronizedType,
typename SynchronizedType::MutexType,
LockPolicy>;
constexpr static bool AllowsConcurrentAccess = constexpr static bool AllowsConcurrentAccess =
LockPolicy::allows_concurrent_access; LockPolicy::allows_concurrent_access;
using UnlockerData = typename Base::UnlockerData;
// CDataType is the DataType with the appropriate const-qualification using CDataType = // the DataType with the appropriate const-qualification
using CDataType =
detail::SynchronizedDataType<SynchronizedType, AllowsConcurrentAccess>; detail::SynchronizedDataType<SynchronizedType, AllowsConcurrentAccess>;
// Enable only if the unlock policy of the other LockPolicy is the same as
// ours
template <typename LockPolicyOther> template <typename LockPolicyOther>
using EnableIfSameUnlockPolicy = std::enable_if_t<std::is_same< using EnableIfSameUnlockPolicy = std::enable_if_t<std::is_same<
typename LockPolicy::UnlockPolicy, typename LockPolicy::UnlockPolicy,
typename LockPolicyOther::UnlockPolicy>::value>; typename LockPolicyOther::UnlockPolicy>::value>;
// friend other LockedPtr types template <typename, typename>
template <typename SynchronizedTypeOther, typename LockPolicyOther>
friend class LockedPtr; friend class LockedPtr;
friend class ScopedUnlocker<SynchronizedType, LockPolicy>;
public: public:
using DataType = typename SynchronizedType::DataType; using DataType = typename SynchronizedType::DataType;
using MutexType = typename SynchronizedType::MutexType; using MutexType = typename SynchronizedType::MutexType;
using Synchronized = typename std::remove_const<SynchronizedType>::type; using Synchronized = typename std::remove_const<SynchronizedType>::type;
friend class ScopedUnlocker<SynchronizedType, LockPolicy>; using LockType = typename LockPolicy::template lock_type<MutexType>;
/** /**
* Creates an uninitialized LockedPtr. * Creates an uninitialized LockedPtr.
* *
* Dereferencing an uninitialized LockedPtr is not allowed. * Dereferencing an uninitialized LockedPtr is not allowed.
*/ */
LockedPtr() {} LockedPtr() = default;
/** /**
* Takes a Synchronized<T> and locks it. * Takes a Synchronized<T> and locks it.
*/ */
explicit LockedPtr(SynchronizedType* parent) : Base(parent) {} explicit LockedPtr(SynchronizedType* parent)
: lock_{!parent ? LockType{} : doLock(parent->mutex_)} {}
/** /**
* Takes a Synchronized<T> and attempts to lock it, within the specified * Takes a Synchronized<T> and attempts to lock it, within the specified
...@@ -1392,7 +1115,7 @@ class LockedPtr : public LockedPtrBase< ...@@ -1392,7 +1115,7 @@ class LockedPtr : public LockedPtrBase<
LockedPtr( LockedPtr(
SynchronizedType* parent, SynchronizedType* parent,
const std::chrono::duration<Rep, Period>& timeout) const std::chrono::duration<Rep, Period>& timeout)
: Base(parent, timeout) {} : lock_{parent ? LockType{parent->mutex_, timeout} : LockType{}} {}
/** /**
* Move constructor. * Move constructor.
...@@ -1401,8 +1124,9 @@ class LockedPtr : public LockedPtrBase< ...@@ -1401,8 +1124,9 @@ class LockedPtr : public LockedPtrBase<
template < template <
typename LockPolicyType, typename LockPolicyType,
EnableIfSameUnlockPolicy<LockPolicyType>* = nullptr> EnableIfSameUnlockPolicy<LockPolicyType>* = nullptr>
LockedPtr(LockedPtr<SynchronizedType, LockPolicyType>&& other) noexcept explicit LockedPtr(
: Base{std::move(other)} {} LockedPtr<SynchronizedType, LockPolicyType>&& other) noexcept
: lock_{std::move(other.lock_)} {}
/** /**
* Move assignment operator. * Move assignment operator.
...@@ -1413,7 +1137,7 @@ class LockedPtr : public LockedPtrBase< ...@@ -1413,7 +1137,7 @@ class LockedPtr : public LockedPtrBase<
EnableIfSameUnlockPolicy<LockPolicyType>* = nullptr> EnableIfSameUnlockPolicy<LockPolicyType>* = nullptr>
LockedPtr& operator=( LockedPtr& operator=(
LockedPtr<SynchronizedType, LockPolicyType>&& other) noexcept { LockedPtr<SynchronizedType, LockPolicyType>&& other) noexcept {
Base::operator=(std::move(other)); lock_ = std::move(other.lock_);
return *this; return *this;
} }
...@@ -1426,7 +1150,31 @@ class LockedPtr : public LockedPtrBase< ...@@ -1426,7 +1150,31 @@ class LockedPtr : public LockedPtrBase<
/** /**
* Destructor releases. * Destructor releases.
*/ */
~LockedPtr() {} ~LockedPtr() = default;
/**
* Access the underlying lock object.
*/
LockType& as_lock() noexcept { return lock_; }
LockType const& as_lock() const noexcept { return lock_; }
/**
* Deprecated. For backward compatibility. Use as_lock() instead.
*/
template <
typename M = MutexType,
std::enable_if_t<std::is_same<M, std::mutex>::value, int> = 0>
LockType& getUniqueLock() noexcept {
static_assert(std::is_same<M, MutexType>::value, "mismatch");
return as_lock();
}
template <
typename M = MutexType,
std::enable_if_t<std::is_same<M, std::mutex>::value, int> = 0>
LockType const& getUniqueLock() const noexcept {
static_assert(std::is_same<M, MutexType>::value, "mismatch");
return as_lock();
}
/** /**
* Check if this LockedPtr is uninitialized, or points to valid locked data. * Check if this LockedPtr is uninitialized, or points to valid locked data.
...@@ -1438,28 +1186,30 @@ class LockedPtr : public LockedPtrBase< ...@@ -1438,28 +1186,30 @@ class LockedPtr : public LockedPtrBase<
* Methods such as scopedUnlock() reset the LockedPtr to null for the * Methods such as scopedUnlock() reset the LockedPtr to null for the
* duration of the unlock. * duration of the unlock.
*/ */
bool isNull() const { return this->parent_ == nullptr; } bool isNull() const { return !lock_.owns_lock(); }
/** /**
* Explicit boolean conversion. * Explicit boolean conversion.
* *
* Returns !isNull() * Returns !isNull()
*/ */
explicit operator bool() const { return this->parent_ != nullptr; } explicit operator bool() const { return lock_.owns_lock(); }
/** /**
* Access the locked data. * Access the locked data.
* *
* This method should only be used if the LockedPtr is valid. * This method should only be used if the LockedPtr is valid.
*/ */
CDataType* operator->() const { return &this->parent_->datum_; } CDataType* operator->() const { return std::addressof(parent()->datum_); }
/** /**
* Access the locked data. * Access the locked data.
* *
* This method should only be used if the LockedPtr is valid. * This method should only be used if the LockedPtr is valid.
*/ */
CDataType& operator*() const { return this->parent_->datum_; } CDataType& operator*() const { return parent()->datum_; }
void unlock() noexcept { lock_ = {}; }
/** /**
* Locks that allow concurrent access (shared, upgrade) force const * Locks that allow concurrent access (shared, upgrade) force const
...@@ -1486,7 +1236,7 @@ class LockedPtr : public LockedPtrBase< ...@@ -1486,7 +1236,7 @@ class LockedPtr : public LockedPtrBase<
"asNonConstUnsafe() is only available on non-exclusive locks" "asNonConstUnsafe() is only available on non-exclusive locks"
" acquired in a non-const context"); " acquired in a non-const context");
return this->parent_->datum_; return parent()->datum_;
} }
/** /**
...@@ -1512,10 +1262,9 @@ class LockedPtr : public LockedPtrBase< ...@@ -1512,10 +1262,9 @@ class LockedPtr : public LockedPtrBase<
typename SyncType = SynchronizedType, typename SyncType = SynchronizedType,
typename = typename std::enable_if< typename = typename std::enable_if<
LockTraits<typename SyncType::MutexType>::is_upgrade>::type> LockTraits<typename SyncType::MutexType>::is_upgrade>::type>
LockedPtr<SynchronizedType, LockPolicyFromUpgradeToExclusive> LockedPtr<SynchronizedType, LockPolicyExclusive> moveFromUpgradeToWrite() {
moveFromUpgradeToWrite() { static_assert(std::is_same<SyncType, SynchronizedType>::value, "mismatch");
return LockedPtr<SynchronizedType, LockPolicyFromUpgradeToExclusive>( return transition_to_unique_lock(lock_);
std::exchange(this->parent_, nullptr));
} }
/** /**
...@@ -1526,10 +1275,9 @@ class LockedPtr : public LockedPtrBase< ...@@ -1526,10 +1275,9 @@ class LockedPtr : public LockedPtrBase<
typename SyncType = SynchronizedType, typename SyncType = SynchronizedType,
typename = typename std::enable_if< typename = typename std::enable_if<
LockTraits<typename SyncType::MutexType>::is_upgrade>::type> LockTraits<typename SyncType::MutexType>::is_upgrade>::type>
LockedPtr<SynchronizedType, LockPolicyFromExclusiveToUpgrade> LockedPtr<SynchronizedType, LockPolicyUpgrade> moveFromWriteToUpgrade() {
moveFromWriteToUpgrade() { static_assert(std::is_same<SyncType, SynchronizedType>::value, "mismatch");
return LockedPtr<SynchronizedType, LockPolicyFromExclusiveToUpgrade>( return transition_to_upgrade_lock(lock_);
std::exchange(this->parent_, nullptr));
} }
/** /**
...@@ -1540,10 +1288,9 @@ class LockedPtr : public LockedPtrBase< ...@@ -1540,10 +1288,9 @@ class LockedPtr : public LockedPtrBase<
typename SyncType = SynchronizedType, typename SyncType = SynchronizedType,
typename = typename std::enable_if< typename = typename std::enable_if<
LockTraits<typename SyncType::MutexType>::is_upgrade>::type> LockTraits<typename SyncType::MutexType>::is_upgrade>::type>
LockedPtr<SynchronizedType, LockPolicyFromUpgradeToShared> LockedPtr<SynchronizedType, LockPolicyShared> moveFromUpgradeToRead() {
moveFromUpgradeToRead() { static_assert(std::is_same<SyncType, SynchronizedType>::value, "mismatch");
return LockedPtr<SynchronizedType, LockPolicyFromUpgradeToShared>( return transition_to_shared_lock(lock_);
std::exchange(this->parent_, nullptr));
} }
/** /**
...@@ -1554,11 +1301,51 @@ class LockedPtr : public LockedPtrBase< ...@@ -1554,11 +1301,51 @@ class LockedPtr : public LockedPtrBase<
typename SyncType = SynchronizedType, typename SyncType = SynchronizedType,
typename = typename std::enable_if< typename = typename std::enable_if<
LockTraits<typename SyncType::MutexType>::is_upgrade>::type> LockTraits<typename SyncType::MutexType>::is_upgrade>::type>
LockedPtr<SynchronizedType, LockPolicyFromExclusiveToShared> LockedPtr<SynchronizedType, LockPolicyShared> moveFromWriteToRead() {
moveFromWriteToRead() { static_assert(std::is_same<SyncType, SynchronizedType>::value, "mismatch");
return LockedPtr<SynchronizedType, LockPolicyFromExclusiveToShared>( return transition_to_shared_lock(lock_);
std::exchange(this->parent_, nullptr));
} }
private:
/* implicit */ LockedPtr(LockType lock) noexcept : lock_{std::move(lock)} {}
template <
typename MT,
typename LT = LockType,
typename LP = LockPolicy,
std::enable_if_t<LP::is_try, int> = 0>
FOLLY_ERASE static LT doLock(MT& mutex) {
return LT{mutex, std::try_to_lock};
}
template <
typename MT,
typename LT = LockType,
typename LP = LockPolicy,
std::enable_if_t<!LP::is_try, int> = 0>
FOLLY_ERASE static LT doLock(MT& mutex) {
return LT{mutex};
}
void releaseLock() noexcept {
DCHECK(lock_.owns_lock());
lock_ = {};
}
void reacquireLock(SynchronizedType* parent) {
DCHECK(parent);
DCHECK(!lock_.owns_lock());
lock_ = doLock(parent->mutex_);
}
SynchronizedType* parent() const {
using simulacrum = typename SynchronizedType::Simulacrum;
static_assert(sizeof(simulacrum) == sizeof(SynchronizedType), "mismatch");
static_assert(alignof(simulacrum) == alignof(SynchronizedType), "mismatch");
constexpr auto off = offsetof(simulacrum, mutex_);
const auto raw = reinterpret_cast<char*>(lock_.mutex());
return reinterpret_cast<SynchronizedType*>(raw - (raw ? off : 0));
}
LockType lock_;
}; };
/** /**
......
...@@ -256,30 +256,6 @@ TEST(LockTraits, LockPolicy) { ...@@ -256,30 +256,6 @@ TEST(LockTraits, LockPolicy) {
mutex.lock_upgrade(); mutex.lock_upgrade();
LockPolicyUpgrade::unlock(mutex); LockPolicyUpgrade::unlock(mutex);
mutex.lock_upgrade();
LockPolicyFromUpgradeToExclusive::lock(mutex);
mutex.unlock();
mutex.lock();
LockPolicyFromUpgradeToExclusive::unlock(mutex);
mutex.lock();
LockPolicyFromExclusiveToUpgrade::lock(mutex);
mutex.unlock_upgrade();
mutex.lock_upgrade();
LockPolicyFromExclusiveToUpgrade::unlock(mutex);
mutex.lock_upgrade();
LockPolicyFromUpgradeToShared::lock(mutex);
mutex.unlock_shared();
mutex.lock_shared();
LockPolicyFromUpgradeToShared::unlock(mutex);
mutex.lock();
LockPolicyFromExclusiveToShared::lock(mutex);
mutex.unlock_shared();
mutex.lock_shared();
LockPolicyFromExclusiveToShared::unlock(mutex);
EXPECT_EQ(mutex.lock_state, Mutex::CurrentLockState::UNLOCKED); EXPECT_EQ(mutex.lock_state, Mutex::CurrentLockState::UNLOCKED);
} }
...@@ -293,30 +269,6 @@ TEST(LockTraits, LockPolicyTimed) { ...@@ -293,30 +269,6 @@ TEST(LockTraits, LockPolicyTimed) {
bool gotLock = LockPolicyUpgrade::try_lock_for(mutex, one_ms); bool gotLock = LockPolicyUpgrade::try_lock_for(mutex, one_ms);
EXPECT_TRUE(gotLock) << "Should have been able to acquire the fake mutex"; EXPECT_TRUE(gotLock) << "Should have been able to acquire the fake mutex";
LockPolicyUpgrade::unlock(mutex); LockPolicyUpgrade::unlock(mutex);
mutex.lock_upgrade();
gotLock = LockPolicyFromUpgradeToExclusive::try_lock_for(mutex, one_ms);
EXPECT_TRUE(gotLock)
<< "Should have been able to upgrade from upgrade to unique";
mutex.unlock();
mutex.lock();
gotLock = LockPolicyFromExclusiveToUpgrade::try_lock_for(mutex, one_ms);
EXPECT_TRUE(gotLock) << "Should have been able to downgrade from exclusive "
"to upgrade";
mutex.unlock_upgrade();
mutex.lock_upgrade();
gotLock = LockPolicyFromUpgradeToShared::try_lock_for(mutex, one_ms);
EXPECT_TRUE(gotLock) << "Should have been able to downgrade from upgrade to "
"shared";
mutex.unlock_shared();
mutex.lock();
gotLock = LockPolicyFromExclusiveToShared::try_lock_for(mutex, one_ms);
EXPECT_TRUE(gotLock) << "Should have been able to downgrade from exclusive "
"to shared";
mutex.unlock_shared();
} }
/** /**
...@@ -330,24 +282,12 @@ TEST(LockTraits, LockPolicyCompatibilities) { ...@@ -330,24 +282,12 @@ TEST(LockTraits, LockPolicyCompatibilities) {
EXPECT_TRUE((std::is_same< EXPECT_TRUE((std::is_same<
LockPolicyExclusive::UnlockPolicy, LockPolicyExclusive::UnlockPolicy,
LockPolicyTryExclusive::UnlockPolicy>::value)); LockPolicyTryExclusive::UnlockPolicy>::value));
EXPECT_TRUE((std::is_same<
LockPolicyExclusive::UnlockPolicy,
LockPolicyFromUpgradeToExclusive::UnlockPolicy>::value));
EXPECT_TRUE((std::is_same< EXPECT_TRUE((std::is_same<
LockPolicyShared::UnlockPolicy, LockPolicyShared::UnlockPolicy,
LockPolicyTryShared::UnlockPolicy>::value)); LockPolicyTryShared::UnlockPolicy>::value));
EXPECT_TRUE((std::is_same<
LockPolicyShared::UnlockPolicy,
LockPolicyFromUpgradeToShared::UnlockPolicy>::value));
EXPECT_TRUE((std::is_same<
LockPolicyShared::UnlockPolicy,
LockPolicyFromExclusiveToShared::UnlockPolicy>::value));
EXPECT_TRUE((std::is_same< EXPECT_TRUE((std::is_same<
LockPolicyUpgrade::UnlockPolicy, LockPolicyUpgrade::UnlockPolicy,
LockPolicyTryUpgrade::UnlockPolicy>::value)); LockPolicyTryUpgrade::UnlockPolicy>::value));
EXPECT_TRUE((std::is_same<
LockPolicyUpgrade::UnlockPolicy,
LockPolicyFromExclusiveToUpgrade::UnlockPolicy>::value));
} }
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <folly/SharedMutex.h> #include <folly/SharedMutex.h>
#include <folly/SpinLock.h> #include <folly/SpinLock.h>
#include <folly/portability/GTest.h> #include <folly/portability/GTest.h>
#include <folly/synchronization/DistributedMutex.h>
#include <folly/synchronization/RWSpinLock.h> #include <folly/synchronization/RWSpinLock.h>
#include <folly/test/SynchronizedTestLib.h> #include <folly/test/SynchronizedTestLib.h>
...@@ -39,6 +40,7 @@ template <class Mutex> ...@@ -39,6 +40,7 @@ template <class Mutex>
class SynchronizedTest : public testing::Test {}; class SynchronizedTest : public testing::Test {};
using SynchronizedTestTypes = testing::Types< using SynchronizedTestTypes = testing::Types<
folly::DistributedMutex,
folly::SharedMutexReadPriority, folly::SharedMutexReadPriority,
folly::SharedMutexWritePriority, folly::SharedMutexWritePriority,
std::mutex, std::mutex,
...@@ -744,9 +746,6 @@ template <template <typename...> class Trait> ...@@ -744,9 +746,6 @@ template <template <typename...> class Trait>
void testLockedPtrCompatibilityExclusive() { void testLockedPtrCompatibilityExclusive() {
EXPECT_TRUE(( EXPECT_TRUE((
Trait<LPtr<LockPolicyExclusive>, LPtr<LockPolicyTryExclusive>&&>::value)); Trait<LPtr<LockPolicyExclusive>, LPtr<LockPolicyTryExclusive>&&>::value));
EXPECT_TRUE((Trait<
LPtr<LockPolicyExclusive>,
LPtr<LockPolicyFromUpgradeToExclusive>&&>::value));
EXPECT_FALSE( EXPECT_FALSE(
(Trait<LPtr<LockPolicyExclusive>&, LPtr<LockPolicyShared>&&>::value)); (Trait<LPtr<LockPolicyExclusive>&, LPtr<LockPolicyShared>&&>::value));
...@@ -756,27 +755,12 @@ void testLockedPtrCompatibilityExclusive() { ...@@ -756,27 +755,12 @@ void testLockedPtrCompatibilityExclusive() {
(Trait<LPtr<LockPolicyExclusive>, LPtr<LockPolicyUpgrade>&&>::value)); (Trait<LPtr<LockPolicyExclusive>, LPtr<LockPolicyUpgrade>&&>::value));
EXPECT_FALSE( EXPECT_FALSE(
(Trait<LPtr<LockPolicyExclusive>, LPtr<LockPolicyTryUpgrade>&&>::value)); (Trait<LPtr<LockPolicyExclusive>, LPtr<LockPolicyTryUpgrade>&&>::value));
EXPECT_FALSE((Trait<
LPtr<LockPolicyExclusive>,
LPtr<LockPolicyFromExclusiveToUpgrade>&&>::value));
EXPECT_FALSE((Trait<
LPtr<LockPolicyExclusive>,
LPtr<LockPolicyFromExclusiveToShared>&&>::value));
EXPECT_FALSE(
(Trait<LPtr<LockPolicyExclusive>, LPtr<LockPolicyFromUpgradeToShared>&&>::
value));
} }
template <template <typename...> class Trait> template <template <typename...> class Trait>
void testLockedPtrCompatibilityShared() { void testLockedPtrCompatibilityShared() {
EXPECT_TRUE( EXPECT_TRUE(
(Trait<LPtr<LockPolicyShared>, LPtr<LockPolicyTryShared>&&>::value)); (Trait<LPtr<LockPolicyShared>, LPtr<LockPolicyTryShared>&&>::value));
EXPECT_TRUE(
(Trait<LPtr<LockPolicyShared>, LPtr<LockPolicyFromUpgradeToShared>&&>::
value));
EXPECT_TRUE(
(Trait<LPtr<LockPolicyShared>, LPtr<LockPolicyFromExclusiveToShared>&&>::
value));
EXPECT_FALSE( EXPECT_FALSE(
(Trait<LPtr<LockPolicyShared>, LPtr<LockPolicyExclusive>&&>::value)); (Trait<LPtr<LockPolicyShared>, LPtr<LockPolicyExclusive>&&>::value));
...@@ -786,21 +770,12 @@ void testLockedPtrCompatibilityShared() { ...@@ -786,21 +770,12 @@ void testLockedPtrCompatibilityShared() {
(Trait<LPtr<LockPolicyShared>, LPtr<LockPolicyUpgrade>&&>::value)); (Trait<LPtr<LockPolicyShared>, LPtr<LockPolicyUpgrade>&&>::value));
EXPECT_FALSE( EXPECT_FALSE(
(Trait<LPtr<LockPolicyShared>, LPtr<LockPolicyTryUpgrade>&&>::value)); (Trait<LPtr<LockPolicyShared>, LPtr<LockPolicyTryUpgrade>&&>::value));
EXPECT_FALSE(
(Trait<LPtr<LockPolicyShared>, LPtr<LockPolicyFromExclusiveToUpgrade>&&>::
value));
EXPECT_FALSE(
(Trait<LPtr<LockPolicyShared>, LPtr<LockPolicyFromUpgradeToExclusive>&&>::
value));
} }
template <template <typename...> class Trait> template <template <typename...> class Trait>
void testLockedPtrCompatibilityUpgrade() { void testLockedPtrCompatibilityUpgrade() {
EXPECT_TRUE( EXPECT_TRUE(
(Trait<LPtr<LockPolicyUpgrade>, LPtr<LockPolicyTryUpgrade>&&>::value)); (Trait<LPtr<LockPolicyUpgrade>, LPtr<LockPolicyTryUpgrade>&&>::value));
EXPECT_TRUE((
Trait<LPtr<LockPolicyUpgrade>, LPtr<LockPolicyFromExclusiveToUpgrade>&&>::
value));
EXPECT_FALSE( EXPECT_FALSE(
(Trait<LPtr<LockPolicyUpgrade>, LPtr<LockPolicyExclusive>&&>::value)); (Trait<LPtr<LockPolicyUpgrade>, LPtr<LockPolicyExclusive>&&>::value));
...@@ -810,15 +785,6 @@ void testLockedPtrCompatibilityUpgrade() { ...@@ -810,15 +785,6 @@ void testLockedPtrCompatibilityUpgrade() {
(Trait<LPtr<LockPolicyUpgrade>, LPtr<LockPolicyShared>&&>::value)); (Trait<LPtr<LockPolicyUpgrade>, LPtr<LockPolicyShared>&&>::value));
EXPECT_FALSE( EXPECT_FALSE(
(Trait<LPtr<LockPolicyUpgrade>, LPtr<LockPolicyTryShared>&&>::value)); (Trait<LPtr<LockPolicyUpgrade>, LPtr<LockPolicyTryShared>&&>::value));
EXPECT_FALSE(
(Trait<LPtr<LockPolicyUpgrade>, LPtr<LockPolicyFromExclusiveToShared>&&>::
value));
EXPECT_FALSE(
(Trait<LPtr<LockPolicyUpgrade>, LPtr<LockPolicyFromUpgradeToShared>&&>::
value));
EXPECT_FALSE((
Trait<LPtr<LockPolicyUpgrade>, LPtr<LockPolicyFromUpgradeToExclusive>&&>::
value));
} }
} // namespace } // namespace
......
...@@ -72,7 +72,7 @@ void runParallel(size_t numThreads, const Function& function) { ...@@ -72,7 +72,7 @@ void runParallel(size_t numThreads, const Function& function) {
// as close to the same time as possible. // as close to the same time as possible.
{ {
auto lockedGo = go.lock(); auto lockedGo = go.lock();
goCV.wait(lockedGo.getUniqueLock(), [&] { return *lockedGo; }); goCV.wait(lockedGo.as_lock(), [&] { return *lockedGo; });
} }
function(threadIndex); function(threadIndex);
...@@ -86,9 +86,8 @@ void runParallel(size_t numThreads, const Function& function) { ...@@ -86,9 +86,8 @@ void runParallel(size_t numThreads, const Function& function) {
// Wait for all threads to become ready // Wait for all threads to become ready
{ {
auto readyLocked = threadsReady.lock(); auto readyLocked = threadsReady.lock();
readyCV.wait(readyLocked.getUniqueLock(), [&] { readyCV.wait(
return *readyLocked == numThreads; readyLocked.as_lock(), [&] { return *readyLocked == numThreads; });
});
} }
// Now signal the threads that they can go // Now signal the threads that they can go
go = true; go = true;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment