Commit 7dd2b580 authored by Aaryaman Sagar's avatar Aaryaman Sagar Committed by Facebook Github Bot 2

Adding policies for all the upgrade and downgrade mutex transitions that are...

Adding policies for all the upgrade and downgrade mutex transitions that are going to be supported by Synchronized

Summary:
This diff contains the lock policies that will be used by Synchronized to
implement mutex transitions by means of heterogenous RAII mutex wrappers

Reviewed By: yfeldblum

Differential Revision: D3665020

fbshipit-source-id: a5509dfd58a1dd6cd60a7d3afe929d0da860926d
parent 84f24236
...@@ -462,8 +462,7 @@ unlock_shared_or_unique(Mutex& mutex) { ...@@ -462,8 +462,7 @@ unlock_shared_or_unique(Mutex& mutex) {
/** /**
* A lock policy that performs exclusive lock operations. * A lock policy that performs exclusive lock operations.
*/ */
class LockPolicyExclusive { struct LockPolicyExclusive {
public:
template <class Mutex> template <class Mutex>
static void lock(Mutex& mutex) { static void lock(Mutex& mutex) {
LockTraits<Mutex>::lock(mutex); LockTraits<Mutex>::lock(mutex);
...@@ -484,8 +483,7 @@ class LockPolicyExclusive { ...@@ -484,8 +483,7 @@ class LockPolicyExclusive {
* A lock policy that performs shared lock operations. * A lock policy that performs shared lock operations.
* This policy only works with shared mutex types. * This policy only works with shared mutex types.
*/ */
class LockPolicyShared { struct LockPolicyShared {
public:
template <class Mutex> template <class Mutex>
static void lock(Mutex& mutex) { static void lock(Mutex& mutex) {
LockTraits<Mutex>::lock_shared(mutex); LockTraits<Mutex>::lock_shared(mutex);
...@@ -506,8 +504,7 @@ class LockPolicyShared { ...@@ -506,8 +504,7 @@ class LockPolicyShared {
* A lock policy that performs a shared lock operation if a shared mutex type * A lock policy that performs a shared lock operation if a shared mutex type
* is given, or a normal exclusive lock operation on non-shared mutex types. * is given, or a normal exclusive lock operation on non-shared mutex types.
*/ */
class LockPolicyShareable { struct LockPolicyShareable {
public:
template <class Mutex> template <class Mutex>
static void lock(Mutex& mutex) { static void lock(Mutex& mutex) {
lock_shared_or_unique(mutex); lock_shared_or_unique(mutex);
...@@ -524,4 +521,120 @@ class LockPolicyShareable { ...@@ -524,4 +521,120 @@ class LockPolicyShareable {
} }
}; };
/**
* A lock policy with the following mapping
*
* lock() -> lock_upgrade()
* unlock() -> unlock_upgrade()
* try_lock_for -> try_lock_upgrade_for()
*/
struct LockPolicyUpgrade {
template <class Mutex>
static void lock(Mutex& mutex) {
LockTraits<Mutex>::lock_upgrade(mutex);
}
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);
}
template <class Mutex>
static void unlock(Mutex& mutex) {
LockTraits<Mutex>::unlock_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 : public LockPolicyExclusive {
template <class Mutex>
static void lock(Mutex& mutex) {
LockTraits<Mutex>::unlock_upgrade_and_lock(mutex);
}
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 : public LockPolicyUpgrade {
template <class Mutex>
static void lock(Mutex& mutex) {
LockTraits<Mutex>::unlock_and_lock_upgrade(mutex);
}
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 : public LockPolicyShared {
template <class Mutex>
static void lock(Mutex& mutex) {
LockTraits<Mutex>::unlock_upgrade_and_lock_shared(mutex);
}
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 : public LockPolicyShared {
template <class Mutex>
static void lock(Mutex& mutex) {
LockTraits<Mutex>::unlock_and_lock_shared(mutex);
}
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;
}
};
} // folly } // folly
...@@ -27,6 +27,83 @@ using namespace folly; ...@@ -27,6 +27,83 @@ using namespace folly;
static constexpr auto one_ms = std::chrono::milliseconds(1); 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) { TEST(LockTraits, std_mutex) {
using traits = LockTraits<std::mutex>; using traits = LockTraits<std::mutex>;
static_assert(!traits::is_timed, "std:mutex is not a timed lock"); static_assert(!traits::is_timed, "std:mutex is not a timed lock");
...@@ -266,3 +343,80 @@ TEST(LockTraits, boost_shared_mutex) { ...@@ -266,3 +343,80 @@ TEST(LockTraits, boost_shared_mutex) {
unlock_shared_or_unique(mutex); unlock_shared_or_unique(mutex);
} }
#endif // FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES #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);
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);
}
/**
* 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);
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();
}
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