X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2Ftest%2FSynchronizedTest.cpp;h=db0f9c93ce6509a8736008d9008b24263223f9e8;hb=7acba7e1a0a75d22087647b3e9e830c9b0e8d41e;hp=29a8d031592951156b552c4ca029145293824a65;hpb=a2b94586d9000f0b87b9653d237fb5e5960a25be;p=folly.git diff --git a/folly/test/SynchronizedTest.cpp b/folly/test/SynchronizedTest.cpp index 29a8d031..db0f9c93 100644 --- a/folly/test/SynchronizedTest.cpp +++ b/folly/test/SynchronizedTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2016 Facebook, Inc. + * Copyright 2017 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,10 +24,10 @@ #include #include #include +#include #include -#include -namespace { +using namespace folly::sync_tests; template class SynchronizedTest : public testing::Test {}; @@ -59,10 +59,30 @@ TYPED_TEST(SynchronizedTest, Basic) { testBasic(); } +TYPED_TEST(SynchronizedTest, WithLock) { + testWithLock(); +} + +TYPED_TEST(SynchronizedTest, Unlock) { + testUnlock(); +} + +TYPED_TEST(SynchronizedTest, Deprecated) { + testDeprecated(); +} + TYPED_TEST(SynchronizedTest, Concurrency) { testConcurrency(); } +TYPED_TEST(SynchronizedTest, AcquireLocked) { + testAcquireLocked(); +} + +TYPED_TEST(SynchronizedTest, AcquireLockedWithConst) { + testAcquireLockedWithConst(); +} + TYPED_TEST(SynchronizedTest, DualLocking) { testDualLocking(); } @@ -94,6 +114,10 @@ using SynchronizedTimedTestTypes = testing::Types< folly::SharedMutexWritePriority>; TYPED_TEST_CASE(SynchronizedTimedTest, SynchronizedTimedTestTypes); +TYPED_TEST(SynchronizedTimedTest, Timed) { + testTimed(); +} + TYPED_TEST(SynchronizedTimedTest, TimedSynchronized) { testTimedSynchronized(); } @@ -114,6 +138,10 @@ using SynchronizedTimedWithConstTestTypes = testing::Types< TYPED_TEST_CASE( SynchronizedTimedWithConstTest, SynchronizedTimedWithConstTestTypes); +TYPED_TEST(SynchronizedTimedWithConstTest, TimedShared) { + testTimedShared(); +} + TYPED_TEST(SynchronizedTimedWithConstTest, TimedSynchronizeWithConst) { testTimedSynchronizedWithConst(); } @@ -126,14 +154,12 @@ using CountPair = std::pair; // This class is specialized only to be uesed in SynchronizedLockTest class FakeMutex { public: - bool lock() { + void lock() { ++lockCount_; - return true; } - bool unlock() { + void unlock() { ++unlockCount_; - return true; } static CountPair getLockUnlockCount() { @@ -163,65 +189,325 @@ class SynchronizedLockTest : public testing::Test { } }; -// Single level of SYNCHRONIZED and UNSYNCHRONIZED, although nested test are -// super set of it, it is possible single level test passes while nested tests -// fail -TEST_F(SynchronizedLockTest, SyncUnSync) { - folly::Synchronized, FakeMutex> obj; - EXPECT_EQ((CountPair{0, 0}), FakeMutex::getLockUnlockCount()); - SYNCHRONIZED(obj) { - EXPECT_EQ((CountPair{1, 0}), FakeMutex::getLockUnlockCount()); - UNSYNCHRONIZED(obj) { - EXPECT_EQ((CountPair{1, 1}), FakeMutex::getLockUnlockCount()); - } - EXPECT_EQ((CountPair{2, 1}), FakeMutex::getLockUnlockCount()); - } - EXPECT_EQ((CountPair{2, 2}), FakeMutex::getLockUnlockCount()); -} - -// Nested SYNCHRONIZED UNSYNCHRONIZED test, 2 levels for each are used here -TEST_F(SynchronizedLockTest, NestedSyncUnSync) { - folly::Synchronized, FakeMutex> obj; - EXPECT_EQ((CountPair{0, 0}), FakeMutex::getLockUnlockCount()); - SYNCHRONIZED(objCopy, obj) { - EXPECT_EQ((CountPair{1, 0}), FakeMutex::getLockUnlockCount()); - SYNCHRONIZED(obj) { - EXPECT_EQ((CountPair{2, 0}), FakeMutex::getLockUnlockCount()); - UNSYNCHRONIZED(obj) { - EXPECT_EQ((CountPair{2, 1}), FakeMutex::getLockUnlockCount()); - UNSYNCHRONIZED(obj) { - EXPECT_EQ((CountPair{2, 2}), - FakeMutex::getLockUnlockCount()); - } - EXPECT_EQ((CountPair{3, 2}), FakeMutex::getLockUnlockCount()); - } - EXPECT_EQ((CountPair{4, 2}), FakeMutex::getLockUnlockCount()); - } - EXPECT_EQ((CountPair{4, 3}), FakeMutex::getLockUnlockCount()); - } - EXPECT_EQ((CountPair{4, 4}), FakeMutex::getLockUnlockCount()); -} - -// Different nesting behavior, UNSYNCHRONIZED called on differen depth of -// SYNCHRONIZED -TEST_F(SynchronizedLockTest, NestedSyncUnSync2) { - folly::Synchronized, FakeMutex> obj; - EXPECT_EQ((CountPair{0, 0}), FakeMutex::getLockUnlockCount()); - SYNCHRONIZED(objCopy, obj) { - EXPECT_EQ((CountPair{1, 0}), FakeMutex::getLockUnlockCount()); - SYNCHRONIZED(obj) { - EXPECT_EQ((CountPair{2, 0}), FakeMutex::getLockUnlockCount()); - UNSYNCHRONIZED(obj) { - EXPECT_EQ((CountPair{2, 1}), FakeMutex::getLockUnlockCount()); - } - EXPECT_EQ((CountPair{3, 1}), FakeMutex::getLockUnlockCount()); - } - EXPECT_EQ((CountPair{3, 2}), FakeMutex::getLockUnlockCount()); - UNSYNCHRONIZED(obj) { - EXPECT_EQ((CountPair{3, 3}), FakeMutex::getLockUnlockCount()); - } - EXPECT_EQ((CountPair{4, 3}), FakeMutex::getLockUnlockCount()); - } - EXPECT_EQ((CountPair{4, 4}), FakeMutex::getLockUnlockCount()); +/** + * Test mutex to help to automate assertions, taken from LockTraitsTest.cpp + */ +class FakeAllPowerfulAssertingMutexInternal { + 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 + bool try_lock_for(const std::chrono::duration&) { + EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED); + this->lock_state = CurrentLockState::UNIQUE; + return true; + } + + template + bool try_lock_upgrade_for(const std::chrono::duration&) { + EXPECT_EQ(this->lock_state, CurrentLockState::UNLOCKED); + this->lock_state = CurrentLockState::UPGRADE; + return true; + } + + template + bool try_unlock_upgrade_and_lock_for( + const std::chrono::duration&) { + 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}; +}; + +/** + * The following works around the internal mutex for synchronized being + * private + * + * This is horridly thread unsafe. + */ +static FakeAllPowerfulAssertingMutexInternal globalAllPowerfulAssertingMutex; + +class FakeAllPowerfulAssertingMutex { + public: + void lock() { + globalAllPowerfulAssertingMutex.lock(); + } + void unlock() { + globalAllPowerfulAssertingMutex.unlock(); + } + void lock_shared() { + globalAllPowerfulAssertingMutex.lock_shared(); + } + void unlock_shared() { + globalAllPowerfulAssertingMutex.unlock_shared(); + } + void lock_upgrade() { + globalAllPowerfulAssertingMutex.lock_upgrade(); + } + void unlock_upgrade() { + globalAllPowerfulAssertingMutex.unlock_upgrade(); + } + + void unlock_upgrade_and_lock() { + globalAllPowerfulAssertingMutex.unlock_upgrade_and_lock(); + } + void unlock_and_lock_upgrade() { + globalAllPowerfulAssertingMutex.unlock_and_lock_upgrade(); + } + void unlock_and_lock_shared() { + globalAllPowerfulAssertingMutex.unlock_and_lock_shared(); + } + void unlock_upgrade_and_lock_shared() { + globalAllPowerfulAssertingMutex.unlock_upgrade_and_lock_shared(); + } + + template + bool try_lock_for(const std::chrono::duration& arg) { + return globalAllPowerfulAssertingMutex.try_lock_for(arg); + } + + template + bool try_lock_upgrade_for(const std::chrono::duration& arg) { + return globalAllPowerfulAssertingMutex.try_lock_upgrade_for(arg); + } + + template + bool try_unlock_upgrade_and_lock_for( + const std::chrono::duration& arg) { + return globalAllPowerfulAssertingMutex.try_unlock_upgrade_and_lock_for(arg); + } + + // reset state on destruction + ~FakeAllPowerfulAssertingMutex() { + globalAllPowerfulAssertingMutex = FakeAllPowerfulAssertingMutexInternal{}; + } +}; + +TEST_F(SynchronizedLockTest, TestCopyConstructibleValues) { + struct NonCopyConstructible { + NonCopyConstructible(const NonCopyConstructible&) = delete; + NonCopyConstructible& operator=(const NonCopyConstructible&) = delete; + }; + struct CopyConstructible {}; + EXPECT_FALSE(std::is_copy_constructible< + folly::Synchronized>::value); + EXPECT_FALSE(std::is_copy_assignable< + folly::Synchronized>::value); + EXPECT_TRUE(std::is_copy_constructible< + folly::Synchronized>::value); + EXPECT_TRUE( + std::is_copy_assignable>::value); } + +TEST_F(SynchronizedLockTest, UpgradableLocking) { + folly::Synchronized sync; + + // sanity assert + static_assert( + std::is_same::type, int>::value, + "The ulock function was not well configured, blame aary@instagram.com"); + + { + auto ulock = sync.ulock(); + EXPECT_EQ( + globalAllPowerfulAssertingMutex.lock_state, + FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE); + } + + // should be unlocked here + EXPECT_EQ( + globalAllPowerfulAssertingMutex.lock_state, + FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED); + + // test going from upgrade to exclusive + { + auto ulock = sync.ulock(); + auto wlock = ulock.moveFromUpgradeToWrite(); + EXPECT_EQ(static_cast(ulock), false); + EXPECT_EQ( + globalAllPowerfulAssertingMutex.lock_state, + FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE); + } + + // should be unlocked here + EXPECT_EQ( + globalAllPowerfulAssertingMutex.lock_state, + FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED); + + // test going from upgrade to shared + { + auto ulock = sync.ulock(); + auto slock = ulock.moveFromUpgradeToRead(); + EXPECT_EQ(static_cast(ulock), false); + EXPECT_EQ( + globalAllPowerfulAssertingMutex.lock_state, + FakeAllPowerfulAssertingMutexInternal::CurrentLockState::SHARED); + } + + // should be unlocked here + EXPECT_EQ( + globalAllPowerfulAssertingMutex.lock_state, + FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED); + + // test going from exclusive to upgrade + { + auto wlock = sync.wlock(); + auto ulock = wlock.moveFromWriteToUpgrade(); + EXPECT_EQ(static_cast(wlock), false); + EXPECT_EQ( + globalAllPowerfulAssertingMutex.lock_state, + FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE); + } + + // should be unlocked here + EXPECT_EQ( + globalAllPowerfulAssertingMutex.lock_state, + FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED); + + // test going from exclusive to shared + { + auto wlock = sync.wlock(); + auto slock = wlock.moveFromWriteToRead(); + EXPECT_EQ(static_cast(wlock), false); + EXPECT_EQ( + globalAllPowerfulAssertingMutex.lock_state, + FakeAllPowerfulAssertingMutexInternal::CurrentLockState::SHARED); + } + + // should be unlocked here + EXPECT_EQ( + globalAllPowerfulAssertingMutex.lock_state, + FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED); +} + +TEST_F(SynchronizedLockTest, UpgradableLockingWithULock) { + folly::Synchronized sync; + + // sanity assert + static_assert( + std::is_same::type, int>::value, + "The ulock function was not well configured, blame aary@instagram.com"); + + // test from upgrade to write + sync.withULockPtr([](auto ulock) { + EXPECT_EQ(static_cast(ulock), true); + EXPECT_EQ( + globalAllPowerfulAssertingMutex.lock_state, + FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE); + + auto wlock = ulock.moveFromUpgradeToWrite(); + EXPECT_EQ(static_cast(ulock), false); + EXPECT_EQ( + globalAllPowerfulAssertingMutex.lock_state, + FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE); + }); + + // should be unlocked here + EXPECT_EQ( + globalAllPowerfulAssertingMutex.lock_state, + FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED); + + // test from write to upgrade + sync.withWLockPtr([](auto wlock) { + EXPECT_EQ(static_cast(wlock), true); + EXPECT_EQ( + globalAllPowerfulAssertingMutex.lock_state, + FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE); + + auto ulock = wlock.moveFromWriteToUpgrade(); + EXPECT_EQ(static_cast(wlock), false); + EXPECT_EQ( + globalAllPowerfulAssertingMutex.lock_state, + FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE); + }); + + // should be unlocked here + EXPECT_EQ( + globalAllPowerfulAssertingMutex.lock_state, + FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED); + + // test from upgrade to shared + sync.withULockPtr([](auto ulock) { + EXPECT_EQ(static_cast(ulock), true); + EXPECT_EQ( + globalAllPowerfulAssertingMutex.lock_state, + FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UPGRADE); + + auto slock = ulock.moveFromUpgradeToRead(); + EXPECT_EQ(static_cast(ulock), false); + EXPECT_EQ( + globalAllPowerfulAssertingMutex.lock_state, + FakeAllPowerfulAssertingMutexInternal::CurrentLockState::SHARED); + }); + + // should be unlocked here + EXPECT_EQ( + globalAllPowerfulAssertingMutex.lock_state, + FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED); + + // test from write to shared + sync.withWLockPtr([](auto wlock) { + EXPECT_EQ(static_cast(wlock), true); + EXPECT_EQ( + globalAllPowerfulAssertingMutex.lock_state, + FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNIQUE); + + auto slock = wlock.moveFromWriteToRead(); + EXPECT_EQ(static_cast(wlock), false); + EXPECT_EQ( + globalAllPowerfulAssertingMutex.lock_state, + FakeAllPowerfulAssertingMutexInternal::CurrentLockState::SHARED); + }); + + // should be unlocked here + EXPECT_EQ( + globalAllPowerfulAssertingMutex.lock_state, + FakeAllPowerfulAssertingMutexInternal::CurrentLockState::UNLOCKED); }