Commit 670a8e06 authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook Github Bot

StaticSingletonManager without RTTI

Summary: [Folly] `StaticSingletonManager` without RTTI as a variant, but without support for dynamic loading either.

Reviewed By: andriigrynenko

Differential Revision: D14018865

fbshipit-source-id: d4eadc8924e6fec2cbb1dae957e311d80e9ce289
parent 0f039163
...@@ -25,7 +25,7 @@ namespace detail { ...@@ -25,7 +25,7 @@ namespace detail {
namespace { namespace {
class StaticSingletonManagerImpl { class StaticSingletonManagerWithRttiImpl {
public: public:
using Make = void*(); using Make = void*();
using Cache = std::atomic<void*>; using Cache = std::atomic<void*>;
...@@ -59,12 +59,12 @@ class StaticSingletonManagerImpl { ...@@ -59,12 +59,12 @@ class StaticSingletonManagerImpl {
} // namespace } // namespace
void* StaticSingletonManager::create_( void* StaticSingletonManagerWithRtti::create_(
Key const& key, Key const& key,
Make& make, Make& make,
Cache& cache) { Cache& cache) {
// This Leaky Meyers Singleton must always live in the .cpp file. // This Leaky Meyers Singleton must always live in the .cpp file.
static auto& instance = *new StaticSingletonManagerImpl(); static auto& instance = *new StaticSingletonManagerWithRttiImpl();
return instance.create(key, make, cache); return instance.create(key, make, cache);
} }
......
...@@ -20,22 +20,45 @@ ...@@ -20,22 +20,45 @@
#include <typeinfo> #include <typeinfo>
#include <folly/CPortability.h> #include <folly/CPortability.h>
#include <folly/Indestructible.h>
#include <folly/Likely.h> #include <folly/Likely.h>
#include <folly/detail/Singleton.h> #include <folly/detail/Singleton.h>
#include <folly/lang/Exception.h>
namespace folly { namespace folly {
namespace detail { namespace detail {
// Does not support dynamic loading but works without rtti.
class StaticSingletonManagerSansRtti {
public:
template <typename T, typename Tag>
FOLLY_EXPORT FOLLY_ALWAYS_INLINE static T& create() {
std::atomic<T*> cache{};
auto const pointer = cache.load(std::memory_order_acquire);
return FOLLY_LIKELY(!!pointer) ? *pointer : create_<T, Tag>(cache);
}
private:
template <typename T, typename Tag>
FOLLY_EXPORT FOLLY_NOINLINE static T& create_(std::atomic<T*>& cache) {
static Indestructible<T> instance;
cache.store(&*instance, std::memory_order_release);
return *instance;
}
};
// This internal-use-only class is used to create all leaked Meyers singletons. // This internal-use-only class is used to create all leaked Meyers singletons.
// It guarantees that only one instance of every such singleton will ever be // It guarantees that only one instance of every such singleton will ever be
// created, even when requested from different compilation units linked // created, even when requested from different compilation units linked
// dynamically. // dynamically.
class StaticSingletonManager { //
// Supports dynamic loading but requires rtti.
class StaticSingletonManagerWithRtti {
public: public:
template <typename T, typename Tag> template <typename T, typename Tag>
FOLLY_EXPORT FOLLY_ALWAYS_INLINE static T& create() { FOLLY_EXPORT FOLLY_ALWAYS_INLINE static T& create() {
static Cache cache{}; static Cache cache{};
auto const& key = typeid(TypeTuple<T, Tag>); auto const& key = key_<T, Tag>();
auto const v = cache.load(std::memory_order_acquire); auto const v = cache.load(std::memory_order_acquire);
auto const p = FOLLY_LIKELY(!!v) ? v : create_(key, make<T>, cache); auto const p = FOLLY_LIKELY(!!v) ? v : create_(key, make<T>, cache);
return *static_cast<T*>(p); return *static_cast<T*>(p);
...@@ -51,9 +74,23 @@ class StaticSingletonManager { ...@@ -51,9 +74,23 @@ class StaticSingletonManager {
return new T(); return new T();
} }
template <typename T, typename Tag>
FOLLY_ALWAYS_INLINE static std::type_info const& key_() {
#if FOLLY_HAS_RTTI
return typeid(TypeTuple<T, Tag>);
#else
throw_exception<std::logic_error>("rtti unavailable");
#endif
}
FOLLY_NOINLINE static void* create_(Key const& key, Make& make, Cache& cache); FOLLY_NOINLINE static void* create_(Key const& key, Make& make, Cache& cache);
}; };
using StaticSingletonManager = std::conditional_t<
FOLLY_HAS_RTTI,
StaticSingletonManagerWithRtti,
StaticSingletonManagerSansRtti>;
template <typename T, typename Tag> template <typename T, typename Tag>
FOLLY_ALWAYS_INLINE FOLLY_ATTR_VISIBILITY_HIDDEN T& createGlobal() { FOLLY_ALWAYS_INLINE FOLLY_ATTR_VISIBILITY_HIDDEN T& createGlobal() {
return StaticSingletonManager::create<T, Tag>(); return StaticSingletonManager::create<T, Tag>();
......
...@@ -33,6 +33,40 @@ struct Tag {}; ...@@ -33,6 +33,40 @@ struct Tag {};
template <int I> template <int I>
using Int = std::integral_constant<int, I>; using Int = std::integral_constant<int, I>;
TEST_F(StaticSingletonManagerTest, example_sans_rtti) {
using K = StaticSingletonManagerSansRtti;
using T = std::integral_constant<int, 3>;
auto& i = K::create<T, Tag<char>>();
EXPECT_EQ(T::value, i);
auto& j = K::create<T, Tag<char>>();
EXPECT_EQ(&i, &j);
EXPECT_EQ(T::value, j);
auto& k = K::create<T, Tag<char*>>();
EXPECT_NE(&i, &k);
EXPECT_EQ(T::value, k);
}
TEST_F(StaticSingletonManagerTest, example_with_rtti) {
using K = StaticSingletonManagerWithRtti;
using T = std::integral_constant<int, 3>;
auto& i = K::create<T, Tag<char>>();
EXPECT_EQ(T::value, i);
auto& j = K::create<T, Tag<char>>();
EXPECT_EQ(&i, &j);
EXPECT_EQ(T::value, j);
auto& k = K::create<T, Tag<char*>>();
EXPECT_NE(&i, &k);
EXPECT_EQ(T::value, k);
}
TEST_F(StaticSingletonManagerTest, example) { TEST_F(StaticSingletonManagerTest, example) {
using T = std::integral_constant<int, 3>; using T = std::integral_constant<int, 3>;
......
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