X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2Ftest%2FSingletonTest.cpp;h=7ed6f1074bae76ef24ce43cccd9ff784810e2393;hb=a9d90ea14f527b0b719a90f014c8c20e13d54866;hp=c86200a3d8472c36b37eed389578ab9aff9fbfb3;hpb=71c140ede54db347f9a971d6e2de0ade95569c1d;p=folly.git diff --git a/folly/test/SingletonTest.cpp b/folly/test/SingletonTest.cpp index c86200a3..7ed6f107 100644 --- a/folly/test/SingletonTest.cpp +++ b/folly/test/SingletonTest.cpp @@ -1,5 +1,5 @@ /* - * 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. @@ -17,13 +17,21 @@ #include #include +#include #include +#include +#include #include +#ifndef _MSC_VER +#include +#endif + #include -#include #include +FOLLY_GCC_DISABLE_WARNING("-Wdeprecated-declarations") + using namespace folly; TEST(Singleton, MissingSingleton) { @@ -168,13 +176,11 @@ TEST(Singleton, NaughtyUsage) { SingletonNaughtyUsage2 watchdog_singleton; // double registration - EXPECT_DEATH([]() { SingletonNaughtyUsage2 watchdog_singleton; }(), - ""); + EXPECT_DEATH([]() { SingletonNaughtyUsage2 w2; }(), ""); vault2.destroyInstances(); // double registration after destroy - EXPECT_DEATH([]() { SingletonNaughtyUsage2 watchdog_singleton; }(), - ""); + EXPECT_DEATH([]() { SingletonNaughtyUsage2 w3; }(), ""); } struct SharedPtrUsageTag {}; @@ -196,7 +202,18 @@ TEST(Singleton, SharedPtrUsage) { auto& vault = *SingletonVault::singleton(); EXPECT_EQ(vault.registeredSingletonCount(), 0); - SingletonSharedPtrUsage watchdog_singleton; + std::vector> watchdog_instances; + SingletonSharedPtrUsage watchdog_singleton( + [&] { + watchdog_instances.push_back(std::make_unique()); + return watchdog_instances.back().get(); + }, + [&](Watchdog* ptr) { + // Make sure that only second instance is destroyed. First instance is + // expected to be leaked. + EXPECT_EQ(watchdog_instances[1].get(), ptr); + watchdog_instances[1].reset(); + }); EXPECT_EQ(vault.registeredSingletonCount(), 1); SingletonSharedPtrUsage child_watchdog_singleton; @@ -387,8 +404,8 @@ template using SingletonCreationError = Singleton; TEST(Singleton, SingletonCreationError) { - SingletonVault::singleton(); SingletonCreationError error_once_singleton; + SingletonVault::singleton()->registrationComplete(); // first time should error out EXPECT_THROW(error_once_singleton.try_get(), std::runtime_error); @@ -405,6 +422,7 @@ using SingletonConcurrencyStress = Singleton ; TEST(Singleton, SingletonConcurrencyStress) { auto& vault = *SingletonVault::singleton(); SingletonConcurrencyStress slowpoke_singleton; + vault.registrationComplete(); std::vector ts; for (size_t i = 0; i < 100; ++i) { @@ -492,7 +510,7 @@ class TestEagerInitParallelExecutor : public folly::Executor { virtual void add(folly::Func func) override { const auto index = (counter_ ++) % eventBases_.size(); - eventBases_[index]->add(func); + eventBases_[index]->add(std::move(func)); } private: @@ -579,16 +597,182 @@ TEST(Singleton, MockTest) { // If serial_count value is the same, then singleton was not replaced. EXPECT_NE(serial_count_first, serial_count_mock); + + // Override existing mock using make_mock one more time + SingletonMock::make_mock(); + + EXPECT_EQ(vault.registeredSingletonCount(), 1); + int serial_count_mock2 = SingletonMock::try_get()->serial_number; + + // 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(); +} + +#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{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 + +// 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; +}; + +folly::Singleton singleton_x([]() { return new X(42, "foo"); }); + +TEST(Singleton, CustomCreator) { + X x1; + std::shared_ptr 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); +} + +struct ConcurrentCreationDestructionTag {}; +template +using SingletonConcurrentCreationDestruction = + Singleton; + +folly::Baton<> slowpokeNeedySingletonBaton; + +struct SlowpokeNeedySingleton { + SlowpokeNeedySingleton() { + slowpokeNeedySingletonBaton.post(); + /* sleep override */ std::this_thread::sleep_for( + std::chrono::milliseconds(100)); + auto unused = + SingletonConcurrentCreationDestruction::try_get(); + EXPECT_NE(unused, nullptr); + } +}; + +TEST(Singleton, ConcurrentCreationDestruction) { + auto& vault = *SingletonVault::singleton(); + SingletonConcurrentCreationDestruction neededSingleton; + SingletonConcurrentCreationDestruction needySingleton; + vault.registrationComplete(); + + std::thread needyThread([&] { needySingleton.try_get(); }); + + slowpokeNeedySingletonBaton.wait(); + + vault.destroyInstances(); + + needyThread.join(); +} + +struct MainThreadDestructorTag {}; +template +using SingletonMainThreadDestructor = + Singleton; + +struct ThreadLoggingSingleton { + ThreadLoggingSingleton() { + initThread = std::this_thread::get_id(); + } + + ~ThreadLoggingSingleton() { + destroyThread = std::this_thread::get_id(); + } + + static std::thread::id initThread; + static std::thread::id destroyThread; +}; +std::thread::id ThreadLoggingSingleton::initThread{}; +std::thread::id ThreadLoggingSingleton::destroyThread{}; + +TEST(Singleton, MainThreadDestructor) { + auto& vault = *SingletonVault::singleton(); + SingletonMainThreadDestructor singleton; + + vault.registrationComplete(); + EXPECT_EQ(std::thread::id(), ThreadLoggingSingleton::initThread); + + singleton.try_get(); + EXPECT_EQ(std::this_thread::get_id(), ThreadLoggingSingleton::initThread); + + std::thread t([instance = singleton.try_get()] { + /* sleep override */ std::this_thread::sleep_for( + std::chrono::milliseconds{100}); + }); + + EXPECT_EQ(std::thread::id(), ThreadLoggingSingleton::destroyThread); + + vault.destroyInstances(); + EXPECT_EQ(std::this_thread::get_id(), ThreadLoggingSingleton::destroyThread); + + t.join(); } -int main(int argc, char* argv[]) { - testing::InitGoogleTest(&argc, argv); - google::InitGoogleLogging(argv[0]); - gflags::ParseCommandLineFlags(&argc, &argv, true); +TEST(Singleton, DoubleMakeMockAfterTryGet) { + // to keep track of calls to ctor and dtor below + struct Counts { + size_t ctor = 0; + size_t dtor = 0; + }; + + // a test type which keeps track of its ctor and dtor calls + struct VaultTag {}; + struct PrivateTag {}; + struct Object { + explicit Object(Counts& counts) : counts_(counts) { + ++counts_.ctor; + } + ~Object() { + ++counts_.dtor; + } + Counts& counts_; + }; + using SingletonObject = Singleton; + + // register everything + Counts counts; + auto& vault = *SingletonVault::singleton(); + auto new_object = [&] { return new Object(counts); }; + SingletonObject object_(new_object); + vault.registrationComplete(); + + // no eager inits, nada (sanity) + EXPECT_EQ(0, counts.ctor); + EXPECT_EQ(0, counts.dtor); - SingletonVault::singleton()->registrationComplete(); + // explicit request, ctor + SingletonObject::try_get(); + EXPECT_EQ(1, counts.ctor); + EXPECT_EQ(0, counts.dtor); - auto ret = RUN_ALL_TESTS(); + // first make_mock, dtor (ctor is lazy) + SingletonObject::make_mock(new_object); + EXPECT_EQ(1, counts.ctor); + EXPECT_EQ(1, counts.dtor); - return ret; + // second make_mock, nada (dtor already ran, ctor is lazy) + SingletonObject::make_mock(new_object); + EXPECT_EQ(1, counts.ctor); + EXPECT_EQ(1, counts.dtor); }