Commit 67bc1788 authored by Andrii Grynenko's avatar Andrii Grynenko Committed by JoelMarcey

Remove locking when getting ptr to Singleton

Summary: This removes one layer on locking on the fast path, when ptr to singleton object is read from SingletonEntry.

Test Plan:
unit test

Before:

============================================================================
folly/experimental/test/SingletonTest.cpp       relative  time/iter  iters/s
============================================================================
NormalSingleton                                            335.26ps    2.98G
MeyersSingleton                                   99.50%   336.96ps    2.97G
FollySingleton                                     0.28%   120.64ns    8.29M
============================================================================

After:

============================================================================
folly/experimental/test/SingletonTest.cpp       relative  time/iter  iters/s
============================================================================
NormalSingleton                                            336.76ps    2.97G
MeyersSingleton                                   99.91%   337.07ps    2.97G
FollySingleton                                     0.36%    92.69ns   10.79M
============================================================================

Reviewed By: alikhtarov@fb.com

Subscribers: trunkagent, folly-diffs@

FB internal diff: D1727604

Signature: t1:1727604:1418701171:1728b516191a8ec4439e981d78634370b4bcf7a1
parent 3490aa71
......@@ -40,8 +40,7 @@ void SingletonVault::destroyInstances() {
for (auto type_iter = creation_order_.rbegin();
type_iter != creation_order_.rend();
++type_iter) {
auto type = *type_iter;
destroyInstance(type);
destroyInstance(singletons_.find(*type_iter));
}
}
......@@ -55,22 +54,17 @@ void SingletonVault::destroyInstances() {
* a read lock on mutex_.
* @param typeDescriptor - the type key for the removed singleton.
*/
void SingletonVault::destroyInstance(
const detail::TypeDescriptor& typeDescriptor) {
auto it = singletons_.find(typeDescriptor);
CHECK(it != singletons_.end());
auto& entry = it->second;
std::lock_guard<std::mutex> entry_guard(entry->mutex);
if (entry->instance.use_count() > 1) {
LOG(ERROR) << "Singleton of typeDescriptor "
<< typeDescriptor.name() << " has a living "
void SingletonVault::destroyInstance(SingletonMap::iterator entry_it) {
const auto& type = entry_it->first;
auto& entry = *(entry_it->second);
if (entry.instance.use_count() > 1) {
LOG(ERROR) << "Singleton of type " << type.name() << " has a living "
<< "reference at destroyInstances time; beware! Raw pointer "
<< "is " << entry->instance.get() << " with use_count of "
<< entry->instance.use_count();
<< "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.state = SingletonEntryState::Dead;
entry.instance.reset();
}
void SingletonVault::reenableInstances() {
......
This diff is collapsed.
......@@ -315,9 +315,9 @@ TEST(Singleton, SingletonDependencies) {
// A test to ensure multiple threads contending on singleton creation
// properly wait for creation rather than thinking it is a circular
// dependency.
class Slowpoke {
class Slowpoke : public Watchdog {
public:
Slowpoke() { std::this_thread::sleep_for(std::chrono::seconds(1)); }
Slowpoke() { std::this_thread::sleep_for(std::chrono::milliseconds(10)); }
};
TEST(Singleton, SingletonConcurrency) {
......@@ -348,6 +348,31 @@ TEST(Singleton, SingletonConcurrency) {
EXPECT_EQ(vault.livingSingletonCount(), 1);
}
TEST(Singleton, SingletonConcurrencyStress) {
SingletonVault vault;
Singleton<Slowpoke> slowpoke_singleton(nullptr, nullptr, &vault);
std::vector<std::thread> ts;
for (size_t i = 0; i < 100; ++i) {
ts.emplace_back([&]() {
slowpoke_singleton.get_weak(&vault).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."
......
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