From: Andrii Grynenko Date: Wed, 22 Apr 2015 02:37:08 +0000 (-0700) Subject: Move Singleton out of folly/experimental into folly/ X-Git-Tag: v0.37.0~2 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=d58181e677965b609be89b49ed7fbc4e78bf70b5;p=folly.git Move Singleton out of folly/experimental into folly/ Test Plan: contbuild Reviewed By: chip@fb.com Subscribers: wormhole-diffs@, trunkagent, hphp-diffs@, jan, simpkins, configerator-diffs@, fbcode-common-diffs@, chaoyc, bill, search-fbcode-diffs@, agallagher, nli, marcelo, ckwalsh, mcduff, hitesh, mshneer, unicorn-diffs@, vighnesh, fugalh, andreib, bmatheny, tw-eng@, tanmoyc, zhuohuang, rvm4, antonl, acampi, alikhtarov, hdoshi, rsethi, panin, folly-diffs@, lins, kennyyu, hannesr, jsedgwick, dominik, yfeldblum, songhao, raghavpi, labrams, lyang, chalfant, #preselection, macsyz, nimishshah FB internal diff: D2012267 Tasks: 5676394 Signature: t1:2012267:1430334667:eaad0262b35ffbfae86df5bdb45bf057ac62c51b --- diff --git a/folly/Makefile.am b/folly/Makefile.am index 245f4c89..442e070b 100644 --- a/folly/Makefile.am +++ b/folly/Makefile.am @@ -105,8 +105,6 @@ nobase_follyinclude_HEADERS = \ 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 \ @@ -227,6 +225,8 @@ nobase_follyinclude_HEADERS = \ Range.h \ RWSpinLock.h \ ScopeGuard.h \ + Singleton.h \ + Singleton-inl.h \ SmallLocks.h \ small_vector.h \ SocketAddress.h \ @@ -377,6 +377,7 @@ libfolly_la_SOURCES = \ Random.cpp \ SafeAssert.cpp \ SocketAddress.cpp \ + Singleton.cpp \ SpookyHashV1.cpp \ SpookyHashV2.cpp \ stats/Instantiations.cpp \ @@ -395,7 +396,6 @@ libfolly_la_SOURCES = \ experimental/JSONSchema.cpp \ experimental/Select64.cpp \ experimental/SharedMutex.cpp \ - experimental/Singleton.cpp \ experimental/TestUtil.cpp \ wangle/acceptor/Acceptor.cpp \ wangle/acceptor/ConnectionManager.cpp \ diff --git a/folly/Singleton-inl.h b/folly/Singleton-inl.h new file mode 100644 index 00000000..c853da65 --- /dev/null +++ b/folly/Singleton-inl.h @@ -0,0 +1,199 @@ +/* + * 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 +template +SingletonHolder& SingletonHolder::singleton() { + static auto entry = new SingletonHolder( + {typeid(T), typeid(Tag)}, + *SingletonVault::singleton()); + return *entry; +} + +template +void SingletonHolder::registerSingleton(CreateFunc c, TeardownFunc t) { + std::lock_guard 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 +void SingletonHolder::registerSingletonMock(CreateFunc c, TeardownFunc t) { + if (state_ == SingletonHolderState::NotRegistered) { + throw std::logic_error("Registering mock before singleton was registered"); + } + destroyInstance(); + + std::lock_guard entry_lock(mutex_); + + create_ = std::move(c); + teardown_ = std::move(t); +} + +template +T* SingletonHolder::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 +std::weak_ptr SingletonHolder::get_weak() { + if (UNLIKELY(state_ != SingletonHolderState::Living)) { + createInstance(); + } + + return instance_weak_; +} + +template +TypeDescriptor SingletonHolder::type() { + return type_; +} + +template +bool SingletonHolder::hasLiveInstance() { + return !instance_weak_.expired(); +} + +template +void SingletonHolder::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 +SingletonHolder::SingletonHolder(TypeDescriptor type__, + SingletonVault& vault) : + type_(type__), vault_(vault) { +} + +template +void SingletonHolder::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 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>(); + auto print_destructor_stack_trace = + std::make_shared>(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( + 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_); + } +} + +} + +} diff --git a/folly/Singleton.cpp b/folly/Singleton.cpp new file mode 100644 index 00000000..bfd63e57 --- /dev/null +++ b/folly/Singleton.cpp @@ -0,0 +1,116 @@ +/* + * 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 + +#include + +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 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; +} + +} diff --git a/folly/Singleton.h b/folly/Singleton.h new file mode 100644 index 00000000..c9868d4d --- /dev/null +++ b/folly/Singleton.h @@ -0,0 +1,517 @@ +/* + * 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 the_singleton; } +// +// Code can access it via: +// +// MyExpensiveService* instance = Singleton::get(); +// or +// std::weak_ptr instance = +// Singleton::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 s_default(); +// folly::Singleton s1(); +// folly::Singleton 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 the_singleton(create, +// destroy); } +// +// Where create and destroy are functions, Singleton::CreateFunc +// Singleton::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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace folly { + +// For actual usage, please see the Singleton 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 +struct SingletonHolder : public SingletonHolderBase { + public: + typedef std::function TeardownFunc; + typedef std::function CreateFunc; + + template + inline static SingletonHolder& singleton(); + + inline T* get(); + inline std::weak_ptr 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 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 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 instance_weak_; + // Time we wait on destroy_baton after releasing Singleton shared_ptr. + std::shared_ptr> destroy_baton_; + T* instance_ptr_ = nullptr; + CreateFunc create_ = nullptr; + TeardownFunc teardown_ = nullptr; + + std::shared_ptr> 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 TeardownFunc; + typedef std::function 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 + static SingletonVault* singleton() { + static SingletonVault* vault = new SingletonVault(); + return vault; + } + + typedef std::string(*StackTraceGetterPtr)(); + + static std::atomic& stackTraceGetter() { + static std::atomic stackTraceGetterPtr; + return stackTraceGetterPtr; + } + + private: + template + 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 SingletonMap; + + mutable folly::RWSpinLock mutex_; + SingletonMap singletons_; + std::vector 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 to register your singleton for later access via +// Singleton::get(). +template +class Singleton { + public: + typedef std::function CreateFunc; + typedef std::function 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 get_weak() { + return getEntry().get_weak(); + } + + // Allow the Singleton 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(); + 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::TeardownFunc t = nullptr) { + make_mock([]() { return new T; }, t); + } + + static void make_mock(CreateFunc c, + typename 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& entry = getEntry(); + + entry.registerSingletonMock(c, getTeardownFunc(t)); + } + + private: + inline static detail::SingletonHolder& getEntry() { + return detail::SingletonHolder::template singleton(); + } + + // Construct TeardownFunc. + static typename detail::SingletonHolder::TeardownFunc getTeardownFunc( + TeardownFunc t) { + if (t == nullptr) { + return [](T* v) { delete v; }; + } else { + return t; + } + } +}; + +} + +#include diff --git a/folly/SingletonStackTrace.cpp b/folly/SingletonStackTrace.cpp new file mode 100644 index 00000000..3e1f87cc --- /dev/null +++ b/folly/SingletonStackTrace.cpp @@ -0,0 +1,53 @@ +/* + * 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 +#include + +namespace folly { + +namespace { + +std::string stackTraceGetter() { + // Get and symbolize stack trace + constexpr size_t kMaxStackTraceDepth = 100; + symbolizer::FrameArray 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; + +} + +} diff --git a/folly/SingletonVault_c.cpp b/folly/SingletonVault_c.cpp new file mode 100644 index 00000000..5a6d6d44 --- /dev/null +++ b/folly/SingletonVault_c.cpp @@ -0,0 +1,42 @@ +/* + * 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 +#include + +#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 diff --git a/folly/SingletonVault_c.h b/folly/SingletonVault_c.h new file mode 100644 index 00000000..da3f5af5 --- /dev/null +++ b/folly/SingletonVault_c.h @@ -0,0 +1,37 @@ +/* + * 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 diff --git a/folly/experimental/JSONSchema.cpp b/folly/experimental/JSONSchema.cpp index 6978cf69..5413da38 100644 --- a/folly/experimental/JSONSchema.cpp +++ b/folly/experimental/JSONSchema.cpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include namespace folly { diff --git a/folly/experimental/Singleton-inl.h b/folly/experimental/Singleton-inl.h deleted file mode 100644 index c853da65..00000000 --- a/folly/experimental/Singleton-inl.h +++ /dev/null @@ -1,199 +0,0 @@ -/* - * 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 -template -SingletonHolder& SingletonHolder::singleton() { - static auto entry = new SingletonHolder( - {typeid(T), typeid(Tag)}, - *SingletonVault::singleton()); - return *entry; -} - -template -void SingletonHolder::registerSingleton(CreateFunc c, TeardownFunc t) { - std::lock_guard 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 -void SingletonHolder::registerSingletonMock(CreateFunc c, TeardownFunc t) { - if (state_ == SingletonHolderState::NotRegistered) { - throw std::logic_error("Registering mock before singleton was registered"); - } - destroyInstance(); - - std::lock_guard entry_lock(mutex_); - - create_ = std::move(c); - teardown_ = std::move(t); -} - -template -T* SingletonHolder::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 -std::weak_ptr SingletonHolder::get_weak() { - if (UNLIKELY(state_ != SingletonHolderState::Living)) { - createInstance(); - } - - return instance_weak_; -} - -template -TypeDescriptor SingletonHolder::type() { - return type_; -} - -template -bool SingletonHolder::hasLiveInstance() { - return !instance_weak_.expired(); -} - -template -void SingletonHolder::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 -SingletonHolder::SingletonHolder(TypeDescriptor type__, - SingletonVault& vault) : - type_(type__), vault_(vault) { -} - -template -void SingletonHolder::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 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>(); - auto print_destructor_stack_trace = - std::make_shared>(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( - 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_); - } -} - -} - -} diff --git a/folly/experimental/Singleton.cpp b/folly/experimental/Singleton.cpp deleted file mode 100644 index 1c39326e..00000000 --- a/folly/experimental/Singleton.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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 - -#include - -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 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; -} - -} diff --git a/folly/experimental/Singleton.h b/folly/experimental/Singleton.h deleted file mode 100644 index 7af17256..00000000 --- a/folly/experimental/Singleton.h +++ /dev/null @@ -1,517 +0,0 @@ -/* - * 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 the_singleton; } -// -// Code can access it via: -// -// MyExpensiveService* instance = Singleton::get(); -// or -// std::weak_ptr instance = -// Singleton::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 s_default(); -// folly::Singleton s1(); -// folly::Singleton 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 the_singleton(create, -// destroy); } -// -// Where create and destroy are functions, Singleton::CreateFunc -// Singleton::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 -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace folly { - -// For actual usage, please see the Singleton 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 -struct SingletonHolder : public SingletonHolderBase { - public: - typedef std::function TeardownFunc; - typedef std::function CreateFunc; - - template - inline static SingletonHolder& singleton(); - - inline T* get(); - inline std::weak_ptr 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 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 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 instance_weak_; - // Time we wait on destroy_baton after releasing Singleton shared_ptr. - std::shared_ptr> destroy_baton_; - T* instance_ptr_ = nullptr; - CreateFunc create_ = nullptr; - TeardownFunc teardown_ = nullptr; - - std::shared_ptr> 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 TeardownFunc; - typedef std::function 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 - static SingletonVault* singleton() { - static SingletonVault* vault = new SingletonVault(); - return vault; - } - - typedef std::string(*StackTraceGetterPtr)(); - - static std::atomic& stackTraceGetter() { - static std::atomic stackTraceGetterPtr; - return stackTraceGetterPtr; - } - - private: - template - 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 SingletonMap; - - mutable folly::RWSpinLock mutex_; - SingletonMap singletons_; - std::vector 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 to register your singleton for later access via -// Singleton::get(). -template -class Singleton { - public: - typedef std::function CreateFunc; - typedef std::function 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 get_weak() { - return getEntry().get_weak(); - } - - // Allow the Singleton 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(); - 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::TeardownFunc t = nullptr) { - make_mock([]() { return new T; }, t); - } - - static void make_mock(CreateFunc c, - typename 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& entry = getEntry(); - - entry.registerSingletonMock(c, getTeardownFunc(t)); - } - - private: - inline static detail::SingletonHolder& getEntry() { - return detail::SingletonHolder::template singleton(); - } - - // Construct TeardownFunc. - static typename detail::SingletonHolder::TeardownFunc getTeardownFunc( - TeardownFunc t) { - if (t == nullptr) { - return [](T* v) { delete v; }; - } else { - return t; - } - } -}; - -} - -#include diff --git a/folly/experimental/SingletonStackTrace.cpp b/folly/experimental/SingletonStackTrace.cpp deleted file mode 100644 index da535743..00000000 --- a/folly/experimental/SingletonStackTrace.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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 -#include - -namespace folly { - -namespace { - -std::string stackTraceGetter() { - // Get and symbolize stack trace - constexpr size_t kMaxStackTraceDepth = 100; - symbolizer::FrameArray 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; - -} - -} diff --git a/folly/experimental/SingletonVault_c.cpp b/folly/experimental/SingletonVault_c.cpp deleted file mode 100644 index 92ac83f5..00000000 --- a/folly/experimental/SingletonVault_c.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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 -#include - -#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 diff --git a/folly/experimental/SingletonVault_c.h b/folly/experimental/SingletonVault_c.h deleted file mode 100644 index da3f5af5..00000000 --- a/folly/experimental/SingletonVault_c.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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 diff --git a/folly/experimental/test/SingletonTest.cpp b/folly/experimental/test/SingletonTest.cpp deleted file mode 100644 index f29db472..00000000 --- a/folly/experimental/test/SingletonTest.cpp +++ /dev/null @@ -1,543 +0,0 @@ -/* - * 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 - -#include - -#include - -#include -#include - -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 global_counter(19770326); -struct Watchdog { - static std::vector 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::creation_order; - -// Some basic types we use for tracking. -struct ChildWatchdog : public Watchdog {}; -struct GlobalWatchdog : public Watchdog {}; -struct UnregisteredWatchdog : public Watchdog {}; - -namespace { -Singleton 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::get(); - EXPECT_NE(wd1, nullptr); - EXPECT_EQ(Watchdog::creation_order.size(), 1); - auto wd2 = Singleton::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::get(); }(), - std::out_of_range); -} - -struct BasicUsageTag {}; -template -using SingletonBasicUsage = Singleton ; - -// 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(); - - EXPECT_EQ(vault.registeredSingletonCount(), 0); - SingletonBasicUsage watchdog_singleton; - EXPECT_EQ(vault.registeredSingletonCount(), 1); - - SingletonBasicUsage child_watchdog_singleton; - EXPECT_EQ(vault.registeredSingletonCount(), 2); - - vault.registrationComplete(); - - Watchdog* s1 = SingletonBasicUsage::get(); - EXPECT_NE(s1, nullptr); - - Watchdog* s2 = SingletonBasicUsage::get(); - EXPECT_NE(s2, nullptr); - - EXPECT_EQ(s1, s2); - - auto s3 = SingletonBasicUsage::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 -using SingletonDirectUsage = Singleton ; - -TEST(Singleton, DirectUsage) { - auto& vault = *SingletonVault::singleton(); - - EXPECT_EQ(vault.registeredSingletonCount(), 0); - - // Verify we can get to the underlying singletons via directly using - // the singleton definition. - SingletonDirectUsage watchdog; - struct TestTag {}; - SingletonDirectUsage named_watchdog; - EXPECT_EQ(vault.registeredSingletonCount(), 2); - vault.registrationComplete(); - - EXPECT_NE(watchdog.get(), nullptr); - EXPECT_EQ(watchdog.get(), SingletonDirectUsage::get()); - EXPECT_NE(watchdog.get(), named_watchdog.get()); - EXPECT_EQ(watchdog->livingWatchdogCount(), 2); - EXPECT_EQ((*watchdog).livingWatchdogCount(), 2); - - vault.destroyInstances(); -} - -struct NamedUsageTag {}; -template -using SingletonNamedUsage = Singleton ; - -TEST(Singleton, NamedUsage) { - auto& vault = *SingletonVault::singleton(); - - EXPECT_EQ(vault.registeredSingletonCount(), 0); - - // Define two named Watchdog singletons and one unnamed singleton. - struct Watchdog1 {}; - struct Watchdog2 {}; - typedef detail::DefaultTag Watchdog3; - SingletonNamedUsage watchdog1_singleton; - EXPECT_EQ(vault.registeredSingletonCount(), 1); - SingletonNamedUsage watchdog2_singleton; - EXPECT_EQ(vault.registeredSingletonCount(), 2); - SingletonNamedUsage watchdog3_singleton; - EXPECT_EQ(vault.registeredSingletonCount(), 3); - - vault.registrationComplete(); - - // Verify our three singletons are distinct and non-nullptr. - Watchdog* s1 = SingletonNamedUsage::get(); - EXPECT_EQ(s1, watchdog1_singleton.get()); - Watchdog* s2 = SingletonNamedUsage::get(); - EXPECT_EQ(s2, watchdog2_singleton.get()); - EXPECT_NE(s1, s2); - Watchdog* s3 = SingletonNamedUsage::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::get(); - EXPECT_EQ(s4, watchdog3_singleton.get()); - - vault.destroyInstances(); -} - -struct NaughtyUsageTag {}; -template -using SingletonNaughtyUsage = Singleton ; -struct NaughtyUsageTag2 {}; -template -using SingletonNaughtyUsage2 = Singleton ; - -// Some pathological cases such as getting unregistered singletons, -// double registration, etc. -TEST(Singleton, NaughtyUsage) { - auto& vault = *SingletonVault::singleton(); - - vault.registrationComplete(); - - // Unregistered. - EXPECT_THROW(Singleton::get(), std::out_of_range); - EXPECT_THROW(SingletonNaughtyUsage::get(), std::out_of_range); - - vault.destroyInstances(); - - auto& vault2 = *SingletonVault::singleton(); - - EXPECT_THROW(SingletonNaughtyUsage2::get(), std::logic_error); - SingletonNaughtyUsage2 watchdog_singleton; - // double registration - EXPECT_THROW([]() { - SingletonNaughtyUsage2 watchdog_singleton; - }(), - std::logic_error); - vault2.destroyInstances(); - // double registration after destroy - EXPECT_THROW([]() { - SingletonNaughtyUsage2 watchdog_singleton; - }(), - std::logic_error); -} - -struct SharedPtrUsageTag {}; -template -using SingletonSharedPtrUsage = Singleton ; - -TEST(Singleton, SharedPtrUsage) { - struct WatchdogHolder { - ~WatchdogHolder() { - if (watchdog) { - LOG(ERROR) << "The following log message with stack trace is expected"; - } - } - - std::shared_ptr watchdog; - }; - - auto& vault = *SingletonVault::singleton(); - - EXPECT_EQ(vault.registeredSingletonCount(), 0); - SingletonSharedPtrUsage watchdog_singleton; - EXPECT_EQ(vault.registeredSingletonCount(), 1); - - SingletonSharedPtrUsage child_watchdog_singleton; - EXPECT_EQ(vault.registeredSingletonCount(), 2); - - struct ATag {}; - SingletonSharedPtrUsage named_watchdog_singleton; - - SingletonSharedPtrUsage 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::get(); - EXPECT_NE(s1, nullptr); - - Watchdog* s2 = SingletonSharedPtrUsage::get(); - EXPECT_NE(s2, nullptr); - - EXPECT_EQ(s1, s2); - - auto weak_s1 = SingletonSharedPtrUsage::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::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::get_weak(); - EXPECT_FALSE(empty_s1.lock()); - - vault.reenableInstances(); - - // Singleton should be re-created only after reenableInstances() was called. - Watchdog* new_s1 = SingletonSharedPtrUsage::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::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 -using SingletonNeedy = Singleton ; - -struct NeededSingleton {}; -struct NeedySingleton { - NeedySingleton() { - auto unused = SingletonNeedy::get(); - EXPECT_NE(unused, nullptr); - } -}; - -// Ensure circular dependencies fail -- a singleton that needs itself, whoops. -struct SelfNeedyTag {}; -template -using SingletonSelfNeedy = Singleton ; - -struct SelfNeedySingleton { - SelfNeedySingleton() { - auto unused = SingletonSelfNeedy::get(); - EXPECT_NE(unused, nullptr); - } -}; - -TEST(Singleton, SingletonDependencies) { - SingletonNeedy needed_singleton; - SingletonNeedy needy_singleton; - auto& needy_vault = *SingletonVault::singleton(); - - needy_vault.registrationComplete(); - - EXPECT_EQ(needy_vault.registeredSingletonCount(), 2); - EXPECT_EQ(needy_vault.livingSingletonCount(), 0); - - auto needy = SingletonNeedy::get(); - EXPECT_EQ(needy_vault.livingSingletonCount(), 2); - - SingletonSelfNeedy self_needy_singleton; - auto& self_needy_vault = *SingletonVault::singleton(); - - self_needy_vault.registrationComplete(); - EXPECT_THROW([]() { - SingletonSelfNeedy::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 -using SingletonConcurrency = Singleton ; - -TEST(Singleton, SingletonConcurrency) { - auto& vault = *SingletonVault::singleton(); - SingletonConcurrency slowpoke_singleton; - vault.registrationComplete(); - - std::mutex gatekeeper; - gatekeeper.lock(); - auto func = [&gatekeeper]() { - gatekeeper.lock(); - gatekeeper.unlock(); - auto unused = SingletonConcurrency::get(); - }; - - EXPECT_EQ(vault.livingSingletonCount(), 0); - std::vector 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 -using SingletonConcurrencyStress = Singleton ; - -TEST(Singleton, SingletonConcurrencyStress) { - auto& vault = *SingletonVault::singleton(); - SingletonConcurrencyStress slowpoke_singleton; - - std::vector 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 -using SingletonMock = Singleton ; - -// Verify that existing Singleton's can be overridden -// using the make_mock functionality. -TEST(Singleton, MockTest) { - auto& vault = *SingletonVault::singleton(); - - SingletonMock 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::get()->serial_number; - - // Override existing mock using make_mock. - SingletonMock::make_mock(); - - EXPECT_EQ(vault.registeredSingletonCount(), 1); - int serial_count_mock = SingletonMock::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 -using SingletonBenchmark = Singleton ; - -struct GetTag{}; -struct GetWeakTag{}; - -SingletonBenchmark benchmark_singleton_get; -SingletonBenchmark benchmark_singleton_get_weak; - -BENCHMARK_RELATIVE(FollySingleton, n) { - for (size_t i = 0; i < n; ++i) { - doNotOptimizeAway(SingletonBenchmark::get()); - } -} - -BENCHMARK_RELATIVE(FollySingletonWeak, n) { - for (size_t i = 0; i < n; ++i) { - SingletonBenchmark::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; -} diff --git a/folly/experimental/test/SingletonVaultCTest.cpp b/folly/experimental/test/SingletonVaultCTest.cpp deleted file mode 100644 index 4fcc1eae..00000000 --- a/folly/experimental/test/SingletonVaultCTest.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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 -#include - -#include - -#include - -__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 -using SingletonTest = folly::Singleton ; - -TEST(SingletonVault, singletonsAreCreatedAndDestroyed) { - auto vault = folly::SingletonVault::singleton(); - SingletonTest counter_singleton; - SingletonVault_registrationComplete((SingletonVault_t*) vault); - InstanceCounter *counter = SingletonTest::get(); - EXPECT_EQ(instance_counter_instances, 1); - SingletonVault_destroyInstances((SingletonVault_t*) vault); - EXPECT_EQ(instance_counter_instances, 0); -} diff --git a/folly/futures/detail/ThreadWheelTimekeeper.cpp b/folly/futures/detail/ThreadWheelTimekeeper.cpp index 51965d10..f861af23 100644 --- a/folly/futures/detail/ThreadWheelTimekeeper.cpp +++ b/folly/futures/detail/ThreadWheelTimekeeper.cpp @@ -15,7 +15,7 @@ */ #include "ThreadWheelTimekeeper.h" -#include +#include #include #include diff --git a/folly/test/SingletonTest.cpp b/folly/test/SingletonTest.cpp new file mode 100644 index 00000000..19baebb2 --- /dev/null +++ b/folly/test/SingletonTest.cpp @@ -0,0 +1,543 @@ +/* + * 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 + +#include + +#include + +#include +#include + +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 global_counter(19770326); +struct Watchdog { + static std::vector 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::creation_order; + +// Some basic types we use for tracking. +struct ChildWatchdog : public Watchdog {}; +struct GlobalWatchdog : public Watchdog {}; +struct UnregisteredWatchdog : public Watchdog {}; + +namespace { +Singleton 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::get(); + EXPECT_NE(wd1, nullptr); + EXPECT_EQ(Watchdog::creation_order.size(), 1); + auto wd2 = Singleton::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::get(); }(), + std::out_of_range); +} + +struct BasicUsageTag {}; +template +using SingletonBasicUsage = Singleton ; + +// 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(); + + EXPECT_EQ(vault.registeredSingletonCount(), 0); + SingletonBasicUsage watchdog_singleton; + EXPECT_EQ(vault.registeredSingletonCount(), 1); + + SingletonBasicUsage child_watchdog_singleton; + EXPECT_EQ(vault.registeredSingletonCount(), 2); + + vault.registrationComplete(); + + Watchdog* s1 = SingletonBasicUsage::get(); + EXPECT_NE(s1, nullptr); + + Watchdog* s2 = SingletonBasicUsage::get(); + EXPECT_NE(s2, nullptr); + + EXPECT_EQ(s1, s2); + + auto s3 = SingletonBasicUsage::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 +using SingletonDirectUsage = Singleton ; + +TEST(Singleton, DirectUsage) { + auto& vault = *SingletonVault::singleton(); + + EXPECT_EQ(vault.registeredSingletonCount(), 0); + + // Verify we can get to the underlying singletons via directly using + // the singleton definition. + SingletonDirectUsage watchdog; + struct TestTag {}; + SingletonDirectUsage named_watchdog; + EXPECT_EQ(vault.registeredSingletonCount(), 2); + vault.registrationComplete(); + + EXPECT_NE(watchdog.get(), nullptr); + EXPECT_EQ(watchdog.get(), SingletonDirectUsage::get()); + EXPECT_NE(watchdog.get(), named_watchdog.get()); + EXPECT_EQ(watchdog->livingWatchdogCount(), 2); + EXPECT_EQ((*watchdog).livingWatchdogCount(), 2); + + vault.destroyInstances(); +} + +struct NamedUsageTag {}; +template +using SingletonNamedUsage = Singleton ; + +TEST(Singleton, NamedUsage) { + auto& vault = *SingletonVault::singleton(); + + EXPECT_EQ(vault.registeredSingletonCount(), 0); + + // Define two named Watchdog singletons and one unnamed singleton. + struct Watchdog1 {}; + struct Watchdog2 {}; + typedef detail::DefaultTag Watchdog3; + SingletonNamedUsage watchdog1_singleton; + EXPECT_EQ(vault.registeredSingletonCount(), 1); + SingletonNamedUsage watchdog2_singleton; + EXPECT_EQ(vault.registeredSingletonCount(), 2); + SingletonNamedUsage watchdog3_singleton; + EXPECT_EQ(vault.registeredSingletonCount(), 3); + + vault.registrationComplete(); + + // Verify our three singletons are distinct and non-nullptr. + Watchdog* s1 = SingletonNamedUsage::get(); + EXPECT_EQ(s1, watchdog1_singleton.get()); + Watchdog* s2 = SingletonNamedUsage::get(); + EXPECT_EQ(s2, watchdog2_singleton.get()); + EXPECT_NE(s1, s2); + Watchdog* s3 = SingletonNamedUsage::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::get(); + EXPECT_EQ(s4, watchdog3_singleton.get()); + + vault.destroyInstances(); +} + +struct NaughtyUsageTag {}; +template +using SingletonNaughtyUsage = Singleton ; +struct NaughtyUsageTag2 {}; +template +using SingletonNaughtyUsage2 = Singleton ; + +// Some pathological cases such as getting unregistered singletons, +// double registration, etc. +TEST(Singleton, NaughtyUsage) { + auto& vault = *SingletonVault::singleton(); + + vault.registrationComplete(); + + // Unregistered. + EXPECT_THROW(Singleton::get(), std::out_of_range); + EXPECT_THROW(SingletonNaughtyUsage::get(), std::out_of_range); + + vault.destroyInstances(); + + auto& vault2 = *SingletonVault::singleton(); + + EXPECT_THROW(SingletonNaughtyUsage2::get(), std::logic_error); + SingletonNaughtyUsage2 watchdog_singleton; + // double registration + EXPECT_THROW([]() { + SingletonNaughtyUsage2 watchdog_singleton; + }(), + std::logic_error); + vault2.destroyInstances(); + // double registration after destroy + EXPECT_THROW([]() { + SingletonNaughtyUsage2 watchdog_singleton; + }(), + std::logic_error); +} + +struct SharedPtrUsageTag {}; +template +using SingletonSharedPtrUsage = Singleton ; + +TEST(Singleton, SharedPtrUsage) { + struct WatchdogHolder { + ~WatchdogHolder() { + if (watchdog) { + LOG(ERROR) << "The following log message with stack trace is expected"; + } + } + + std::shared_ptr watchdog; + }; + + auto& vault = *SingletonVault::singleton(); + + EXPECT_EQ(vault.registeredSingletonCount(), 0); + SingletonSharedPtrUsage watchdog_singleton; + EXPECT_EQ(vault.registeredSingletonCount(), 1); + + SingletonSharedPtrUsage child_watchdog_singleton; + EXPECT_EQ(vault.registeredSingletonCount(), 2); + + struct ATag {}; + SingletonSharedPtrUsage named_watchdog_singleton; + + SingletonSharedPtrUsage 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::get(); + EXPECT_NE(s1, nullptr); + + Watchdog* s2 = SingletonSharedPtrUsage::get(); + EXPECT_NE(s2, nullptr); + + EXPECT_EQ(s1, s2); + + auto weak_s1 = SingletonSharedPtrUsage::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::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::get_weak(); + EXPECT_FALSE(empty_s1.lock()); + + vault.reenableInstances(); + + // Singleton should be re-created only after reenableInstances() was called. + Watchdog* new_s1 = SingletonSharedPtrUsage::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::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 +using SingletonNeedy = Singleton ; + +struct NeededSingleton {}; +struct NeedySingleton { + NeedySingleton() { + auto unused = SingletonNeedy::get(); + EXPECT_NE(unused, nullptr); + } +}; + +// Ensure circular dependencies fail -- a singleton that needs itself, whoops. +struct SelfNeedyTag {}; +template +using SingletonSelfNeedy = Singleton ; + +struct SelfNeedySingleton { + SelfNeedySingleton() { + auto unused = SingletonSelfNeedy::get(); + EXPECT_NE(unused, nullptr); + } +}; + +TEST(Singleton, SingletonDependencies) { + SingletonNeedy needed_singleton; + SingletonNeedy needy_singleton; + auto& needy_vault = *SingletonVault::singleton(); + + needy_vault.registrationComplete(); + + EXPECT_EQ(needy_vault.registeredSingletonCount(), 2); + EXPECT_EQ(needy_vault.livingSingletonCount(), 0); + + auto needy = SingletonNeedy::get(); + EXPECT_EQ(needy_vault.livingSingletonCount(), 2); + + SingletonSelfNeedy self_needy_singleton; + auto& self_needy_vault = *SingletonVault::singleton(); + + self_needy_vault.registrationComplete(); + EXPECT_THROW([]() { + SingletonSelfNeedy::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 +using SingletonConcurrency = Singleton ; + +TEST(Singleton, SingletonConcurrency) { + auto& vault = *SingletonVault::singleton(); + SingletonConcurrency slowpoke_singleton; + vault.registrationComplete(); + + std::mutex gatekeeper; + gatekeeper.lock(); + auto func = [&gatekeeper]() { + gatekeeper.lock(); + gatekeeper.unlock(); + auto unused = SingletonConcurrency::get(); + }; + + EXPECT_EQ(vault.livingSingletonCount(), 0); + std::vector 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 +using SingletonConcurrencyStress = Singleton ; + +TEST(Singleton, SingletonConcurrencyStress) { + auto& vault = *SingletonVault::singleton(); + SingletonConcurrencyStress slowpoke_singleton; + + std::vector 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 +using SingletonMock = Singleton ; + +// Verify that existing Singleton's can be overridden +// using the make_mock functionality. +TEST(Singleton, MockTest) { + auto& vault = *SingletonVault::singleton(); + + SingletonMock 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::get()->serial_number; + + // Override existing mock using make_mock. + SingletonMock::make_mock(); + + EXPECT_EQ(vault.registeredSingletonCount(), 1); + int serial_count_mock = SingletonMock::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 +using SingletonBenchmark = Singleton ; + +struct GetTag{}; +struct GetWeakTag{}; + +SingletonBenchmark benchmark_singleton_get; +SingletonBenchmark benchmark_singleton_get_weak; + +BENCHMARK_RELATIVE(FollySingleton, n) { + for (size_t i = 0; i < n; ++i) { + doNotOptimizeAway(SingletonBenchmark::get()); + } +} + +BENCHMARK_RELATIVE(FollySingletonWeak, n) { + for (size_t i = 0; i < n; ++i) { + SingletonBenchmark::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; +} diff --git a/folly/test/SingletonVaultCTest.cpp b/folly/test/SingletonVaultCTest.cpp new file mode 100644 index 00000000..1182278e --- /dev/null +++ b/folly/test/SingletonVaultCTest.cpp @@ -0,0 +1,55 @@ +/* + * 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 +#include + +#include + +#include + +__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 +using SingletonTest = folly::Singleton ; + +TEST(SingletonVault, singletonsAreCreatedAndDestroyed) { + auto vault = folly::SingletonVault::singleton(); + SingletonTest counter_singleton; + SingletonVault_registrationComplete((SingletonVault_t*) vault); + InstanceCounter *counter = SingletonTest::get(); + EXPECT_EQ(instance_counter_instances, 1); + SingletonVault_destroyInstances((SingletonVault_t*) vault); + EXPECT_EQ(instance_counter_instances, 0); +} diff --git a/folly/wangle/concurrent/GlobalExecutor.cpp b/folly/wangle/concurrent/GlobalExecutor.cpp index ac7b9bac..36a82155 100644 --- a/folly/wangle/concurrent/GlobalExecutor.cpp +++ b/folly/wangle/concurrent/GlobalExecutor.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include +#include #include #include #include