folly::copy
[folly.git] / folly / test / SynchronizedTestLib-inl.h
index 3166de8ac4c63d75ad389228cd507a1f624aa5f2..10514089bfc59c142ad7eb805444e0ab5b9848bb 100644 (file)
 
 #pragma once
 
-#include <gtest/gtest.h>
-
 #include <folly/Foreach.h>
 #include <folly/Random.h>
 #include <folly/Synchronized.h>
+#include <folly/portability/GTest.h>
 #include <glog/logging.h>
 #include <algorithm>
 #include <condition_variable>
@@ -42,6 +41,7 @@ inline std::mt19937& getRNG() {
 void randomSleep(std::chrono::milliseconds min, std::chrono::milliseconds max) {
   std::uniform_int_distribution<> range(min.count(), max.count());
   std::chrono::milliseconds duration(range(getRNG()));
+  /* sleep override */
   std::this_thread::sleep_for(duration);
 }
 
@@ -108,6 +108,7 @@ template <class Mutex>
 typename std::enable_if<folly::LockTraits<Mutex>::is_shared>::type
 testBasicImpl() {
   folly::Synchronized<std::vector<int>, Mutex> obj;
+  const auto& constObj = obj;
 
   obj.wlock()->resize(1000);
 
@@ -141,7 +142,6 @@ testBasicImpl() {
   obj.wlock()->front() = 2;
 
   {
-    const auto& constObj = obj;
     // contextualLock() on a const reference should grab a shared lock
     auto lockedObj = constObj.contextualLock();
     EXPECT_EQ(2, lockedObj->front());
@@ -160,6 +160,7 @@ template <class Mutex>
 typename std::enable_if<!folly::LockTraits<Mutex>::is_shared>::type
 testBasicImpl() {
   folly::Synchronized<std::vector<int>, Mutex> obj;
+  const auto& constObj = obj;
 
   obj.lock()->resize(1000);
 
@@ -178,6 +179,12 @@ testBasicImpl() {
       EXPECT_EQ(1001, obj.lock()->size());
     }
   }
+  {
+    auto lockedObj = constObj.lock();
+    EXPECT_EQ(1001, lockedObj->size());
+    EXPECT_EQ(10, lockedObj->back());
+    EXPECT_EQ(1000, obj2.lock()->size());
+  }
 
   obj.lock()->front() = 2;
 
@@ -193,6 +200,258 @@ void testBasic() {
   testBasicImpl<Mutex>();
 }
 
+// testWithLock() version for shared lock types
+template <class Mutex>
+typename std::enable_if<folly::LockTraits<Mutex>::is_shared>::type
+testWithLock() {
+  folly::Synchronized<std::vector<int>, Mutex> obj;
+  const auto& constObj = obj;
+
+  // Test withWLock() and withRLock()
+  obj.withWLock([](std::vector<int>& lockedObj) {
+    lockedObj.resize(1000);
+    lockedObj.push_back(10);
+    lockedObj.push_back(11);
+  });
+  obj.withWLock([](const std::vector<int>& lockedObj) {
+    EXPECT_EQ(1002, lockedObj.size());
+  });
+  constObj.withWLock([](const std::vector<int>& lockedObj) {
+    EXPECT_EQ(1002, lockedObj.size());
+    EXPECT_EQ(11, lockedObj.back());
+  });
+  obj.withRLock([](const std::vector<int>& lockedObj) {
+    EXPECT_EQ(1002, lockedObj.size());
+    EXPECT_EQ(11, lockedObj.back());
+  });
+  constObj.withRLock([](const std::vector<int>& lockedObj) {
+    EXPECT_EQ(1002, lockedObj.size());
+  });
+
+#if __cpp_generic_lambdas >= 201304
+  obj.withWLock([](auto& lockedObj) { lockedObj.push_back(12); });
+  obj.withWLock(
+      [](const auto& lockedObj) { EXPECT_EQ(1003, lockedObj.size()); });
+  constObj.withWLock([](const auto& lockedObj) {
+    EXPECT_EQ(1003, lockedObj.size());
+    EXPECT_EQ(12, lockedObj.back());
+  });
+  obj.withRLock([](const auto& lockedObj) {
+    EXPECT_EQ(1003, lockedObj.size());
+    EXPECT_EQ(12, lockedObj.back());
+  });
+  constObj.withRLock(
+      [](const auto& lockedObj) { EXPECT_EQ(1003, lockedObj.size()); });
+  obj.withWLock([](auto& lockedObj) { lockedObj.pop_back(); });
+#endif
+
+  // Test withWLockPtr() and withRLockPtr()
+  using SynchType = folly::Synchronized<std::vector<int>, Mutex>;
+#if __cpp_generic_lambdas >= 201304
+  obj.withWLockPtr([](auto&& lockedObj) { lockedObj->push_back(13); });
+  obj.withRLockPtr([](auto&& lockedObj) {
+    EXPECT_EQ(1003, lockedObj->size());
+    EXPECT_EQ(13, lockedObj->back());
+  });
+  constObj.withRLockPtr([](auto&& lockedObj) {
+    EXPECT_EQ(1003, lockedObj->size());
+    EXPECT_EQ(13, lockedObj->back());
+  });
+  obj.withWLockPtr([&](auto&& lockedObj) {
+    lockedObj->push_back(14);
+    {
+      auto unlocker = lockedObj.scopedUnlock();
+      obj.wlock()->push_back(15);
+    }
+    EXPECT_EQ(15, lockedObj->back());
+  });
+  constObj.withWLockPtr([](auto&& lockedObj) {
+    EXPECT_EQ(1005, lockedObj->size());
+    EXPECT_EQ(15, lockedObj->back());
+  });
+#else
+  obj.withWLockPtr([](typename SynchType::LockedPtr&& lockedObj) {
+    lockedObj->push_back(13);
+    lockedObj->push_back(14);
+    lockedObj->push_back(15);
+  });
+#endif
+
+  obj.withWLockPtr([](typename SynchType::LockedPtr&& lockedObj) {
+    lockedObj->push_back(16);
+    EXPECT_EQ(1006, lockedObj->size());
+  });
+  constObj.withWLockPtr([](typename SynchType::ConstWLockedPtr&& lockedObj) {
+    EXPECT_EQ(1006, lockedObj->size());
+    EXPECT_EQ(16, lockedObj->back());
+  });
+  obj.withRLockPtr([](typename SynchType::ConstLockedPtr&& lockedObj) {
+    EXPECT_EQ(1006, lockedObj->size());
+    EXPECT_EQ(16, lockedObj->back());
+  });
+  constObj.withRLockPtr([](typename SynchType::ConstLockedPtr&& lockedObj) {
+    EXPECT_EQ(1006, lockedObj->size());
+    EXPECT_EQ(16, lockedObj->back());
+  });
+}
+
+// testWithLock() version for non-shared lock types
+template <class Mutex>
+typename std::enable_if<!folly::LockTraits<Mutex>::is_shared>::type
+testWithLock() {
+  folly::Synchronized<std::vector<int>, Mutex> obj;
+
+  // Test withLock()
+  obj.withLock([](std::vector<int>& lockedObj) {
+    lockedObj.resize(1000);
+    lockedObj.push_back(10);
+    lockedObj.push_back(11);
+  });
+  obj.withLock([](const std::vector<int>& lockedObj) {
+    EXPECT_EQ(1002, lockedObj.size());
+  });
+
+#if __cpp_generic_lambdas >= 201304
+  obj.withLock([](auto& lockedObj) { lockedObj.push_back(12); });
+  obj.withLock(
+      [](const auto& lockedObj) { EXPECT_EQ(1003, lockedObj.size()); });
+  obj.withLock([](auto& lockedObj) { lockedObj.pop_back(); });
+#endif
+
+  // Test withLockPtr()
+  using SynchType = folly::Synchronized<std::vector<int>, Mutex>;
+#if __cpp_generic_lambdas >= 201304
+  obj.withLockPtr([](auto&& lockedObj) { lockedObj->push_back(13); });
+  obj.withLockPtr([](auto&& lockedObj) {
+    EXPECT_EQ(1003, lockedObj->size());
+    EXPECT_EQ(13, lockedObj->back());
+  });
+  obj.withLockPtr([&](auto&& lockedObj) {
+    lockedObj->push_back(14);
+    {
+      auto unlocker = lockedObj.scopedUnlock();
+      obj.lock()->push_back(15);
+    }
+    EXPECT_EQ(1005, lockedObj->size());
+    EXPECT_EQ(15, lockedObj->back());
+  });
+#else
+  obj.withLockPtr([](typename SynchType::LockedPtr&& lockedObj) {
+    lockedObj->push_back(13);
+    lockedObj->push_back(14);
+    lockedObj->push_back(15);
+  });
+#endif
+
+  obj.withLockPtr([](typename SynchType::LockedPtr&& lockedObj) {
+    lockedObj->push_back(16);
+    EXPECT_EQ(1006, lockedObj->size());
+  });
+  const auto& constObj = obj;
+  constObj.withLockPtr([](typename SynchType::ConstLockedPtr&& lockedObj) {
+    EXPECT_EQ(1006, lockedObj->size());
+    EXPECT_EQ(16, lockedObj->back());
+  });
+}
+
+template <class Mutex>
+void testUnlockCommon() {
+  folly::Synchronized<int, Mutex> value{7};
+  const auto& cv = value;
+
+  {
+    auto lv = value.contextualLock();
+    EXPECT_EQ(7, *lv);
+    *lv = 5;
+    lv.unlock();
+    EXPECT_TRUE(lv.isNull());
+    EXPECT_FALSE(lv);
+
+    auto rlv = cv.contextualLock();
+    EXPECT_EQ(5, *rlv);
+    rlv.unlock();
+    EXPECT_TRUE(rlv.isNull());
+    EXPECT_FALSE(rlv);
+
+    auto rlv2 = cv.contextualRLock();
+    EXPECT_EQ(5, *rlv2);
+    rlv2.unlock();
+
+    lv = value.contextualLock();
+    EXPECT_EQ(5, *lv);
+    *lv = 9;
+  }
+
+  EXPECT_EQ(9, *value.contextualRLock());
+}
+
+// testUnlock() version for shared lock types
+template <class Mutex>
+typename std::enable_if<folly::LockTraits<Mutex>::is_shared>::type
+testUnlock() {
+  folly::Synchronized<int, Mutex> value{10};
+  {
+    auto lv = value.wlock();
+    EXPECT_EQ(10, *lv);
+    *lv = 5;
+    lv.unlock();
+    EXPECT_FALSE(lv);
+    EXPECT_TRUE(lv.isNull());
+
+    auto rlv = value.rlock();
+    EXPECT_EQ(5, *rlv);
+    rlv.unlock();
+    EXPECT_FALSE(rlv);
+    EXPECT_TRUE(rlv.isNull());
+
+    auto lv2 = value.wlock();
+    EXPECT_EQ(5, *lv2);
+    *lv2 = 7;
+
+    lv = std::move(lv2);
+    EXPECT_FALSE(lv2);
+    EXPECT_TRUE(lv2.isNull());
+    EXPECT_FALSE(lv.isNull());
+    EXPECT_EQ(7, *lv);
+  }
+
+  testUnlockCommon<Mutex>();
+}
+
+// testUnlock() version for non-shared lock types
+template <class Mutex>
+typename std::enable_if<!folly::LockTraits<Mutex>::is_shared>::type
+testUnlock() {
+  folly::Synchronized<int, Mutex> value{10};
+  {
+    auto lv = value.lock();
+    EXPECT_EQ(10, *lv);
+    *lv = 5;
+    lv.unlock();
+    EXPECT_TRUE(lv.isNull());
+    EXPECT_FALSE(lv);
+
+    auto lv2 = value.lock();
+    EXPECT_EQ(5, *lv2);
+    *lv2 = 6;
+    lv2.unlock();
+    EXPECT_TRUE(lv2.isNull());
+    EXPECT_FALSE(lv2);
+
+    lv = value.lock();
+    EXPECT_EQ(6, *lv);
+    *lv = 7;
+
+    lv2 = std::move(lv);
+    EXPECT_TRUE(lv.isNull());
+    EXPECT_FALSE(lv);
+    EXPECT_FALSE(lv2.isNull());
+    EXPECT_EQ(7, *lv2);
+  }
+
+  testUnlockCommon<Mutex>();
+}
+
 // Testing the deprecated SYNCHRONIZED and SYNCHRONIZED_CONST APIs
 template <class Mutex>
 void testDeprecated() {
@@ -208,17 +467,10 @@ void testDeprecated() {
     EXPECT_EQ(1001, obj.size());
     EXPECT_EQ(10, obj.back());
     EXPECT_EQ(1000, obj2->size());
-
-    UNSYNCHRONIZED(obj) {
-      EXPECT_EQ(1001, obj->size());
-    }
   }
 
   SYNCHRONIZED_CONST (obj) {
     EXPECT_EQ(1001, obj.size());
-    UNSYNCHRONIZED(obj) {
-      EXPECT_EQ(1001, obj->size());
-    }
   }
 
   SYNCHRONIZED (lockedObj, *&obj) {
@@ -437,7 +689,7 @@ void testTimed() {
   // Make sure we can lock with various timeout duration units
   {
     auto lv = v.contextualLock(std::chrono::milliseconds(5));
-    EXPECT_TRUE(lv);
+    EXPECT_TRUE(bool(lv));
     EXPECT_FALSE(lv.isNull());
     auto lv2 = v.contextualLock(std::chrono::microseconds(5));
     // We may or may not acquire lv2 successfully, depending on whether
@@ -445,7 +697,7 @@ void testTimed() {
   }
   {
     auto lv = v.contextualLock(std::chrono::seconds(1));
-    EXPECT_TRUE(lv);
+    EXPECT_TRUE(bool(lv));
   }
 }