Commit 8f7c257f authored by Andrii Grynenko's avatar Andrii Grynenko Committed by Sara Golemon

Making each SingletonEntry a singleton

Summary:
Most of the singleton construction logic is moved to SingletonEntry, and each SingletonEntry is now also a singleton.
SingletonVault becomes only responsible for keeping singleton construction order (and potentially dependencies) and destoying them in correct order.
This also significantly improves perf of get() / get_weak() (not-fast)

This diff is based on D1823663.

Test Plan:
unit test

============================================================================
folly/experimental/test/SingletonTest.cpp       relative  time/iter  iters/s
============================================================================
NormalSingleton                                            333.35ps    3.00G
MeyersSingleton                                   99.99%   333.39ps    3.00G
FollySingletonSlow                                49.99%   666.84ps    1.50G
FollySingletonFast                                95.90%   347.61ps    2.88G
FollySingletonFastWeak                             2.22%    15.00ns   66.66M
============================================================================

Reviewed By: chip@fb.com

Subscribers: trunkagent, folly-diffs@, yfeldblum

FB internal diff: D1827390

Signature: t1:1827390:1423268514:da322d1dcaba54905d478b253f26dd76f890fb4e
parent a11dc9b7
...@@ -73,6 +73,7 @@ nobase_follyinclude_HEADERS = \ ...@@ -73,6 +73,7 @@ nobase_follyinclude_HEADERS = \
experimental/EventCount.h \ experimental/EventCount.h \
experimental/io/FsUtil.h \ experimental/io/FsUtil.h \
experimental/Singleton.h \ experimental/Singleton.h \
experimental/Singleton-inl.h \
experimental/TestUtil.h \ experimental/TestUtil.h \
FBString.h \ FBString.h \
FBVector.h \ FBVector.h \
......
/*
* Copyright 2015 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace folly {
namespace detail {
template <typename T>
template <typename Tag, typename VaultTag>
SingletonHolder<T>& SingletonHolder<T>::singleton() {
static auto entry = new SingletonHolder<T>(
{typeid(T), typeid(Tag)},
*SingletonVault::singleton<VaultTag>());
return *entry;
}
template <typename T>
void SingletonHolder<T>::registerSingleton(CreateFunc c, TeardownFunc t) {
std::lock_guard<std::mutex> entry_lock(mutex_);
if (state_ != SingletonHolderState::NotRegistered) {
throw std::logic_error("Double registration");
}
create_ = std::move(c);
teardown_ = std::move(t);
state_ = SingletonHolderState::Dead;
}
template <typename T>
void SingletonHolder<T>::registerSingletonMock(CreateFunc c, TeardownFunc t) {
if (state_ == SingletonHolderState::NotRegistered) {
throw std::logic_error("Registering mock before singleton was registered");
}
destroyInstance();
std::lock_guard<std::mutex> entry_lock(mutex_);
create_ = std::move(c);
teardown_ = std::move(t);
}
template <typename T>
T* SingletonHolder<T>::get() {
if (LIKELY(state_ == SingletonHolderState::Living)) {
return instance_ptr_;
}
createInstance();
if (instance_weak_.expired()) {
throw std::runtime_error(
"Raw pointer to a singleton requested after its destruction.");
}
return instance_ptr_;
}
template <typename T>
std::weak_ptr<T> SingletonHolder<T>::get_weak() {
if (UNLIKELY(state_ != SingletonHolderState::Living)) {
createInstance();
}
return instance_weak_;
}
template <typename T>
TypeDescriptor SingletonHolder<T>::type() {
return type_;
}
template <typename T>
bool SingletonHolder<T>::hasLiveInstance() {
return state_ == SingletonHolderState::Living;
}
template <typename T>
void SingletonHolder<T>::destroyInstance() {
state_ = SingletonHolderState::Dead;
instance_.reset();
auto wait_result = destroy_baton_->timed_wait(
std::chrono::steady_clock::now() + kDestroyWaitTime);
if (!wait_result) {
LOG(ERROR) << "Singleton of type " << type_.name() << " has a "
<< "living reference at destroyInstances time; beware! Raw "
<< "pointer is " << instance_ptr_ << ". It is very likely "
<< "that some other singleton is holding a shared_ptr to it. "
<< "Make sure dependencies between these singletons are "
<< "properly defined.";
}
}
template <typename T>
SingletonHolder<T>::SingletonHolder(TypeDescriptor type__,
SingletonVault& vault) :
type_(type__), vault_(vault) {
}
template <typename T>
void SingletonHolder<T>::createInstance() {
// There's no synchronization here, so we may not see the current value
// for creating_thread if it was set by other thread, but we only care about
// it if it was set by current thread anyways.
if (creating_thread_ == std::this_thread::get_id()) {
throw std::out_of_range(std::string("circular singleton dependency: ") +
type_.name());
}
std::lock_guard<std::mutex> entry_lock(mutex_);
if (state_ == SingletonHolderState::Living) {
return;
}
if (state_ == SingletonHolderState::NotRegistered) {
throw std::out_of_range("Creating instance for unregistered singleton");
}
if (state_ == SingletonHolderState::Living) {
return;
}
creating_thread_ = std::this_thread::get_id();
RWSpinLock::ReadHolder rh(&vault_.stateMutex_);
if (vault_.state_ == SingletonVault::SingletonVaultState::Quiescing) {
creating_thread_ = std::thread::id();
return;
}
auto destroy_baton = std::make_shared<folly::Baton<>>();
auto teardown = teardown_;
// Can't use make_shared -- no support for a custom deleter, sadly.
instance_ = std::shared_ptr<T>(
create_(),
[destroy_baton, teardown](T* instance_ptr) mutable {
teardown(instance_ptr);
destroy_baton->post();
});
// 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);
// 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_);
}
}
}
}
...@@ -20,9 +20,9 @@ ...@@ -20,9 +20,9 @@
namespace folly { namespace folly {
namespace { namespace detail {
static constexpr std::chrono::seconds kDestroyWaitTime{5}; constexpr std::chrono::seconds SingletonHolderBase::kDestroyWaitTime;
} }
...@@ -46,7 +46,7 @@ void SingletonVault::destroyInstances() { ...@@ -46,7 +46,7 @@ void SingletonVault::destroyInstances() {
for (auto type_iter = creation_order_.rbegin(); for (auto type_iter = creation_order_.rbegin();
type_iter != creation_order_.rend(); type_iter != creation_order_.rend();
++type_iter) { ++type_iter) {
destroyInstance(singletons_.find(*type_iter)); singletons_[*type_iter]->destroyInstance();
} }
} }
...@@ -56,28 +56,6 @@ void SingletonVault::destroyInstances() { ...@@ -56,28 +56,6 @@ void SingletonVault::destroyInstances() {
} }
} }
/* Destroy and clean-up one singleton. Must be invoked while holding
* a read lock on mutex_.
* @param typeDescriptor - the type key for the removed singleton.
*/
void SingletonVault::destroyInstance(SingletonMap::iterator entry_it) {
const auto& type = entry_it->first;
auto& entry = *(entry_it->second);
entry.state = detail::SingletonEntryState::Dead;
entry.instance.reset();
auto wait_result = entry.destroy_baton->timed_wait(
std::chrono::steady_clock::now() + kDestroyWaitTime);
if (!wait_result) {
LOG(ERROR) << "Singleton of type " << type.prettyName() << " has a living "
<< "reference at destroyInstances time; beware! Raw pointer "
<< "is " << entry.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.";
}
}
void SingletonVault::reenableInstances() { void SingletonVault::reenableInstances() {
RWSpinLock::WriteHolder state_wh(&stateMutex_); RWSpinLock::WriteHolder state_wh(&stateMutex_);
......
...@@ -136,6 +136,8 @@ namespace folly { ...@@ -136,6 +136,8 @@ namespace folly {
// ensure registrationComplete() is called near the top of your main() // ensure registrationComplete() is called near the top of your main()
// function, otherwise no singletons can be instantiated. // function, otherwise no singletons can be instantiated.
class SingletonVault;
namespace detail { namespace detail {
struct DefaultTag {}; struct DefaultTag {};
...@@ -163,7 +165,7 @@ class TypeDescriptor { ...@@ -163,7 +165,7 @@ class TypeDescriptor {
return *this; return *this;
} }
std::string prettyName() const { std::string name() const {
auto ret = demangle(ti_.name()); auto ret = demangle(ti_.name());
if (tag_ti_ != std::type_index(typeid(DefaultTag))) { if (tag_ti_ != std::type_index(typeid(DefaultTag))) {
ret += "/"; ret += "/";
...@@ -190,51 +192,86 @@ class TypeDescriptorHasher { ...@@ -190,51 +192,86 @@ class TypeDescriptorHasher {
} }
}; };
enum class SingletonEntryState { // This interface is used by SingletonVault to interact with SingletonHolders.
Dead, // Having a non-template interface allows SingletonVault to keep a list of all
Living, // 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, // An actual instance of a singleton, tracking the instance itself,
// its state as described above, and the create and teardown // its state as described above, and the create and teardown
// functions. // functions.
struct SingletonEntry { template <typename T>
typedef std::function<void(void*)> TeardownFunc; struct SingletonHolder : public SingletonHolderBase {
typedef std::function<void*(void)> CreateFunc; public:
typedef std::function<void(T*)> TeardownFunc;
typedef std::function<T*(void)> CreateFunc;
template <typename Tag, typename VaultTag>
inline static SingletonHolder<T>& singleton();
inline T* get();
inline std::weak_ptr<T> get_weak();
void registerSingleton(CreateFunc c, TeardownFunc t);
void registerSingletonMock(CreateFunc c, TeardownFunc t);
virtual TypeDescriptor type();
virtual bool hasLiveInstance();
virtual void destroyInstance();
private:
SingletonHolder(TypeDescriptor type, SingletonVault& vault);
void createInstance();
enum class SingletonHolderState {
NotRegistered,
Dead,
Living,
};
SingletonEntry(CreateFunc c, TeardownFunc t) : TypeDescriptor type_;
create(std::move(c)), teardown(std::move(t)) {} SingletonVault& vault_;
// mutex protects the entire entry during construction/destruction // mutex protects the entire entry during construction/destruction
std::mutex mutex; std::mutex mutex_;
// State of the singleton entry. If state is Living, instance_ptr and // State of the singleton entry. If state is Living, instance_ptr and
// instance_weak can be safely accessed w/o synchronization. // instance_weak can be safely accessed w/o synchronization.
std::atomic<SingletonEntryState> state{SingletonEntryState::Dead}; std::atomic<SingletonHolderState> state_{SingletonHolderState::NotRegistered};
// the thread creating the singleton (only valid while creating an object) // the thread creating the singleton (only valid while creating an object)
std::thread::id creating_thread; std::thread::id creating_thread_;
// The singleton itself and related functions. // The singleton itself and related functions.
// holds a shared_ptr to singleton instance, set when state is changed from // 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. // Dead to Living. Reset when state is changed from Living to Dead.
std::shared_ptr<void> instance; std::shared_ptr<T> instance_;
// weak_ptr to the singleton instance, set when state is changed from Dead // 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 // 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 // safe to read it from different threads w/o synchronization if we know
// that state is set to Living // that state is set to Living
std::weak_ptr<void> instance_weak; std::weak_ptr<T> instance_weak_;
// Time we wait on destroy_baton after releasing Singleton shared_ptr. // Time we wait on destroy_baton after releasing Singleton shared_ptr.
std::shared_ptr<folly::Baton<>> destroy_baton; std::shared_ptr<folly::Baton<>> destroy_baton_;
void* instance_ptr = nullptr; T* instance_ptr_ = nullptr;
CreateFunc create = nullptr; CreateFunc create_ = nullptr;
TeardownFunc teardown = nullptr; TeardownFunc teardown_ = nullptr;
SingletonEntry(const SingletonEntry&) = delete; SingletonHolder(const SingletonHolder&) = delete;
SingletonEntry& operator=(const SingletonEntry&) = delete; SingletonHolder& operator=(const SingletonHolder&) = delete;
SingletonEntry& operator=(SingletonEntry&&) = delete; SingletonHolder& operator=(SingletonHolder&&) = delete;
SingletonEntry(SingletonEntry&&) = delete; SingletonHolder(SingletonHolder&&) = delete;
}; };
} }
...@@ -255,9 +292,7 @@ class SingletonVault { ...@@ -255,9 +292,7 @@ class SingletonVault {
// registration is not complete. If validations succeeds, // registration is not complete. If validations succeeds,
// register a singleton of a given type with the create and teardown // register a singleton of a given type with the create and teardown
// functions. // functions.
detail::SingletonEntry& registerSingleton(detail::TypeDescriptor type, void registerSingleton(detail::SingletonHolderBase* entry) {
CreateFunc create,
TeardownFunc teardown) {
RWSpinLock::ReadHolder rh(&stateMutex_); RWSpinLock::ReadHolder rh(&stateMutex_);
stateCheck(SingletonVaultState::Running); stateCheck(SingletonVaultState::Running);
...@@ -268,64 +303,11 @@ class SingletonVault { ...@@ -268,64 +303,11 @@ class SingletonVault {
} }
RWSpinLock::ReadHolder rhMutex(&mutex_); RWSpinLock::ReadHolder rhMutex(&mutex_);
CHECK_THROW(singletons_.find(type) == singletons_.end(), std::logic_error); CHECK_THROW(singletons_.find(entry->type()) == singletons_.end(),
std::logic_error);
return registerSingletonImpl(type, create, teardown);
}
// Register a singleton of a given type with the create and teardown
// functions. Must hold reader locks on stateMutex_ and mutex_
// when invoking this function.
detail::SingletonEntry& registerSingletonImpl(detail::TypeDescriptor type,
CreateFunc create,
TeardownFunc teardown) {
RWSpinLock::UpgradedHolder wh(&mutex_); RWSpinLock::UpgradedHolder wh(&mutex_);
singletons_[entry->type()] = entry;
singletons_[type] =
folly::make_unique<detail::SingletonEntry>(std::move(create),
std::move(teardown));
return *singletons_[type];
}
/* Register a mock singleton used for testing of singletons which
* depend on other private singletons which cannot be otherwise injected.
*/
void registerMockSingleton(detail::TypeDescriptor type,
CreateFunc create,
TeardownFunc teardown) {
RWSpinLock::ReadHolder rh(&stateMutex_);
RWSpinLock::ReadHolder rhMutex(&mutex_);
auto entry_it = singletons_.find(type);
// Mock singleton registration, we allow existing entry to be overridden.
if (entry_it == singletons_.end()) {
throw std::logic_error(
"Registering mock before the singleton was registered");
}
{
auto& entry = *(entry_it->second);
// Destroy existing singleton.
std::lock_guard<std::mutex> entry_lg(entry.mutex);
destroyInstance(entry_it);
entry.create = create;
entry.teardown = teardown;
}
// Upgrade to write lock.
RWSpinLock::UpgradedHolder whMutex(&mutex_);
// Remove singleton from creation order and singletons_.
// This happens only in test code and not frequently.
// Performance is not a concern here.
auto creation_order_it = std::find(
creation_order_.begin(),
creation_order_.end(),
type);
if (creation_order_it != creation_order_.end()) {
creation_order_.erase(creation_order_it);
}
} }
// Mark registration is complete; no more singletons can be // Mark registration is complete; no more singletons can be
...@@ -339,9 +321,8 @@ class SingletonVault { ...@@ -339,9 +321,8 @@ class SingletonVault {
stateCheck(SingletonVaultState::Running); stateCheck(SingletonVaultState::Running);
if (type_ == Type::Strict) { if (type_ == Type::Strict) {
for (const auto& id_singleton_entry: singletons_) { for (const auto& p: singletons_) {
const auto& singleton_entry = *id_singleton_entry.second; if (p.second->hasLiveInstance()) {
if (singleton_entry.state != detail::SingletonEntryState::Dead) {
throw std::runtime_error( throw std::runtime_error(
"Singleton created before registration was complete."); "Singleton created before registration was complete.");
} }
...@@ -358,25 +339,6 @@ class SingletonVault { ...@@ -358,25 +339,6 @@ class SingletonVault {
// Enable re-creating singletons after destroyInstances() was called. // Enable re-creating singletons after destroyInstances() was called.
void reenableInstances(); void reenableInstances();
// Retrieve a singleton from the vault, creating it if necessary.
std::weak_ptr<void> get_weak(detail::TypeDescriptor type) {
auto entry = get_entry_create(type);
return entry->instance_weak;
}
// This function is inherently racy since we don't hold the
// shared_ptr that contains the Singleton. It is the caller's
// responsibility to be sane with this, but it is preferable to use
// the weak_ptr interface for true safety.
void* get_ptr(detail::TypeDescriptor type) {
auto entry = get_entry_create(type);
if (UNLIKELY(entry->instance_weak.expired())) {
throw std::runtime_error(
"Raw pointer to a singleton requested after its destruction.");
}
return entry->instance_ptr;
}
// For testing; how many registered and living singletons we have. // For testing; how many registered and living singletons we have.
size_t registeredSingletonCount() const { size_t registeredSingletonCount() const {
RWSpinLock::ReadHolder rh(&mutex_); RWSpinLock::ReadHolder rh(&mutex_);
...@@ -389,7 +351,7 @@ class SingletonVault { ...@@ -389,7 +351,7 @@ class SingletonVault {
size_t ret = 0; size_t ret = 0;
for (const auto& p : singletons_) { for (const auto& p : singletons_) {
if (p.second->state == detail::SingletonEntryState::Living) { if (p.second->hasLiveInstance()) {
++ret; ++ret;
} }
} }
...@@ -412,6 +374,9 @@ class SingletonVault { ...@@ -412,6 +374,9 @@ class SingletonVault {
} }
private: private:
template <typename T>
friend class detail::SingletonHolder;
// The two stages of life for a vault, as mentioned in the class comment. // The two stages of life for a vault, as mentioned in the class comment.
enum class SingletonVaultState { enum class SingletonVaultState {
Running, Running,
...@@ -441,95 +406,10 @@ class SingletonVault { ...@@ -441,95 +406,10 @@ class SingletonVault {
// any of the singletons managed by folly::Singleton was requested. // any of the singletons managed by folly::Singleton was requested.
static void scheduleDestroyInstances(); static void scheduleDestroyInstances();
detail::SingletonEntry* get_entry(detail::TypeDescriptor type) {
RWSpinLock::ReadHolder rh(&mutex_);
auto it = singletons_.find(type);
if (it == singletons_.end()) {
throw std::out_of_range(std::string("non-existent singleton: ") +
type.prettyName());
}
return it->second.get();
}
// Get a pointer to the living SingletonEntry for the specified
// type. The singleton is created as part of this function, if
// necessary.
detail::SingletonEntry* get_entry_create(detail::TypeDescriptor type) {
auto entry = get_entry(type);
if (LIKELY(entry->state == detail::SingletonEntryState::Living)) {
return entry;
}
// 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 (entry->creating_thread == std::this_thread::get_id()) {
throw std::out_of_range(std::string("circular singleton dependency: ") +
type.prettyName());
}
std::lock_guard<std::mutex> entry_lock(entry->mutex);
if (entry->state == detail::SingletonEntryState::Living) {
return entry;
}
entry->creating_thread = std::this_thread::get_id();
RWSpinLock::ReadHolder rh(&stateMutex_);
if (state_ == SingletonVaultState::Quiescing) {
entry->creating_thread = std::thread::id();
return entry;
}
auto destroy_baton = std::make_shared<folly::Baton<>>();
auto teardown = entry->teardown;
// Can't use make_shared -- no support for a custom deleter, sadly.
auto instance = std::shared_ptr<void>(
entry->create(),
[destroy_baton, teardown](void* instance_ptr) mutable {
teardown(instance_ptr);
destroy_baton->post();
});
// 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
scheduleDestroyInstances();
entry->instance = instance;
entry->instance_weak = instance;
entry->instance_ptr = instance.get();
entry->creating_thread = std::thread::id();
entry->destroy_baton = std::move(destroy_baton);
// This has to be the last step, because once state is Living other threads
// may access instance and instance_weak w/o synchronization.
entry->state.store(detail::SingletonEntryState::Living);
{
RWSpinLock::WriteHolder wh(&mutex_);
creation_order_.push_back(type);
}
return entry;
}
typedef std::unique_ptr<detail::SingletonEntry> SingletonEntryPtr;
typedef std::unordered_map<detail::TypeDescriptor, typedef std::unordered_map<detail::TypeDescriptor,
SingletonEntryPtr, detail::SingletonHolderBase*,
detail::TypeDescriptorHasher> SingletonMap; detail::TypeDescriptorHasher> SingletonMap;
/* Destroy and clean-up one singleton. Must be invoked while holding
* a read lock on mutex_.
* @param typeDescriptor - the type key for the removed singleton.
*/
void destroyInstance(SingletonMap::iterator entry_it);
mutable folly::RWSpinLock mutex_; mutable folly::RWSpinLock mutex_;
SingletonMap singletons_; SingletonMap singletons_;
std::vector<detail::TypeDescriptor> creation_order_; std::vector<detail::TypeDescriptor> creation_order_;
...@@ -556,18 +436,13 @@ class Singleton { ...@@ -556,18 +436,13 @@ class Singleton {
// get() repeatedly rather than saving the reference, and then not // get() repeatedly rather than saving the reference, and then not
// call get() during process shutdown. // call get() during process shutdown.
static T* get() { static T* get() {
return static_cast<T*>( return getEntry().get();
SingletonVault::singleton<VaultTag>()->get_ptr(typeDescriptor()));
} }
// Same as get, but should be preffered to it in the same compilation // Same as get, but should be preffered to it in the same compilation
// unit, where Singleton is registered. // unit, where Singleton is registered.
T* get_fast() { T* get_fast() {
if (LIKELY(entry_->state == detail::SingletonEntryState::Living)) { return entry_.get();
return reinterpret_cast<T*>(entry_->instance_ptr);
} else {
return get();
}
} }
// If, however, you do need to hold a reference to the specific // If, however, you do need to hold a reference to the specific
...@@ -575,32 +450,13 @@ class Singleton { ...@@ -575,32 +450,13 @@ class Singleton {
// possible but the inability to lock the weak pointer can be a // possible but the inability to lock the weak pointer can be a
// signal that the vault has been destroyed. // signal that the vault has been destroyed.
static std::weak_ptr<T> get_weak() { static std::weak_ptr<T> get_weak() {
auto weak_void_ptr = return getEntry().get_weak();
(SingletonVault::singleton<VaultTag>())->get_weak(typeDescriptor());
// This is ugly and inefficient, but there's no other way to do it, because
// there's no static_pointer_cast for weak_ptr.
auto shared_void_ptr = weak_void_ptr.lock();
if (!shared_void_ptr) {
return std::weak_ptr<T>();
}
return std::static_pointer_cast<T>(shared_void_ptr);
} }
// Same as get_weak, but should be preffered to it in the same compilation // Same as get_weak, but should be preffered to it in the same compilation
// unit, where Singleton is registered. // unit, where Singleton is registered.
std::weak_ptr<T> get_weak_fast() { std::weak_ptr<T> get_weak_fast() {
if (LIKELY(entry_->state == detail::SingletonEntryState::Living)) { return entry_.get_weak();
// This is ugly and inefficient, but there's no other way to do it,
// because there's no static_pointer_cast for weak_ptr.
auto shared_void_ptr = entry_->instance_weak.lock();
if (!shared_void_ptr) {
return std::weak_ptr<T>();
}
return std::static_pointer_cast<T>(shared_void_ptr);
} else {
return get_weak();
}
} }
// Allow the Singleton<t> instance to also retrieve the underlying // Allow the Singleton<t> instance to also retrieve the underlying
...@@ -615,16 +471,15 @@ class Singleton { ...@@ -615,16 +471,15 @@ class Singleton {
} }
explicit Singleton(Singleton::CreateFunc c, explicit Singleton(Singleton::CreateFunc c,
Singleton::TeardownFunc t = nullptr) { Singleton::TeardownFunc t = nullptr) : entry_(getEntry()) {
if (c == nullptr) { if (c == nullptr) {
throw std::logic_error( throw std::logic_error(
"nullptr_t should be passed if you want T to be default constructed"); "nullptr_t should be passed if you want T to be default constructed");
} }
auto vault = SingletonVault::singleton<VaultTag>(); auto vault = SingletonVault::singleton<VaultTag>();
entry_.registerSingleton(std::move(c), getTeardownFunc(std::move(t)));
entry_ = vault->registerSingleton(&entry_);
&(vault->registerSingleton(typeDescriptor(), c, getTeardownFunc(t)));
} }
/** /**
...@@ -649,37 +504,33 @@ class Singleton { ...@@ -649,37 +504,33 @@ class Singleton {
"nullptr_t should be passed if you want T to be default constructed"); "nullptr_t should be passed if you want T to be default constructed");
} }
auto vault = SingletonVault::singleton<VaultTag>(); auto& entry = getEntry();
vault->registerMockSingleton( entry.registerSingletonMock(c, getTeardownFunc(t));
typeDescriptor(),
c,
getTeardownFunc(t));
} }
private: private:
static detail::TypeDescriptor typeDescriptor() { inline static detail::SingletonHolder<T>& getEntry() {
return {typeid(T), typeid(Tag)}; return detail::SingletonHolder<T>::template singleton<Tag, VaultTag>();
} }
// Construct SingletonVault::TeardownFunc. // Construct TeardownFunc.
static SingletonVault::TeardownFunc getTeardownFunc( static typename detail::SingletonHolder<T>::TeardownFunc getTeardownFunc(
TeardownFunc t) { TeardownFunc t) {
SingletonVault::TeardownFunc teardown;
if (t == nullptr) { if (t == nullptr) {
teardown = [](void* v) { delete static_cast<T*>(v); }; return [](T* v) { delete v; };
} else { } else {
teardown = [t](void* v) { t(static_cast<T*>(v)); }; return t;
} }
return teardown;
} }
// This is pointing to SingletonEntry paired with this singleton object. This // This is pointing to SingletonHolder paired with this singleton object. This
// is never reset, so each SingletonEntry should never be destroyed. // is never reset, so each SingletonHolder should never be destroyed.
// We rely on the fact that Singleton destructor won't reset this pointer, so // We rely on the fact that Singleton destructor won't reset this pointer, so
// it can be "safely" used even after static Singleton object is destroyed. // it can be "safely" used even after static Singleton object is destroyed.
detail::SingletonEntry* entry_; detail::SingletonHolder<T>& entry_;
}; };
} }
#include <folly/experimental/Singleton-inl.h>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment