#include <thread>
+#include <boost/thread/barrier.hpp>
+#include <glog/logging.h>
+
#include <folly/Singleton.h>
#include <folly/experimental/io/FsUtil.h>
#include <folly/io/async/EventBase.h>
#include <folly/Subprocess.h>
#endif
-#include <glog/logging.h>
-#include <boost/thread/barrier.hpp>
-
-FOLLY_GCC_DISABLE_WARNING(deprecated-declarations)
+FOLLY_GCC_DISABLE_WARNING("-Wdeprecated-declarations")
using namespace folly;
auto& vault = *SingletonVault::singleton<SharedPtrUsageTag>();
EXPECT_EQ(vault.registeredSingletonCount(), 0);
- SingletonSharedPtrUsage<Watchdog> watchdog_singleton;
+ std::vector<std::unique_ptr<Watchdog>> watchdog_instances;
+ SingletonSharedPtrUsage<Watchdog> watchdog_singleton(
+ [&] {
+ watchdog_instances.push_back(std::make_unique<Watchdog>());
+ 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<ChildWatchdog> child_watchdog_singleton;
auto start_time = std::chrono::steady_clock::now();
vault.destroyInstances();
auto duration = std::chrono::steady_clock::now() - start_time;
- EXPECT_TRUE(duration > std::chrono::seconds{4} &&
- duration < std::chrono::seconds{6});
+ EXPECT_TRUE(
+ duration > std::chrono::seconds{4} &&
+ duration < std::chrono::seconds{folly::kIsSanitizeAddress ? 30 : 6});
}
EXPECT_EQ(vault.registeredSingletonCount(), 4);
EXPECT_EQ(vault.livingSingletonCount(), 0);
namespace {
struct EagerInitSyncTag {};
-}
+} // namespace
template <typename T, typename Tag = detail::DefaultTag>
using SingletonEagerInitSync = Singleton<T, Tag, EagerInitSyncTag>;
TEST(Singleton, SingletonEagerInitSync) {
namespace {
struct EagerInitAsyncTag {};
-}
+} // namespace
template <typename T, typename Tag = detail::DefaultTag>
using SingletonEagerInitAsync = Singleton<T, Tag, EagerInitAsyncTag>;
TEST(Singleton, SingletonEagerInitAsync) {
}
}
- virtual ~TestEagerInitParallelExecutor() override {
+ ~TestEagerInitParallelExecutor() override {
for (auto eb : eventBases_) {
eb->runInEventBaseThread([eb] { eb->terminateLoopSoon(); });
}
}
}
- virtual void add(folly::Func func) override {
+ void add(folly::Func func) override {
const auto index = (counter_ ++) % eventBases_.size();
eventBases_[index]->add(std::move(func));
}
std::vector<std::shared_ptr<std::thread>> threads_;
std::atomic<size_t> counter_ {0};
};
-} // namespace
+} // namespace
namespace {
struct EagerInitParallelTag {};
-}
+} // namespace
template <typename T, typename Tag = detail::DefaultTag>
using SingletonEagerInitParallel = Singleton<T, Tag, EagerInitParallelTag>;
TEST(Singleton, SingletonEagerInitParallel) {
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<Object, PrivateTag, VaultTag>;
+
+ // register everything
+ Counts counts;
+ auto& vault = *SingletonVault::singleton<VaultTag>();
+ 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);
+}