#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>
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);
}
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);
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());
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);
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;
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() {
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) {
// 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
}
{
auto lv = v.contextualLock(std::chrono::seconds(1));
- EXPECT_TRUE(lv);
+ EXPECT_TRUE(bool(lv));
}
}