/*
- * Copyright 2015 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.
#include <thread>
#include <folly/Singleton.h>
+#include <folly/experimental/io/FsUtil.h>
#include <folly/io/async/EventBase.h>
+#include <folly/portability/GMock.h>
+#include <folly/portability/GTest.h>
#include <folly/test/SingletonTestStructs.h>
-#include <folly/Benchmark.h>
+
+#ifndef _MSC_VER
+#include <folly/Subprocess.h>
+#endif
#include <glog/logging.h>
-#include <gtest/gtest.h>
#include <boost/thread/barrier.hpp>
+FOLLY_GCC_DISABLE_WARNING(deprecated-declarations)
+
using namespace folly;
TEST(Singleton, MissingSingleton) {
EXPECT_NE(s2, nullptr);
EXPECT_EQ(s1, s2);
+ EXPECT_EQ(s1.get(), SingletonBasicUsage<Watchdog>::try_get_fast().get());
std::shared_ptr<ChildWatchdog> s3 =
SingletonBasicUsage<ChildWatchdog>::try_get();
SingletonNaughtyUsage2<Watchdog> watchdog_singleton;
// double registration
- EXPECT_DEATH([]() { SingletonNaughtyUsage2<Watchdog> watchdog_singleton; }(),
- "");
+ EXPECT_DEATH([]() { SingletonNaughtyUsage2<Watchdog> w2; }(), "");
vault2.destroyInstances();
// double registration after destroy
- EXPECT_DEATH([]() { SingletonNaughtyUsage2<Watchdog> watchdog_singleton; }(),
- "");
+ EXPECT_DEATH([]() { SingletonNaughtyUsage2<Watchdog> w3; }(), "");
}
struct SharedPtrUsageTag {};
using SingletonCreationError = Singleton<T, Tag, CreationErrorTag>;
TEST(Singleton, SingletonCreationError) {
- SingletonVault::singleton<CreationErrorTag>();
SingletonCreationError<ErrorConstructor> error_once_singleton;
+ SingletonVault::singleton<CreationErrorTag>()->registrationComplete();
// first time should error out
- EXPECT_THROW(error_once_singleton.get_weak().lock(), std::runtime_error);
+ EXPECT_THROW(error_once_singleton.try_get(), std::runtime_error);
// second time it'll work fine
- error_once_singleton.get_weak().lock();
+ error_once_singleton.try_get();
SUCCEED();
}
TEST(Singleton, SingletonConcurrencyStress) {
auto& vault = *SingletonVault::singleton<ConcurrencyStressTag>();
SingletonConcurrencyStress<Slowpoke> slowpoke_singleton;
+ vault.registrationComplete();
std::vector<std::thread> ts;
for (size_t i = 0; i < 100; ++i) {
ts.emplace_back([&]() {
- slowpoke_singleton.get_weak().lock();
+ slowpoke_singleton.try_get();
});
}
[&] {didEagerInit = true; return new std::string("foo"); })
.shouldEagerInit();
folly::EventBase eb;
+ folly::Baton<> done;
vault.registrationComplete();
EXPECT_FALSE(didEagerInit);
- auto result = vault.doEagerInitVia(&eb); // a Future<Unit> is returned
+ vault.doEagerInitVia(eb, &done);
eb.loop();
- result.get(); // ensure this completed successfully and didn't hang forever
+ done.wait();
EXPECT_TRUE(didEagerInit);
sing.get_weak(); // (avoid compile error complaining about unused var 'sing')
}
virtual void add(folly::Func func) override {
const auto index = (counter_ ++) % eventBases_.size();
- eventBases_[index]->add(func);
+ eventBases_[index]->add(std::move(func));
}
private:
for (size_t j = 0; j < kThreads; j++) {
threads.push_back(std::make_shared<std::thread>([&] {
barrier.wait();
- vault.doEagerInitVia(&exe).get();
+ vault.doEagerInitVia(exe);
}));
}
}
}
-// Benchmarking a normal singleton vs a Meyers singleton vs a Folly
-// singleton. Meyers are insanely fast, but (hopefully) Folly
-// singletons are fast "enough."
-int* getMeyersSingleton() {
- static auto ret = new int(0);
- return ret;
-}
-
-int normal_singleton_value = 0;
-int* getNormalSingleton() {
- doNotOptimizeAway(&normal_singleton_value);
- return &normal_singleton_value;
-}
-
struct MockTag {};
template <typename T, typename Tag = detail::DefaultTag>
using SingletonMock = Singleton <T, Tag, MockTag>;
// If serial_count value is the same, then singleton was not replaced.
EXPECT_NE(serial_count_first, serial_count_mock);
-}
-struct BenchmarkSingleton {
- int val = 0;
-};
+ // Override existing mock using make_mock one more time
+ SingletonMock<Watchdog>::make_mock();
-BENCHMARK(NormalSingleton, n) {
- for (size_t i = 0; i < n; ++i) {
- doNotOptimizeAway(getNormalSingleton());
- }
-}
+ EXPECT_EQ(vault.registeredSingletonCount(), 1);
+ int serial_count_mock2 = SingletonMock<Watchdog>::try_get()->serial_number;
-BENCHMARK_RELATIVE(MeyersSingleton, n) {
- for (size_t i = 0; i < n; ++i) {
- doNotOptimizeAway(getMeyersSingleton());
- }
+ // If serial_count value is the same, then singleton was not replaced.
+ EXPECT_NE(serial_count_first, serial_count_mock2);
+ EXPECT_NE(serial_count_mock, serial_count_mock2);
+
+ vault.destroyInstances();
}
-struct BenchmarkTag {};
-template <typename T, typename Tag = detail::DefaultTag>
-using SingletonBenchmark = Singleton <T, Tag, BenchmarkTag>;
+#ifndef _MSC_VER
+// Subprocess isn't currently supported under MSVC.
+TEST(Singleton, DoubleRegistrationLogging) {
+ const auto basename = "singleton_double_registration";
+ const auto sub = fs::executable_path().remove_filename() / basename;
+ auto p = Subprocess(
+ std::vector<std::string>{sub.string()},
+ Subprocess::Options()
+ .stdinFd(Subprocess::CLOSE)
+ .stdoutFd(Subprocess::CLOSE)
+ .pipeStderr()
+ .closeOtherFds());
+ auto err = p.communicate("").second;
+ auto res = p.wait();
+ EXPECT_EQ(ProcessReturnCode::KILLED, res.state());
+ EXPECT_EQ(SIGABRT, res.killSignal());
+ EXPECT_THAT(err, testing::StartsWith("Double registration of singletons"));
+}
+#endif
-struct GetTag{};
-struct GetWeakTag{};
+// Singleton using a non default constructor test/example:
+struct X {
+ X() : X(-1, "unset") {}
+ X(int a1, std::string a2) : a1(a1), a2(a2) {
+ LOG(INFO) << "X(" << a1 << "," << a2 << ")";
+ }
+ const int a1;
+ const std::string a2;
+};
-SingletonBenchmark<BenchmarkSingleton, GetTag> benchmark_singleton_get;
-SingletonBenchmark<BenchmarkSingleton, GetWeakTag> benchmark_singleton_get_weak;
+folly::Singleton<X> singleton_x([]() { return new X(42, "foo"); });
-BENCHMARK_RELATIVE(FollySingleton, n) {
- for (size_t i = 0; i < n; ++i) {
- SingletonBenchmark<BenchmarkSingleton, GetTag>::try_get();
- }
+TEST(Singleton, CustomCreator) {
+ X x1;
+ std::shared_ptr<X> x2p = singleton_x.try_get();
+ EXPECT_NE(nullptr, x2p);
+ EXPECT_NE(x1.a1, x2p->a1);
+ EXPECT_NE(x1.a2, x2p->a2);
+ EXPECT_EQ(42, x2p->a1);
+ EXPECT_EQ(std::string("foo"), x2p->a2);
}
-BENCHMARK_RELATIVE(FollySingletonWeak, n) {
- for (size_t i = 0; i < n; ++i) {
- SingletonBenchmark<BenchmarkSingleton, GetWeakTag>::get_weak();
+struct ConcurrentCreationDestructionTag {};
+template <typename T, typename Tag = detail::DefaultTag>
+using SingletonConcurrentCreationDestruction =
+ Singleton<T, Tag, ConcurrentCreationDestructionTag>;
+
+folly::Baton<> slowpokeNeedySingletonBaton;
+
+struct SlowpokeNeedySingleton {
+ SlowpokeNeedySingleton() {
+ slowpokeNeedySingletonBaton.post();
+ /* sleep override */ std::this_thread::sleep_for(
+ std::chrono::milliseconds(100));
+ auto unused =
+ SingletonConcurrentCreationDestruction<NeededSingleton>::try_get();
+ EXPECT_NE(unused, nullptr);
}
-}
+};
-int main(int argc, char* argv[]) {
- testing::InitGoogleTest(&argc, argv);
- google::InitGoogleLogging(argv[0]);
- gflags::ParseCommandLineFlags(&argc, &argv, true);
+TEST(Singleton, ConcurrentCreationDestruction) {
+ auto& vault = *SingletonVault::singleton<ConcurrentCreationDestructionTag>();
+ SingletonConcurrentCreationDestruction<NeededSingleton> neededSingleton;
+ SingletonConcurrentCreationDestruction<SlowpokeNeedySingleton> needySingleton;
+ vault.registrationComplete();
- SingletonVault::singleton()->registrationComplete();
+ std::thread needyThread([&] { needySingleton.try_get(); });
- auto ret = RUN_ALL_TESTS();
- if (!ret) {
- folly::runBenchmarksOnFlag();
- }
- return ret;
+ slowpokeNeedySingletonBaton.wait();
+
+ vault.destroyInstances();
+
+ needyThread.join();
}