experimental/JSONSchema.h \
experimental/Select64.h \
experimental/SharedMutex.h \
- experimental/Singleton.h \
- experimental/Singleton-inl.h \
experimental/StringKeyedCommon.h \
experimental/StringKeyedUnorderedMap.h \
experimental/StringKeyedUnorderedSet.h \
Range.h \
RWSpinLock.h \
ScopeGuard.h \
+ Singleton.h \
+ Singleton-inl.h \
SmallLocks.h \
small_vector.h \
SocketAddress.h \
Random.cpp \
SafeAssert.cpp \
SocketAddress.cpp \
+ Singleton.cpp \
SpookyHashV1.cpp \
SpookyHashV2.cpp \
stats/Instantiations.cpp \
experimental/JSONSchema.cpp \
experimental/Select64.cpp \
experimental/SharedMutex.cpp \
- experimental/Singleton.cpp \
experimental/TestUtil.cpp \
wangle/acceptor/Acceptor.cpp \
wangle/acceptor/ConnectionManager.cpp \
--- /dev/null
+/*
+ * Copyright 2015 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace folly {
+
+namespace detail {
+
+template <typename T>
+template <typename Tag, typename VaultTag>
+SingletonHolder<T>& SingletonHolder<T>::singleton() {
+ static auto entry = new SingletonHolder<T>(
+ {typeid(T), typeid(Tag)},
+ *SingletonVault::singleton<VaultTag>());
+ return *entry;
+}
+
+template <typename T>
+void SingletonHolder<T>::registerSingleton(CreateFunc c, TeardownFunc t) {
+ std::lock_guard<std::mutex> entry_lock(mutex_);
+
+ if (state_ != SingletonHolderState::NotRegistered) {
+ throw std::logic_error("Double registration");
+ }
+
+ create_ = std::move(c);
+ teardown_ = std::move(t);
+
+ state_ = SingletonHolderState::Dead;
+}
+
+template <typename T>
+void SingletonHolder<T>::registerSingletonMock(CreateFunc c, TeardownFunc t) {
+ if (state_ == SingletonHolderState::NotRegistered) {
+ throw std::logic_error("Registering mock before singleton was registered");
+ }
+ destroyInstance();
+
+ std::lock_guard<std::mutex> entry_lock(mutex_);
+
+ create_ = std::move(c);
+ teardown_ = std::move(t);
+}
+
+template <typename T>
+T* SingletonHolder<T>::get() {
+ if (LIKELY(state_ == SingletonHolderState::Living)) {
+ return instance_ptr_;
+ }
+ createInstance();
+
+ if (instance_weak_.expired()) {
+ throw std::runtime_error(
+ "Raw pointer to a singleton requested after its destruction.");
+ }
+
+ return instance_ptr_;
+}
+
+template <typename T>
+std::weak_ptr<T> SingletonHolder<T>::get_weak() {
+ if (UNLIKELY(state_ != SingletonHolderState::Living)) {
+ createInstance();
+ }
+
+ return instance_weak_;
+}
+
+template <typename T>
+TypeDescriptor SingletonHolder<T>::type() {
+ return type_;
+}
+
+template <typename T>
+bool SingletonHolder<T>::hasLiveInstance() {
+ return !instance_weak_.expired();
+}
+
+template <typename T>
+void SingletonHolder<T>::destroyInstance() {
+ state_ = SingletonHolderState::Dead;
+ instance_.reset();
+ if (destroy_baton_) {
+ auto wait_result = destroy_baton_->timed_wait(
+ std::chrono::steady_clock::now() + kDestroyWaitTime);
+ if (!wait_result) {
+ print_destructor_stack_trace_->store(true);
+ LOG(ERROR) << "Singleton of type " << type_.name() << " has a "
+ << "living reference at destroyInstances time; beware! Raw "
+ << "pointer is " << instance_ptr_ << ". It is very likely "
+ << "that some other singleton is holding a shared_ptr to it. "
+ << "Make sure dependencies between these singletons are "
+ << "properly defined.";
+ }
+ }
+}
+
+template <typename T>
+SingletonHolder<T>::SingletonHolder(TypeDescriptor type__,
+ SingletonVault& vault) :
+ type_(type__), vault_(vault) {
+}
+
+template <typename T>
+void SingletonHolder<T>::createInstance() {
+ // There's no synchronization here, so we may not see the current value
+ // for creating_thread if it was set by other thread, but we only care about
+ // it if it was set by current thread anyways.
+ if (creating_thread_ == std::this_thread::get_id()) {
+ throw std::out_of_range(std::string("circular singleton dependency: ") +
+ type_.name());
+ }
+
+ std::lock_guard<std::mutex> entry_lock(mutex_);
+ if (state_ == SingletonHolderState::Living) {
+ return;
+ }
+ if (state_ == SingletonHolderState::NotRegistered) {
+ throw std::out_of_range("Creating instance for unregistered singleton");
+ }
+
+ if (state_ == SingletonHolderState::Living) {
+ return;
+ }
+
+ creating_thread_ = std::this_thread::get_id();
+
+ RWSpinLock::ReadHolder rh(&vault_.stateMutex_);
+ if (vault_.state_ == SingletonVault::SingletonVaultState::Quiescing) {
+ creating_thread_ = std::thread::id();
+ return;
+ }
+
+ auto destroy_baton = std::make_shared<folly::Baton<>>();
+ auto print_destructor_stack_trace =
+ std::make_shared<std::atomic<bool>>(false);
+ auto teardown = teardown_;
+ auto type_name = type_.name();
+
+ // Can't use make_shared -- no support for a custom deleter, sadly.
+ instance_ = std::shared_ptr<T>(
+ create_(),
+ [destroy_baton, print_destructor_stack_trace, teardown, type_name]
+ (T* instance_ptr) mutable {
+ teardown(instance_ptr);
+ destroy_baton->post();
+ if (print_destructor_stack_trace->load()) {
+ std::string output = "Singleton " + type_name + " was destroyed.\n";
+
+ auto stack_trace_getter = SingletonVault::stackTraceGetter().load();
+ auto stack_trace = stack_trace_getter ? stack_trace_getter() : "";
+ if (stack_trace.empty()) {
+ output += "Failed to get destructor stack trace.";
+ } else {
+ output += "Destructor stack trace:\n";
+ output += stack_trace;
+ }
+
+ LOG(ERROR) << output;
+ }
+ });
+
+ // We should schedule destroyInstances() only after the singleton was
+ // created. This will ensure it will be destroyed before singletons,
+ // not managed by folly::Singleton, which were initialized in its
+ // constructor
+ SingletonVault::scheduleDestroyInstances();
+
+ instance_weak_ = instance_;
+ instance_ptr_ = instance_.get();
+ creating_thread_ = std::thread::id();
+ destroy_baton_ = std::move(destroy_baton);
+ print_destructor_stack_trace_ = std::move(print_destructor_stack_trace);
+
+ // This has to be the last step, because once state is Living other threads
+ // may access instance and instance_weak w/o synchronization.
+ state_.store(SingletonHolderState::Living);
+
+ {
+ RWSpinLock::WriteHolder wh(&vault_.mutex_);
+ vault_.creation_order_.push_back(type_);
+ }
+}
+
+}
+
+}
--- /dev/null
+/*
+ * Copyright 2015 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/Singleton.h>
+
+#include <string>
+
+namespace folly {
+
+namespace detail {
+
+constexpr std::chrono::seconds SingletonHolderBase::kDestroyWaitTime;
+
+}
+
+namespace {
+
+struct FatalHelper {
+ ~FatalHelper() {
+ if (!leakedSingletons_.empty()) {
+ std::string leakedTypes;
+ for (const auto& singleton : leakedSingletons_) {
+ leakedTypes += "\t" + singleton.name() + "\n";
+ }
+ LOG(DFATAL) << "Singletons of the following types had living references "
+ << "after destroyInstances was finished:\n" << leakedTypes
+ << "beware! It is very likely that those singleton instances "
+ << "are leaked.";
+ }
+ }
+
+ std::vector<detail::TypeDescriptor> leakedSingletons_;
+};
+
+FatalHelper __attribute__ ((__init_priority__ (101))) fatalHelper;
+
+}
+
+SingletonVault::~SingletonVault() { destroyInstances(); }
+
+void SingletonVault::destroyInstances() {
+ RWSpinLock::WriteHolder state_wh(&stateMutex_);
+
+ if (state_ == SingletonVaultState::Quiescing) {
+ return;
+ }
+ state_ = SingletonVaultState::Quiescing;
+
+ RWSpinLock::ReadHolder state_rh(std::move(state_wh));
+
+ {
+ RWSpinLock::ReadHolder rh(&mutex_);
+
+ CHECK_GE(singletons_.size(), creation_order_.size());
+
+ for (auto type_iter = creation_order_.rbegin();
+ type_iter != creation_order_.rend();
+ ++type_iter) {
+ singletons_[*type_iter]->destroyInstance();
+ }
+
+ for (auto& singleton_type: creation_order_) {
+ auto singleton = singletons_[singleton_type];
+ if (!singleton->hasLiveInstance()) {
+ continue;
+ }
+
+ fatalHelper.leakedSingletons_.push_back(singleton->type());
+ }
+ }
+
+ {
+ RWSpinLock::WriteHolder wh(&mutex_);
+ creation_order_.clear();
+ }
+}
+
+void SingletonVault::reenableInstances() {
+ RWSpinLock::WriteHolder state_wh(&stateMutex_);
+
+ stateCheck(SingletonVaultState::Quiescing);
+
+ state_ = SingletonVaultState::Running;
+}
+
+void SingletonVault::scheduleDestroyInstances() {
+ RequestContext::getStaticContext();
+
+ class SingletonVaultDestructor {
+ public:
+ ~SingletonVaultDestructor() {
+ SingletonVault::singleton()->destroyInstances();
+ }
+ };
+
+ // Here we intialize a singleton, which calls destroyInstances in its
+ // destructor. Because of singleton destruction order - it will be destroyed
+ // before all the singletons, which were initialized before it and after all
+ // the singletons initialized after it.
+ static SingletonVaultDestructor singletonVaultDestructor;
+}
+
+}
--- /dev/null
+/*
+ * Copyright 2015 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// SingletonVault - a library to manage the creation and destruction
+// of interdependent singletons.
+//
+// Basic usage of this class is very simple; suppose you have a class
+// called MyExpensiveService, and you only want to construct one (ie,
+// it's a singleton), but you only want to construct it if it is used.
+//
+// In your .h file:
+// class MyExpensiveService { ... };
+//
+// In your .cpp file:
+// namespace { folly::Singleton<MyExpensiveService> the_singleton; }
+//
+// Code can access it via:
+//
+// MyExpensiveService* instance = Singleton<MyExpensiveService>::get();
+// or
+// std::weak_ptr<MyExpensiveService> instance =
+// Singleton<MyExpensiveService>::get_weak();
+//
+// You also can directly access it by the variable defining the
+// singleton rather than via get(), and even treat that variable like
+// a smart pointer (dereferencing it or using the -> operator).
+//
+// Please note, however, that all non-weak_ptr interfaces are
+// inherently subject to races with destruction. Use responsibly.
+//
+// The singleton will be created on demand. If the constructor for
+// MyExpensiveService actually makes use of *another* Singleton, then
+// the right thing will happen -- that other singleton will complete
+// construction before get() returns. However, in the event of a
+// circular dependency, a runtime error will occur.
+//
+// You can have multiple singletons of the same underlying type, but
+// each must be given a unique tag. If no tag is specified - default tag is used
+//
+// namespace {
+// struct Tag1 {};
+// struct Tag2 {};
+// folly::Singleton<MyExpensiveService> s_default();
+// folly::Singleton<MyExpensiveService, Tag1> s1();
+// folly::Singleton<MyExpensiveService, Tag2> s2();
+// }
+// ...
+// MyExpensiveService* svc_default = s_default.get();
+// MyExpensiveService* svc1 = s1.get();
+// MyExpensiveService* svc2 = s2.get();
+//
+// By default, the singleton instance is constructed via new and
+// deleted via delete, but this is configurable:
+//
+// namespace { folly::Singleton<MyExpensiveService> the_singleton(create,
+// destroy); }
+//
+// Where create and destroy are functions, Singleton<T>::CreateFunc
+// Singleton<T>::TeardownFunc.
+//
+// What if you need to destroy all of your singletons? Say, some of
+// your singletons manage threads, but you need to fork? Or your unit
+// test wants to clean up all global state? Then you can call
+// SingletonVault::singleton()->destroyInstances(), which invokes the
+// TeardownFunc for each singleton, in the reverse order they were
+// created. It is your responsibility to ensure your singletons can
+// handle cases where the singletons they depend on go away, however.
+// Singletons won't be recreated after destroyInstances call. If you
+// want to re-enable singleton creation (say after fork was called) you
+// should call reenableInstances.
+
+#pragma once
+#include <folly/Baton.h>
+#include <folly/Exception.h>
+#include <folly/Hash.h>
+#include <folly/Memory.h>
+#include <folly/RWSpinLock.h>
+#include <folly/Demangle.h>
+#include <folly/io/async/Request.h>
+
+#include <algorithm>
+#include <vector>
+#include <mutex>
+#include <thread>
+#include <condition_variable>
+#include <string>
+#include <unordered_map>
+#include <functional>
+#include <typeinfo>
+#include <typeindex>
+
+#include <glog/logging.h>
+
+namespace folly {
+
+// For actual usage, please see the Singleton<T> class at the bottom
+// of this file; that is what you will actually interact with.
+
+// SingletonVault is the class that manages singleton instances. It
+// is unaware of the underlying types of singletons, and simply
+// manages lifecycles and invokes CreateFunc and TeardownFunc when
+// appropriate. In general, you won't need to interact with the
+// SingletonVault itself.
+//
+// A vault goes through a few stages of life:
+//
+// 1. Registration phase; singletons can be registered, but no
+// singleton can be created.
+// 2. registrationComplete() has been called; singletons can no
+// longer be registered, but they can be created.
+// 3. A vault can return to stage 1 when destroyInstances is called.
+//
+// In general, you don't need to worry about any of the above; just
+// ensure registrationComplete() is called near the top of your main()
+// function, otherwise no singletons can be instantiated.
+
+class SingletonVault;
+
+namespace detail {
+
+struct DefaultTag {};
+
+// A TypeDescriptor is the unique handle for a given singleton. It is
+// a combinaiton of the type and of the optional name, and is used as
+// a key in unordered_maps.
+class TypeDescriptor {
+ public:
+ TypeDescriptor(const std::type_info& ti,
+ const std::type_info& tag_ti)
+ : ti_(ti), tag_ti_(tag_ti) {
+ }
+
+ TypeDescriptor(const TypeDescriptor& other)
+ : ti_(other.ti_), tag_ti_(other.tag_ti_) {
+ }
+
+ TypeDescriptor& operator=(const TypeDescriptor& other) {
+ if (this != &other) {
+ ti_ = other.ti_;
+ tag_ti_ = other.tag_ti_;
+ }
+
+ return *this;
+ }
+
+ std::string name() const {
+ auto ret = demangle(ti_.name());
+ if (tag_ti_ != std::type_index(typeid(DefaultTag))) {
+ ret += "/";
+ ret += demangle(tag_ti_.name());
+ }
+ return ret.toStdString();
+ }
+
+ friend class TypeDescriptorHasher;
+
+ bool operator==(const TypeDescriptor& other) const {
+ return ti_ == other.ti_ && tag_ti_ == other.tag_ti_;
+ }
+
+ private:
+ std::type_index ti_;
+ std::type_index tag_ti_;
+};
+
+class TypeDescriptorHasher {
+ public:
+ size_t operator()(const TypeDescriptor& ti) const {
+ return folly::hash::hash_combine(ti.ti_, ti.tag_ti_);
+ }
+};
+
+// This interface is used by SingletonVault to interact with SingletonHolders.
+// Having a non-template interface allows SingletonVault to keep a list of all
+// SingletonHolders.
+class SingletonHolderBase {
+ public:
+ virtual ~SingletonHolderBase() {}
+
+ virtual TypeDescriptor type() = 0;
+ virtual bool hasLiveInstance() = 0;
+ virtual void destroyInstance() = 0;
+
+ protected:
+ static constexpr std::chrono::seconds kDestroyWaitTime{5};
+};
+
+// An actual instance of a singleton, tracking the instance itself,
+// its state as described above, and the create and teardown
+// functions.
+template <typename T>
+struct SingletonHolder : public SingletonHolderBase {
+ public:
+ typedef std::function<void(T*)> TeardownFunc;
+ typedef std::function<T*(void)> CreateFunc;
+
+ template <typename Tag, typename VaultTag>
+ inline static SingletonHolder<T>& singleton();
+
+ inline T* get();
+ inline std::weak_ptr<T> get_weak();
+
+ void registerSingleton(CreateFunc c, TeardownFunc t);
+ void registerSingletonMock(CreateFunc c, TeardownFunc t);
+ virtual TypeDescriptor type();
+ virtual bool hasLiveInstance();
+ virtual void destroyInstance();
+
+ private:
+ SingletonHolder(TypeDescriptor type, SingletonVault& vault);
+
+ void createInstance();
+
+ enum class SingletonHolderState {
+ NotRegistered,
+ Dead,
+ Living,
+ };
+
+ TypeDescriptor type_;
+ SingletonVault& vault_;
+
+ // mutex protects the entire entry during construction/destruction
+ std::mutex mutex_;
+
+ // State of the singleton entry. If state is Living, instance_ptr and
+ // instance_weak can be safely accessed w/o synchronization.
+ std::atomic<SingletonHolderState> state_{SingletonHolderState::NotRegistered};
+
+ // the thread creating the singleton (only valid while creating an object)
+ std::thread::id creating_thread_;
+
+ // The singleton itself and related functions.
+
+ // holds a shared_ptr to singleton instance, set when state is changed from
+ // Dead to Living. Reset when state is changed from Living to Dead.
+ std::shared_ptr<T> instance_;
+ // weak_ptr to the singleton instance, set when state is changed from Dead
+ // to Living. We never write to this object after initialization, so it is
+ // safe to read it from different threads w/o synchronization if we know
+ // that state is set to Living
+ std::weak_ptr<T> instance_weak_;
+ // Time we wait on destroy_baton after releasing Singleton shared_ptr.
+ std::shared_ptr<folly::Baton<>> destroy_baton_;
+ T* instance_ptr_ = nullptr;
+ CreateFunc create_ = nullptr;
+ TeardownFunc teardown_ = nullptr;
+
+ std::shared_ptr<std::atomic<bool>> print_destructor_stack_trace_;
+
+ SingletonHolder(const SingletonHolder&) = delete;
+ SingletonHolder& operator=(const SingletonHolder&) = delete;
+ SingletonHolder& operator=(SingletonHolder&&) = delete;
+ SingletonHolder(SingletonHolder&&) = delete;
+};
+
+}
+
+class SingletonVault {
+ public:
+ enum class Type { Strict, Relaxed };
+
+ explicit SingletonVault(Type type = Type::Relaxed) : type_(type) {}
+
+ // Destructor is only called by unit tests to check destroyInstances.
+ ~SingletonVault();
+
+ typedef std::function<void(void*)> TeardownFunc;
+ typedef std::function<void*(void)> CreateFunc;
+
+ // Ensure that Singleton has not been registered previously and that
+ // registration is not complete. If validations succeeds,
+ // register a singleton of a given type with the create and teardown
+ // functions.
+ void registerSingleton(detail::SingletonHolderBase* entry) {
+ RWSpinLock::ReadHolder rh(&stateMutex_);
+
+ stateCheck(SingletonVaultState::Running);
+
+ if (UNLIKELY(registrationComplete_)) {
+ throw std::logic_error(
+ "Registering singleton after registrationComplete().");
+ }
+
+ RWSpinLock::ReadHolder rhMutex(&mutex_);
+ CHECK_THROW(singletons_.find(entry->type()) == singletons_.end(),
+ std::logic_error);
+
+ RWSpinLock::UpgradedHolder wh(&mutex_);
+ singletons_[entry->type()] = entry;
+ }
+
+ // Mark registration is complete; no more singletons can be
+ // registered at this point.
+ void registrationComplete() {
+ RequestContext::getStaticContext();
+ std::atexit([](){ SingletonVault::singleton()->destroyInstances(); });
+
+ RWSpinLock::WriteHolder wh(&stateMutex_);
+
+ stateCheck(SingletonVaultState::Running);
+
+ if (type_ == Type::Strict) {
+ for (const auto& p: singletons_) {
+ if (p.second->hasLiveInstance()) {
+ throw std::runtime_error(
+ "Singleton created before registration was complete.");
+ }
+ }
+ }
+
+ registrationComplete_ = true;
+ }
+
+ // Destroy all singletons; when complete, the vault can't create
+ // singletons once again until reenableInstances() is called.
+ void destroyInstances();
+
+ // Enable re-creating singletons after destroyInstances() was called.
+ void reenableInstances();
+
+ // For testing; how many registered and living singletons we have.
+ size_t registeredSingletonCount() const {
+ RWSpinLock::ReadHolder rh(&mutex_);
+
+ return singletons_.size();
+ }
+
+ size_t livingSingletonCount() const {
+ RWSpinLock::ReadHolder rh(&mutex_);
+
+ size_t ret = 0;
+ for (const auto& p : singletons_) {
+ if (p.second->hasLiveInstance()) {
+ ++ret;
+ }
+ }
+
+ return ret;
+ }
+
+ // A well-known vault; you can actually have others, but this is the
+ // default.
+ static SingletonVault* singleton() {
+ return singleton<>();
+ }
+
+ // Gets singleton vault for any Tag. Non-default tag should be used in unit
+ // tests only.
+ template <typename VaultTag = detail::DefaultTag>
+ static SingletonVault* singleton() {
+ static SingletonVault* vault = new SingletonVault();
+ return vault;
+ }
+
+ typedef std::string(*StackTraceGetterPtr)();
+
+ static std::atomic<StackTraceGetterPtr>& stackTraceGetter() {
+ static std::atomic<StackTraceGetterPtr> stackTraceGetterPtr;
+ return stackTraceGetterPtr;
+ }
+
+ private:
+ template <typename T>
+ friend class detail::SingletonHolder;
+
+ // The two stages of life for a vault, as mentioned in the class comment.
+ enum class SingletonVaultState {
+ Running,
+ Quiescing,
+ };
+
+ // Each singleton in the vault can be in two states: dead
+ // (registered but never created), living (CreateFunc returned an instance).
+
+ void stateCheck(SingletonVaultState expected,
+ const char* msg="Unexpected singleton state change") {
+ if (expected != state_) {
+ throw std::logic_error(msg);
+ }
+ }
+
+ // This method only matters if registrationComplete() is never called.
+ // Otherwise destroyInstances is scheduled to be executed atexit.
+ //
+ // Initializes static object, which calls destroyInstances on destruction.
+ // Used to have better deletion ordering with singleton not managed by
+ // folly::Singleton. The desruction will happen in the following order:
+ // 1. Singletons, not managed by folly::Singleton, which were created after
+ // any of the singletons managed by folly::Singleton was requested.
+ // 2. All singletons managed by folly::Singleton
+ // 3. Singletons, not managed by folly::Singleton, which were created before
+ // any of the singletons managed by folly::Singleton was requested.
+ static void scheduleDestroyInstances();
+
+ typedef std::unordered_map<detail::TypeDescriptor,
+ detail::SingletonHolderBase*,
+ detail::TypeDescriptorHasher> SingletonMap;
+
+ mutable folly::RWSpinLock mutex_;
+ SingletonMap singletons_;
+ std::vector<detail::TypeDescriptor> creation_order_;
+ SingletonVaultState state_{SingletonVaultState::Running};
+ bool registrationComplete_{false};
+ folly::RWSpinLock stateMutex_;
+ Type type_{Type::Relaxed};
+};
+
+// This is the wrapper class that most users actually interact with.
+// It allows for simple access to registering and instantiating
+// singletons. Create instances of this class in the global scope of
+// type Singleton<T> to register your singleton for later access via
+// Singleton<T>::get().
+template <typename T,
+ typename Tag = detail::DefaultTag,
+ typename VaultTag = detail::DefaultTag /* for testing */>
+class Singleton {
+ public:
+ typedef std::function<T*(void)> CreateFunc;
+ typedef std::function<void(T*)> TeardownFunc;
+
+ // Generally your program life cycle should be fine with calling
+ // get() repeatedly rather than saving the reference, and then not
+ // call get() during process shutdown.
+ static T* get() {
+ return getEntry().get();
+ }
+
+ // If, however, you do need to hold a reference to the specific
+ // singleton, you can try to do so with a weak_ptr. Avoid this when
+ // possible but the inability to lock the weak pointer can be a
+ // signal that the vault has been destroyed.
+ static std::weak_ptr<T> get_weak() {
+ return getEntry().get_weak();
+ }
+
+ // Allow the Singleton<t> instance to also retrieve the underlying
+ // singleton, if desired.
+ T& operator*() { return *get(); }
+ T* operator->() { return get(); }
+
+ explicit Singleton(std::nullptr_t _ = nullptr,
+ Singleton::TeardownFunc t = nullptr) :
+ Singleton ([]() { return new T; }, std::move(t)) {
+ }
+
+ explicit Singleton(Singleton::CreateFunc c,
+ Singleton::TeardownFunc t = nullptr) {
+ if (c == nullptr) {
+ throw std::logic_error(
+ "nullptr_t should be passed if you want T to be default constructed");
+ }
+
+ auto vault = SingletonVault::singleton<VaultTag>();
+ getEntry().registerSingleton(std::move(c), getTeardownFunc(std::move(t)));
+ vault->registerSingleton(&getEntry());
+ }
+
+ /**
+ * Construct and inject a mock singleton which should be used only from tests.
+ * Unlike regular singletons which are initialized once per process lifetime,
+ * mock singletons live for the duration of a test. This means that one process
+ * running multiple tests can initialize and register the same singleton
+ * multiple times. This functionality should be used only from tests
+ * since it relaxes validation and performance in order to be able to perform
+ * the injection. The returned mock singleton is functionality identical to
+ * regular singletons.
+ */
+ static void make_mock(std::nullptr_t c = nullptr,
+ typename Singleton<T>::TeardownFunc t = nullptr) {
+ make_mock([]() { return new T; }, t);
+ }
+
+ static void make_mock(CreateFunc c,
+ typename Singleton<T>::TeardownFunc t = nullptr) {
+ if (c == nullptr) {
+ throw std::logic_error(
+ "nullptr_t should be passed if you want T to be default constructed");
+ }
+
+ auto& entry = getEntry();
+
+ entry.registerSingletonMock(c, getTeardownFunc(t));
+ }
+
+ private:
+ inline static detail::SingletonHolder<T>& getEntry() {
+ return detail::SingletonHolder<T>::template singleton<Tag, VaultTag>();
+ }
+
+ // Construct TeardownFunc.
+ static typename detail::SingletonHolder<T>::TeardownFunc getTeardownFunc(
+ TeardownFunc t) {
+ if (t == nullptr) {
+ return [](T* v) { delete v; };
+ } else {
+ return t;
+ }
+ }
+};
+
+}
+
+#include <folly/Singleton-inl.h>
--- /dev/null
+/*
+ * Copyright 2015 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <folly/Singleton.h>
+#include <folly/experimental/symbolizer/Symbolizer.h>
+
+namespace folly {
+
+namespace {
+
+std::string stackTraceGetter() {
+ // Get and symbolize stack trace
+ constexpr size_t kMaxStackTraceDepth = 100;
+ symbolizer::FrameArray<kMaxStackTraceDepth> addresses;
+
+ if (!getStackTraceSafe(addresses)) {
+ return "";
+ } else {
+ constexpr size_t kDefaultCapacity = 500;
+ symbolizer::ElfCache elfCache(kDefaultCapacity);
+
+ symbolizer::Symbolizer symbolizer(&elfCache);
+ symbolizer.symbolize(addresses);
+
+ symbolizer::StringSymbolizePrinter printer;
+ printer.println(addresses);
+ return printer.str();
+ }
+}
+
+struct SetStackTraceGetter {
+ SetStackTraceGetter() {
+ SingletonVault::stackTraceGetter().store(stackTraceGetter);
+ }
+};
+
+SetStackTraceGetter setStackTraceGetter;
+
+}
+
+}
--- /dev/null
+/*
+ * Copyright 2015 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/SingletonVault_c.h>
+#include <folly/Singleton.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+SingletonVault_t *SingletonVault_singleton() {
+ return folly::SingletonVault::singleton();
+}
+
+void SingletonVault_registrationComplete(SingletonVault_t *vault) {
+ ((folly::SingletonVault*) vault)->registrationComplete();
+}
+
+void SingletonVault_destroyInstances(SingletonVault_t *vault) {
+ ((folly::SingletonVault*) vault)->destroyInstances();
+}
+
+void SingletonVault_reenableInstances(SingletonVault_t *vault) {
+ ((folly::SingletonVault*) vault)->reenableInstances();
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
--- /dev/null
+/*
+ * Copyright 2015 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Plain C interface to SingletonVault. This facilitates combining programs
+// that cannot use C++ (e.g. programs written in C) with libraries that use
+// Singleton, by allowing the program to perform the required SingletonVault
+// lifecycle calls.
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void SingletonVault_t;
+
+SingletonVault_t *SingletonVault_singleton();
+void SingletonVault_registrationComplete(SingletonVault_t *vault);
+void SingletonVault_destroyInstances(SingletonVault_t *vault);
+void SingletonVault_reenableInstances(SingletonVault_t *vault);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
#include <folly/Memory.h>
#include <folly/Optional.h>
#include <folly/String.h>
-#include <folly/experimental/Singleton.h>
+#include <folly/Singleton.h>
#include <folly/json.h>
namespace folly {
+++ /dev/null
-/*
- * Copyright 2015 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-namespace folly {
-
-namespace detail {
-
-template <typename T>
-template <typename Tag, typename VaultTag>
-SingletonHolder<T>& SingletonHolder<T>::singleton() {
- static auto entry = new SingletonHolder<T>(
- {typeid(T), typeid(Tag)},
- *SingletonVault::singleton<VaultTag>());
- return *entry;
-}
-
-template <typename T>
-void SingletonHolder<T>::registerSingleton(CreateFunc c, TeardownFunc t) {
- std::lock_guard<std::mutex> entry_lock(mutex_);
-
- if (state_ != SingletonHolderState::NotRegistered) {
- throw std::logic_error("Double registration");
- }
-
- create_ = std::move(c);
- teardown_ = std::move(t);
-
- state_ = SingletonHolderState::Dead;
-}
-
-template <typename T>
-void SingletonHolder<T>::registerSingletonMock(CreateFunc c, TeardownFunc t) {
- if (state_ == SingletonHolderState::NotRegistered) {
- throw std::logic_error("Registering mock before singleton was registered");
- }
- destroyInstance();
-
- std::lock_guard<std::mutex> entry_lock(mutex_);
-
- create_ = std::move(c);
- teardown_ = std::move(t);
-}
-
-template <typename T>
-T* SingletonHolder<T>::get() {
- if (LIKELY(state_ == SingletonHolderState::Living)) {
- return instance_ptr_;
- }
- createInstance();
-
- if (instance_weak_.expired()) {
- throw std::runtime_error(
- "Raw pointer to a singleton requested after its destruction.");
- }
-
- return instance_ptr_;
-}
-
-template <typename T>
-std::weak_ptr<T> SingletonHolder<T>::get_weak() {
- if (UNLIKELY(state_ != SingletonHolderState::Living)) {
- createInstance();
- }
-
- return instance_weak_;
-}
-
-template <typename T>
-TypeDescriptor SingletonHolder<T>::type() {
- return type_;
-}
-
-template <typename T>
-bool SingletonHolder<T>::hasLiveInstance() {
- return !instance_weak_.expired();
-}
-
-template <typename T>
-void SingletonHolder<T>::destroyInstance() {
- state_ = SingletonHolderState::Dead;
- instance_.reset();
- if (destroy_baton_) {
- auto wait_result = destroy_baton_->timed_wait(
- std::chrono::steady_clock::now() + kDestroyWaitTime);
- if (!wait_result) {
- print_destructor_stack_trace_->store(true);
- LOG(ERROR) << "Singleton of type " << type_.name() << " has a "
- << "living reference at destroyInstances time; beware! Raw "
- << "pointer is " << instance_ptr_ << ". It is very likely "
- << "that some other singleton is holding a shared_ptr to it. "
- << "Make sure dependencies between these singletons are "
- << "properly defined.";
- }
- }
-}
-
-template <typename T>
-SingletonHolder<T>::SingletonHolder(TypeDescriptor type__,
- SingletonVault& vault) :
- type_(type__), vault_(vault) {
-}
-
-template <typename T>
-void SingletonHolder<T>::createInstance() {
- // There's no synchronization here, so we may not see the current value
- // for creating_thread if it was set by other thread, but we only care about
- // it if it was set by current thread anyways.
- if (creating_thread_ == std::this_thread::get_id()) {
- throw std::out_of_range(std::string("circular singleton dependency: ") +
- type_.name());
- }
-
- std::lock_guard<std::mutex> entry_lock(mutex_);
- if (state_ == SingletonHolderState::Living) {
- return;
- }
- if (state_ == SingletonHolderState::NotRegistered) {
- throw std::out_of_range("Creating instance for unregistered singleton");
- }
-
- if (state_ == SingletonHolderState::Living) {
- return;
- }
-
- creating_thread_ = std::this_thread::get_id();
-
- RWSpinLock::ReadHolder rh(&vault_.stateMutex_);
- if (vault_.state_ == SingletonVault::SingletonVaultState::Quiescing) {
- creating_thread_ = std::thread::id();
- return;
- }
-
- auto destroy_baton = std::make_shared<folly::Baton<>>();
- auto print_destructor_stack_trace =
- std::make_shared<std::atomic<bool>>(false);
- auto teardown = teardown_;
- auto type_name = type_.name();
-
- // Can't use make_shared -- no support for a custom deleter, sadly.
- instance_ = std::shared_ptr<T>(
- create_(),
- [destroy_baton, print_destructor_stack_trace, teardown, type_name]
- (T* instance_ptr) mutable {
- teardown(instance_ptr);
- destroy_baton->post();
- if (print_destructor_stack_trace->load()) {
- std::string output = "Singleton " + type_name + " was destroyed.\n";
-
- auto stack_trace_getter = SingletonVault::stackTraceGetter().load();
- auto stack_trace = stack_trace_getter ? stack_trace_getter() : "";
- if (stack_trace.empty()) {
- output += "Failed to get destructor stack trace.";
- } else {
- output += "Destructor stack trace:\n";
- output += stack_trace;
- }
-
- LOG(ERROR) << output;
- }
- });
-
- // We should schedule destroyInstances() only after the singleton was
- // created. This will ensure it will be destroyed before singletons,
- // not managed by folly::Singleton, which were initialized in its
- // constructor
- SingletonVault::scheduleDestroyInstances();
-
- instance_weak_ = instance_;
- instance_ptr_ = instance_.get();
- creating_thread_ = std::thread::id();
- destroy_baton_ = std::move(destroy_baton);
- print_destructor_stack_trace_ = std::move(print_destructor_stack_trace);
-
- // This has to be the last step, because once state is Living other threads
- // may access instance and instance_weak w/o synchronization.
- state_.store(SingletonHolderState::Living);
-
- {
- RWSpinLock::WriteHolder wh(&vault_.mutex_);
- vault_.creation_order_.push_back(type_);
- }
-}
-
-}
-
-}
+++ /dev/null
-/*
- * Copyright 2015 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <folly/experimental/Singleton.h>
-
-#include <string>
-
-namespace folly {
-
-namespace detail {
-
-constexpr std::chrono::seconds SingletonHolderBase::kDestroyWaitTime;
-
-}
-
-namespace {
-
-struct FatalHelper {
- ~FatalHelper() {
- if (!leakedSingletons_.empty()) {
- std::string leakedTypes;
- for (const auto& singleton : leakedSingletons_) {
- leakedTypes += "\t" + singleton.name() + "\n";
- }
- LOG(DFATAL) << "Singletons of the following types had living references "
- << "after destroyInstances was finished:\n" << leakedTypes
- << "beware! It is very likely that those singleton instances "
- << "are leaked.";
- }
- }
-
- std::vector<detail::TypeDescriptor> leakedSingletons_;
-};
-
-FatalHelper __attribute__ ((__init_priority__ (101))) fatalHelper;
-
-}
-
-SingletonVault::~SingletonVault() { destroyInstances(); }
-
-void SingletonVault::destroyInstances() {
- RWSpinLock::WriteHolder state_wh(&stateMutex_);
-
- if (state_ == SingletonVaultState::Quiescing) {
- return;
- }
- state_ = SingletonVaultState::Quiescing;
-
- RWSpinLock::ReadHolder state_rh(std::move(state_wh));
-
- {
- RWSpinLock::ReadHolder rh(&mutex_);
-
- CHECK_GE(singletons_.size(), creation_order_.size());
-
- for (auto type_iter = creation_order_.rbegin();
- type_iter != creation_order_.rend();
- ++type_iter) {
- singletons_[*type_iter]->destroyInstance();
- }
-
- for (auto& singleton_type: creation_order_) {
- auto singleton = singletons_[singleton_type];
- if (!singleton->hasLiveInstance()) {
- continue;
- }
-
- fatalHelper.leakedSingletons_.push_back(singleton->type());
- }
- }
-
- {
- RWSpinLock::WriteHolder wh(&mutex_);
- creation_order_.clear();
- }
-}
-
-void SingletonVault::reenableInstances() {
- RWSpinLock::WriteHolder state_wh(&stateMutex_);
-
- stateCheck(SingletonVaultState::Quiescing);
-
- state_ = SingletonVaultState::Running;
-}
-
-void SingletonVault::scheduleDestroyInstances() {
- RequestContext::getStaticContext();
-
- class SingletonVaultDestructor {
- public:
- ~SingletonVaultDestructor() {
- SingletonVault::singleton()->destroyInstances();
- }
- };
-
- // Here we intialize a singleton, which calls destroyInstances in its
- // destructor. Because of singleton destruction order - it will be destroyed
- // before all the singletons, which were initialized before it and after all
- // the singletons initialized after it.
- static SingletonVaultDestructor singletonVaultDestructor;
-}
-
-}
+++ /dev/null
-/*
- * Copyright 2015 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// SingletonVault - a library to manage the creation and destruction
-// of interdependent singletons.
-//
-// Basic usage of this class is very simple; suppose you have a class
-// called MyExpensiveService, and you only want to construct one (ie,
-// it's a singleton), but you only want to construct it if it is used.
-//
-// In your .h file:
-// class MyExpensiveService { ... };
-//
-// In your .cpp file:
-// namespace { folly::Singleton<MyExpensiveService> the_singleton; }
-//
-// Code can access it via:
-//
-// MyExpensiveService* instance = Singleton<MyExpensiveService>::get();
-// or
-// std::weak_ptr<MyExpensiveService> instance =
-// Singleton<MyExpensiveService>::get_weak();
-//
-// You also can directly access it by the variable defining the
-// singleton rather than via get(), and even treat that variable like
-// a smart pointer (dereferencing it or using the -> operator).
-//
-// Please note, however, that all non-weak_ptr interfaces are
-// inherently subject to races with destruction. Use responsibly.
-//
-// The singleton will be created on demand. If the constructor for
-// MyExpensiveService actually makes use of *another* Singleton, then
-// the right thing will happen -- that other singleton will complete
-// construction before get() returns. However, in the event of a
-// circular dependency, a runtime error will occur.
-//
-// You can have multiple singletons of the same underlying type, but
-// each must be given a unique tag. If no tag is specified - default tag is used
-//
-// namespace {
-// struct Tag1 {};
-// struct Tag2 {};
-// folly::Singleton<MyExpensiveService> s_default();
-// folly::Singleton<MyExpensiveService, Tag1> s1();
-// folly::Singleton<MyExpensiveService, Tag2> s2();
-// }
-// ...
-// MyExpensiveService* svc_default = s_default.get();
-// MyExpensiveService* svc1 = s1.get();
-// MyExpensiveService* svc2 = s2.get();
-//
-// By default, the singleton instance is constructed via new and
-// deleted via delete, but this is configurable:
-//
-// namespace { folly::Singleton<MyExpensiveService> the_singleton(create,
-// destroy); }
-//
-// Where create and destroy are functions, Singleton<T>::CreateFunc
-// Singleton<T>::TeardownFunc.
-//
-// What if you need to destroy all of your singletons? Say, some of
-// your singletons manage threads, but you need to fork? Or your unit
-// test wants to clean up all global state? Then you can call
-// SingletonVault::singleton()->destroyInstances(), which invokes the
-// TeardownFunc for each singleton, in the reverse order they were
-// created. It is your responsibility to ensure your singletons can
-// handle cases where the singletons they depend on go away, however.
-// Singletons won't be recreated after destroyInstances call. If you
-// want to re-enable singleton creation (say after fork was called) you
-// should call reenableInstances.
-
-#pragma once
-#include <folly/Baton.h>
-#include <folly/Exception.h>
-#include <folly/Hash.h>
-#include <folly/Memory.h>
-#include <folly/RWSpinLock.h>
-#include <folly/Demangle.h>
-#include <folly/io/async/Request.h>
-
-#include <algorithm>
-#include <vector>
-#include <mutex>
-#include <thread>
-#include <condition_variable>
-#include <string>
-#include <unordered_map>
-#include <functional>
-#include <typeinfo>
-#include <typeindex>
-
-#include <glog/logging.h>
-
-namespace folly {
-
-// For actual usage, please see the Singleton<T> class at the bottom
-// of this file; that is what you will actually interact with.
-
-// SingletonVault is the class that manages singleton instances. It
-// is unaware of the underlying types of singletons, and simply
-// manages lifecycles and invokes CreateFunc and TeardownFunc when
-// appropriate. In general, you won't need to interact with the
-// SingletonVault itself.
-//
-// A vault goes through a few stages of life:
-//
-// 1. Registration phase; singletons can be registered, but no
-// singleton can be created.
-// 2. registrationComplete() has been called; singletons can no
-// longer be registered, but they can be created.
-// 3. A vault can return to stage 1 when destroyInstances is called.
-//
-// In general, you don't need to worry about any of the above; just
-// ensure registrationComplete() is called near the top of your main()
-// function, otherwise no singletons can be instantiated.
-
-class SingletonVault;
-
-namespace detail {
-
-struct DefaultTag {};
-
-// A TypeDescriptor is the unique handle for a given singleton. It is
-// a combinaiton of the type and of the optional name, and is used as
-// a key in unordered_maps.
-class TypeDescriptor {
- public:
- TypeDescriptor(const std::type_info& ti,
- const std::type_info& tag_ti)
- : ti_(ti), tag_ti_(tag_ti) {
- }
-
- TypeDescriptor(const TypeDescriptor& other)
- : ti_(other.ti_), tag_ti_(other.tag_ti_) {
- }
-
- TypeDescriptor& operator=(const TypeDescriptor& other) {
- if (this != &other) {
- ti_ = other.ti_;
- tag_ti_ = other.tag_ti_;
- }
-
- return *this;
- }
-
- std::string name() const {
- auto ret = demangle(ti_.name());
- if (tag_ti_ != std::type_index(typeid(DefaultTag))) {
- ret += "/";
- ret += demangle(tag_ti_.name());
- }
- return ret.toStdString();
- }
-
- friend class TypeDescriptorHasher;
-
- bool operator==(const TypeDescriptor& other) const {
- return ti_ == other.ti_ && tag_ti_ == other.tag_ti_;
- }
-
- private:
- std::type_index ti_;
- std::type_index tag_ti_;
-};
-
-class TypeDescriptorHasher {
- public:
- size_t operator()(const TypeDescriptor& ti) const {
- return folly::hash::hash_combine(ti.ti_, ti.tag_ti_);
- }
-};
-
-// This interface is used by SingletonVault to interact with SingletonHolders.
-// Having a non-template interface allows SingletonVault to keep a list of all
-// SingletonHolders.
-class SingletonHolderBase {
- public:
- virtual ~SingletonHolderBase() {}
-
- virtual TypeDescriptor type() = 0;
- virtual bool hasLiveInstance() = 0;
- virtual void destroyInstance() = 0;
-
- protected:
- static constexpr std::chrono::seconds kDestroyWaitTime{5};
-};
-
-// An actual instance of a singleton, tracking the instance itself,
-// its state as described above, and the create and teardown
-// functions.
-template <typename T>
-struct SingletonHolder : public SingletonHolderBase {
- public:
- typedef std::function<void(T*)> TeardownFunc;
- typedef std::function<T*(void)> CreateFunc;
-
- template <typename Tag, typename VaultTag>
- inline static SingletonHolder<T>& singleton();
-
- inline T* get();
- inline std::weak_ptr<T> get_weak();
-
- void registerSingleton(CreateFunc c, TeardownFunc t);
- void registerSingletonMock(CreateFunc c, TeardownFunc t);
- virtual TypeDescriptor type();
- virtual bool hasLiveInstance();
- virtual void destroyInstance();
-
- private:
- SingletonHolder(TypeDescriptor type, SingletonVault& vault);
-
- void createInstance();
-
- enum class SingletonHolderState {
- NotRegistered,
- Dead,
- Living,
- };
-
- TypeDescriptor type_;
- SingletonVault& vault_;
-
- // mutex protects the entire entry during construction/destruction
- std::mutex mutex_;
-
- // State of the singleton entry. If state is Living, instance_ptr and
- // instance_weak can be safely accessed w/o synchronization.
- std::atomic<SingletonHolderState> state_{SingletonHolderState::NotRegistered};
-
- // the thread creating the singleton (only valid while creating an object)
- std::thread::id creating_thread_;
-
- // The singleton itself and related functions.
-
- // holds a shared_ptr to singleton instance, set when state is changed from
- // Dead to Living. Reset when state is changed from Living to Dead.
- std::shared_ptr<T> instance_;
- // weak_ptr to the singleton instance, set when state is changed from Dead
- // to Living. We never write to this object after initialization, so it is
- // safe to read it from different threads w/o synchronization if we know
- // that state is set to Living
- std::weak_ptr<T> instance_weak_;
- // Time we wait on destroy_baton after releasing Singleton shared_ptr.
- std::shared_ptr<folly::Baton<>> destroy_baton_;
- T* instance_ptr_ = nullptr;
- CreateFunc create_ = nullptr;
- TeardownFunc teardown_ = nullptr;
-
- std::shared_ptr<std::atomic<bool>> print_destructor_stack_trace_;
-
- SingletonHolder(const SingletonHolder&) = delete;
- SingletonHolder& operator=(const SingletonHolder&) = delete;
- SingletonHolder& operator=(SingletonHolder&&) = delete;
- SingletonHolder(SingletonHolder&&) = delete;
-};
-
-}
-
-class SingletonVault {
- public:
- enum class Type { Strict, Relaxed };
-
- explicit SingletonVault(Type type = Type::Relaxed) : type_(type) {}
-
- // Destructor is only called by unit tests to check destroyInstances.
- ~SingletonVault();
-
- typedef std::function<void(void*)> TeardownFunc;
- typedef std::function<void*(void)> CreateFunc;
-
- // Ensure that Singleton has not been registered previously and that
- // registration is not complete. If validations succeeds,
- // register a singleton of a given type with the create and teardown
- // functions.
- void registerSingleton(detail::SingletonHolderBase* entry) {
- RWSpinLock::ReadHolder rh(&stateMutex_);
-
- stateCheck(SingletonVaultState::Running);
-
- if (UNLIKELY(registrationComplete_)) {
- throw std::logic_error(
- "Registering singleton after registrationComplete().");
- }
-
- RWSpinLock::ReadHolder rhMutex(&mutex_);
- CHECK_THROW(singletons_.find(entry->type()) == singletons_.end(),
- std::logic_error);
-
- RWSpinLock::UpgradedHolder wh(&mutex_);
- singletons_[entry->type()] = entry;
- }
-
- // Mark registration is complete; no more singletons can be
- // registered at this point.
- void registrationComplete() {
- RequestContext::getStaticContext();
- std::atexit([](){ SingletonVault::singleton()->destroyInstances(); });
-
- RWSpinLock::WriteHolder wh(&stateMutex_);
-
- stateCheck(SingletonVaultState::Running);
-
- if (type_ == Type::Strict) {
- for (const auto& p: singletons_) {
- if (p.second->hasLiveInstance()) {
- throw std::runtime_error(
- "Singleton created before registration was complete.");
- }
- }
- }
-
- registrationComplete_ = true;
- }
-
- // Destroy all singletons; when complete, the vault can't create
- // singletons once again until reenableInstances() is called.
- void destroyInstances();
-
- // Enable re-creating singletons after destroyInstances() was called.
- void reenableInstances();
-
- // For testing; how many registered and living singletons we have.
- size_t registeredSingletonCount() const {
- RWSpinLock::ReadHolder rh(&mutex_);
-
- return singletons_.size();
- }
-
- size_t livingSingletonCount() const {
- RWSpinLock::ReadHolder rh(&mutex_);
-
- size_t ret = 0;
- for (const auto& p : singletons_) {
- if (p.second->hasLiveInstance()) {
- ++ret;
- }
- }
-
- return ret;
- }
-
- // A well-known vault; you can actually have others, but this is the
- // default.
- static SingletonVault* singleton() {
- return singleton<>();
- }
-
- // Gets singleton vault for any Tag. Non-default tag should be used in unit
- // tests only.
- template <typename VaultTag = detail::DefaultTag>
- static SingletonVault* singleton() {
- static SingletonVault* vault = new SingletonVault();
- return vault;
- }
-
- typedef std::string(*StackTraceGetterPtr)();
-
- static std::atomic<StackTraceGetterPtr>& stackTraceGetter() {
- static std::atomic<StackTraceGetterPtr> stackTraceGetterPtr;
- return stackTraceGetterPtr;
- }
-
- private:
- template <typename T>
- friend class detail::SingletonHolder;
-
- // The two stages of life for a vault, as mentioned in the class comment.
- enum class SingletonVaultState {
- Running,
- Quiescing,
- };
-
- // Each singleton in the vault can be in two states: dead
- // (registered but never created), living (CreateFunc returned an instance).
-
- void stateCheck(SingletonVaultState expected,
- const char* msg="Unexpected singleton state change") {
- if (expected != state_) {
- throw std::logic_error(msg);
- }
- }
-
- // This method only matters if registrationComplete() is never called.
- // Otherwise destroyInstances is scheduled to be executed atexit.
- //
- // Initializes static object, which calls destroyInstances on destruction.
- // Used to have better deletion ordering with singleton not managed by
- // folly::Singleton. The desruction will happen in the following order:
- // 1. Singletons, not managed by folly::Singleton, which were created after
- // any of the singletons managed by folly::Singleton was requested.
- // 2. All singletons managed by folly::Singleton
- // 3. Singletons, not managed by folly::Singleton, which were created before
- // any of the singletons managed by folly::Singleton was requested.
- static void scheduleDestroyInstances();
-
- typedef std::unordered_map<detail::TypeDescriptor,
- detail::SingletonHolderBase*,
- detail::TypeDescriptorHasher> SingletonMap;
-
- mutable folly::RWSpinLock mutex_;
- SingletonMap singletons_;
- std::vector<detail::TypeDescriptor> creation_order_;
- SingletonVaultState state_{SingletonVaultState::Running};
- bool registrationComplete_{false};
- folly::RWSpinLock stateMutex_;
- Type type_{Type::Relaxed};
-};
-
-// This is the wrapper class that most users actually interact with.
-// It allows for simple access to registering and instantiating
-// singletons. Create instances of this class in the global scope of
-// type Singleton<T> to register your singleton for later access via
-// Singleton<T>::get().
-template <typename T,
- typename Tag = detail::DefaultTag,
- typename VaultTag = detail::DefaultTag /* for testing */>
-class Singleton {
- public:
- typedef std::function<T*(void)> CreateFunc;
- typedef std::function<void(T*)> TeardownFunc;
-
- // Generally your program life cycle should be fine with calling
- // get() repeatedly rather than saving the reference, and then not
- // call get() during process shutdown.
- static T* get() {
- return getEntry().get();
- }
-
- // If, however, you do need to hold a reference to the specific
- // singleton, you can try to do so with a weak_ptr. Avoid this when
- // possible but the inability to lock the weak pointer can be a
- // signal that the vault has been destroyed.
- static std::weak_ptr<T> get_weak() {
- return getEntry().get_weak();
- }
-
- // Allow the Singleton<t> instance to also retrieve the underlying
- // singleton, if desired.
- T& operator*() { return *get(); }
- T* operator->() { return get(); }
-
- explicit Singleton(std::nullptr_t _ = nullptr,
- Singleton::TeardownFunc t = nullptr) :
- Singleton ([]() { return new T; }, std::move(t)) {
- }
-
- explicit Singleton(Singleton::CreateFunc c,
- Singleton::TeardownFunc t = nullptr) {
- if (c == nullptr) {
- throw std::logic_error(
- "nullptr_t should be passed if you want T to be default constructed");
- }
-
- auto vault = SingletonVault::singleton<VaultTag>();
- getEntry().registerSingleton(std::move(c), getTeardownFunc(std::move(t)));
- vault->registerSingleton(&getEntry());
- }
-
- /**
- * Construct and inject a mock singleton which should be used only from tests.
- * Unlike regular singletons which are initialized once per process lifetime,
- * mock singletons live for the duration of a test. This means that one process
- * running multiple tests can initialize and register the same singleton
- * multiple times. This functionality should be used only from tests
- * since it relaxes validation and performance in order to be able to perform
- * the injection. The returned mock singleton is functionality identical to
- * regular singletons.
- */
- static void make_mock(std::nullptr_t c = nullptr,
- typename Singleton<T>::TeardownFunc t = nullptr) {
- make_mock([]() { return new T; }, t);
- }
-
- static void make_mock(CreateFunc c,
- typename Singleton<T>::TeardownFunc t = nullptr) {
- if (c == nullptr) {
- throw std::logic_error(
- "nullptr_t should be passed if you want T to be default constructed");
- }
-
- auto& entry = getEntry();
-
- entry.registerSingletonMock(c, getTeardownFunc(t));
- }
-
- private:
- inline static detail::SingletonHolder<T>& getEntry() {
- return detail::SingletonHolder<T>::template singleton<Tag, VaultTag>();
- }
-
- // Construct TeardownFunc.
- static typename detail::SingletonHolder<T>::TeardownFunc getTeardownFunc(
- TeardownFunc t) {
- if (t == nullptr) {
- return [](T* v) { delete v; };
- } else {
- return t;
- }
- }
-};
-
-}
-
-#include <folly/experimental/Singleton-inl.h>
+++ /dev/null
-/*
- * Copyright 2015 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include <folly/experimental/Singleton.h>
-#include <folly/experimental/symbolizer/Symbolizer.h>
-
-namespace folly {
-
-namespace {
-
-std::string stackTraceGetter() {
- // Get and symbolize stack trace
- constexpr size_t kMaxStackTraceDepth = 100;
- symbolizer::FrameArray<kMaxStackTraceDepth> addresses;
-
- if (!getStackTraceSafe(addresses)) {
- return "";
- } else {
- constexpr size_t kDefaultCapacity = 500;
- symbolizer::ElfCache elfCache(kDefaultCapacity);
-
- symbolizer::Symbolizer symbolizer(&elfCache);
- symbolizer.symbolize(addresses);
-
- symbolizer::StringSymbolizePrinter printer;
- printer.println(addresses);
- return printer.str();
- }
-}
-
-struct SetStackTraceGetter {
- SetStackTraceGetter() {
- SingletonVault::stackTraceGetter().store(stackTraceGetter);
- }
-};
-
-SetStackTraceGetter setStackTraceGetter;
-
-}
-
-}
+++ /dev/null
-/*
- * Copyright 2015 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <folly/experimental/SingletonVault_c.h>
-#include <folly/experimental/Singleton.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-SingletonVault_t *SingletonVault_singleton() {
- return folly::SingletonVault::singleton();
-}
-
-void SingletonVault_registrationComplete(SingletonVault_t *vault) {
- ((folly::SingletonVault*) vault)->registrationComplete();
-}
-
-void SingletonVault_destroyInstances(SingletonVault_t *vault) {
- ((folly::SingletonVault*) vault)->destroyInstances();
-}
-
-void SingletonVault_reenableInstances(SingletonVault_t *vault) {
- ((folly::SingletonVault*) vault)->reenableInstances();
-}
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
+++ /dev/null
-/*
- * Copyright 2015 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// Plain C interface to SingletonVault. This facilitates combining programs
-// that cannot use C++ (e.g. programs written in C) with libraries that use
-// Singleton, by allowing the program to perform the required SingletonVault
-// lifecycle calls.
-
-#pragma once
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef void SingletonVault_t;
-
-SingletonVault_t *SingletonVault_singleton();
-void SingletonVault_registrationComplete(SingletonVault_t *vault);
-void SingletonVault_destroyInstances(SingletonVault_t *vault);
-void SingletonVault_reenableInstances(SingletonVault_t *vault);
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
+++ /dev/null
-/*
- * Copyright 2015 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <thread>
-
-#include <folly/experimental/Singleton.h>
-
-#include <folly/Benchmark.h>
-
-#include <glog/logging.h>
-#include <gtest/gtest.h>
-
-using namespace folly;
-
-// A simple class that tracks how often instances of the class and
-// subclasses are created, and the ordering. Also tracks a global
-// unique counter for each object.
-std::atomic<size_t> global_counter(19770326);
-struct Watchdog {
- static std::vector<Watchdog*> creation_order;
- Watchdog() : serial_number(++global_counter) {
- creation_order.push_back(this);
- }
-
- ~Watchdog() {
- if (creation_order.back() != this) {
- throw std::out_of_range("Watchdog destruction order mismatch");
- }
- creation_order.pop_back();
- }
-
- const size_t serial_number;
- size_t livingWatchdogCount() const { return creation_order.size(); }
-
- Watchdog(const Watchdog&) = delete;
- Watchdog& operator=(const Watchdog&) = delete;
- Watchdog(Watchdog&&) noexcept = default;
-};
-
-std::vector<Watchdog*> Watchdog::creation_order;
-
-// Some basic types we use for tracking.
-struct ChildWatchdog : public Watchdog {};
-struct GlobalWatchdog : public Watchdog {};
-struct UnregisteredWatchdog : public Watchdog {};
-
-namespace {
-Singleton<GlobalWatchdog> global_watchdog;
-}
-
-// Test basic global usage (the default way singletons will generally
-// be used).
-TEST(Singleton, BasicGlobalUsage) {
- EXPECT_EQ(Watchdog::creation_order.size(), 0);
- EXPECT_EQ(SingletonVault::singleton()->registeredSingletonCount(), 1);
- EXPECT_EQ(SingletonVault::singleton()->livingSingletonCount(), 0);
- auto wd1 = Singleton<GlobalWatchdog>::get();
- EXPECT_NE(wd1, nullptr);
- EXPECT_EQ(Watchdog::creation_order.size(), 1);
- auto wd2 = Singleton<GlobalWatchdog>::get();
- EXPECT_NE(wd2, nullptr);
- EXPECT_EQ(wd1, wd2);
- EXPECT_EQ(Watchdog::creation_order.size(), 1);
- SingletonVault::singleton()->destroyInstances();
- EXPECT_EQ(Watchdog::creation_order.size(), 0);
-}
-
-TEST(Singleton, MissingSingleton) {
- EXPECT_THROW([]() { auto u = Singleton<UnregisteredWatchdog>::get(); }(),
- std::out_of_range);
-}
-
-struct BasicUsageTag {};
-template <typename T, typename Tag = detail::DefaultTag>
-using SingletonBasicUsage = Singleton <T, Tag, BasicUsageTag>;
-
-// Exercise some basic codepaths ensuring registration order and
-// destruction order happen as expected, that instances are created
-// when expected, etc etc.
-TEST(Singleton, BasicUsage) {
- auto& vault = *SingletonVault::singleton<BasicUsageTag>();
-
- EXPECT_EQ(vault.registeredSingletonCount(), 0);
- SingletonBasicUsage<Watchdog> watchdog_singleton;
- EXPECT_EQ(vault.registeredSingletonCount(), 1);
-
- SingletonBasicUsage<ChildWatchdog> child_watchdog_singleton;
- EXPECT_EQ(vault.registeredSingletonCount(), 2);
-
- vault.registrationComplete();
-
- Watchdog* s1 = SingletonBasicUsage<Watchdog>::get();
- EXPECT_NE(s1, nullptr);
-
- Watchdog* s2 = SingletonBasicUsage<Watchdog>::get();
- EXPECT_NE(s2, nullptr);
-
- EXPECT_EQ(s1, s2);
-
- auto s3 = SingletonBasicUsage<ChildWatchdog>::get();
- EXPECT_NE(s3, nullptr);
- EXPECT_NE(s2, s3);
-
- EXPECT_EQ(vault.registeredSingletonCount(), 2);
- EXPECT_EQ(vault.livingSingletonCount(), 2);
-
- vault.destroyInstances();
- EXPECT_EQ(vault.registeredSingletonCount(), 2);
- EXPECT_EQ(vault.livingSingletonCount(), 0);
-}
-
-struct DirectUsageTag {};
-template <typename T, typename Tag = detail::DefaultTag>
-using SingletonDirectUsage = Singleton <T, Tag, DirectUsageTag>;
-
-TEST(Singleton, DirectUsage) {
- auto& vault = *SingletonVault::singleton<DirectUsageTag>();
-
- EXPECT_EQ(vault.registeredSingletonCount(), 0);
-
- // Verify we can get to the underlying singletons via directly using
- // the singleton definition.
- SingletonDirectUsage<Watchdog> watchdog;
- struct TestTag {};
- SingletonDirectUsage<Watchdog, TestTag> named_watchdog;
- EXPECT_EQ(vault.registeredSingletonCount(), 2);
- vault.registrationComplete();
-
- EXPECT_NE(watchdog.get(), nullptr);
- EXPECT_EQ(watchdog.get(), SingletonDirectUsage<Watchdog>::get());
- EXPECT_NE(watchdog.get(), named_watchdog.get());
- EXPECT_EQ(watchdog->livingWatchdogCount(), 2);
- EXPECT_EQ((*watchdog).livingWatchdogCount(), 2);
-
- vault.destroyInstances();
-}
-
-struct NamedUsageTag {};
-template <typename T, typename Tag = detail::DefaultTag>
-using SingletonNamedUsage = Singleton <T, Tag, NamedUsageTag>;
-
-TEST(Singleton, NamedUsage) {
- auto& vault = *SingletonVault::singleton<NamedUsageTag>();
-
- EXPECT_EQ(vault.registeredSingletonCount(), 0);
-
- // Define two named Watchdog singletons and one unnamed singleton.
- struct Watchdog1 {};
- struct Watchdog2 {};
- typedef detail::DefaultTag Watchdog3;
- SingletonNamedUsage<Watchdog, Watchdog1> watchdog1_singleton;
- EXPECT_EQ(vault.registeredSingletonCount(), 1);
- SingletonNamedUsage<Watchdog, Watchdog2> watchdog2_singleton;
- EXPECT_EQ(vault.registeredSingletonCount(), 2);
- SingletonNamedUsage<Watchdog, Watchdog3> watchdog3_singleton;
- EXPECT_EQ(vault.registeredSingletonCount(), 3);
-
- vault.registrationComplete();
-
- // Verify our three singletons are distinct and non-nullptr.
- Watchdog* s1 = SingletonNamedUsage<Watchdog, Watchdog1>::get();
- EXPECT_EQ(s1, watchdog1_singleton.get());
- Watchdog* s2 = SingletonNamedUsage<Watchdog, Watchdog2>::get();
- EXPECT_EQ(s2, watchdog2_singleton.get());
- EXPECT_NE(s1, s2);
- Watchdog* s3 = SingletonNamedUsage<Watchdog, Watchdog3>::get();
- EXPECT_EQ(s3, watchdog3_singleton.get());
- EXPECT_NE(s3, s1);
- EXPECT_NE(s3, s2);
-
- // Verify the "default" singleton is the same as the DefaultTag-tagged
- // singleton.
- Watchdog* s4 = SingletonNamedUsage<Watchdog>::get();
- EXPECT_EQ(s4, watchdog3_singleton.get());
-
- vault.destroyInstances();
-}
-
-struct NaughtyUsageTag {};
-template <typename T, typename Tag = detail::DefaultTag>
-using SingletonNaughtyUsage = Singleton <T, Tag, NaughtyUsageTag>;
-struct NaughtyUsageTag2 {};
-template <typename T, typename Tag = detail::DefaultTag>
-using SingletonNaughtyUsage2 = Singleton <T, Tag, NaughtyUsageTag2>;
-
-// Some pathological cases such as getting unregistered singletons,
-// double registration, etc.
-TEST(Singleton, NaughtyUsage) {
- auto& vault = *SingletonVault::singleton<NaughtyUsageTag>();
-
- vault.registrationComplete();
-
- // Unregistered.
- EXPECT_THROW(Singleton<Watchdog>::get(), std::out_of_range);
- EXPECT_THROW(SingletonNaughtyUsage<Watchdog>::get(), std::out_of_range);
-
- vault.destroyInstances();
-
- auto& vault2 = *SingletonVault::singleton<NaughtyUsageTag2>();
-
- EXPECT_THROW(SingletonNaughtyUsage2<Watchdog>::get(), std::logic_error);
- SingletonNaughtyUsage2<Watchdog> watchdog_singleton;
- // double registration
- EXPECT_THROW([]() {
- SingletonNaughtyUsage2<Watchdog> watchdog_singleton;
- }(),
- std::logic_error);
- vault2.destroyInstances();
- // double registration after destroy
- EXPECT_THROW([]() {
- SingletonNaughtyUsage2<Watchdog> watchdog_singleton;
- }(),
- std::logic_error);
-}
-
-struct SharedPtrUsageTag {};
-template <typename T, typename Tag = detail::DefaultTag>
-using SingletonSharedPtrUsage = Singleton <T, Tag, SharedPtrUsageTag>;
-
-TEST(Singleton, SharedPtrUsage) {
- struct WatchdogHolder {
- ~WatchdogHolder() {
- if (watchdog) {
- LOG(ERROR) << "The following log message with stack trace is expected";
- }
- }
-
- std::shared_ptr<Watchdog> watchdog;
- };
-
- auto& vault = *SingletonVault::singleton<SharedPtrUsageTag>();
-
- EXPECT_EQ(vault.registeredSingletonCount(), 0);
- SingletonSharedPtrUsage<Watchdog> watchdog_singleton;
- EXPECT_EQ(vault.registeredSingletonCount(), 1);
-
- SingletonSharedPtrUsage<ChildWatchdog> child_watchdog_singleton;
- EXPECT_EQ(vault.registeredSingletonCount(), 2);
-
- struct ATag {};
- SingletonSharedPtrUsage<Watchdog, ATag> named_watchdog_singleton;
-
- SingletonSharedPtrUsage<WatchdogHolder> watchdog_holder_singleton;
-
- vault.registrationComplete();
-
- // Initilize holder singleton first, so that it's the last one to be
- // destroyed.
- watchdog_holder_singleton.get();
-
- Watchdog* s1 = SingletonSharedPtrUsage<Watchdog>::get();
- EXPECT_NE(s1, nullptr);
-
- Watchdog* s2 = SingletonSharedPtrUsage<Watchdog>::get();
- EXPECT_NE(s2, nullptr);
-
- EXPECT_EQ(s1, s2);
-
- auto weak_s1 = SingletonSharedPtrUsage<Watchdog>::get_weak();
-
- auto shared_s1 = weak_s1.lock();
- EXPECT_EQ(shared_s1.get(), s1);
- EXPECT_EQ(shared_s1.use_count(), 2);
-
- auto old_serial = shared_s1->serial_number;
-
- {
- auto named_weak_s1 =
- SingletonSharedPtrUsage<Watchdog, ATag>::get_weak();
- auto locked = named_weak_s1.lock();
- EXPECT_NE(locked.get(), shared_s1.get());
- }
-
- // We should release externally locked shared_ptr, otherwise it will be
- // considered a leak
- watchdog_holder_singleton->watchdog = std::move(shared_s1);
-
- LOG(ERROR) << "The following log message regarding shared_ptr is expected";
- {
- 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_EQ(vault.registeredSingletonCount(), 4);
- EXPECT_EQ(vault.livingSingletonCount(), 0);
-
- EXPECT_TRUE(weak_s1.expired());
-
- auto empty_s1 = SingletonSharedPtrUsage<Watchdog>::get_weak();
- EXPECT_FALSE(empty_s1.lock());
-
- vault.reenableInstances();
-
- // Singleton should be re-created only after reenableInstances() was called.
- Watchdog* new_s1 = SingletonSharedPtrUsage<Watchdog>::get();
- // Track serial number rather than pointer since the memory could be
- // re-used when we create new_s1.
- EXPECT_NE(new_s1->serial_number, old_serial);
-
- auto new_s1_weak = SingletonSharedPtrUsage<Watchdog>::get_weak();
- auto new_s1_shared = new_s1_weak.lock();
- std::thread t([new_s1_shared]() mutable {
- std::this_thread::sleep_for(std::chrono::seconds{2});
- new_s1_shared.reset();
- });
- new_s1_shared.reset();
- {
- 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{1} &&
- duration < std::chrono::seconds{3});
- }
- EXPECT_TRUE(new_s1_weak.expired());
- t.join();
-}
-
-// Some classes to test singleton dependencies. NeedySingleton has a
-// dependency on NeededSingleton, which happens during its
-// construction.
-struct NeedyTag {};
-template <typename T, typename Tag = detail::DefaultTag>
-using SingletonNeedy = Singleton <T, Tag, NeedyTag>;
-
-struct NeededSingleton {};
-struct NeedySingleton {
- NeedySingleton() {
- auto unused = SingletonNeedy<NeededSingleton>::get();
- EXPECT_NE(unused, nullptr);
- }
-};
-
-// Ensure circular dependencies fail -- a singleton that needs itself, whoops.
-struct SelfNeedyTag {};
-template <typename T, typename Tag = detail::DefaultTag>
-using SingletonSelfNeedy = Singleton <T, Tag, SelfNeedyTag>;
-
-struct SelfNeedySingleton {
- SelfNeedySingleton() {
- auto unused = SingletonSelfNeedy<SelfNeedySingleton>::get();
- EXPECT_NE(unused, nullptr);
- }
-};
-
-TEST(Singleton, SingletonDependencies) {
- SingletonNeedy<NeededSingleton> needed_singleton;
- SingletonNeedy<NeedySingleton> needy_singleton;
- auto& needy_vault = *SingletonVault::singleton<NeedyTag>();
-
- needy_vault.registrationComplete();
-
- EXPECT_EQ(needy_vault.registeredSingletonCount(), 2);
- EXPECT_EQ(needy_vault.livingSingletonCount(), 0);
-
- auto needy = SingletonNeedy<NeedySingleton>::get();
- EXPECT_EQ(needy_vault.livingSingletonCount(), 2);
-
- SingletonSelfNeedy<SelfNeedySingleton> self_needy_singleton;
- auto& self_needy_vault = *SingletonVault::singleton<SelfNeedyTag>();
-
- self_needy_vault.registrationComplete();
- EXPECT_THROW([]() {
- SingletonSelfNeedy<SelfNeedySingleton>::get();
- }(),
- std::out_of_range);
-}
-
-// A test to ensure multiple threads contending on singleton creation
-// properly wait for creation rather than thinking it is a circular
-// dependency.
-class Slowpoke : public Watchdog {
- public:
- Slowpoke() { std::this_thread::sleep_for(std::chrono::milliseconds(10)); }
-};
-
-struct ConcurrencyTag {};
-template <typename T, typename Tag = detail::DefaultTag>
-using SingletonConcurrency = Singleton <T, Tag, ConcurrencyTag>;
-
-TEST(Singleton, SingletonConcurrency) {
- auto& vault = *SingletonVault::singleton<ConcurrencyTag>();
- SingletonConcurrency<Slowpoke> slowpoke_singleton;
- vault.registrationComplete();
-
- std::mutex gatekeeper;
- gatekeeper.lock();
- auto func = [&gatekeeper]() {
- gatekeeper.lock();
- gatekeeper.unlock();
- auto unused = SingletonConcurrency<Slowpoke>::get();
- };
-
- EXPECT_EQ(vault.livingSingletonCount(), 0);
- std::vector<std::thread> threads;
- for (int i = 0; i < 100; ++i) {
- threads.emplace_back(func);
- }
- // If circular dependency checks fail, the unlock would trigger a
- // crash. Instead, it succeeds, and we have exactly one living
- // singleton.
- gatekeeper.unlock();
- for (auto& t : threads) {
- t.join();
- }
- EXPECT_EQ(vault.livingSingletonCount(), 1);
-}
-
-struct ConcurrencyStressTag {};
-template <typename T, typename Tag = detail::DefaultTag>
-using SingletonConcurrencyStress = Singleton <T, Tag, ConcurrencyStressTag>;
-
-TEST(Singleton, SingletonConcurrencyStress) {
- auto& vault = *SingletonVault::singleton<ConcurrencyStressTag>();
- SingletonConcurrencyStress<Slowpoke> slowpoke_singleton;
-
- std::vector<std::thread> ts;
- for (size_t i = 0; i < 100; ++i) {
- ts.emplace_back([&]() {
- slowpoke_singleton.get_weak().lock();
- });
- }
-
- for (size_t i = 0; i < 100; ++i) {
- std::chrono::milliseconds d(20);
-
- std::this_thread::sleep_for(d);
- vault.destroyInstances();
- std::this_thread::sleep_for(d);
- vault.destroyInstances();
- }
-
- for (auto& t : ts) {
- t.join();
- }
-}
-
-// 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>;
-
-// Verify that existing Singleton's can be overridden
-// using the make_mock functionality.
-TEST(Singleton, MockTest) {
- auto& vault = *SingletonVault::singleton<MockTag>();
-
- SingletonMock<Watchdog> watchdog_singleton;
- vault.registrationComplete();
-
- // Registring singletons after registrationComplete called works
- // with make_mock (but not with Singleton ctor).
- EXPECT_EQ(vault.registeredSingletonCount(), 1);
- int serial_count_first = SingletonMock<Watchdog>::get()->serial_number;
-
- // Override existing mock using make_mock.
- SingletonMock<Watchdog>::make_mock();
-
- EXPECT_EQ(vault.registeredSingletonCount(), 1);
- int serial_count_mock = SingletonMock<Watchdog>::get()->serial_number;
-
- // 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;
-};
-
-BENCHMARK(NormalSingleton, n) {
- for (size_t i = 0; i < n; ++i) {
- doNotOptimizeAway(getNormalSingleton());
- }
-}
-
-BENCHMARK_RELATIVE(MeyersSingleton, n) {
- for (size_t i = 0; i < n; ++i) {
- doNotOptimizeAway(getMeyersSingleton());
- }
-}
-
-struct BenchmarkTag {};
-template <typename T, typename Tag = detail::DefaultTag>
-using SingletonBenchmark = Singleton <T, Tag, BenchmarkTag>;
-
-struct GetTag{};
-struct GetWeakTag{};
-
-SingletonBenchmark<BenchmarkSingleton, GetTag> benchmark_singleton_get;
-SingletonBenchmark<BenchmarkSingleton, GetWeakTag> benchmark_singleton_get_weak;
-
-BENCHMARK_RELATIVE(FollySingleton, n) {
- for (size_t i = 0; i < n; ++i) {
- doNotOptimizeAway(SingletonBenchmark<BenchmarkSingleton, GetTag>::get());
- }
-}
-
-BENCHMARK_RELATIVE(FollySingletonWeak, n) {
- for (size_t i = 0; i < n; ++i) {
- SingletonBenchmark<BenchmarkSingleton, GetWeakTag>::get_weak();
- }
-}
-
-int main(int argc, char* argv[]) {
- testing::InitGoogleTest(&argc, argv);
- google::InitGoogleLogging(argv[0]);
- google::ParseCommandLineFlags(&argc, &argv, true);
-
- SingletonVault::singleton()->registrationComplete();
-
- auto ret = RUN_ALL_TESTS();
- if (!ret) {
- folly::runBenchmarksOnFlag();
- }
- return ret;
-}
+++ /dev/null
-/*
- * Copyright 2015 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <folly/experimental/Singleton.h>
-#include <folly/experimental/SingletonVault_c.h>
-
-#include <gtest/gtest.h>
-
-#include <thread>
-
-__thread long instance_counter_instances = 0;
-
-class InstanceCounter {
- public:
- InstanceCounter() {
- instance_counter_instances++;
- }
-
- ~InstanceCounter() {
- instance_counter_instances--;
- }
-};
-
-TEST(SingletonVault, singletonReturnsSingletonInstance) {
- SingletonVault_t *c = SingletonVault_singleton();
- auto *cpp = folly::SingletonVault::singleton();
- EXPECT_EQ(c, cpp);
-}
-
-struct TestTag {};
-template <typename T, typename Tag = folly::detail::DefaultTag>
-using SingletonTest = folly::Singleton <T, Tag, TestTag>;
-
-TEST(SingletonVault, singletonsAreCreatedAndDestroyed) {
- auto vault = folly::SingletonVault::singleton<TestTag>();
- SingletonTest<InstanceCounter> counter_singleton;
- SingletonVault_registrationComplete((SingletonVault_t*) vault);
- InstanceCounter *counter = SingletonTest<InstanceCounter>::get();
- EXPECT_EQ(instance_counter_instances, 1);
- SingletonVault_destroyInstances((SingletonVault_t*) vault);
- EXPECT_EQ(instance_counter_instances, 0);
-}
*/
#include "ThreadWheelTimekeeper.h"
-#include <folly/experimental/Singleton.h>
+#include <folly/Singleton.h>
#include <folly/futures/Future.h>
#include <future>
--- /dev/null
+/*
+ * Copyright 2015 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <thread>
+
+#include <folly/Singleton.h>
+
+#include <folly/Benchmark.h>
+
+#include <glog/logging.h>
+#include <gtest/gtest.h>
+
+using namespace folly;
+
+// A simple class that tracks how often instances of the class and
+// subclasses are created, and the ordering. Also tracks a global
+// unique counter for each object.
+std::atomic<size_t> global_counter(19770326);
+struct Watchdog {
+ static std::vector<Watchdog*> creation_order;
+ Watchdog() : serial_number(++global_counter) {
+ creation_order.push_back(this);
+ }
+
+ ~Watchdog() {
+ if (creation_order.back() != this) {
+ throw std::out_of_range("Watchdog destruction order mismatch");
+ }
+ creation_order.pop_back();
+ }
+
+ const size_t serial_number;
+ size_t livingWatchdogCount() const { return creation_order.size(); }
+
+ Watchdog(const Watchdog&) = delete;
+ Watchdog& operator=(const Watchdog&) = delete;
+ Watchdog(Watchdog&&) noexcept = default;
+};
+
+std::vector<Watchdog*> Watchdog::creation_order;
+
+// Some basic types we use for tracking.
+struct ChildWatchdog : public Watchdog {};
+struct GlobalWatchdog : public Watchdog {};
+struct UnregisteredWatchdog : public Watchdog {};
+
+namespace {
+Singleton<GlobalWatchdog> global_watchdog;
+}
+
+// Test basic global usage (the default way singletons will generally
+// be used).
+TEST(Singleton, BasicGlobalUsage) {
+ EXPECT_EQ(Watchdog::creation_order.size(), 0);
+ EXPECT_EQ(SingletonVault::singleton()->registeredSingletonCount(), 1);
+ EXPECT_EQ(SingletonVault::singleton()->livingSingletonCount(), 0);
+ auto wd1 = Singleton<GlobalWatchdog>::get();
+ EXPECT_NE(wd1, nullptr);
+ EXPECT_EQ(Watchdog::creation_order.size(), 1);
+ auto wd2 = Singleton<GlobalWatchdog>::get();
+ EXPECT_NE(wd2, nullptr);
+ EXPECT_EQ(wd1, wd2);
+ EXPECT_EQ(Watchdog::creation_order.size(), 1);
+ SingletonVault::singleton()->destroyInstances();
+ EXPECT_EQ(Watchdog::creation_order.size(), 0);
+}
+
+TEST(Singleton, MissingSingleton) {
+ EXPECT_THROW([]() { auto u = Singleton<UnregisteredWatchdog>::get(); }(),
+ std::out_of_range);
+}
+
+struct BasicUsageTag {};
+template <typename T, typename Tag = detail::DefaultTag>
+using SingletonBasicUsage = Singleton <T, Tag, BasicUsageTag>;
+
+// Exercise some basic codepaths ensuring registration order and
+// destruction order happen as expected, that instances are created
+// when expected, etc etc.
+TEST(Singleton, BasicUsage) {
+ auto& vault = *SingletonVault::singleton<BasicUsageTag>();
+
+ EXPECT_EQ(vault.registeredSingletonCount(), 0);
+ SingletonBasicUsage<Watchdog> watchdog_singleton;
+ EXPECT_EQ(vault.registeredSingletonCount(), 1);
+
+ SingletonBasicUsage<ChildWatchdog> child_watchdog_singleton;
+ EXPECT_EQ(vault.registeredSingletonCount(), 2);
+
+ vault.registrationComplete();
+
+ Watchdog* s1 = SingletonBasicUsage<Watchdog>::get();
+ EXPECT_NE(s1, nullptr);
+
+ Watchdog* s2 = SingletonBasicUsage<Watchdog>::get();
+ EXPECT_NE(s2, nullptr);
+
+ EXPECT_EQ(s1, s2);
+
+ auto s3 = SingletonBasicUsage<ChildWatchdog>::get();
+ EXPECT_NE(s3, nullptr);
+ EXPECT_NE(s2, s3);
+
+ EXPECT_EQ(vault.registeredSingletonCount(), 2);
+ EXPECT_EQ(vault.livingSingletonCount(), 2);
+
+ vault.destroyInstances();
+ EXPECT_EQ(vault.registeredSingletonCount(), 2);
+ EXPECT_EQ(vault.livingSingletonCount(), 0);
+}
+
+struct DirectUsageTag {};
+template <typename T, typename Tag = detail::DefaultTag>
+using SingletonDirectUsage = Singleton <T, Tag, DirectUsageTag>;
+
+TEST(Singleton, DirectUsage) {
+ auto& vault = *SingletonVault::singleton<DirectUsageTag>();
+
+ EXPECT_EQ(vault.registeredSingletonCount(), 0);
+
+ // Verify we can get to the underlying singletons via directly using
+ // the singleton definition.
+ SingletonDirectUsage<Watchdog> watchdog;
+ struct TestTag {};
+ SingletonDirectUsage<Watchdog, TestTag> named_watchdog;
+ EXPECT_EQ(vault.registeredSingletonCount(), 2);
+ vault.registrationComplete();
+
+ EXPECT_NE(watchdog.get(), nullptr);
+ EXPECT_EQ(watchdog.get(), SingletonDirectUsage<Watchdog>::get());
+ EXPECT_NE(watchdog.get(), named_watchdog.get());
+ EXPECT_EQ(watchdog->livingWatchdogCount(), 2);
+ EXPECT_EQ((*watchdog).livingWatchdogCount(), 2);
+
+ vault.destroyInstances();
+}
+
+struct NamedUsageTag {};
+template <typename T, typename Tag = detail::DefaultTag>
+using SingletonNamedUsage = Singleton <T, Tag, NamedUsageTag>;
+
+TEST(Singleton, NamedUsage) {
+ auto& vault = *SingletonVault::singleton<NamedUsageTag>();
+
+ EXPECT_EQ(vault.registeredSingletonCount(), 0);
+
+ // Define two named Watchdog singletons and one unnamed singleton.
+ struct Watchdog1 {};
+ struct Watchdog2 {};
+ typedef detail::DefaultTag Watchdog3;
+ SingletonNamedUsage<Watchdog, Watchdog1> watchdog1_singleton;
+ EXPECT_EQ(vault.registeredSingletonCount(), 1);
+ SingletonNamedUsage<Watchdog, Watchdog2> watchdog2_singleton;
+ EXPECT_EQ(vault.registeredSingletonCount(), 2);
+ SingletonNamedUsage<Watchdog, Watchdog3> watchdog3_singleton;
+ EXPECT_EQ(vault.registeredSingletonCount(), 3);
+
+ vault.registrationComplete();
+
+ // Verify our three singletons are distinct and non-nullptr.
+ Watchdog* s1 = SingletonNamedUsage<Watchdog, Watchdog1>::get();
+ EXPECT_EQ(s1, watchdog1_singleton.get());
+ Watchdog* s2 = SingletonNamedUsage<Watchdog, Watchdog2>::get();
+ EXPECT_EQ(s2, watchdog2_singleton.get());
+ EXPECT_NE(s1, s2);
+ Watchdog* s3 = SingletonNamedUsage<Watchdog, Watchdog3>::get();
+ EXPECT_EQ(s3, watchdog3_singleton.get());
+ EXPECT_NE(s3, s1);
+ EXPECT_NE(s3, s2);
+
+ // Verify the "default" singleton is the same as the DefaultTag-tagged
+ // singleton.
+ Watchdog* s4 = SingletonNamedUsage<Watchdog>::get();
+ EXPECT_EQ(s4, watchdog3_singleton.get());
+
+ vault.destroyInstances();
+}
+
+struct NaughtyUsageTag {};
+template <typename T, typename Tag = detail::DefaultTag>
+using SingletonNaughtyUsage = Singleton <T, Tag, NaughtyUsageTag>;
+struct NaughtyUsageTag2 {};
+template <typename T, typename Tag = detail::DefaultTag>
+using SingletonNaughtyUsage2 = Singleton <T, Tag, NaughtyUsageTag2>;
+
+// Some pathological cases such as getting unregistered singletons,
+// double registration, etc.
+TEST(Singleton, NaughtyUsage) {
+ auto& vault = *SingletonVault::singleton<NaughtyUsageTag>();
+
+ vault.registrationComplete();
+
+ // Unregistered.
+ EXPECT_THROW(Singleton<Watchdog>::get(), std::out_of_range);
+ EXPECT_THROW(SingletonNaughtyUsage<Watchdog>::get(), std::out_of_range);
+
+ vault.destroyInstances();
+
+ auto& vault2 = *SingletonVault::singleton<NaughtyUsageTag2>();
+
+ EXPECT_THROW(SingletonNaughtyUsage2<Watchdog>::get(), std::logic_error);
+ SingletonNaughtyUsage2<Watchdog> watchdog_singleton;
+ // double registration
+ EXPECT_THROW([]() {
+ SingletonNaughtyUsage2<Watchdog> watchdog_singleton;
+ }(),
+ std::logic_error);
+ vault2.destroyInstances();
+ // double registration after destroy
+ EXPECT_THROW([]() {
+ SingletonNaughtyUsage2<Watchdog> watchdog_singleton;
+ }(),
+ std::logic_error);
+}
+
+struct SharedPtrUsageTag {};
+template <typename T, typename Tag = detail::DefaultTag>
+using SingletonSharedPtrUsage = Singleton <T, Tag, SharedPtrUsageTag>;
+
+TEST(Singleton, SharedPtrUsage) {
+ struct WatchdogHolder {
+ ~WatchdogHolder() {
+ if (watchdog) {
+ LOG(ERROR) << "The following log message with stack trace is expected";
+ }
+ }
+
+ std::shared_ptr<Watchdog> watchdog;
+ };
+
+ auto& vault = *SingletonVault::singleton<SharedPtrUsageTag>();
+
+ EXPECT_EQ(vault.registeredSingletonCount(), 0);
+ SingletonSharedPtrUsage<Watchdog> watchdog_singleton;
+ EXPECT_EQ(vault.registeredSingletonCount(), 1);
+
+ SingletonSharedPtrUsage<ChildWatchdog> child_watchdog_singleton;
+ EXPECT_EQ(vault.registeredSingletonCount(), 2);
+
+ struct ATag {};
+ SingletonSharedPtrUsage<Watchdog, ATag> named_watchdog_singleton;
+
+ SingletonSharedPtrUsage<WatchdogHolder> watchdog_holder_singleton;
+
+ vault.registrationComplete();
+
+ // Initilize holder singleton first, so that it's the last one to be
+ // destroyed.
+ watchdog_holder_singleton.get();
+
+ Watchdog* s1 = SingletonSharedPtrUsage<Watchdog>::get();
+ EXPECT_NE(s1, nullptr);
+
+ Watchdog* s2 = SingletonSharedPtrUsage<Watchdog>::get();
+ EXPECT_NE(s2, nullptr);
+
+ EXPECT_EQ(s1, s2);
+
+ auto weak_s1 = SingletonSharedPtrUsage<Watchdog>::get_weak();
+
+ auto shared_s1 = weak_s1.lock();
+ EXPECT_EQ(shared_s1.get(), s1);
+ EXPECT_EQ(shared_s1.use_count(), 2);
+
+ auto old_serial = shared_s1->serial_number;
+
+ {
+ auto named_weak_s1 =
+ SingletonSharedPtrUsage<Watchdog, ATag>::get_weak();
+ auto locked = named_weak_s1.lock();
+ EXPECT_NE(locked.get(), shared_s1.get());
+ }
+
+ // We should release externally locked shared_ptr, otherwise it will be
+ // considered a leak
+ watchdog_holder_singleton->watchdog = std::move(shared_s1);
+
+ LOG(ERROR) << "The following log message regarding shared_ptr is expected";
+ {
+ 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_EQ(vault.registeredSingletonCount(), 4);
+ EXPECT_EQ(vault.livingSingletonCount(), 0);
+
+ EXPECT_TRUE(weak_s1.expired());
+
+ auto empty_s1 = SingletonSharedPtrUsage<Watchdog>::get_weak();
+ EXPECT_FALSE(empty_s1.lock());
+
+ vault.reenableInstances();
+
+ // Singleton should be re-created only after reenableInstances() was called.
+ Watchdog* new_s1 = SingletonSharedPtrUsage<Watchdog>::get();
+ // Track serial number rather than pointer since the memory could be
+ // re-used when we create new_s1.
+ EXPECT_NE(new_s1->serial_number, old_serial);
+
+ auto new_s1_weak = SingletonSharedPtrUsage<Watchdog>::get_weak();
+ auto new_s1_shared = new_s1_weak.lock();
+ std::thread t([new_s1_shared]() mutable {
+ std::this_thread::sleep_for(std::chrono::seconds{2});
+ new_s1_shared.reset();
+ });
+ new_s1_shared.reset();
+ {
+ 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{1} &&
+ duration < std::chrono::seconds{3});
+ }
+ EXPECT_TRUE(new_s1_weak.expired());
+ t.join();
+}
+
+// Some classes to test singleton dependencies. NeedySingleton has a
+// dependency on NeededSingleton, which happens during its
+// construction.
+struct NeedyTag {};
+template <typename T, typename Tag = detail::DefaultTag>
+using SingletonNeedy = Singleton <T, Tag, NeedyTag>;
+
+struct NeededSingleton {};
+struct NeedySingleton {
+ NeedySingleton() {
+ auto unused = SingletonNeedy<NeededSingleton>::get();
+ EXPECT_NE(unused, nullptr);
+ }
+};
+
+// Ensure circular dependencies fail -- a singleton that needs itself, whoops.
+struct SelfNeedyTag {};
+template <typename T, typename Tag = detail::DefaultTag>
+using SingletonSelfNeedy = Singleton <T, Tag, SelfNeedyTag>;
+
+struct SelfNeedySingleton {
+ SelfNeedySingleton() {
+ auto unused = SingletonSelfNeedy<SelfNeedySingleton>::get();
+ EXPECT_NE(unused, nullptr);
+ }
+};
+
+TEST(Singleton, SingletonDependencies) {
+ SingletonNeedy<NeededSingleton> needed_singleton;
+ SingletonNeedy<NeedySingleton> needy_singleton;
+ auto& needy_vault = *SingletonVault::singleton<NeedyTag>();
+
+ needy_vault.registrationComplete();
+
+ EXPECT_EQ(needy_vault.registeredSingletonCount(), 2);
+ EXPECT_EQ(needy_vault.livingSingletonCount(), 0);
+
+ auto needy = SingletonNeedy<NeedySingleton>::get();
+ EXPECT_EQ(needy_vault.livingSingletonCount(), 2);
+
+ SingletonSelfNeedy<SelfNeedySingleton> self_needy_singleton;
+ auto& self_needy_vault = *SingletonVault::singleton<SelfNeedyTag>();
+
+ self_needy_vault.registrationComplete();
+ EXPECT_THROW([]() {
+ SingletonSelfNeedy<SelfNeedySingleton>::get();
+ }(),
+ std::out_of_range);
+}
+
+// A test to ensure multiple threads contending on singleton creation
+// properly wait for creation rather than thinking it is a circular
+// dependency.
+class Slowpoke : public Watchdog {
+ public:
+ Slowpoke() { std::this_thread::sleep_for(std::chrono::milliseconds(10)); }
+};
+
+struct ConcurrencyTag {};
+template <typename T, typename Tag = detail::DefaultTag>
+using SingletonConcurrency = Singleton <T, Tag, ConcurrencyTag>;
+
+TEST(Singleton, SingletonConcurrency) {
+ auto& vault = *SingletonVault::singleton<ConcurrencyTag>();
+ SingletonConcurrency<Slowpoke> slowpoke_singleton;
+ vault.registrationComplete();
+
+ std::mutex gatekeeper;
+ gatekeeper.lock();
+ auto func = [&gatekeeper]() {
+ gatekeeper.lock();
+ gatekeeper.unlock();
+ auto unused = SingletonConcurrency<Slowpoke>::get();
+ };
+
+ EXPECT_EQ(vault.livingSingletonCount(), 0);
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 100; ++i) {
+ threads.emplace_back(func);
+ }
+ // If circular dependency checks fail, the unlock would trigger a
+ // crash. Instead, it succeeds, and we have exactly one living
+ // singleton.
+ gatekeeper.unlock();
+ for (auto& t : threads) {
+ t.join();
+ }
+ EXPECT_EQ(vault.livingSingletonCount(), 1);
+}
+
+struct ConcurrencyStressTag {};
+template <typename T, typename Tag = detail::DefaultTag>
+using SingletonConcurrencyStress = Singleton <T, Tag, ConcurrencyStressTag>;
+
+TEST(Singleton, SingletonConcurrencyStress) {
+ auto& vault = *SingletonVault::singleton<ConcurrencyStressTag>();
+ SingletonConcurrencyStress<Slowpoke> slowpoke_singleton;
+
+ std::vector<std::thread> ts;
+ for (size_t i = 0; i < 100; ++i) {
+ ts.emplace_back([&]() {
+ slowpoke_singleton.get_weak().lock();
+ });
+ }
+
+ for (size_t i = 0; i < 100; ++i) {
+ std::chrono::milliseconds d(20);
+
+ std::this_thread::sleep_for(d);
+ vault.destroyInstances();
+ std::this_thread::sleep_for(d);
+ vault.destroyInstances();
+ }
+
+ for (auto& t : ts) {
+ t.join();
+ }
+}
+
+// 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>;
+
+// Verify that existing Singleton's can be overridden
+// using the make_mock functionality.
+TEST(Singleton, MockTest) {
+ auto& vault = *SingletonVault::singleton<MockTag>();
+
+ SingletonMock<Watchdog> watchdog_singleton;
+ vault.registrationComplete();
+
+ // Registring singletons after registrationComplete called works
+ // with make_mock (but not with Singleton ctor).
+ EXPECT_EQ(vault.registeredSingletonCount(), 1);
+ int serial_count_first = SingletonMock<Watchdog>::get()->serial_number;
+
+ // Override existing mock using make_mock.
+ SingletonMock<Watchdog>::make_mock();
+
+ EXPECT_EQ(vault.registeredSingletonCount(), 1);
+ int serial_count_mock = SingletonMock<Watchdog>::get()->serial_number;
+
+ // 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;
+};
+
+BENCHMARK(NormalSingleton, n) {
+ for (size_t i = 0; i < n; ++i) {
+ doNotOptimizeAway(getNormalSingleton());
+ }
+}
+
+BENCHMARK_RELATIVE(MeyersSingleton, n) {
+ for (size_t i = 0; i < n; ++i) {
+ doNotOptimizeAway(getMeyersSingleton());
+ }
+}
+
+struct BenchmarkTag {};
+template <typename T, typename Tag = detail::DefaultTag>
+using SingletonBenchmark = Singleton <T, Tag, BenchmarkTag>;
+
+struct GetTag{};
+struct GetWeakTag{};
+
+SingletonBenchmark<BenchmarkSingleton, GetTag> benchmark_singleton_get;
+SingletonBenchmark<BenchmarkSingleton, GetWeakTag> benchmark_singleton_get_weak;
+
+BENCHMARK_RELATIVE(FollySingleton, n) {
+ for (size_t i = 0; i < n; ++i) {
+ doNotOptimizeAway(SingletonBenchmark<BenchmarkSingleton, GetTag>::get());
+ }
+}
+
+BENCHMARK_RELATIVE(FollySingletonWeak, n) {
+ for (size_t i = 0; i < n; ++i) {
+ SingletonBenchmark<BenchmarkSingleton, GetWeakTag>::get_weak();
+ }
+}
+
+int main(int argc, char* argv[]) {
+ testing::InitGoogleTest(&argc, argv);
+ google::InitGoogleLogging(argv[0]);
+ google::ParseCommandLineFlags(&argc, &argv, true);
+
+ SingletonVault::singleton()->registrationComplete();
+
+ auto ret = RUN_ALL_TESTS();
+ if (!ret) {
+ folly::runBenchmarksOnFlag();
+ }
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright 2015 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/Singleton.h>
+#include <folly/SingletonVault_c.h>
+
+#include <gtest/gtest.h>
+
+#include <thread>
+
+__thread long instance_counter_instances = 0;
+
+class InstanceCounter {
+ public:
+ InstanceCounter() {
+ instance_counter_instances++;
+ }
+
+ ~InstanceCounter() {
+ instance_counter_instances--;
+ }
+};
+
+TEST(SingletonVault, singletonReturnsSingletonInstance) {
+ SingletonVault_t *c = SingletonVault_singleton();
+ auto *cpp = folly::SingletonVault::singleton();
+ EXPECT_EQ(c, cpp);
+}
+
+struct TestTag {};
+template <typename T, typename Tag = folly::detail::DefaultTag>
+using SingletonTest = folly::Singleton <T, Tag, TestTag>;
+
+TEST(SingletonVault, singletonsAreCreatedAndDestroyed) {
+ auto vault = folly::SingletonVault::singleton<TestTag>();
+ SingletonTest<InstanceCounter> counter_singleton;
+ SingletonVault_registrationComplete((SingletonVault_t*) vault);
+ InstanceCounter *counter = SingletonTest<InstanceCounter>::get();
+ EXPECT_EQ(instance_counter_instances, 1);
+ SingletonVault_destroyInstances((SingletonVault_t*) vault);
+ EXPECT_EQ(instance_counter_instances, 0);
+}
* limitations under the License.
*/
-#include <folly/experimental/Singleton.h>
+#include <folly/Singleton.h>
#include <folly/wangle/concurrent/IOExecutor.h>
#include <folly/wangle/concurrent/IOThreadPoolExecutor.h>
#include <folly/futures/InlineExecutor.h>