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

complete the transition away from LockTraits

Summary: `Synchronized` no longer needs a full lock-traits facility. Absorb the few things it needs and cut the rest.

Reviewed By: simpkins

Differential Revision: D28774648

fbshipit-source-id: 0679a3192a8eb17444628d12704cdc34fe5911b3
parent b65ef9f8
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* This module provides a traits class for describing properties about mutex
* classes.
*
* This is a primitive for building higher-level abstractions that can work
* with a variety of mutex classes. For instance, this allows
* folly::Synchronized to support a number of different mutex types.
*/
#pragma once
#include <chrono>
#include <mutex>
#include <shared_mutex>
#include <type_traits>
#include <folly/functional/Invoke.h>
#include <folly/synchronization/Lock.h>
// Android, OSX, and Cygwin don't have timed mutexes
#if defined(ANDROID) || defined(__ANDROID__) || defined(__APPLE__) || \
defined(__CYGWIN__)
#define FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES 0
#else
#define FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES 1
#endif
namespace folly {
namespace detail {
namespace member {
FOLLY_CREATE_MEMBER_INVOKER(lock_invoker, lock);
FOLLY_CREATE_MEMBER_INVOKER(try_lock_for_invoker, try_lock_for);
FOLLY_CREATE_MEMBER_INVOKER(lock_shared_invoker, lock_shared);
FOLLY_CREATE_MEMBER_INVOKER(lock_upgrade_invoker, lock_upgrade);
} // namespace member
/**
* An enum to describe the "level" of a mutex. The supported levels are
* Unique - a normal mutex that supports only exclusive locking
* Shared - a shared mutex which has shared locking and unlocking functions;
* Upgrade - a mutex that has all the methods of the two above along with
* support for upgradable locking
*/
enum class MutexLevel { UNIQUE, SHARED, UPGRADE };
/**
* A template dispatch mechanism that is used to determine the level of the
* mutex based on its interface. As decided by LockInterfaceDispatcher.
*/
template <bool is_unique, bool is_shared, bool is_upgrade>
struct MutexLevelValueImpl;
template <>
struct MutexLevelValueImpl<true, false, false> {
static constexpr MutexLevel value = MutexLevel::UNIQUE;
};
template <>
struct MutexLevelValueImpl<true, true, false> {
static constexpr MutexLevel value = MutexLevel::SHARED;
};
template <>
struct MutexLevelValueImpl<true, true, true> {
static constexpr MutexLevel value = MutexLevel::UPGRADE;
};
/**
* An internal helper class to help identify the interface supported by the
* mutex. This is used in conjunction with the above MutexLevel
* specializations and the LockTraitsImpl to determine what functions are
* supported by objects of type Mutex
*/
template <class Mutex>
class LockInterfaceDispatcher {
private:
// assert that the mutex type has basic lock and unlock functions
static_assert(
folly::is_invocable_v<member::lock_invoker, Mutex>,
"The mutex type must support lock and unlock functions");
using duration = std::chrono::milliseconds;
public:
static constexpr bool has_lock_unique = true;
static constexpr bool has_lock_timed =
folly::is_invocable_v<member::try_lock_for_invoker, Mutex, duration>;
static constexpr bool has_lock_shared =
folly::is_invocable_v<member::lock_shared_invoker, Mutex>;
static constexpr bool has_lock_upgrade =
folly::is_invocable_v<member::lock_upgrade_invoker, Mutex>;
};
/**
* LockTraitsImpl is the base that is used to desribe the interface used by
* different mutex types. It accepts a MutexLevel argument and a boolean to
* show whether the mutex is a timed mutex or not. The implementations are
* partially specialized and inherit from the other implementations to get
* similar functionality
*/
template <class Mutex, MutexLevel level, bool is_timed>
struct LockTraitsImpl;
template <class Mutex>
struct LockTraitsImpl<Mutex, MutexLevel::UNIQUE, false> {
static constexpr bool is_timed{false};
static constexpr bool is_shared{false};
static constexpr bool is_upgrade{false};
/**
* Acquire the lock exclusively.
*/
static void lock(Mutex& mutex) { mutex.lock(); }
/**
* Release an exclusively-held lock.
*/
static void unlock(Mutex& mutex) { mutex.unlock(); }
/**
* Try to acquire the mutex
*/
static bool try_lock(Mutex& mutex) { return mutex.try_lock(); }
};
/**
* Higher level mutexes have all the capabilities of the lower levels so
* inherit
*/
template <class Mutex>
struct LockTraitsImpl<Mutex, MutexLevel::SHARED, false>
: public LockTraitsImpl<Mutex, MutexLevel::UNIQUE, false> {
static constexpr bool is_timed{false};
static constexpr bool is_shared{true};
static constexpr bool is_upgrade{false};
/**
* Acquire the lock in shared (read) mode.
*/
static void lock_shared(Mutex& mutex) { mutex.lock_shared(); }
/**
* Release a lock held in shared mode.
*/
static void unlock_shared(Mutex& mutex) { mutex.unlock_shared(); }
/**
* Try to acquire the mutex in shared mode
*/
static bool try_lock_shared(Mutex& mutex) { return mutex.try_lock_shared(); }
};
/**
* The following methods are supported. There are a few methods
*
* m.lock_upgrade()
* m.unlock_upgrade()
* m.try_lock_upgrade()
*
* m.unlock_upgrade_and_lock()
*
* m.unlock_and_lock_upgrade()
* m.unlock_and_lock_shared()
* m.unlock_upgrade_and_lock_shared()
*
* m.try_lock_upgrade_for(rel_time)
* m.try_unlock_upgrade_and_lock_for(rel_time)
*
* Upgrading a shared lock is likely to deadlock when there is more than one
* thread performing an upgrade. This applies both to upgrading a shared lock
* to an upgrade lock and to upgrading a shared lock to a unique lock.
*
* Therefore, none of the following methods is supported:
* unlock_shared_and_lock_upgrade
* unlock_shared_and_lock
* try_unlock_shared_and_lock_upgrade
* try_unlock_shared_and_lock
* try_unlock_shared_and_lock_upgrade_for
* try_unlock_shared_and_lock_for
*/
template <class Mutex>
struct LockTraitsImpl<Mutex, MutexLevel::UPGRADE, false>
: public LockTraitsImpl<Mutex, MutexLevel::SHARED, false> {
static constexpr bool is_timed{false};
static constexpr bool is_shared{true};
static constexpr bool is_upgrade{true};
/**
* Acquire the lock in upgradable mode.
*/
static void lock_upgrade(Mutex& mutex) { mutex.lock_upgrade(); }
/**
* Release the lock in upgrade mode
*/
static void unlock_upgrade(Mutex& mutex) { mutex.unlock_upgrade(); }
/**
* Try and acquire the lock in upgrade mode
*/
static bool try_lock_upgrade(Mutex& mutex) {
return mutex.try_lock_upgrade();
}
/**
* Upgrade from an upgradable state to an exclusive state
*/
static void unlock_upgrade_and_lock(Mutex& mutex) {
mutex.unlock_upgrade_and_lock();
}
/**
* Downgrade from an exclusive state to an upgrade state
*/
static void unlock_and_lock_upgrade(Mutex& mutex) {
mutex.unlock_and_lock_upgrade();
}
/**
* Downgrade from an exclusive state to a shared state
*/
static void unlock_and_lock_shared(Mutex& mutex) {
mutex.unlock_and_lock_shared();
}
/**
* Downgrade from an upgrade state to a shared state
*/
static void unlock_upgrade_and_lock_shared(Mutex& mutex) {
mutex.unlock_upgrade_and_lock_shared();
}
};
template <class Mutex>
struct LockTraitsImpl<Mutex, MutexLevel::UNIQUE, true>
: public LockTraitsImpl<Mutex, MutexLevel::UNIQUE, false> {
using lock_type = std::unique_lock<Mutex>;
static constexpr bool is_timed{true};
static constexpr bool is_shared{false};
static constexpr bool is_upgrade{false};
/**
* Acquire the lock exclusively, with a timeout.
*
* Returns true or false indicating if the lock was acquired or not.
*/
template <class Rep, class Period>
static bool try_lock_for(
Mutex& mutex, const std::chrono::duration<Rep, Period>& timeout) {
return mutex.try_lock_for(timeout);
}
};
/**
* Note that there is no deadly diamond here because all the structs only have
* static functions and static bools which are going to be overridden by the
* lowest level implementation
*/
template <class Mutex>
struct LockTraitsImpl<Mutex, MutexLevel::SHARED, true>
: public LockTraitsImpl<Mutex, MutexLevel::SHARED, false>,
public LockTraitsImpl<Mutex, MutexLevel::UNIQUE, true> {
using lock_type = std::shared_lock<Mutex>;
static constexpr bool is_timed{true};
static constexpr bool is_shared{true};
static constexpr bool is_upgrade{false};
/**
* Acquire the lock exclusively, with a timeout.
*
* Returns true or false indicating if the lock was acquired or not.
*/
template <class Rep, class Period>
static bool try_lock_for(
Mutex& mutex, const std::chrono::duration<Rep, Period>& timeout) {
return mutex.try_lock_for(timeout);
}
/**
* Acquire the lock in shared (read) mode, with a timeout.
*
* Returns true or false indicating if the lock was acquired or not.
*/
template <class Rep, class Period>
static bool try_lock_shared_for(
Mutex& mutex, const std::chrono::duration<Rep, Period>& timeout) {
return mutex.try_lock_shared_for(timeout);
}
};
template <class Mutex>
struct LockTraitsImpl<Mutex, MutexLevel::UPGRADE, true>
: public LockTraitsImpl<Mutex, MutexLevel::UPGRADE, false>,
public LockTraitsImpl<Mutex, MutexLevel::SHARED, true> {
using lock_type = upgrade_lock<Mutex>;
static constexpr bool is_timed{true};
static constexpr bool is_shared{true};
static constexpr bool is_upgrade{true};
/**
* Acquire the lock in upgrade mode with a timeout
*
* Returns true or false indicating whether the lock was acquired or not
*/
template <class Rep, class Period>
static bool try_lock_upgrade_for(
Mutex& mutex, const std::chrono::duration<Rep, Period>& timeout) {
return mutex.try_lock_upgrade_for(timeout);
}
/**
* Try to upgrade from an upgradable state to an exclusive state.
*
* Returns true or false indicating whether the lock was acquired or not
*/
template <class Rep, class Period>
static bool try_unlock_upgrade_and_lock_for(
Mutex& mutex, const std::chrono::duration<Rep, Period>& timeout) {
return mutex.try_unlock_upgrade_and_lock_for(timeout);
}
};
/**
* Unlock helpers
*
* These help in determining whether it is safe for Synchronized::LockedPtr
* instances to be move assigned from one another. It is safe if they both
* have the same unlock policy, and it is not if they don't have the same
* unlock policy. For example
*
* auto wlock = synchronized.wlock();
* wlock.unlock();
*
* wlock = synchronized.rlock();
*
* This code would try to release the shared lock with a call to unlock(),
* resulting in possibly undefined behavior. By allowing the LockPolicy
* classes (defined below) to know what their unlocking behavior is, we can
* prevent against this by disabling unsafe conversions to and from
* incompatible LockedPtr types (they are incompatible if the underlying
* LockPolicy has different unlock policies.
*/
template <template <typename...> class LockTraits>
struct UnlockPolicyExclusive {
template <typename Mutex>
using lock_type = std::unique_lock<Mutex>;
constexpr static bool allows_concurrent_access = false;
template <typename Mutex>
static void unlock(Mutex& mutex) {
LockTraits<Mutex>::unlock(mutex);
}
};
template <template <typename...> class LockTraits>
struct UnlockPolicyShared {
template <typename Mutex>
using lock_type = std::shared_lock<Mutex>;
constexpr static bool allows_concurrent_access = true;
template <typename Mutex>
static void unlock(Mutex& mutex) {
LockTraits<Mutex>::unlock_shared(mutex);
}
};
template <template <typename...> class LockTraits>
struct UnlockPolicyUpgrade {
template <typename Mutex>
using lock_type = upgrade_lock<Mutex>;
constexpr static bool allows_concurrent_access = true;
template <typename Mutex>
static void unlock(Mutex& mutex) {
LockTraits<Mutex>::unlock_upgrade(mutex);
}
};
} // namespace detail
/**
* LockTraits describes details about a particular mutex type.
*
* The default implementation automatically attempts to detect traits
* based on the presence of various member functions.
*
* You can specialize LockTraits to provide custom behavior for lock
* classes that do not use the standard method names
* (lock()/unlock()/lock_shared()/unlock_shared()/try_lock_for())
*
*
* LockTraits contains the following members variables:
* - static constexpr bool is_shared
* True if the lock supports separate shared vs exclusive locking states.
* - static constexpr bool is_timed
* True if the lock supports acquiring the lock with a timeout.
* - static constexpr bool is_upgrade
* True if the lock supports an upgradable state
*
* The following static methods always exist:
* - lock(Mutex& mutex)
* - unlock(Mutex& mutex)
* - try_lock(Mutex& mutex)
*
* The following static methods may exist, depending on is_shared, is_timed
* and is_upgrade:
* - lock_shared()
* - try_lock_shared()
*
* - try_lock_for()
* - try_lock_shared_for()
*
* - lock_upgrade()
* - try_lock_upgrade()
* - unlock_upgrade_and_lock()
* - unlock_and_lock_upgrade()
* - unlock_and_lock_shared()
* - unlock_upgrade_and_lock_shared()
*
* - try_lock_upgrade_for()
* - try_unlock_upgrade_and_lock_for()
*
* - unlock_shared()
* - unlock_upgrade()
*/
/**
* Decoupling LockTraits and LockTraitsBase so that if people want to fully
* specialize LockTraits then they can inherit from LockTraitsBase instead
* of LockTraits with all the same goodies :)
*/
template <class Mutex>
struct LockTraitsBase
: public detail::LockTraitsImpl<
Mutex,
detail::MutexLevelValueImpl<
detail::LockInterfaceDispatcher<Mutex>::has_lock_unique,
detail::LockInterfaceDispatcher<Mutex>::has_lock_shared,
detail::LockInterfaceDispatcher<Mutex>::has_lock_upgrade>::value,
detail::LockInterfaceDispatcher<Mutex>::has_lock_timed> {};
template <class Mutex>
struct LockTraits : public LockTraitsBase<Mutex> {};
/*
* Lock policy classes.
*
* These can be used as template parameters to provide compile-time
* selection over the type of lock operation to perform.
*/
/**
* A lock policy that performs exclusive lock operations.
*/
struct LockPolicyExclusive : detail::UnlockPolicyExclusive<LockTraits> {
using UnlockPolicy = detail::UnlockPolicyExclusive<LockTraits>;
static constexpr bool is_try = false;
template <class Mutex>
static std::true_type lock(Mutex& mutex) {
LockTraits<Mutex>::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_lock_for(mutex, timeout);
}
};
/**
* A lock policy that performs shared lock operations.
* This policy only works with shared mutex types.
*/
struct LockPolicyShared : detail::UnlockPolicyShared<LockTraits> {
using UnlockPolicy = detail::UnlockPolicyShared<LockTraits>;
static constexpr bool is_try = false;
template <class Mutex>
static std::true_type lock(Mutex& mutex) {
LockTraits<Mutex>::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>& timeout) {
return LockTraits<Mutex>::try_lock_shared_for(mutex, timeout);
}
};
/**
* A lock policy with the following mapping
*
* lock() -> lock_upgrade()
* unlock() -> unlock_upgrade()
* try_lock_for -> try_lock_upgrade_for()
*/
struct LockPolicyUpgrade : detail::UnlockPolicyUpgrade<LockTraits> {
using UnlockPolicy = detail::UnlockPolicyUpgrade<LockTraits>;
static constexpr bool is_try = false;
template <class Mutex>
static std::true_type lock(Mutex& mutex) {
LockTraits<Mutex>::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>& timeout) {
return LockTraits<Mutex>::try_lock_upgrade_for(mutex, timeout);
}
};
/*****************************************************************************
* Policies for optional mutex locking
****************************************************************************/
/**
* A lock policy that tries to acquire write locks and returns true or false
* based on whether the lock operation succeeds
*/
struct LockPolicyTryExclusive : detail::UnlockPolicyExclusive<LockTraits> {
using UnlockPolicy = detail::UnlockPolicyExclusive<LockTraits>;
static constexpr bool is_try = true;
template <class Mutex>
static bool lock(Mutex& mutex) {
return LockTraits<Mutex>::try_lock(mutex);
}
};
/**
* A lock policy that tries to acquire a read lock and returns true or false
* based on whether the lock operation succeeds
*/
struct LockPolicyTryShared : detail::UnlockPolicyShared<LockTraits> {
using UnlockPolicy = detail::UnlockPolicyShared<LockTraits>;
static constexpr bool is_try = true;
template <class Mutex>
static bool lock(Mutex& mutex) {
return LockTraits<Mutex>::try_lock_shared(mutex);
}
};
/**
* A lock policy that tries to acquire an upgrade lock and returns true or
* false based on whether the lock operation succeeds
*/
struct LockPolicyTryUpgrade : detail::UnlockPolicyUpgrade<LockTraits> {
using UnlockPolicy = detail::UnlockPolicyUpgrade<LockTraits>;
static constexpr bool is_try = true;
template <class Mutex>
static bool lock(Mutex& mutex) {
return LockTraits<Mutex>::try_lock_upgrade(mutex);
}
};
} // namespace folly
......@@ -27,7 +27,6 @@
#include <folly/Function.h>
#include <folly/Likely.h>
#include <folly/LockTraits.h>
#include <folly/Preprocessor.h>
#include <folly/SharedMutex.h>
#include <folly/Traits.h>
......@@ -46,27 +45,93 @@
namespace folly {
template <class LockedType, class Mutex, class LockPolicy>
class LockedPtrBase;
template <class LockedType, class LockPolicy>
class LockedPtr;
namespace detail {
template <typename, typename Mutex>
FOLLY_INLINE_VARIABLE constexpr bool kSynchronizedMutexIsUnique = false;
template <typename Mutex>
FOLLY_INLINE_VARIABLE constexpr bool kSynchronizedMutexIsUnique<
decltype(void(std::declval<Mutex&>().lock())),
Mutex> = true;
template <typename, typename Mutex>
FOLLY_INLINE_VARIABLE constexpr bool kSynchronizedMutexIsShared = false;
template <typename Mutex>
FOLLY_INLINE_VARIABLE constexpr bool kSynchronizedMutexIsShared<
decltype(void(std::declval<Mutex&>().lock_shared())),
Mutex> = true;
template <typename, typename Mutex>
FOLLY_INLINE_VARIABLE constexpr bool kSynchronizedMutexIsUpgrade = false;
template <typename Mutex>
FOLLY_INLINE_VARIABLE constexpr bool kSynchronizedMutexIsUpgrade<
decltype(void(std::declval<Mutex&>().lock_upgrade())),
Mutex> = true;
/**
* Public version of LockInterfaceDispatcher that contains the MutexLevel enum
* for the passed in mutex type
*
* This is decoupled from MutexLevelValueImpl in LockTraits.h because this
* ensures that a heterogenous mutex with a different API can be used. For
* example - if a mutex does not have a lock_shared() method but the
* LockTraits specialization for it supports a static non member
* lock_shared(Mutex&) it can be used as a shared mutex and will provide
* rlock() and wlock() functions.
*/
template <class Mutex>
using MutexLevelValue = detail::MutexLevelValueImpl<
true,
LockTraits<Mutex>::is_shared,
LockTraits<Mutex>::is_upgrade>;
* An enum to describe the "level" of a mutex. The supported levels are
* Unique - a normal mutex that supports only exclusive locking
* Shared - a shared mutex which has shared locking and unlocking functions;
* Upgrade - a mutex that has all the methods of the two above along with
* support for upgradable locking
*/
enum class SynchronizedMutexLevel { Unknown, Unique, Shared, Upgrade };
template <typename Mutex>
FOLLY_INLINE_VARIABLE constexpr SynchronizedMutexLevel kSynchronizedMutexLevel =
kSynchronizedMutexIsUpgrade<void, Mutex> ? SynchronizedMutexLevel::Upgrade
: kSynchronizedMutexIsShared<void, Mutex> ? SynchronizedMutexLevel::Shared
: kSynchronizedMutexIsUnique<void, Mutex> ? SynchronizedMutexLevel::Unique
: SynchronizedMutexLevel::Unknown;
enum class SynchronizedMutexMethod { Lock, TryLock };
template <SynchronizedMutexLevel Level, SynchronizedMutexMethod Method>
struct SynchronizedLockPolicy {
static constexpr SynchronizedMutexLevel level = Level;
static constexpr SynchronizedMutexMethod method = Method;
};
using SynchronizedLockPolicyExclusive = SynchronizedLockPolicy<
SynchronizedMutexLevel::Unique,
SynchronizedMutexMethod::Lock>;
using SynchronizedLockPolicyTryExclusive = SynchronizedLockPolicy<
SynchronizedMutexLevel::Unique,
SynchronizedMutexMethod::TryLock>;
using SynchronizedLockPolicyShared = SynchronizedLockPolicy<
SynchronizedMutexLevel::Shared,
SynchronizedMutexMethod::Lock>;
using SynchronizedLockPolicyTryShared = SynchronizedLockPolicy<
SynchronizedMutexLevel::Shared,
SynchronizedMutexMethod::TryLock>;
using SynchronizedLockPolicyUpgrade = SynchronizedLockPolicy<
SynchronizedMutexLevel::Upgrade,
SynchronizedMutexMethod::Lock>;
using SynchronizedLockPolicyTryUpgrade = SynchronizedLockPolicy<
SynchronizedMutexLevel::Upgrade,
SynchronizedMutexMethod::TryLock>;
template <SynchronizedMutexLevel>
struct SynchronizedLockType_ {};
template <>
struct SynchronizedLockType_<SynchronizedMutexLevel::Unique> {
template <typename Mutex>
using apply = std::unique_lock<Mutex>;
};
template <>
struct SynchronizedLockType_<SynchronizedMutexLevel::Shared> {
template <typename Mutex>
using apply = std::shared_lock<Mutex>;
};
template <>
struct SynchronizedLockType_<SynchronizedMutexLevel::Upgrade> {
template <typename Mutex>
using apply = upgrade_lock<Mutex>;
};
template <SynchronizedMutexLevel Level, typename MutexType>
using SynchronizedLockType =
typename SynchronizedLockType_<Level>::template apply<MutexType>;
} // namespace detail
/**
* SynchronizedBase is a helper parent class for Synchronized<T>.
......@@ -74,9 +139,14 @@ using MutexLevelValue = detail::MutexLevelValueImpl<
* It provides wlock() and rlock() methods for shared mutex types,
* or lock() methods for purely exclusive mutex types.
*/
template <class Subclass, detail::MutexLevel level>
template <class Subclass, detail::SynchronizedMutexLevel level>
class SynchronizedBase;
template <class LockedType, class Mutex, class LockPolicy>
class LockedPtrBase;
template <class LockedType, class LockPolicy>
class LockedPtr;
/**
* SynchronizedBase specialization for shared mutex types.
*
......@@ -84,22 +154,28 @@ class SynchronizedBase;
* accessing the data.
*/
template <class Subclass>
class SynchronizedBase<Subclass, detail::MutexLevel::SHARED> {
class SynchronizedBase<Subclass, detail::SynchronizedMutexLevel::Shared> {
private:
template <typename T, typename P>
using LockedPtr_ = ::folly::LockedPtr<T, P>;
public:
using WLockedPtr = ::folly::LockedPtr<Subclass, LockPolicyExclusive>;
using ConstWLockedPtr =
::folly::LockedPtr<const Subclass, LockPolicyExclusive>;
using LockPolicyExclusive = detail::SynchronizedLockPolicyExclusive;
using LockPolicyShared = detail::SynchronizedLockPolicyShared;
using LockPolicyTryExclusive = detail::SynchronizedLockPolicyTryExclusive;
using LockPolicyTryShared = detail::SynchronizedLockPolicyTryShared;
using RLockedPtr = ::folly::LockedPtr<Subclass, LockPolicyShared>;
using ConstRLockedPtr = ::folly::LockedPtr<const Subclass, LockPolicyShared>;
using WLockedPtr = LockedPtr_<Subclass, LockPolicyExclusive>;
using ConstWLockedPtr = LockedPtr_<const Subclass, LockPolicyExclusive>;
using TryWLockedPtr = ::folly::LockedPtr<Subclass, LockPolicyTryExclusive>;
using ConstTryWLockedPtr =
::folly::LockedPtr<const Subclass, LockPolicyTryExclusive>;
using RLockedPtr = LockedPtr_<Subclass, LockPolicyShared>;
using ConstRLockedPtr = LockedPtr_<const Subclass, LockPolicyShared>;
using TryRLockedPtr = ::folly::LockedPtr<Subclass, LockPolicyTryShared>;
using ConstTryRLockedPtr =
::folly::LockedPtr<const Subclass, LockPolicyTryShared>;
using TryWLockedPtr = LockedPtr_<Subclass, LockPolicyTryExclusive>;
using ConstTryWLockedPtr = LockedPtr_<const Subclass, LockPolicyTryExclusive>;
using TryRLockedPtr = LockedPtr_<Subclass, LockPolicyTryShared>;
using ConstTryRLockedPtr = LockedPtr_<const Subclass, LockPolicyTryShared>;
// These aliases are deprecated.
// TODO: Codemod them away.
......@@ -260,17 +336,24 @@ class SynchronizedBase<Subclass, detail::MutexLevel::SHARED> {
* upgrade lock RAII proxy
*/
template <class Subclass>
class SynchronizedBase<Subclass, detail::MutexLevel::UPGRADE>
: public SynchronizedBase<Subclass, detail::MutexLevel::SHARED> {
class SynchronizedBase<Subclass, detail::SynchronizedMutexLevel::Upgrade>
: public SynchronizedBase<
Subclass,
detail::SynchronizedMutexLevel::Shared> {
private:
template <typename T, typename P>
using LockedPtr_ = ::folly::LockedPtr<T, P>;
public:
using UpgradeLockedPtr = ::folly::LockedPtr<Subclass, LockPolicyUpgrade>;
using ConstUpgradeLockedPtr =
::folly::LockedPtr<const Subclass, LockPolicyUpgrade>;
using LockPolicyUpgrade = detail::SynchronizedLockPolicyUpgrade;
using LockPolicyTryUpgrade = detail::SynchronizedLockPolicyTryUpgrade;
using TryUpgradeLockedPtr =
::folly::LockedPtr<Subclass, LockPolicyTryUpgrade>;
using UpgradeLockedPtr = LockedPtr_<Subclass, LockPolicyUpgrade>;
using ConstUpgradeLockedPtr = LockedPtr_<const Subclass, LockPolicyUpgrade>;
using TryUpgradeLockedPtr = LockedPtr_<Subclass, LockPolicyTryUpgrade>;
using ConstTryUpgradeLockedPtr =
::folly::LockedPtr<const Subclass, LockPolicyTryUpgrade>;
LockedPtr_<const Subclass, LockPolicyTryUpgrade>;
/**
* Acquire an upgrade lock. The returned LockedPtr will have force
......@@ -364,15 +447,20 @@ class SynchronizedBase<Subclass, detail::MutexLevel::UPGRADE>
* data.
*/
template <class Subclass>
class SynchronizedBase<Subclass, detail::MutexLevel::UNIQUE> {
class SynchronizedBase<Subclass, detail::SynchronizedMutexLevel::Unique> {
private:
template <typename T, typename P>
using LockedPtr_ = ::folly::LockedPtr<T, P>;
public:
using LockedPtr = ::folly::LockedPtr<Subclass, LockPolicyExclusive>;
using ConstLockedPtr =
::folly::LockedPtr<const Subclass, LockPolicyExclusive>;
using LockPolicyExclusive = detail::SynchronizedLockPolicyExclusive;
using LockPolicyTryExclusive = detail::SynchronizedLockPolicyTryExclusive;
using LockedPtr = LockedPtr_<Subclass, LockPolicyExclusive>;
using ConstLockedPtr = LockedPtr_<const Subclass, LockPolicyExclusive>;
using TryLockedPtr = ::folly::LockedPtr<Subclass, LockPolicyTryExclusive>;
using ConstTryLockedPtr =
::folly::LockedPtr<const Subclass, LockPolicyTryExclusive>;
using TryLockedPtr = LockedPtr_<Subclass, LockPolicyTryExclusive>;
using ConstTryLockedPtr = LockedPtr_<const Subclass, LockPolicyTryExclusive>;
/**
* Acquire a lock, and return a LockedPtr that can be used to safely access
......@@ -469,23 +557,17 @@ class SynchronizedBase<Subclass, detail::MutexLevel::UNIQUE> {
* reviewer. In contrast, the code that uses Synchronized<T> correctly
* looks simple and intuitive.
*
* The second parameter must be a mutex type. Any mutex type supported by
* LockTraits<Mutex> can be used. By default any class with lock() and
* unlock() methods will work automatically. LockTraits can be specialized to
* teach Synchronized how to use other custom mutex types. See the
* documentation in LockTraits.h for additional details.
*
* Supported mutexes that work by default include std::mutex,
* std::recursive_mutex, std::timed_mutex, std::recursive_timed_mutex,
* folly::SharedMutex, folly::RWSpinLock, and folly::SpinLock.
* The second parameter must be a mutex type matching the lockable
* family of concepts in the standard.
*/
template <class T, class Mutex = SharedMutex>
struct Synchronized : public SynchronizedBase<
Synchronized<T, Mutex>,
MutexLevelValue<Mutex>::value> {
detail::kSynchronizedMutexLevel<Mutex>> {
private:
using Base =
SynchronizedBase<Synchronized<T, Mutex>, MutexLevelValue<Mutex>::value>;
using Base = SynchronizedBase<
Synchronized<T, Mutex>,
detail::kSynchronizedMutexLevel<Mutex>>;
static constexpr bool nxCopyCtor{
std::is_nothrow_copy_constructible<T>::value};
static constexpr bool nxMoveCtor{
......@@ -1069,15 +1151,14 @@ template <class SynchronizedType, class LockPolicy>
class LockedPtr {
private:
constexpr static bool AllowsConcurrentAccess =
LockPolicy::allows_concurrent_access;
LockPolicy::level != detail::SynchronizedMutexLevel::Unique;
using CDataType = // the DataType with the appropriate const-qualification
detail::SynchronizedDataType<SynchronizedType, AllowsConcurrentAccess>;
template <typename LockPolicyOther>
using EnableIfSameUnlockPolicy = std::enable_if_t<std::is_same<
typename LockPolicy::UnlockPolicy,
typename LockPolicyOther::UnlockPolicy>::value>;
using EnableIfSameLevel =
std::enable_if_t<LockPolicy::level == LockPolicyOther::level>;
template <typename, typename>
friend class LockedPtr;
......@@ -1088,7 +1169,7 @@ class LockedPtr {
using DataType = typename SynchronizedType::DataType;
using MutexType = typename SynchronizedType::MutexType;
using Synchronized = typename std::remove_const<SynchronizedType>::type;
using LockType = typename LockPolicy::template lock_type<MutexType>;
using LockType = detail::SynchronizedLockType<LockPolicy::level, MutexType>;
/**
* Creates an uninitialized LockedPtr.
......@@ -1123,7 +1204,7 @@ class LockedPtr {
LockedPtr(LockedPtr&& rhs) noexcept = default;
template <
typename LockPolicyType,
EnableIfSameUnlockPolicy<LockPolicyType>* = nullptr>
EnableIfSameLevel<LockPolicyType>* = nullptr>
explicit LockedPtr(
LockedPtr<SynchronizedType, LockPolicyType>&& other) noexcept
: lock_{std::move(other.lock_)} {}
......@@ -1134,7 +1215,7 @@ class LockedPtr {
LockedPtr& operator=(LockedPtr&& rhs) noexcept = default;
template <
typename LockPolicyType,
EnableIfSameUnlockPolicy<LockPolicyType>* = nullptr>
EnableIfSameLevel<LockPolicyType>* = nullptr>
LockedPtr& operator=(
LockedPtr<SynchronizedType, LockPolicyType>&& other) noexcept {
lock_ = std::move(other.lock_);
......@@ -1242,9 +1323,10 @@ class LockedPtr {
*/
template <
typename SyncType = SynchronizedType,
typename = typename std::enable_if<
LockTraits<typename SyncType::MutexType>::is_upgrade>::type>
LockedPtr<SynchronizedType, LockPolicyExclusive> moveFromUpgradeToWrite() {
decltype(void(std::declval<typename SyncType::MutexType&>()
.lock_upgrade()))* = nullptr>
LockedPtr<SynchronizedType, detail::SynchronizedLockPolicyExclusive>
moveFromUpgradeToWrite() {
static_assert(std::is_same<SyncType, SynchronizedType>::value, "mismatch");
return transition_to_unique_lock(lock_);
}
......@@ -1255,9 +1337,10 @@ class LockedPtr {
*/
template <
typename SyncType = SynchronizedType,
typename = typename std::enable_if<
LockTraits<typename SyncType::MutexType>::is_upgrade>::type>
LockedPtr<SynchronizedType, LockPolicyUpgrade> moveFromWriteToUpgrade() {
decltype(void(std::declval<typename SyncType::MutexType&>()
.lock_upgrade()))* = nullptr>
LockedPtr<SynchronizedType, detail::SynchronizedLockPolicyUpgrade>
moveFromWriteToUpgrade() {
static_assert(std::is_same<SyncType, SynchronizedType>::value, "mismatch");
return transition_to_upgrade_lock(lock_);
}
......@@ -1268,9 +1351,10 @@ class LockedPtr {
*/
template <
typename SyncType = SynchronizedType,
typename = typename std::enable_if<
LockTraits<typename SyncType::MutexType>::is_upgrade>::type>
LockedPtr<SynchronizedType, LockPolicyShared> moveFromUpgradeToRead() {
decltype(void(std::declval<typename SyncType::MutexType&>()
.lock_upgrade()))* = nullptr>
LockedPtr<SynchronizedType, detail::SynchronizedLockPolicyShared>
moveFromUpgradeToRead() {
static_assert(std::is_same<SyncType, SynchronizedType>::value, "mismatch");
return transition_to_shared_lock(lock_);
}
......@@ -1281,9 +1365,10 @@ class LockedPtr {
*/
template <
typename SyncType = SynchronizedType,
typename = typename std::enable_if<
LockTraits<typename SyncType::MutexType>::is_upgrade>::type>
LockedPtr<SynchronizedType, LockPolicyShared> moveFromWriteToRead() {
decltype(void(std::declval<typename SyncType::MutexType&>()
.lock_shared()))* = nullptr>
LockedPtr<SynchronizedType, detail::SynchronizedLockPolicyShared>
moveFromWriteToRead() {
static_assert(std::is_same<SyncType, SynchronizedType>::value, "mismatch");
return transition_to_shared_lock(lock_);
}
......@@ -1291,11 +1376,15 @@ class LockedPtr {
private:
/* implicit */ LockedPtr(LockType lock) noexcept : lock_{std::move(lock)} {}
template <typename LP>
static constexpr bool is_try =
LP::method == detail::SynchronizedMutexMethod::TryLock;
template <
typename MT,
typename LT = LockType,
typename LP = LockPolicy,
std::enable_if_t<LP::is_try, int> = 0>
std::enable_if_t<is_try<LP>, int> = 0>
FOLLY_ERASE static LT doLock(MT& mutex) {
return LT{mutex, std::try_to_lock};
}
......@@ -1303,7 +1392,7 @@ class LockedPtr {
typename MT,
typename LT = LockType,
typename LP = LockPolicy,
std::enable_if_t<!LP::is_try, int> = 0>
std::enable_if_t<!is_try<LP>, int> = 0>
FOLLY_ERASE static LT doLock(MT& mutex) {
return LT{mutex};
}
......
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/LockTraits.h>
#include <mutex>
#include <folly/SharedMutex.h>
#include <folly/SpinLock.h>
#include <folly/portability/GTest.h>
#include <folly/synchronization/RWSpinLock.h>
using namespace folly;
static constexpr auto one_ms = std::chrono::milliseconds(1);
/**
* Test mutex to help to automate assertions
*/
class FakeAllPowerfulAssertingMutex {
public:
enum class CurrentLockState { UNLOCKED, SHARED, UPGRADE, UNIQUE };
void lock() {
EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
this->lock_state = CurrentLockState::UNIQUE;
}
void unlock() {
EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
this->lock_state = CurrentLockState::UNLOCKED;
}
void lock_shared() {
EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
this->lock_state = CurrentLockState::SHARED;
}
void unlock_shared() {
EXPECT_EQ(this->lock_state, CurrentLockState::SHARED);
this->lock_state = CurrentLockState::UNLOCKED;
}
void lock_upgrade() {
EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
this->lock_state = CurrentLockState::UPGRADE;
}
void unlock_upgrade() {
EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
this->lock_state = CurrentLockState::UNLOCKED;
}
void unlock_upgrade_and_lock() {
EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
this->lock_state = CurrentLockState::UNIQUE;
}
void unlock_and_lock_upgrade() {
EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
this->lock_state = CurrentLockState::UPGRADE;
}
void unlock_and_lock_shared() {
EXPECT_EQ(this->lock_state, CurrentLockState::UNIQUE);
this->lock_state = CurrentLockState::SHARED;
}
void unlock_upgrade_and_lock_shared() {
EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
this->lock_state = CurrentLockState::SHARED;
}
template <class Rep, class Period>
bool try_lock_for(const std::chrono::duration<Rep, Period>&) {
EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
this->lock_state = CurrentLockState::UNIQUE;
return true;
}
template <class Rep, class Period>
bool try_lock_upgrade_for(const std::chrono::duration<Rep, Period>&) {
EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED);
this->lock_state = CurrentLockState::UPGRADE;
return true;
}
template <class Rep, class Period>
bool try_unlock_upgrade_and_lock_for(
const std::chrono::duration<Rep, Period>&) {
EXPECT_EQ(this->lock_state, CurrentLockState::UPGRADE);
this->lock_state = CurrentLockState::UNIQUE;
return true;
}
/*
* Initialize the FakeMutex with an unlocked state
*/
CurrentLockState lock_state{CurrentLockState::UNLOCKED};
};
TEST(LockTraits, std_mutex) {
using traits = LockTraits<std::mutex>;
static_assert(!traits::is_timed, "std:mutex is not a timed lock");
static_assert(!traits::is_shared, "std:mutex is not a shared lock");
static_assert(!traits::is_upgrade, "std::mutex is not an upgradable lock");
std::mutex mutex;
traits::lock(mutex);
traits::unlock(mutex);
}
TEST(LockTraits, SharedMutex) {
using traits = LockTraits<SharedMutex>;
static_assert(traits::is_timed, "folly::SharedMutex is a timed lock");
static_assert(traits::is_shared, "folly::SharedMutex is a shared lock");
static_assert(traits::is_upgrade, "folly::SharedMutex is an upgradable lock");
SharedMutex mutex;
traits::lock(mutex);
traits::unlock(mutex);
traits::lock_shared(mutex);
traits::lock_shared(mutex);
traits::unlock_shared(mutex);
traits::unlock_shared(mutex);
traits::lock_upgrade(mutex);
traits::unlock_upgrade(mutex);
// test upgrade and downgrades
traits::lock_upgrade(mutex);
traits::unlock_upgrade_and_lock(mutex);
bool gotLock = traits::try_lock_for(mutex, one_ms);
EXPECT_FALSE(gotLock) << "Should not have been able to acquire an exclusive "
"lock after upgrading to an exclusive lock";
gotLock = traits::try_lock_upgrade_for(mutex, one_ms);
EXPECT_FALSE(gotLock) << "Should not have been able to acquire an upgrade "
"lock after upgrading to an exclusive lock";
gotLock = traits::try_lock_shared_for(mutex, one_ms);
EXPECT_FALSE(gotLock) << "Should not have been able to acquire a shared "
"lock after upgrading to an exclusive lock";
traits::unlock(mutex);
traits::lock_upgrade(mutex);
traits::unlock_upgrade_and_lock_shared(mutex);
gotLock = traits::try_lock_for(mutex, one_ms);
EXPECT_FALSE(gotLock) << "Should not have been able to acquire an exclusive "
"mutex after downgrading from an upgrade to a "
"shared lock";
traits::unlock_shared(mutex);
traits::lock(mutex);
gotLock = traits::try_lock_for(mutex, one_ms);
EXPECT_FALSE(gotLock) << "Should not have been able to acquire an exclusive "
"lock after acquiring an exclusive lock";
gotLock = traits::try_lock_upgrade_for(mutex, one_ms);
EXPECT_FALSE(gotLock) << "Should not have been able to acquire an upgrade "
"lock after acquiring an exclusive lock";
gotLock = traits::try_lock_shared_for(mutex, one_ms);
EXPECT_FALSE(gotLock) << "Should not have been able to acquire a shared "
"lock after acquiring an exclusive lock";
traits::unlock_and_lock_upgrade(mutex);
gotLock = traits::try_lock_for(mutex, one_ms);
EXPECT_FALSE(gotLock) << "Should not have been able to acquire an exclusive "
"lock after downgrading to an upgrade lock";
traits::unlock_upgrade(mutex);
traits::lock(mutex);
traits::unlock_and_lock_shared(mutex);
gotLock = traits::try_lock_for(mutex, one_ms);
EXPECT_FALSE(gotLock) << "Should not have been able to acquire an exclusive "
"lock after downgrading to a shared lock";
traits::unlock_shared(mutex);
}
TEST(LockTraits, SpinLock) {
using traits = LockTraits<SpinLock>;
static_assert(!traits::is_timed, "folly::SpinLock is not a timed lock");
static_assert(!traits::is_shared, "folly::SpinLock is not a shared lock");
static_assert(
!traits::is_upgrade, "folly::SpinLock is not an upgradable lock");
SpinLock mutex;
traits::lock(mutex);
traits::unlock(mutex);
}
TEST(LockTraits, RWSpinLock) {
using traits = LockTraits<RWSpinLock>;
static_assert(!traits::is_timed, "folly::RWSpinLock is not a timed lock");
static_assert(traits::is_shared, "folly::RWSpinLock is a shared lock");
static_assert(traits::is_upgrade, "folly::RWSpinLock is an upgradable lock");
RWSpinLock mutex;
traits::lock(mutex);
traits::unlock(mutex);
traits::lock_shared(mutex);
traits::lock_shared(mutex);
traits::unlock_shared(mutex);
traits::unlock_shared(mutex);
}
#if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
TEST(LockTraits, timed_mutex) {
using traits = LockTraits<std::timed_mutex>;
static_assert(traits::is_timed, "std::timed_mutex is a timed lock");
static_assert(!traits::is_shared, "std::timed_mutex is not a shared lock");
static_assert(
!traits::is_upgrade, "std::timed_mutex is not an upgradable lock");
std::timed_mutex mutex;
traits::lock(mutex);
bool gotLock = traits::try_lock_for(mutex, std::chrono::milliseconds(1));
EXPECT_FALSE(gotLock) << "should not have been able to acquire the "
<< "timed_mutex a second time";
traits::unlock(mutex);
}
TEST(LockTraits, recursive_timed_mutex) {
using traits = LockTraits<std::recursive_timed_mutex>;
static_assert(traits::is_timed, "std::recursive_timed_mutex is a timed lock");
static_assert(
!traits::is_shared, "std::recursive_timed_mutex is not a shared lock");
static_assert(
!traits::is_upgrade,
"std::recursive_timed_mutex is not an upgradable lock");
std::recursive_timed_mutex mutex;
traits::lock(mutex);
auto gotLock = traits::try_lock_for(mutex, std::chrono::milliseconds(10));
EXPECT_TRUE(gotLock) << "should have been able to acquire the "
<< "recursive_timed_mutex a second time";
traits::unlock(mutex);
traits::unlock(mutex);
}
#endif // FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
/**
* Chain the asserts from the previous test to the next lock, unlock or
* upgrade method calls. Each making sure that the previous was correct.
*/
TEST(LockTraits, LockPolicy) {
using Mutex = FakeAllPowerfulAssertingMutex;
Mutex mutex;
// test the lock and unlock functions
LockPolicyUpgrade::lock(mutex);
mutex.unlock_upgrade();
mutex.lock_upgrade();
LockPolicyUpgrade::unlock(mutex);
EXPECT_EQ(mutex.lock_state, Mutex::CurrentLockState::UNLOCKED);
}
/**
* Similar to the test above but tests the timed version of the updates
*/
TEST(LockTraits, LockPolicyTimed) {
using Mutex = FakeAllPowerfulAssertingMutex;
Mutex mutex;
bool gotLock = LockPolicyUpgrade::try_lock_for(mutex, one_ms);
EXPECT_TRUE(gotLock) << "Should have been able to acquire the fake mutex";
LockPolicyUpgrade::unlock(mutex);
}
/**
* Test compatibility of the different lock policies
*
* This should be correct because the compatibilities here are used to
* determine whether or not the different LockedPtr instances can be moved
* from each other
*/
TEST(LockTraits, LockPolicyCompatibilities) {
EXPECT_TRUE((std::is_same<
LockPolicyExclusive::UnlockPolicy,
LockPolicyTryExclusive::UnlockPolicy>::value));
EXPECT_TRUE((std::is_same<
LockPolicyShared::UnlockPolicy,
LockPolicyTryShared::UnlockPolicy>::value));
EXPECT_TRUE((std::is_same<
LockPolicyUpgrade::UnlockPolicy,
LockPolicyTryUpgrade::UnlockPolicy>::value));
}
......@@ -744,47 +744,62 @@ using LPtr = LockedPtr<Synchronized<int>, LockPolicy>;
namespace {
template <template <typename...> class Trait>
void testLockedPtrCompatibilityExclusive() {
EXPECT_TRUE((
Trait<LPtr<LockPolicyExclusive>, LPtr<LockPolicyTryExclusive>&&>::value));
EXPECT_FALSE(
(Trait<LPtr<LockPolicyExclusive>&, LPtr<LockPolicyShared>&&>::value));
EXPECT_FALSE(
(Trait<LPtr<LockPolicyExclusive>, LPtr<LockPolicyTryShared>&&>::value));
EXPECT_FALSE(
(Trait<LPtr<LockPolicyExclusive>, LPtr<LockPolicyUpgrade>&&>::value));
EXPECT_FALSE(
(Trait<LPtr<LockPolicyExclusive>, LPtr<LockPolicyTryUpgrade>&&>::value));
EXPECT_TRUE((Trait<
LPtr<detail::SynchronizedLockPolicyExclusive>,
LPtr<detail::SynchronizedLockPolicyTryExclusive>&&>::value));
EXPECT_FALSE((Trait<
LPtr<detail::SynchronizedLockPolicyExclusive>&,
LPtr<detail::SynchronizedLockPolicyShared>&&>::value));
EXPECT_FALSE((Trait<
LPtr<detail::SynchronizedLockPolicyExclusive>,
LPtr<detail::SynchronizedLockPolicyTryShared>&&>::value));
EXPECT_FALSE((Trait<
LPtr<detail::SynchronizedLockPolicyExclusive>,
LPtr<detail::SynchronizedLockPolicyUpgrade>&&>::value));
EXPECT_FALSE((Trait<
LPtr<detail::SynchronizedLockPolicyExclusive>,
LPtr<detail::SynchronizedLockPolicyTryUpgrade>&&>::value));
}
template <template <typename...> class Trait>
void testLockedPtrCompatibilityShared() {
EXPECT_TRUE(
(Trait<LPtr<LockPolicyShared>, LPtr<LockPolicyTryShared>&&>::value));
EXPECT_FALSE(
(Trait<LPtr<LockPolicyShared>, LPtr<LockPolicyExclusive>&&>::value));
EXPECT_FALSE(
(Trait<LPtr<LockPolicyShared>, LPtr<LockPolicyTryExclusive>&&>::value));
EXPECT_FALSE(
(Trait<LPtr<LockPolicyShared>, LPtr<LockPolicyUpgrade>&&>::value));
EXPECT_FALSE(
(Trait<LPtr<LockPolicyShared>, LPtr<LockPolicyTryUpgrade>&&>::value));
EXPECT_TRUE((Trait<
LPtr<detail::SynchronizedLockPolicyShared>,
LPtr<detail::SynchronizedLockPolicyTryShared>&&>::value));
EXPECT_FALSE((Trait<
LPtr<detail::SynchronizedLockPolicyShared>,
LPtr<detail::SynchronizedLockPolicyExclusive>&&>::value));
EXPECT_FALSE((Trait<
LPtr<detail::SynchronizedLockPolicyShared>,
LPtr<detail::SynchronizedLockPolicyTryExclusive>&&>::value));
EXPECT_FALSE((Trait<
LPtr<detail::SynchronizedLockPolicyShared>,
LPtr<detail::SynchronizedLockPolicyUpgrade>&&>::value));
EXPECT_FALSE((Trait<
LPtr<detail::SynchronizedLockPolicyShared>,
LPtr<detail::SynchronizedLockPolicyTryUpgrade>&&>::value));
}
template <template <typename...> class Trait>
void testLockedPtrCompatibilityUpgrade() {
EXPECT_TRUE(
(Trait<LPtr<LockPolicyUpgrade>, LPtr<LockPolicyTryUpgrade>&&>::value));
EXPECT_FALSE(
(Trait<LPtr<LockPolicyUpgrade>, LPtr<LockPolicyExclusive>&&>::value));
EXPECT_FALSE(
(Trait<LPtr<LockPolicyUpgrade>, LPtr<LockPolicyTryExclusive>&&>::value));
EXPECT_FALSE(
(Trait<LPtr<LockPolicyUpgrade>, LPtr<LockPolicyShared>&&>::value));
EXPECT_FALSE(
(Trait<LPtr<LockPolicyUpgrade>, LPtr<LockPolicyTryShared>&&>::value));
EXPECT_TRUE((Trait<
LPtr<detail::SynchronizedLockPolicyUpgrade>,
LPtr<detail::SynchronizedLockPolicyTryUpgrade>&&>::value));
EXPECT_FALSE((Trait<
LPtr<detail::SynchronizedLockPolicyUpgrade>,
LPtr<detail::SynchronizedLockPolicyExclusive>&&>::value));
EXPECT_FALSE((Trait<
LPtr<detail::SynchronizedLockPolicyUpgrade>,
LPtr<detail::SynchronizedLockPolicyTryExclusive>&&>::value));
EXPECT_FALSE((Trait<
LPtr<detail::SynchronizedLockPolicyUpgrade>,
LPtr<detail::SynchronizedLockPolicyShared>&&>::value));
EXPECT_FALSE((Trait<
LPtr<detail::SynchronizedLockPolicyUpgrade>,
LPtr<detail::SynchronizedLockPolicyTryShared>&&>::value));
}
} // namespace
......
......@@ -101,7 +101,7 @@ void runParallel(size_t numThreads, const Function& function) {
// testBasic() version for shared lock types
template <class Mutex>
typename std::enable_if<folly::LockTraits<Mutex>::is_shared>::type
std::enable_if_t<folly::detail::kSynchronizedMutexIsShared<void, Mutex>>
testBasicImpl() {
folly::Synchronized<std::vector<int>, Mutex> obj;
const auto& constObj = obj;
......@@ -153,7 +153,7 @@ testBasicImpl() {
// testBasic() version for non-shared lock types
template <class Mutex>
typename std::enable_if<!folly::LockTraits<Mutex>::is_shared>::type
std::enable_if_t<!folly::detail::kSynchronizedMutexIsShared<void, Mutex>>
testBasicImpl() {
folly::Synchronized<std::vector<int>, Mutex> obj;
const auto& constObj = obj;
......@@ -198,7 +198,7 @@ void testBasic() {
// testWithLock() version for shared lock types
template <class Mutex>
typename std::enable_if<folly::LockTraits<Mutex>::is_shared>::type
std::enable_if_t<folly::detail::kSynchronizedMutexIsShared<void, Mutex>>
testWithLock() {
folly::Synchronized<std::vector<int>, Mutex> obj;
const auto& constObj = obj;
......@@ -297,7 +297,7 @@ testWithLock() {
// testWithLock() version for non-shared lock types
template <class Mutex>
typename std::enable_if<!folly::LockTraits<Mutex>::is_shared>::type
std::enable_if_t<!folly::detail::kSynchronizedMutexIsShared<void, Mutex>>
testWithLock() {
folly::Synchronized<std::vector<int>, Mutex> obj;
......@@ -387,7 +387,7 @@ void testUnlockCommon() {
// testUnlock() version for shared lock types
template <class Mutex>
typename std::enable_if<folly::LockTraits<Mutex>::is_shared>::type
std::enable_if_t<folly::detail::kSynchronizedMutexIsShared<void, Mutex>>
testUnlock() {
folly::Synchronized<int, Mutex> value{10};
{
......@@ -420,7 +420,7 @@ testUnlock() {
// testUnlock() version for non-shared lock types
template <class Mutex>
typename std::enable_if<!folly::LockTraits<Mutex>::is_shared>::type
std::enable_if_t<!folly::detail::kSynchronizedMutexIsShared<void, Mutex>>
testUnlock() {
folly::Synchronized<int, Mutex> value{10};
{
......
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