Commit 4833e0ef authored by Andrii Grynenko's avatar Andrii Grynenko Committed by Dave Watson

Use RWSpinLock for Singleton mutex

Summary: We only need exclusive lock when we add items to singletons_. Each SingletonEntry has its own mutex, so it's safe to rely on it for any modifications within individual entries.

Test Plan: Applied D1573880 and ran fbconfig -r servicerouter/client/cpp2 && fbmake runtests

Reviewed By: chip@fb.com

Subscribers: trunkagent, njormrod, hitesh, mshneer

FB internal diff: D1579877
parent 23d9f0ec
...@@ -23,29 +23,35 @@ namespace folly { ...@@ -23,29 +23,35 @@ namespace folly {
SingletonVault::~SingletonVault() { destroyInstances(); } SingletonVault::~SingletonVault() { destroyInstances(); }
void SingletonVault::destroyInstances() { void SingletonVault::destroyInstances() {
std::lock_guard<std::mutex> guard(mutex_); {
CHECK_GE(singletons_.size(), creation_order_.size()); RWSpinLock::ReadHolder rh(&mutex_);
for (auto type_iter = creation_order_.rbegin(); CHECK_GE(singletons_.size(), creation_order_.size());
type_iter != creation_order_.rend();
++type_iter) { for (auto type_iter = creation_order_.rbegin();
auto type = *type_iter; type_iter != creation_order_.rend();
auto it = singletons_.find(type); ++type_iter) {
CHECK(it != singletons_.end()); auto type = *type_iter;
auto& entry = it->second; auto it = singletons_.find(type);
std::lock_guard<std::mutex> entry_guard(entry->mutex); CHECK(it != singletons_.end());
if (entry->instance.use_count() > 1) { auto& entry = it->second;
LOG(ERROR) << "Singleton of type " << type.name() << " has a living " std::lock_guard<std::mutex> entry_guard(entry->mutex);
<< "reference at destroyInstances time; beware! Raw pointer " if (entry->instance.use_count() > 1) {
<< "is " << entry->instance.get() << " with use_count of " LOG(ERROR) << "Singleton of type " << type.name() << " has a living "
<< entry->instance.use_count(); << "reference at destroyInstances time; beware! Raw pointer "
<< "is " << entry->instance.get() << " with use_count of "
<< entry->instance.use_count();
}
entry->instance.reset();
entry->state = SingletonEntryState::Dead;
entry->state_condvar.notify_all();
} }
entry->instance.reset();
entry->state = SingletonEntryState::Dead;
entry->state_condvar.notify_all();
} }
creation_order_.clear(); {
RWSpinLock::WriteHolder wh(&mutex_);
creation_order_.clear();
}
} }
SingletonVault* SingletonVault::singleton() { SingletonVault* SingletonVault::singleton() {
......
...@@ -78,6 +78,7 @@ ...@@ -78,6 +78,7 @@
#pragma once #pragma once
#include <folly/Exception.h> #include <folly/Exception.h>
#include <folly/Hash.h> #include <folly/Hash.h>
#include <folly/RWSpinLock.h>
#include <vector> #include <vector>
#include <mutex> #include <mutex>
...@@ -176,14 +177,12 @@ class SingletonVault { ...@@ -176,14 +177,12 @@ class SingletonVault {
void registerSingleton(detail::TypeDescriptor type, void registerSingleton(detail::TypeDescriptor type,
CreateFunc create, CreateFunc create,
TeardownFunc teardown) { TeardownFunc teardown) {
std::lock_guard<std::mutex> guard(mutex_); RWSpinLock::WriteHolder wh(&mutex_);
stateCheck(SingletonVaultState::Registering); stateCheck(SingletonVaultState::Registering);
CHECK_THROW(singletons_.find(type) == singletons_.end(), std::logic_error); CHECK_THROW(singletons_.find(type) == singletons_.end(), std::logic_error);
auto& entry = singletons_[type]; auto& entry = singletons_[type];
if (!entry) { entry.reset(new SingletonEntry);
entry.reset(new SingletonEntry);
}
std::lock_guard<std::mutex> entry_guard(entry->mutex); std::lock_guard<std::mutex> entry_guard(entry->mutex);
CHECK(entry->instance == nullptr); CHECK(entry->instance == nullptr);
...@@ -197,7 +196,8 @@ class SingletonVault { ...@@ -197,7 +196,8 @@ class SingletonVault {
// Mark registration is complete; no more singletons can be // Mark registration is complete; no more singletons can be
// registered at this point. // registered at this point.
void registrationComplete() { void registrationComplete() {
std::lock_guard<std::mutex> guard(mutex_); RWSpinLock::WriteHolder wh(&mutex_);
stateCheck(SingletonVaultState::Registering); stateCheck(SingletonVaultState::Registering);
state_ = SingletonVaultState::Running; state_ = SingletonVaultState::Running;
} }
...@@ -208,8 +208,7 @@ class SingletonVault { ...@@ -208,8 +208,7 @@ class SingletonVault {
// Retrieve a singleton from the vault, creating it if necessary. // Retrieve a singleton from the vault, creating it if necessary.
std::shared_ptr<void> get_shared(detail::TypeDescriptor type) { std::shared_ptr<void> get_shared(detail::TypeDescriptor type) {
std::unique_lock<std::mutex> lock(mutex_); auto entry = get_entry_create(type);
auto entry = get_entry(type, &lock);
return entry->instance; return entry->instance;
} }
...@@ -218,21 +217,23 @@ class SingletonVault { ...@@ -218,21 +217,23 @@ class SingletonVault {
// responsibility to be sane with this, but it is preferable to use // responsibility to be sane with this, but it is preferable to use
// the weak_ptr interface for true safety. // the weak_ptr interface for true safety.
void* get_ptr(detail::TypeDescriptor type) { void* get_ptr(detail::TypeDescriptor type) {
std::unique_lock<std::mutex> lock(mutex_); auto entry = get_entry_create(type);
auto entry = get_entry(type, &lock);
return entry->instance_ptr; 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 {
std::lock_guard<std::mutex> guard(mutex_); RWSpinLock::ReadHolder rh(&mutex_);
return singletons_.size(); return singletons_.size();
} }
size_t livingSingletonCount() const { size_t livingSingletonCount() const {
std::lock_guard<std::mutex> guard(mutex_); RWSpinLock::ReadHolder rh(&mutex_);
size_t ret = 0; size_t ret = 0;
for (const auto& p : singletons_) { for (const auto& p : singletons_) {
std::lock_guard<std::mutex> entry_guard(p.second->mutex);
if (p.second->instance) { if (p.second->instance) {
++ret; ++ret;
} }
...@@ -295,17 +296,15 @@ class SingletonVault { ...@@ -295,17 +296,15 @@ class SingletonVault {
SingletonEntry(SingletonEntry&&) = delete; SingletonEntry(SingletonEntry&&) = delete;
}; };
// Get a pointer to the living SingletonEntry for the specified SingletonEntry* get_entry(detail::TypeDescriptor type) {
// type. The singleton is created as part of this function, if RWSpinLock::ReadHolder rh(&mutex_);
// necessary.
SingletonEntry* get_entry(detail::TypeDescriptor type,
std::unique_lock<std::mutex>* lock) {
// mutex must be held when calling this function // mutex must be held when calling this function
stateCheck( stateCheck(
SingletonVaultState::Running, SingletonVaultState::Running,
"Attempt to load a singleton before " "Attempt to load a singleton before "
"SingletonVault::registrationComplete was called (hint: you probably " "SingletonVault::registrationComplete was called (hint: you probably "
"didn't call initFacebook)"); "didn't call initFacebook)");
auto it = singletons_.find(type); auto it = singletons_.find(type);
if (it == singletons_.end()) { if (it == singletons_.end()) {
...@@ -313,7 +312,15 @@ class SingletonVault { ...@@ -313,7 +312,15 @@ class SingletonVault {
type.name()); type.name());
} }
auto entry = it->second.get(); 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.
SingletonEntry* get_entry_create(detail::TypeDescriptor type) {
auto entry = get_entry(type);
std::unique_lock<std::mutex> entry_lock(entry->mutex); std::unique_lock<std::mutex> entry_lock(entry->mutex);
if (entry->state == SingletonEntryState::BeingBorn) { if (entry->state == SingletonEntryState::BeingBorn) {
...@@ -324,14 +331,9 @@ class SingletonVault { ...@@ -324,14 +331,9 @@ class SingletonVault {
type.name()); type.name());
} }
// Otherwise, another thread is constructing the singleton;
// let's wait on a condvar to see it complete. We release and
// reaquire lock while waiting on the entry to resolve its state.
lock->unlock();
entry->state_condvar.wait(entry_lock, [&entry]() { entry->state_condvar.wait(entry_lock, [&entry]() {
return entry->state != SingletonEntryState::BeingBorn; return entry->state != SingletonEntryState::BeingBorn;
}); });
lock->lock();
} }
if (entry->instance == nullptr) { if (entry->instance == nullptr) {
...@@ -340,10 +342,8 @@ class SingletonVault { ...@@ -340,10 +342,8 @@ class SingletonVault {
entry->creating_thread = std::this_thread::get_id(); entry->creating_thread = std::this_thread::get_id();
entry_lock.unlock(); entry_lock.unlock();
lock->unlock();
// Can't use make_shared -- no support for a custom deleter, sadly. // Can't use make_shared -- no support for a custom deleter, sadly.
auto instance = std::shared_ptr<void>(entry->create(), entry->teardown); auto instance = std::shared_ptr<void>(entry->create(), entry->teardown);
lock->lock();
entry_lock.lock(); entry_lock.lock();
CHECK(entry->state == SingletonEntryState::BeingBorn); CHECK(entry->state == SingletonEntryState::BeingBorn);
...@@ -352,13 +352,17 @@ class SingletonVault { ...@@ -352,13 +352,17 @@ class SingletonVault {
entry->state = SingletonEntryState::Living; entry->state = SingletonEntryState::Living;
entry->state_condvar.notify_all(); entry->state_condvar.notify_all();
creation_order_.push_back(type); {
RWSpinLock::WriteHolder wh(&mutex_);
creation_order_.push_back(type);
}
} }
CHECK(entry->state == SingletonEntryState::Living); CHECK(entry->state == SingletonEntryState::Living);
return entry; return entry;
} }
mutable std::mutex mutex_; mutable folly::RWSpinLock mutex_;
typedef std::unique_ptr<SingletonEntry> SingletonEntryPtr; typedef std::unique_ptr<SingletonEntry> SingletonEntryPtr;
std::unordered_map<detail::TypeDescriptor, std::unordered_map<detail::TypeDescriptor,
SingletonEntryPtr, SingletonEntryPtr,
......
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