Commit 71c140ed authored by Andrii Grynenko's avatar Andrii Grynenko Committed by facebook-github-bot-4

Switch folly::Singleton to ReadMostlySharedPtr

Summary:
This also introduces a new try_get_fast() API which returns ReadMostlySharedPtr.
We should eventually migrate all users onto this API.

Reviewed By: elsteveogrande

Differential Revision: D2706745

fb-gh-sync-id: d558d705f431fc6147334c188fd0c6156644ba5c
parent 7e6064c6
...@@ -112,6 +112,16 @@ std::shared_ptr<T> SingletonHolder<T>::try_get() { ...@@ -112,6 +112,16 @@ std::shared_ptr<T> SingletonHolder<T>::try_get() {
return instance_weak_.lock(); return instance_weak_.lock();
} }
template <typename T>
folly::ReadMostlySharedPtr<T> SingletonHolder<T>::try_get_fast() {
if (UNLIKELY(state_.load(std::memory_order_acquire) !=
SingletonHolderState::Living)) {
createInstance();
}
return instance_weak_fast_.lock();
}
template <typename T> template <typename T>
TypeDescriptor SingletonHolder<T>::type() { TypeDescriptor SingletonHolder<T>::type() {
return type_; return type_;
...@@ -208,7 +218,7 @@ void SingletonHolder<T>::createInstance() { ...@@ -208,7 +218,7 @@ void SingletonHolder<T>::createInstance() {
auto type_name = type_.name(); auto type_name = type_.name();
// Can't use make_shared -- no support for a custom deleter, sadly. // Can't use make_shared -- no support for a custom deleter, sadly.
instance_ = std::shared_ptr<T>( std::shared_ptr<T> instance(
create_(), create_(),
[destroy_baton, print_destructor_stack_trace, teardown, type_name] [destroy_baton, print_destructor_stack_trace, teardown, type_name]
(T* instance_ptr) mutable { (T* instance_ptr) mutable {
...@@ -236,8 +246,11 @@ void SingletonHolder<T>::createInstance() { ...@@ -236,8 +246,11 @@ void SingletonHolder<T>::createInstance() {
// constructor // constructor
SingletonVault::scheduleDestroyInstances(); SingletonVault::scheduleDestroyInstances();
instance_weak_ = instance_; instance_weak_ = instance;
instance_ptr_ = instance_.get(); instance_ptr_ = instance.get();
instance_.reset(std::move(instance));
instance_weak_fast_ = instance_;
destroy_baton_ = std::move(destroy_baton); destroy_baton_ = std::move(destroy_baton);
print_destructor_stack_trace_ = std::move(print_destructor_stack_trace); print_destructor_stack_trace_ = std::move(print_destructor_stack_trace);
......
...@@ -110,6 +110,7 @@ ...@@ -110,6 +110,7 @@
#include <folly/Demangle.h> #include <folly/Demangle.h>
#include <folly/Executor.h> #include <folly/Executor.h>
#include <folly/io/async/Request.h> #include <folly/io/async/Request.h>
#include <folly/experimental/ReadMostlySharedPtr.h>
#include <algorithm> #include <algorithm>
#include <atomic> #include <atomic>
...@@ -244,6 +245,7 @@ struct SingletonHolder : public SingletonHolderBase { ...@@ -244,6 +245,7 @@ struct SingletonHolder : public SingletonHolderBase {
inline T* get(); inline T* get();
inline std::weak_ptr<T> get_weak(); inline std::weak_ptr<T> get_weak();
inline std::shared_ptr<T> try_get(); inline std::shared_ptr<T> try_get();
inline folly::ReadMostlySharedPtr<T> try_get_fast();
void registerSingleton(CreateFunc c, TeardownFunc t); void registerSingleton(CreateFunc c, TeardownFunc t);
void registerSingletonMock(CreateFunc c, TeardownFunc t); void registerSingletonMock(CreateFunc c, TeardownFunc t);
...@@ -277,14 +279,16 @@ struct SingletonHolder : public SingletonHolderBase { ...@@ -277,14 +279,16 @@ struct SingletonHolder : public SingletonHolderBase {
// 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 ReadMostlyMainPtr to singleton instance, set when state is changed
// Dead to Living. Reset when state is changed from Living to Dead. // from Dead to Living. Reset when state is changed from Living to Dead.
std::shared_ptr<T> instance_; folly::ReadMostlyMainPtr<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<T> instance_weak_; std::weak_ptr<T> instance_weak_;
// Fast equivalent of instance_weak_
folly::ReadMostlyWeakPtr<T> instance_weak_fast_;
// 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_;
T* instance_ptr_ = nullptr; T* instance_ptr_ = nullptr;
...@@ -502,6 +506,10 @@ class Singleton { ...@@ -502,6 +506,10 @@ class Singleton {
return getEntry().try_get(); return getEntry().try_get();
} }
static folly::ReadMostlySharedPtr<T> try_get_fast() {
return getEntry().try_get_fast();
}
explicit Singleton(std::nullptr_t _ = nullptr, explicit Singleton(std::nullptr_t _ = nullptr,
typename Singleton::TeardownFunc t = nullptr) : typename Singleton::TeardownFunc t = nullptr) :
Singleton ([]() { return new T; }, std::move(t)) { Singleton ([]() { return new T; }, std::move(t)) {
......
...@@ -18,7 +18,6 @@ ...@@ -18,7 +18,6 @@
#include <atomic> #include <atomic>
#include <folly/experimental/RCURefCount.h>
#include <folly/experimental/TLRefCount.h> #include <folly/experimental/TLRefCount.h>
namespace folly { namespace folly {
...@@ -60,7 +59,7 @@ class ReadMostlySharedPtrCore { ...@@ -60,7 +59,7 @@ class ReadMostlySharedPtrCore {
void increfWeak() { void increfWeak() {
auto value = ++weakCount_; auto value = ++weakCount_;
assert(value > 0); DCHECK_GT(value, 0);
} }
void decrefWeak() { void decrefWeak() {
...@@ -197,6 +196,10 @@ class ReadMostlyWeakPtr { ...@@ -197,6 +196,10 @@ class ReadMostlyWeakPtr {
reset(mainPtr.impl_); reset(mainPtr.impl_);
} }
explicit ReadMostlyWeakPtr(const ReadMostlySharedPtr<T, RefCount>& ptr) {
reset(ptr.impl_);
}
ReadMostlyWeakPtr(const ReadMostlyWeakPtr& other) { ReadMostlyWeakPtr(const ReadMostlyWeakPtr& other) {
*this = other; *this = other;
} }
...@@ -206,6 +209,11 @@ class ReadMostlyWeakPtr { ...@@ -206,6 +209,11 @@ class ReadMostlyWeakPtr {
return *this; return *this;
} }
ReadMostlyWeakPtr& operator=(const ReadMostlyMainPtr<T, RefCount>& mainPtr) {
reset(mainPtr.impl_);
return *this;
}
ReadMostlyWeakPtr(ReadMostlyWeakPtr&& other) noexcept { ReadMostlyWeakPtr(ReadMostlyWeakPtr&& other) noexcept {
*this = other; *this = other;
} }
...@@ -335,6 +343,8 @@ class ReadMostlySharedPtr { ...@@ -335,6 +343,8 @@ class ReadMostlySharedPtr {
} }
private: private:
friend class ReadMostlyWeakPtr<T, RefCount>;
void reset(detail::ReadMostlySharedPtrCore<T, RefCount>* impl) { void reset(detail::ReadMostlySharedPtrCore<T, RefCount>* impl) {
if (impl_) { if (impl_) {
impl_->decref(); impl_->decref();
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <folly/Memory.h> #include <folly/Memory.h>
#include <gflags/gflags.h> #include <gflags/gflags.h>
#include <folly/experimental/RCURefCount.h>
#include <folly/experimental/ReadMostlySharedPtr.h> #include <folly/experimental/ReadMostlySharedPtr.h>
template <template<typename> class MainPtr, template <template<typename> class MainPtr,
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <folly/Baton.h> #include <folly/Baton.h>
#include <folly/experimental/RCURefCount.h>
#include <folly/experimental/ReadMostlySharedPtr.h> #include <folly/experimental/ReadMostlySharedPtr.h>
using folly::ReadMostlyMainPtr; using folly::ReadMostlyMainPtr;
......
/*
* 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.
*/
/* -*- Mode: C++; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */
#include <thread>
#include <iostream>
#include <folly/Benchmark.h>
#include <folly/Memory.h>
#include <gflags/gflags.h>
#include <folly/Singleton.h>
using namespace folly;
// 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 BenchmarkSingleton {
int val = 0;
};
void run4Threads(std::function<void()> f) {
std::vector<std::thread> threads;
for (size_t i = 0; i < 4; ++ i) {
threads.emplace_back(f);
}
for (auto& thread : threads) {
thread.join();
}
}
void normalSingleton(size_t n) {
for (size_t i = 0; i < n; ++ i) {
doNotOptimizeAway(getNormalSingleton());
}
}
BENCHMARK(NormalSingleton, n) {
normalSingleton(n);
}
BENCHMARK(NormalSingleton4Threads, n) {
run4Threads([=]() {
normalSingleton(n);
});
}
void meyersSingleton(size_t n) {
for (size_t i = 0; i < n; ++i) {
doNotOptimizeAway(getMeyersSingleton());
}
}
BENCHMARK(MeyersSingleton, n) {
meyersSingleton(n);
}
BENCHMARK(MeyersSingleton4Threads, n) {
run4Threads([=]() {
meyersSingleton(n);
});
}
struct BenchmarkTag {};
template <typename T, typename Tag = detail::DefaultTag>
using SingletonBenchmark = Singleton <T, Tag, BenchmarkTag>;
struct GetTag{};
struct TryGetTag{};
struct TryGetFastTag{};
SingletonBenchmark<BenchmarkSingleton, GetTag> benchmark_singleton_get;
SingletonBenchmark<BenchmarkSingleton, TryGetTag> benchmark_singleton_try_get;
SingletonBenchmark<BenchmarkSingleton, TryGetFastTag>
benchmark_singleton_try_get_fast;
void follySingletonRaw(size_t n) {
for (size_t i = 0; i < n; ++i) {
SingletonBenchmark<BenchmarkSingleton, GetTag>::get();
}
}
BENCHMARK(FollySingletonRaw, n) {
follySingletonRaw(n);
}
BENCHMARK(FollySingletonRaw4Threads, n) {
run4Threads([=]() {
follySingletonRaw(n);
});
}
void follySingletonTryGet(size_t n) {
for (size_t i = 0; i < n; ++i) {
SingletonBenchmark<BenchmarkSingleton, TryGetTag>::try_get();
}
}
BENCHMARK(FollySingletonTryGet, n) {
follySingletonTryGet(n);
}
BENCHMARK(FollySingletonTryGet4Threads, n) {
run4Threads([=]() {
follySingletonTryGet(n);
});
}
void follySingletonTryGetFast(size_t n) {
for (size_t i = 0; i < n; ++i) {
SingletonBenchmark<BenchmarkSingleton, TryGetFastTag>::try_get_fast();
}
}
BENCHMARK(FollySingletonTryGetFast, n) {
follySingletonTryGetFast(n);
}
BENCHMARK(FollySingletonTryGetFast4Threads, n) {
run4Threads([=]() {
follySingletonTryGetFast(n);
});
}
int main(int argc, char** argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
gflags::SetCommandLineOptionWithMode(
"bm_min_usec", "100000", gflags::SET_FLAG_IF_DEFAULT
);
folly::runBenchmarks();
return 0;
}
...@@ -19,7 +19,6 @@ ...@@ -19,7 +19,6 @@
#include <folly/Singleton.h> #include <folly/Singleton.h>
#include <folly/io/async/EventBase.h> #include <folly/io/async/EventBase.h>
#include <folly/test/SingletonTestStructs.h> #include <folly/test/SingletonTestStructs.h>
#include <folly/Benchmark.h>
#include <glog/logging.h> #include <glog/logging.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
...@@ -60,6 +59,7 @@ TEST(Singleton, BasicUsage) { ...@@ -60,6 +59,7 @@ TEST(Singleton, BasicUsage) {
EXPECT_NE(s2, nullptr); EXPECT_NE(s2, nullptr);
EXPECT_EQ(s1, s2); EXPECT_EQ(s1, s2);
EXPECT_EQ(s1.get(), SingletonBasicUsage<Watchdog>::try_get_fast().get());
std::shared_ptr<ChildWatchdog> s3 = std::shared_ptr<ChildWatchdog> s3 =
SingletonBasicUsage<ChildWatchdog>::try_get(); SingletonBasicUsage<ChildWatchdog>::try_get();
...@@ -554,20 +554,6 @@ TEST(Singleton, SingletonEagerInitParallel) { ...@@ -554,20 +554,6 @@ TEST(Singleton, SingletonEagerInitParallel) {
} }
} }
// 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 {}; struct MockTag {};
template <typename T, typename Tag = detail::DefaultTag> template <typename T, typename Tag = detail::DefaultTag>
using SingletonMock = Singleton <T, Tag, MockTag>; using SingletonMock = Singleton <T, Tag, MockTag>;
...@@ -595,114 +581,6 @@ TEST(Singleton, MockTest) { ...@@ -595,114 +581,6 @@ TEST(Singleton, MockTest) {
EXPECT_NE(serial_count_first, serial_count_mock); EXPECT_NE(serial_count_first, serial_count_mock);
} }
struct BenchmarkSingleton {
int val = 0;
};
void run4Threads(std::function<void()> f) {
std::vector<std::thread> threads;
for (size_t i = 0; i < 4; ++ i) {
threads.emplace_back(f);
}
for (auto& thread : threads) {
thread.join();
}
}
void normalSingleton(size_t n) {
for (size_t i = 0; i < n; ++ i) {
doNotOptimizeAway(getNormalSingleton());
}
}
BENCHMARK(NormalSingleton, n) {
normalSingleton(n);
}
BENCHMARK(NormalSingleton4Threads, n) {
run4Threads([=]() {
normalSingleton(n);
});
}
void meyersSingleton(size_t n) {
for (size_t i = 0; i < n; ++i) {
doNotOptimizeAway(getMeyersSingleton());
}
}
BENCHMARK_RELATIVE(MeyersSingleton, n) {
meyersSingleton(n);
}
BENCHMARK_RELATIVE(MeyersSingleton4Threads, n) {
run4Threads([=]() {
meyersSingleton(n);
});
}
struct BenchmarkTag {};
template <typename T, typename Tag = detail::DefaultTag>
using SingletonBenchmark = Singleton <T, Tag, BenchmarkTag>;
struct GetTag{};
struct GetSharedTag{};
struct GetWeakTag{};
SingletonBenchmark<BenchmarkSingleton, GetTag> benchmark_singleton_get;
SingletonBenchmark<BenchmarkSingleton, GetSharedTag>
benchmark_singleton_get_shared;
SingletonBenchmark<BenchmarkSingleton, GetWeakTag> benchmark_singleton_get_weak;
void follySingletonRaw(size_t n) {
for (size_t i = 0; i < n; ++i) {
SingletonBenchmark<BenchmarkSingleton, GetTag>::get();
}
}
BENCHMARK_RELATIVE(FollySingletonRaw, n) {
follySingletonRaw(n);
}
BENCHMARK_RELATIVE(FollySingletonRaw4Threads, n) {
run4Threads([=]() {
follySingletonRaw(n);
});
}
void follySingletonSharedPtr(size_t n) {
for (size_t i = 0; i < n; ++i) {
SingletonBenchmark<BenchmarkSingleton, GetSharedTag>::try_get();
}
}
BENCHMARK_RELATIVE(FollySingletonSharedPtr, n) {
follySingletonSharedPtr(n);
}
BENCHMARK_RELATIVE(FollySingletonSharedPtr4Threads, n) {
run4Threads([=]() {
follySingletonSharedPtr(n);
});
}
void follySingletonWeakPtr(size_t n) {
for (size_t i = 0; i < n; ++i) {
SingletonBenchmark<BenchmarkSingleton, GetWeakTag>::get_weak();
}
}
BENCHMARK_RELATIVE(FollySingletonWeakPtr, n) {
follySingletonWeakPtr(n);
}
BENCHMARK_RELATIVE(FollySingletonWeakPtr4Threads, n) {
run4Threads([=]() {
follySingletonWeakPtr(n);
});
}
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
testing::InitGoogleTest(&argc, argv); testing::InitGoogleTest(&argc, argv);
google::InitGoogleLogging(argv[0]); google::InitGoogleLogging(argv[0]);
...@@ -711,8 +589,6 @@ int main(int argc, char* argv[]) { ...@@ -711,8 +589,6 @@ int main(int argc, char* argv[]) {
SingletonVault::singleton()->registrationComplete(); SingletonVault::singleton()->registrationComplete();
auto ret = RUN_ALL_TESTS(); auto ret = RUN_ALL_TESTS();
if (!ret) {
folly::runBenchmarksOnFlag();
}
return ret; return ret;
} }
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