Commit 79212dac authored by Aaryaman Sagar's avatar Aaryaman Sagar Committed by Facebook Github Bot 1

Adding upgradable locks to Synchronized

Summary: This diff adds support for upgradeable locks to folly::Synchronized

Reviewed By: yfeldblum

Differential Revision: D3683205

fbshipit-source-id: 1b91ab07076566b4e5b535f2a2dbe1c8d9f3d1c2
parent 2714e5b6
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#pragma once #pragma once
#include <folly/Likely.h>
#include <folly/LockTraits.h> #include <folly/LockTraits.h>
#include <folly/Preprocessor.h> #include <folly/Preprocessor.h>
#include <folly/SharedMutex.h> #include <folly/SharedMutex.h>
...@@ -42,13 +43,30 @@ class LockedPtr; ...@@ -42,13 +43,30 @@ class LockedPtr;
template <class LockedType, class LockPolicy = LockPolicyExclusive> template <class LockedType, class LockPolicy = LockPolicyExclusive>
class LockedGuardPtr; class LockedGuardPtr;
/**
* 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>;
/** /**
* SynchronizedBase is a helper parent class for Synchronized<T>. * SynchronizedBase is a helper parent class for Synchronized<T>.
* *
* It provides wlock() and rlock() methods for shared mutex types, * It provides wlock() and rlock() methods for shared mutex types,
* or lock() methods for purely exclusive mutex types. * or lock() methods for purely exclusive mutex types.
*/ */
template <class Subclass, bool is_shared> template <class Subclass, detail::MutexLevel level>
class SynchronizedBase; class SynchronizedBase;
/** /**
...@@ -58,7 +76,7 @@ class SynchronizedBase; ...@@ -58,7 +76,7 @@ class SynchronizedBase;
* accessing the data. * accessing the data.
*/ */
template <class Subclass> template <class Subclass>
class SynchronizedBase<Subclass, true> { class SynchronizedBase<Subclass, detail::MutexLevel::SHARED> {
public: public:
using LockedPtr = ::folly::LockedPtr<Subclass, LockPolicyExclusive>; using LockedPtr = ::folly::LockedPtr<Subclass, LockPolicyExclusive>;
using ConstWLockedPtr = using ConstWLockedPtr =
...@@ -185,6 +203,102 @@ class SynchronizedBase<Subclass, true> { ...@@ -185,6 +203,102 @@ class SynchronizedBase<Subclass, true> {
} }
}; };
/**
* SynchronizedBase specialization for upgrade mutex types.
*
* This class provides all the functionality provided by the SynchronizedBase
* specialization for shared mutexes and a ulock() method that returns an
* upgradable lock RAII proxy
*/
template <class Subclass>
class SynchronizedBase<Subclass, detail::MutexLevel::UPGRADE>
: public SynchronizedBase<Subclass, detail::MutexLevel::SHARED> {
public:
using UpgradeLockedPtr = ::folly::LockedPtr<Subclass, LockPolicyUpgrade>;
using ConstUpgradeLockedPtr =
::folly::LockedPtr<const Subclass, LockPolicyUpgrade>;
using UpgradeLockedGuardPtr =
::folly::LockedGuardPtr<Subclass, LockPolicyUpgrade>;
using ConstUpgradeLockedGuardPtr =
::folly::LockedGuardPtr<const Subclass, LockPolicyUpgrade>;
/**
* Acquire an upgrade lock and return a LockedPtr that can be used to safely
* access the datum
*
* And the const version
*/
UpgradeLockedPtr ulock() {
return UpgradeLockedPtr(static_cast<Subclass*>(this));
}
ConstUpgradeLockedPtr ulock() const {
return ConstUpgradeLockedPtr(static_cast<const Subclass*>(this));
}
/**
* Acquire an upgrade lock and return a LockedPtr that can be used to safely
* access the datum
*
* And the const version
*/
template <class Rep, class Period>
UpgradeLockedPtr ulock(const std::chrono::duration<Rep, Period>& timeout) {
return UpgradeLockedPtr(static_cast<Subclass*>(this), timeout);
}
template <class Rep, class Period>
UpgradeLockedPtr ulock(
const std::chrono::duration<Rep, Period>& timeout) const {
return ConstUpgradeLockedPtr(static_cast<const Subclass*>(this), timeout);
}
/**
* Invoke a function while holding the lock.
*
* A reference to the datum will be passed into the function as its only
* argument.
*
* This can be used with a lambda argument for easily defining small critical
* sections in the code. For example:
*
* auto value = obj.withULock([](auto& data) {
* data.doStuff();
* return data.getValue();
* });
*
* This is probably not the function you want. If the intent is to read the
* data object and determine whether you should upgrade to a write lock then
* the withULockPtr() method should be called instead, since it gives access
* to the LockedPtr proxy (which can be upgraded via the
* moveFromUpgradeToWrite() method)
*/
template <class Function>
auto withULock(Function&& function) const {
ConstUpgradeLockedGuardPtr guardPtr(static_cast<const Subclass*>(this));
return function(*guardPtr);
}
/**
* Invoke a function while holding the lock exclusively.
*
* This is similar to withULock(), but the function will be passed a
* LockedPtr rather than a reference to the data itself.
*
* This allows scopedUnlock() and getUniqueLock() to be called on the
* LockedPtr argument.
*
* This also allows you to upgrade the LockedPtr proxy to a write state so
* that changes can be made to the underlying data
*/
template <class Function>
auto withULockPtr(Function&& function) {
return function(ulock());
}
template <class Function>
auto withULockPtr(Function&& function) const {
return function(ulock());
}
};
/** /**
* SynchronizedBase specialization for non-shared mutex types. * SynchronizedBase specialization for non-shared mutex types.
* *
...@@ -192,7 +306,7 @@ class SynchronizedBase<Subclass, true> { ...@@ -192,7 +306,7 @@ class SynchronizedBase<Subclass, true> {
* data. * data.
*/ */
template <class Subclass> template <class Subclass>
class SynchronizedBase<Subclass, false> { class SynchronizedBase<Subclass, detail::MutexLevel::UNIQUE> {
public: public:
using LockedPtr = ::folly::LockedPtr<Subclass, LockPolicyExclusive>; using LockedPtr = ::folly::LockedPtr<Subclass, LockPolicyExclusive>;
using ConstLockedPtr = using ConstLockedPtr =
...@@ -310,10 +424,10 @@ class SynchronizedBase<Subclass, false> { ...@@ -310,10 +424,10 @@ class SynchronizedBase<Subclass, false> {
template <class T, class Mutex = SharedMutex> template <class T, class Mutex = SharedMutex>
struct Synchronized : public SynchronizedBase< struct Synchronized : public SynchronizedBase<
Synchronized<T, Mutex>, Synchronized<T, Mutex>,
LockTraits<Mutex>::is_shared> { MutexLevelValue<Mutex>::value> {
private: private:
using Base = using Base =
SynchronizedBase<Synchronized<T, Mutex>, LockTraits<Mutex>::is_shared>; SynchronizedBase<Synchronized<T, Mutex>, MutexLevelValue<Mutex>::value>;
static constexpr bool nxCopyCtor{ static constexpr bool nxCopyCtor{
std::is_nothrow_copy_constructible<T>::value}; std::is_nothrow_copy_constructible<T>::value};
static constexpr bool nxMoveCtor{ static constexpr bool nxMoveCtor{
...@@ -991,6 +1105,74 @@ class LockedPtr : public LockedPtrBase< ...@@ -991,6 +1105,74 @@ class LockedPtr : public LockedPtrBase<
ScopedUnlocker<SynchronizedType, LockPolicy> scopedUnlock() { ScopedUnlocker<SynchronizedType, LockPolicy> scopedUnlock() {
return ScopedUnlocker<SynchronizedType, LockPolicy>(this); return ScopedUnlocker<SynchronizedType, LockPolicy>(this);
} }
/***************************************************************************
* Upgradable lock methods.
* These are disabled via SFINAE when the mutex is not upgradable
**************************************************************************/
/**
* Move the locked ptr from an upgrade state to an exclusive state. The
* current lock is left in a null state.
*/
template <
typename SyncType = SynchronizedType,
typename = typename std::enable_if<
LockTraits<typename SyncType::MutexType>::is_upgrade>::type>
LockedPtr<SynchronizedType, LockPolicyFromUpgradeToExclusive>
moveFromUpgradeToWrite() {
auto* parent_to_pass_on = this->parent_;
this->parent_ = nullptr;
return LockedPtr<SynchronizedType, LockPolicyFromUpgradeToExclusive>(
parent_to_pass_on);
}
/**
* Move the locked ptr from an exclusive state to an upgrade state. The
* current lock is left in a null state.
*/
template <
typename SyncType = SynchronizedType,
typename = typename std::enable_if<
LockTraits<typename SyncType::MutexType>::is_upgrade>::type>
LockedPtr<SynchronizedType, LockPolicyFromExclusiveToUpgrade>
moveFromWriteToUpgrade() {
auto* parent_to_pass_on = this->parent_;
this->parent_ = nullptr;
return LockedPtr<SynchronizedType, LockPolicyFromExclusiveToUpgrade>(
parent_to_pass_on);
}
/**
* Move the locked ptr from an upgrade state to a shared state. The
* current lock is left in a null state.
*/
template <
typename SyncType = SynchronizedType,
typename = typename std::enable_if<
LockTraits<typename SyncType::MutexType>::is_upgrade>::type>
LockedPtr<SynchronizedType, LockPolicyFromUpgradeToShared>
moveFromUpgradeToShared() {
auto* parent_to_pass_on = this->parent_;
this->parent_ = nullptr;
return LockedPtr<SynchronizedType, LockPolicyFromUpgradeToShared>(
parent_to_pass_on);
}
/**
* Move the locked ptr from an exclusive state to a shared state. The
* current lock is left in a null state.
*/
template <
typename SyncType = SynchronizedType,
typename = typename std::enable_if<
LockTraits<typename SyncType::MutexType>::is_upgrade>::type>
LockedPtr<SynchronizedType, LockPolicyFromExclusiveToShared>
moveFromWriteToShared() {
auto* parent_to_pass_on = this->parent_;
this->parent_ = nullptr;
return LockedPtr<SynchronizedType, LockPolicyFromExclusiveToShared>(
parent_to_pass_on);
}
}; };
/** /**
......
This diff is collapsed.
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