Commit 8ed2344b authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook GitHub Bot

StaticSingletonManager type-erased create

Summary: `StaticSingletonManager::create<T, Tag>` is larger than necessary, which may not be desirable when using many tags with the same type. Permit call-sites to use static data to shrink the functions.

Reviewed By: aary

Differential Revision: D27863037

fbshipit-source-id: 9f6ced3e7d7a85b78e671f69d7a475ab4de5eea0
parent 6fcad841
...@@ -33,7 +33,7 @@ class StaticSingletonManagerWithRttiImpl { ...@@ -33,7 +33,7 @@ class StaticSingletonManagerWithRttiImpl {
static void* create(Arg& arg) { static void* create(Arg& arg) {
// 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 StaticSingletonManagerWithRttiImpl(); static auto& instance = *new StaticSingletonManagerWithRttiImpl();
auto const ptr = instance.entry(*arg.key).get(arg.make); auto const ptr = instance.entry(*arg.key).get(*arg.make);
arg.cache.store(ptr, std::memory_order_release); arg.cache.store(ptr, std::memory_order_release);
return ptr; return ptr;
} }
......
...@@ -32,20 +32,67 @@ namespace detail { ...@@ -32,20 +32,67 @@ namespace detail {
// Does not support dynamic loading but works without rtti. // Does not support dynamic loading but works without rtti.
class StaticSingletonManagerSansRtti { class StaticSingletonManagerSansRtti {
private:
using Cache = std::atomic<void*>;
using Make = void*();
struct Arg {
Cache cache{}; // should be first field
Make* make;
template <typename T, typename Tag>
/* implicit */ constexpr Arg(tag_t<T, Tag>) noexcept
: make{thunk::make<T>} {}
};
template <bool Noexcept>
static void* create_(Arg&) noexcept(Noexcept); // no defn; only for decltype
template <bool Noexcept>
using Create = decltype(create_<Noexcept>);
public: public:
template <bool Noexcept>
struct ArgCreate : private Arg {
friend class StaticSingletonManagerSansRtti;
template <typename T, typename Tag>
/* implicit */ constexpr ArgCreate(tag_t<T, Tag> t) noexcept
: Arg{t}, create{create_<T, Tag>} {
static_assert(Noexcept == noexcept(T()), "mismatched noexcept");
}
private:
Create<Noexcept>* create;
};
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 std::atomic<T*> cache{}; static Arg arg{tag<T, Tag>};
auto const pointer = cache.load(std::memory_order_acquire); return create<T, Tag>(arg);
return FOLLY_LIKELY(!!pointer) ? *pointer : create_<T, Tag>(cache); }
template <typename T, typename..., bool Noexcept = noexcept(T())>
FOLLY_ERASE static T& create(ArgCreate<Noexcept>& arg) {
auto const v = arg.cache.load(std::memory_order_acquire);
auto const p = FOLLY_LIKELY(!!v) ? v : arg.create(arg);
return *static_cast<T*>(p);
} }
private: private:
template <typename T, typename Tag> template <typename T, typename Tag>
FOLLY_EXPORT FOLLY_NOINLINE static T& create_(std::atomic<T*>& cache) { FOLLY_ERASE static T& create(Arg& arg) {
auto const v = arg.cache.load(std::memory_order_acquire);
auto const p = FOLLY_LIKELY(!!v) ? v : create_<T, Tag>(arg);
return *static_cast<T*>(p);
}
template <typename T, typename Tag>
FOLLY_EXPORT FOLLY_NOINLINE static void* create_(Arg& arg) noexcept(
noexcept(T())) {
auto& cache = arg.cache;
static Indestructible<T> instance; static Indestructible<T> instance;
cache.store(&*instance, std::memory_order_release); cache.store(&*instance, std::memory_order_release);
return *instance; return &*instance;
} }
}; };
...@@ -56,30 +103,63 @@ class StaticSingletonManagerSansRtti { ...@@ -56,30 +103,63 @@ class StaticSingletonManagerSansRtti {
// //
// Supports dynamic loading but requires rtti. // Supports dynamic loading but requires rtti.
class StaticSingletonManagerWithRtti { class StaticSingletonManagerWithRtti {
public:
template <typename T, typename Tag>
FOLLY_EXPORT FOLLY_ALWAYS_INLINE static T& create() {
constexpr auto& make = thunk::make<T>;
// gcc and clang behave poorly if typeid is hidden behind a non-constexpr
// function, but typeid is not constexpr under msvc
static Arg arg{{nullptr}, FOLLY_TYPE_INFO_OF(tag_t<T, Tag>), make};
auto const v = arg.cache.load(std::memory_order_acquire);
auto const p = FOLLY_LIKELY(!!v) ? v : create_<noexcept(T())>(arg);
return *static_cast<T*>(p);
}
private: private:
using Key = std::type_info; using Key = std::type_info;
using Make = void*(); using Make = void*();
using Cache = std::atomic<void*>; using Cache = std::atomic<void*>;
struct Arg { struct Arg {
Cache cache; // should be first field Cache cache{}; // should be first field
Key const* key; Key const* key;
Make& make; Make* make;
// gcc and clang behave poorly if typeid is hidden behind a non-constexpr
// function, but typeid is not constexpr under msvc
template <typename T, typename Tag>
/* implicit */ constexpr Arg(tag_t<T, Tag>) noexcept
: key{FOLLY_TYPE_INFO_OF(tag_t<T, Tag>)}, make{thunk::make<T>} {}
}; };
template <bool Noexcept> template <bool Noexcept>
FOLLY_NOINLINE static void* create_(Arg& arg) noexcept(Noexcept); FOLLY_NOINLINE static void* create_(Arg&) noexcept(Noexcept);
template <bool Noexcept>
using Create = decltype(create_<Noexcept>);
public:
template <bool Noexcept>
struct ArgCreate : private Arg {
friend class StaticSingletonManagerWithRtti;
template <typename T, typename Tag>
/* implicit */ constexpr ArgCreate(tag_t<T, Tag> t) noexcept
: Arg{t}, create{create_<Noexcept>} {
static_assert(Noexcept == noexcept(T()), "mismatched noexcept");
}
private:
Create<Noexcept>* create;
};
template <typename T, typename Tag>
FOLLY_EXPORT FOLLY_ALWAYS_INLINE static T& create() {
static Arg arg{tag<T, Tag>};
return create<T, Tag>(arg);
}
template <typename T, typename..., bool Noexcept = noexcept(T())>
FOLLY_ERASE static T& create(ArgCreate<Noexcept>& arg) {
auto const v = arg.cache.load(std::memory_order_acquire);
auto const p = FOLLY_LIKELY(!!v) ? v : arg.create(arg);
return *static_cast<T*>(p);
}
private:
template <typename T, typename Tag>
FOLLY_ERASE static T& create(Arg& arg) {
auto const v = arg.cache.load(std::memory_order_acquire);
auto const p = FOLLY_LIKELY(!!v) ? v : create_<noexcept(T())>(arg);
return *static_cast<T*>(p);
}
}; };
using StaticSingletonManager = std::conditional_t< using StaticSingletonManager = std::conditional_t<
......
...@@ -67,6 +67,11 @@ TEST_F(StaticSingletonManagerTest, example_sans_rtti) { ...@@ -67,6 +67,11 @@ TEST_F(StaticSingletonManagerTest, example_sans_rtti) {
auto& k = K::create<T, Tag<char*>>(); auto& k = K::create<T, Tag<char*>>();
EXPECT_NE(&i, &k); EXPECT_NE(&i, &k);
EXPECT_EQ(T::value, k); EXPECT_EQ(T::value, k);
static K::ArgCreate<true> m_arg{tag<T, Tag<int>>};
auto& m = K::create<T>(m_arg);
EXPECT_NE(&i, &m);
EXPECT_EQ(T::value, m);
} }
TEST_F(StaticSingletonManagerTest, example_with_rtti) { TEST_F(StaticSingletonManagerTest, example_with_rtti) {
...@@ -84,6 +89,11 @@ TEST_F(StaticSingletonManagerTest, example_with_rtti) { ...@@ -84,6 +89,11 @@ TEST_F(StaticSingletonManagerTest, example_with_rtti) {
auto& k = K::create<T, Tag<char*>>(); auto& k = K::create<T, Tag<char*>>();
EXPECT_NE(&i, &k); EXPECT_NE(&i, &k);
EXPECT_EQ(T::value, k); EXPECT_EQ(T::value, k);
static K::ArgCreate<true> m_arg{tag<T, Tag<int>>};
auto& m = K::create<T>(m_arg);
EXPECT_NE(&i, &m);
EXPECT_EQ(T::value, m);
} }
TEST_F(StaticSingletonManagerTest, example) { TEST_F(StaticSingletonManagerTest, example) {
......
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