X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2Ftest%2FSingletonTest.cpp;h=7ed6f1074bae76ef24ce43cccd9ff784810e2393;hb=a9d90ea14f527b0b719a90f014c8c20e13d54866;hp=14c4efa621c5f371bdfb7cdf45dd82c937ea7a18;hpb=2f6eb18232a6171794142ad8e7fa234b14726d7f;p=folly.git diff --git a/folly/test/SingletonTest.cpp b/folly/test/SingletonTest.cpp index 14c4efa6..7ed6f107 100644 --- a/folly/test/SingletonTest.cpp +++ b/folly/test/SingletonTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2016 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. @@ -30,7 +30,7 @@ #include #include -FOLLY_GCC_DISABLE_WARNING(deprecated-declarations) +FOLLY_GCC_DISABLE_WARNING("-Wdeprecated-declarations") using namespace folly; @@ -202,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; @@ -641,3 +652,127 @@ TEST(Singleton, CustomCreator) { 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(); +} + +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); + + // explicit request, ctor + SingletonObject::try_get(); + EXPECT_EQ(1, counts.ctor); + EXPECT_EQ(0, counts.dtor); + + // first make_mock, dtor (ctor is lazy) + SingletonObject::make_mock(new_object); + EXPECT_EQ(1, counts.ctor); + EXPECT_EQ(1, counts.dtor); + + // 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); +}