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

Let SingletonThreadLocal creator return derived type

Summary: [Folly] Let `SingletonThreadLocal` creator return derived type. This is a different interface for the creator type, and means supporting non-moveable/non-copyable types with an extra pointer indirection pre-C++17.

Reviewed By: andriigrynenko

Differential Revision: D13165769

fbshipit-source-id: 39f2b5fed48342dd3178a20838af50c43aabee86
parent ed0cacef
...@@ -95,36 +95,22 @@ class SingletonThreadLocal { ...@@ -95,36 +95,22 @@ class SingletonThreadLocal {
boost::intrusive::list<Node, boost::intrusive::constant_time_size<false>>; boost::intrusive::list<Node, boost::intrusive::constant_time_size<false>>;
struct Wrapper { struct Wrapper {
template <typename S> using Object = invoke_result_t<Make>;
using MakeRet = is_invocable_r<S, Make>; static_assert(std::is_convertible<Object&, T&>::value, "inconvertible");
// keep as first field, to save 1 instr in the fast path // keep as first field, to save 1 instr in the fast path
union { Object object{Make{}()};
alignas(alignof(T)) unsigned char storage[sizeof(T)];
T object;
};
List caches; List caches;
/* implicit */ operator T&() { /* implicit */ operator T&() {
return object; return object;
} }
// normal make types
template <typename S = T, _t<std::enable_if<MakeRet<S>::value, int>> = 0>
Wrapper() {
(void)new (storage) S(Make{}());
}
// default and special make types for non-move-constructible T, until C++17
template <typename S = T, _t<std::enable_if<!MakeRet<S>::value, int>> = 0>
Wrapper() {
(void)Make{}(storage);
}
~Wrapper() { ~Wrapper() {
for (auto& node : caches) { for (auto& node : caches) {
node.clear(); node.clear();
} }
caches.clear(); caches.clear();
object.~T();
} }
}; };
......
...@@ -16,6 +16,10 @@ ...@@ -16,6 +16,10 @@
#pragma once #pragma once
#include <type_traits>
#include <folly/Traits.h>
namespace folly { namespace folly {
namespace detail { namespace detail {
...@@ -23,10 +27,28 @@ struct DefaultTag {}; ...@@ -23,10 +27,28 @@ struct DefaultTag {};
template <typename T> template <typename T>
struct DefaultMake { struct DefaultMake {
// Required form until C++17, which permits returning objects of types which struct Heap {
// are neither copy-constructible nor move-constructible. std::unique_ptr<T> ptr{std::make_unique<T>()};
T* operator()(unsigned char (&buf)[sizeof(T)]) const { /* implicit */ operator T&() {
return new (buf) T(); return *ptr;
}
};
using is_returnable = StrictDisjunction<
bool_constant<__cplusplus >= 201703ULL>,
std::is_copy_constructible<T>,
std::is_move_constructible<T>>;
using type = std::conditional_t<is_returnable::value, T, Heap>;
T make(std::true_type) const {
return T();
}
Heap make(std::false_type) const {
return Heap();
}
type operator()() const {
return make(is_returnable{});
} }
}; };
......
...@@ -64,7 +64,35 @@ TEST(SingletonThreadLocalTest, OneSingletonPerThread) { ...@@ -64,7 +64,35 @@ TEST(SingletonThreadLocalTest, OneSingletonPerThread) {
EXPECT_EQ(threads.size(), fooDeletedCount); EXPECT_EQ(threads.size(), fooDeletedCount);
} }
TEST(SingletonThreadLocalTest, MoveConstructibleMake) { TEST(SingletonThreadLocalTest, DefaultMakeMoveConstructible) {
struct Foo {
int a = 4;
Foo() = default;
Foo(Foo&&) = default;
Foo& operator=(Foo&&) = default;
};
using Real = detail::DefaultMake<Foo>::type;
EXPECT_TRUE((std::is_same<Real, Foo>::value));
struct Tag {};
auto& single = SingletonThreadLocal<Foo, Tag>::get();
EXPECT_EQ(4, single.a);
}
TEST(SingletonThreadLocalTest, DefaultMakeNotMoveConstructible) {
struct Foo {
int a = 4;
Foo() = default;
Foo(Foo&&) = delete;
Foo& operator=(Foo&&) = delete;
};
using Real = detail::DefaultMake<Foo>::type;
EXPECT_TRUE(((__cplusplus >= 201703ULL) == std::is_same<Real, Foo>::value));
struct Tag {};
auto& single = SingletonThreadLocal<Foo, Tag>::get();
EXPECT_EQ(4, single.a);
}
TEST(SingletonThreadLocalTest, SameTypeMake) {
struct Foo { struct Foo {
int a, b; int a, b;
Foo(int a_, int b_) : a(a_), b(b_) {} Foo(int a_, int b_) : a(a_), b(b_) {}
...@@ -81,21 +109,23 @@ TEST(SingletonThreadLocalTest, MoveConstructibleMake) { ...@@ -81,21 +109,23 @@ TEST(SingletonThreadLocalTest, MoveConstructibleMake) {
EXPECT_EQ(4, single.b); EXPECT_EQ(4, single.b);
} }
TEST(SingletonThreadLocalTest, NotMoveConstructibleMake) { TEST(SingletonThreadLocalTest, ReferenceConvertibleTypeMake) {
struct Foo { struct Foo {
int a, b; int b = 3;
Foo(int a_, int b_) : a(a_), b(b_) {}
Foo(Foo&&) = delete;
Foo& operator=(Foo&&) = delete;
}; };
struct Tag {}; struct A {
struct Make { int a = 2;
Foo* operator()(unsigned char (&buf)[sizeof(Foo)]) const {
return new (buf) Foo(3, 4);
}
}; };
auto& single = SingletonThreadLocal<Foo, Tag, Make>::get(); struct C {
EXPECT_EQ(4, single.b); int c = 4;
};
struct Bar : A, Foo, C {};
struct Tag {};
using Make = detail::DefaultMake<Bar>;
auto& foo = SingletonThreadLocal<Foo, Tag, Make>::get();
EXPECT_EQ(3, foo.b);
auto& bar = static_cast<Bar&>(foo);
EXPECT_EQ(2, bar.a);
} }
TEST(SingletonThreadLocalTest, AccessAfterFastPathDestruction) { TEST(SingletonThreadLocalTest, AccessAfterFastPathDestruction) {
......
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