/*
- * Copyright 2014 Facebook, Inc.
+ * Copyright 2016 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
// Test bed for folly/Synchronized.h
-#include <folly/Synchronized.h>
+#include <folly/LockTraitsBoost.h>
+#include <folly/Portability.h>
#include <folly/RWSpinLock.h>
+#include <folly/SharedMutex.h>
+#include <folly/SpinLock.h>
+#include <folly/Synchronized.h>
#include <folly/test/SynchronizedTestLib.h>
#include <gtest/gtest.h>
+using namespace folly::sync_tests;
-TEST(Synchronized, Basic) {
- testBasic<std::mutex>();
- testBasic<std::recursive_mutex>();
-#ifndef __APPLE__
- testBasic<std::timed_mutex>();
- testBasic<std::recursive_timed_mutex>();
-#endif
+template <class Mutex>
+class SynchronizedTest : public testing::Test {};
+using SynchronizedTestTypes = testing::Types<
+ folly::SharedMutexReadPriority,
+ folly::SharedMutexWritePriority,
+ std::mutex,
+ std::recursive_mutex,
+#if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
+ std::timed_mutex,
+ std::recursive_timed_mutex,
+#endif
+ boost::mutex,
+ boost::recursive_mutex,
+#if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
+ boost::timed_mutex,
+ boost::recursive_timed_mutex,
+#endif
#ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
- testBasic<folly::RWTicketSpinLock32>();
+ folly::RWTicketSpinLock32,
+ folly::RWTicketSpinLock64,
#endif
+ boost::shared_mutex,
+ folly::SpinLock>;
+TYPED_TEST_CASE(SynchronizedTest, SynchronizedTestTypes);
- testBasic<boost::mutex>();
- testBasic<boost::recursive_mutex>();
- testBasic<boost::shared_mutex>();
-#ifndef __APPLE__
- testBasic<boost::timed_mutex>();
- testBasic<boost::recursive_timed_mutex>();
-#endif
+TYPED_TEST(SynchronizedTest, Basic) {
+ testBasic<TypeParam>();
}
-TEST(Synchronized, Concurrency) {
- testConcurrency<std::mutex>();
- testConcurrency<std::recursive_mutex>();
-#ifndef __APPLE__
- testConcurrency<std::timed_mutex>();
- testConcurrency<std::recursive_timed_mutex>();
-#endif
+TYPED_TEST(SynchronizedTest, WithLock) {
+ testWithLock<TypeParam>();
+}
-#ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
- testConcurrency<folly::RWTicketSpinLock32>();
-#endif
+TYPED_TEST(SynchronizedTest, Deprecated) {
+ testDeprecated<TypeParam>();
+}
- testConcurrency<boost::mutex>();
- testConcurrency<boost::recursive_mutex>();
- testConcurrency<boost::shared_mutex>();
-#ifndef __APPLE__
- testConcurrency<boost::timed_mutex>();
- testConcurrency<boost::recursive_timed_mutex>();
-#endif
+TYPED_TEST(SynchronizedTest, Concurrency) {
+ testConcurrency<TypeParam>();
+}
+
+TYPED_TEST(SynchronizedTest, AcquireLocked) {
+ testAcquireLocked<TypeParam>();
}
+TYPED_TEST(SynchronizedTest, AcquireLockedWithConst) {
+ testAcquireLockedWithConst<TypeParam>();
+}
-TEST(Synchronized, DualLocking) {
- testDualLocking<std::mutex>();
- testDualLocking<std::recursive_mutex>();
-#ifndef __APPLE__
- testDualLocking<std::timed_mutex>();
- testDualLocking<std::recursive_timed_mutex>();
-#endif
+TYPED_TEST(SynchronizedTest, DualLocking) {
+ testDualLocking<TypeParam>();
+}
+TYPED_TEST(SynchronizedTest, DualLockingWithConst) {
+ testDualLockingWithConst<TypeParam>();
+}
+
+TYPED_TEST(SynchronizedTest, ConstCopy) {
+ testConstCopy<TypeParam>();
+}
+
+template <class Mutex>
+class SynchronizedTimedTest : public testing::Test {};
+
+using SynchronizedTimedTestTypes = testing::Types<
+#if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
+ std::timed_mutex,
+ std::recursive_timed_mutex,
+ boost::timed_mutex,
+ boost::recursive_timed_mutex,
+ boost::shared_mutex,
+#endif
#ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
- testDualLocking<folly::RWTicketSpinLock32>();
+ folly::RWTicketSpinLock32,
+ folly::RWTicketSpinLock64,
#endif
+ folly::SharedMutexReadPriority,
+ folly::SharedMutexWritePriority>;
+TYPED_TEST_CASE(SynchronizedTimedTest, SynchronizedTimedTestTypes);
- testDualLocking<boost::mutex>();
- testDualLocking<boost::recursive_mutex>();
- testDualLocking<boost::shared_mutex>();
-#ifndef __APPLE__
- testDualLocking<boost::timed_mutex>();
- testDualLocking<boost::recursive_timed_mutex>();
-#endif
+TYPED_TEST(SynchronizedTimedTest, Timed) {
+ testTimed<TypeParam>();
}
+TYPED_TEST(SynchronizedTimedTest, TimedSynchronized) {
+ testTimedSynchronized<TypeParam>();
+}
-TEST(Synchronized, DualLockingWithConst) {
- testDualLockingWithConst<std::mutex>();
- testDualLockingWithConst<std::recursive_mutex>();
-#ifndef __APPLE__
- testDualLockingWithConst<std::timed_mutex>();
- testDualLockingWithConst<std::recursive_timed_mutex>();
-#endif
+template <class Mutex>
+class SynchronizedTimedWithConstTest : public testing::Test {};
+using SynchronizedTimedWithConstTestTypes = testing::Types<
+#if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
+ boost::shared_mutex,
+#endif
#ifdef RW_SPINLOCK_USE_X86_INTRINSIC_
- testDualLockingWithConst<folly::RWTicketSpinLock32>();
+ folly::RWTicketSpinLock32,
+ folly::RWTicketSpinLock64,
#endif
+ folly::SharedMutexReadPriority,
+ folly::SharedMutexWritePriority>;
+TYPED_TEST_CASE(
+ SynchronizedTimedWithConstTest, SynchronizedTimedWithConstTestTypes);
- testDualLockingWithConst<boost::mutex>();
- testDualLockingWithConst<boost::recursive_mutex>();
- testDualLockingWithConst<boost::shared_mutex>();
-#ifndef __APPLE__
- testDualLockingWithConst<boost::timed_mutex>();
- testDualLockingWithConst<boost::recursive_timed_mutex>();
-#endif
+TYPED_TEST(SynchronizedTimedWithConstTest, TimedShared) {
+ testTimedShared<TypeParam>();
}
+TYPED_TEST(SynchronizedTimedWithConstTest, TimedSynchronizeWithConst) {
+ testTimedSynchronizedWithConst<TypeParam>();
+}
-#ifndef __APPLE__
-TEST(Synchronized, TimedSynchronized) {
- testTimedSynchronized<std::timed_mutex>();
- testTimedSynchronized<std::recursive_timed_mutex>();
-
- testTimedSynchronized<boost::timed_mutex>();
- testTimedSynchronized<boost::recursive_timed_mutex>();
- testTimedSynchronized<boost::shared_mutex>();
+TYPED_TEST(SynchronizedTest, InPlaceConstruction) {
+ testInPlaceConstruction<TypeParam>();
+}
- testTimedSynchronizedWithConst<boost::shared_mutex>();
+using CountPair = std::pair<int, int>;
+// This class is specialized only to be uesed in SynchronizedLockTest
+class FakeMutex {
+ public:
+ bool lock() {
+ ++lockCount_;
+ return true;
+ }
+
+ bool unlock() {
+ ++unlockCount_;
+ return true;
+ }
+
+ static CountPair getLockUnlockCount() {
+ return CountPair{lockCount_, unlockCount_};
+ }
+
+ static void resetLockUnlockCount() {
+ lockCount_ = 0;
+ unlockCount_ = 0;
+ }
+ private:
+ // Keep these two static for test access
+ // Keep them thread_local in case of tests are run in parallel within one
+ // process
+ static FOLLY_TLS int lockCount_;
+ static FOLLY_TLS int unlockCount_;
+};
+FOLLY_TLS int FakeMutex::lockCount_{0};
+FOLLY_TLS int FakeMutex::unlockCount_{0};
+
+// SynchronizedLockTest is used to verify the correct lock unlock behavior
+// happens per design
+class SynchronizedLockTest : public testing::Test {
+ public:
+ void SetUp() override {
+ FakeMutex::resetLockUnlockCount();
+ }
+};
+
+// 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<std::vector<int>, 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());
}
-#endif
-TEST(Synchronized, ConstCopy) {
-#ifndef __APPLE__
- testConstCopy<std::timed_mutex>();
- testConstCopy<std::recursive_timed_mutex>();
+// Nested SYNCHRONIZED UNSYNCHRONIZED test, 2 levels of synchronization
+TEST_F(SynchronizedLockTest, NestedSyncUnSync) {
+ folly::Synchronized<std::vector<int>, 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());
+ // Note: UNSYNCHRONIZED has always been kind of broken here.
+ // The input parameter is ignored (other than to overwrite what the input
+ // variable name refers to), and it unlocks the most object acquired in
+ // the most recent SYNCHRONIZED scope.
+ UNSYNCHRONIZED(obj) {
+ EXPECT_EQ((CountPair{2, 1}), FakeMutex::getLockUnlockCount());
+ }
+ EXPECT_EQ((CountPair{3, 1}), FakeMutex::getLockUnlockCount());
+ UNSYNCHRONIZED(obj) {
+ 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());
+}
- testConstCopy<boost::timed_mutex>();
- testConstCopy<boost::recursive_timed_mutex>();
-#endif
- testConstCopy<boost::shared_mutex>();
+// Different nesting behavior, UNSYNCHRONIZED called on different depth of
+// SYNCHRONIZED
+TEST_F(SynchronizedLockTest, NestedSyncUnSync2) {
+ folly::Synchronized<std::vector<int>, 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());
}