Make sure singleton destructor is always called from main thread.
[folly.git] / folly / test / SingletonTest.cpp
index 14c4efa621c5f371bdfb7cdf45dd82c937ea7a18..36aeb4e1fe2322bd887d7d3bdb78f622021226d3 100644 (file)
@@ -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.
@@ -641,3 +641,79 @@ TEST(Singleton, CustomCreator) {
   EXPECT_EQ(42, x2p->a1);
   EXPECT_EQ(std::string("foo"), x2p->a2);
 }
+
+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);
+  }
+};
+
+TEST(Singleton, ConcurrentCreationDestruction) {
+  auto& vault = *SingletonVault::singleton<ConcurrentCreationDestructionTag>();
+  SingletonConcurrentCreationDestruction<NeededSingleton> neededSingleton;
+  SingletonConcurrentCreationDestruction<SlowpokeNeedySingleton> needySingleton;
+  vault.registrationComplete();
+
+  std::thread needyThread([&] { needySingleton.try_get(); });
+
+  slowpokeNeedySingletonBaton.wait();
+
+  vault.destroyInstances();
+
+  needyThread.join();
+}
+
+struct MainThreadDestructorTag {};
+template <typename T, typename Tag = detail::DefaultTag>
+using SingletonMainThreadDestructor =
+    Singleton<T, Tag, MainThreadDestructorTag>;
+
+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<MainThreadDestructorTag>();
+  SingletonMainThreadDestructor<ThreadLoggingSingleton> 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();
+}