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 {
boost::intrusive::list<Node, boost::intrusive::constant_time_size<false>>;
struct Wrapper {
template <typename S>
using MakeRet = is_invocable_r<S, Make>;
using Object = invoke_result_t<Make>;
static_assert(std::is_convertible<Object&, T&>::value, "inconvertible");
// keep as first field, to save 1 instr in the fast path
union {
alignas(alignof(T)) unsigned char storage[sizeof(T)];
T object;
};
Object object{Make{}()};
List caches;
/* implicit */ operator T&() {
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() {
for (auto& node : caches) {
node.clear();
}
caches.clear();
object.~T();
}
};
......
......@@ -16,6 +16,10 @@
#pragma once
#include <type_traits>
#include <folly/Traits.h>
namespace folly {
namespace detail {
......@@ -23,10 +27,28 @@ struct DefaultTag {};
template <typename T>
struct DefaultMake {
// Required form until C++17, which permits returning objects of types which
// are neither copy-constructible nor move-constructible.
T* operator()(unsigned char (&buf)[sizeof(T)]) const {
return new (buf) T();
struct Heap {
std::unique_ptr<T> ptr{std::make_unique<T>()};
/* implicit */ operator 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) {
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 {
int a, b;
Foo(int a_, int b_) : a(a_), b(b_) {}
......@@ -81,21 +109,23 @@ TEST(SingletonThreadLocalTest, MoveConstructibleMake) {
EXPECT_EQ(4, single.b);
}
TEST(SingletonThreadLocalTest, NotMoveConstructibleMake) {
TEST(SingletonThreadLocalTest, ReferenceConvertibleTypeMake) {
struct Foo {
int a, b;
Foo(int a_, int b_) : a(a_), b(b_) {}
Foo(Foo&&) = delete;
Foo& operator=(Foo&&) = delete;
int b = 3;
};
struct Tag {};
struct Make {
Foo* operator()(unsigned char (&buf)[sizeof(Foo)]) const {
return new (buf) Foo(3, 4);
}
struct A {
int a = 2;
};
auto& single = SingletonThreadLocal<Foo, Tag, Make>::get();
EXPECT_EQ(4, single.b);
struct C {
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) {
......
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