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

Extract the unique-instance enforcer

Summary: [Folly] Extract the unique-instance enforcer from `SingletonThreadLocal` into a standalone `UniqueInstance`.

Reviewed By: andriigrynenko

Differential Revision: D14927839

fbshipit-source-id: cfe98ef7a88746a557a64263f3165cd01caeec37
parent 9900b2a0
......@@ -16,47 +16,17 @@
#pragma once
#include <typeinfo>
#include <boost/intrusive/list.hpp>
#include <folly/ScopeGuard.h>
#include <folly/ThreadLocal.h>
#include <folly/detail/Iterators.h>
#include <folly/detail/Singleton.h>
#include <folly/detail/UniqueInstance.h>
#include <folly/functional/Invoke.h>
namespace folly {
namespace detail {
class SingletonThreadLocalBase {
public:
class UniqueBase {
public:
using Ptr = std::type_info const*;
using Ref = std::type_info const&;
struct Value {
bool init;
Ptr make;
Ptr tltag;
};
template <typename T, typename Tag, typename Make, typename TLTag>
explicit UniqueBase(tag_t<T, Tag, Make, TLTag>) noexcept
: UniqueBase(
typeid(T),
typeid(Tag),
typeid(Make),
typeid(TLTag),
detail::createGlobal<Value, tag_t<T, Tag, UniqueBase>>()) {}
UniqueBase(Ref type, Ref tag, Ref make, Ref tltag, Value& value) noexcept;
};
};
} // namespace detail
/// SingletonThreadLocal
///
/// Useful for a per-thread leaky-singleton model in libraries and applications.
......@@ -95,12 +65,9 @@ template <
typename Make = detail::DefaultMake<T>,
typename TLTag = std::
conditional_t<std::is_same<Tag, detail::DefaultTag>::value, void, Tag>>
class SingletonThreadLocal : private detail::SingletonThreadLocalBase {
class SingletonThreadLocal {
private:
struct Unique final : UniqueBase {
Unique() noexcept : UniqueBase(tag_t<T, Tag, Make, TLTag>{}) {}
};
static Unique unique;
static detail::UniqueInstance unique;
struct Wrapper;
......@@ -232,8 +199,10 @@ class SingletonThreadLocal : private detail::SingletonThreadLocalBase {
};
template <typename T, typename Tag, typename Make, typename TLTag>
typename SingletonThreadLocal<T, Tag, Make, TLTag>::Unique
SingletonThreadLocal<T, Tag, Make, TLTag>::unique;
detail::UniqueInstance SingletonThreadLocal<T, Tag, Make, TLTag>::unique{
"folly::SingletonThreadLocal",
tag_t<T, Tag>{},
tag_t<Make, TLTag>{}};
} // namespace folly
......
/*
* Copyright 2019-present 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.
*/
#include <folly/detail/UniqueInstance.h>
#include <cstdlib>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>
#include <folly/Demangle.h>
#include <folly/lang/Exception.h>
namespace folly {
namespace detail {
namespace {
using Ptr = std::type_info const*;
struct PtrRange {
Ptr const* b;
Ptr const* e;
};
template <typename Value>
PtrRange ptr_range_key(Value value) {
auto const data = value.ptrs;
return {data, data + value.key_size};
}
template <typename Value>
PtrRange ptr_range_mapped(Value value) {
auto const data = value.ptrs + value.key_size;
return {data, data + value.mapped_size};
}
bool equal(PtrRange lhs, PtrRange rhs) {
auto const cmp = [](auto a, auto b) { return *a == *b; };
return std::equal(lhs.b, lhs.e, rhs.b, rhs.e, cmp);
}
std::string join(PtrRange types) {
std::ostringstream ret;
for (auto t = types.b; t != types.e; ++t) {
if (t != types.b) {
ret << ", ";
}
ret << demangle((*t)->name());
}
return ret.str();
}
template <typename Value>
std::string render(Value value) {
auto const key_s = join(ptr_range_key(value));
auto const mapped_s = join(ptr_range_mapped(value));
std::ostringstream ret;
ret << value.tmpl << "<" << key_s << ", " << mapped_s << ">";
return ret.str();
}
} // namespace
void UniqueInstance::enforce(
char const* tmpl,
Ptr const* ptrs,
std::uint32_t key_size,
std::uint32_t mapped_size,
Value& global) noexcept {
Value const local{tmpl, ptrs, key_size, mapped_size};
if (!global.tmpl) {
global = local;
return;
}
if (!equal(ptr_range_key(global), ptr_range_key(local))) {
throw_exception<std::logic_error>("mismatched unique instance");
}
if (std::strcmp(global.tmpl, local.tmpl) == 0 &&
equal(ptr_range_mapped(global), ptr_range_mapped(local))) {
return;
}
auto const key = ptr_range_key(local);
std::ios_base::Init io_init;
std::cerr << "Overloaded unique instance over <" << join(key) << ", ...> "
<< "with differing trailing arguments:\n"
<< " " << render(global) << "\n"
<< " " << render(local) << "\n";
std::abort();
}
} // namespace detail
} // namespace folly
/*
* Copyright 2016-present Facebook, Inc.
* Copyright 2019-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -14,48 +14,57 @@
* limitations under the License.
*/
#include <folly/SingletonThreadLocal.h>
#pragma once
#include <cstdlib>
#include <iostream>
#include <cstdint>
#include <typeinfo>
#include <folly/Demangle.h>
#include <folly/detail/StaticSingletonManager.h>
namespace folly {
namespace detail {
SingletonThreadLocalBase::UniqueBase::UniqueBase(
Ref type,
Ref tag,
Ref make,
Ref tltag,
Value& value) noexcept {
if (!value.init) {
value.init = true;
value.make = &make;
value.tltag = &tltag;
}
if (*value.make == make && *value.tltag == tltag) {
return;
class UniqueInstance {
public:
template <typename... Key, typename... Mapped>
FOLLY_EXPORT explicit UniqueInstance(
char const* tmpl,
tag_t<Key...>,
tag_t<Mapped...>) noexcept {
static Ptr const ptrs[] = {&typeid(Key)..., &typeid(Mapped)...};
auto& global = createGlobal<Value, tag_t<Tag, Key...>>();
enforce(tmpl, ptrs, sizeof...(Key), sizeof...(Mapped), global);
}
auto const type_s = demangle(type.name());
auto const tag_s = demangle(tag.name());
auto const make_0_s = demangle(value.make->name());
auto const make_1_s = demangle(make.name());
auto const tltag_0_s = demangle(value.tltag->name());
auto const tltag_1_s = demangle(tltag.name());
std::ios_base::Init io_init;
std::cerr << "Overloaded folly::SingletonThreadLocal<" << type_s << ", "
<< tag_s << ", ...> with differing trailing arguments:\n"
<< " folly::SingletonThreadLocal<" << type_s << ", " << tag_s
<< ", " << make_0_s << ", " << tltag_0_s << ">\n"
<< " folly::SingletonThreadLocal<" << type_s << ", " << tag_s
<< ", " << make_1_s << ", " << tltag_1_s << ">\n";
std::abort();
}
UniqueInstance(UniqueInstance const&) = delete;
UniqueInstance(UniqueInstance&&) = delete;
UniqueInstance& operator=(UniqueInstance const&) = delete;
UniqueInstance& operator=(UniqueInstance&&) = delete;
} // namespace detail
private:
struct Tag {};
using Ptr = std::type_info const*;
struct PtrRange {
Ptr const* b;
Ptr const* e;
};
struct Value {
char const* tmpl;
Ptr const* ptrs;
std::uint32_t key_size;
std::uint32_t mapped_size;
};
// Under Clang, this call signature shrinks the aligned and padded size of
// call-sites, as compared to a call signature taking Value or Value const&.
static void enforce(
char const* tmpl,
Ptr const* ptrs,
std::uint32_t key_size,
std::uint32_t mapped_size,
Value& global) noexcept;
};
} // namespace detail
} // namespace folly
/*
* Copyright 2019-present 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.
*/
#include <folly/detail/UniqueInstance.h>
#include <tuple>
#include <folly/String.h>
#include <folly/Traits.h>
#include <folly/portability/GTest.h>
struct Key1 {};
struct Key2 {};
struct TagA {};
struct TagB {};
namespace folly {
namespace detail {
class UniqueInstanceDeathTest : public testing::Test {};
template <typename... Key, typename... Mapped>
static void make(char const* tmpl, tag_t<Key...> key, tag_t<Mapped...> mapped) {
UniqueInstance _(tmpl, key, mapped);
std::ignore = _;
}
TEST_F(UniqueInstanceDeathTest, basic) {
constexpr auto const tname = "tname";
make(tname, tag_t<Key1, Key2>{}, tag_t<TagA, TagB>{});
make(tname, tag_t<Key1, Key2>{}, tag_t<TagA, TagB>{}); // same everything
make(tname, tag_t<Key2, Key1>{}, tag_t<TagA, TagB>{}); // different key
EXPECT_DEATH( // different name
make("wrong", tag_t<Key1, Key2>{}, tag_t<TagA, TagB>{}),
stripLeftMargin(R"MESSAGE(
Overloaded unique instance over <Key1, Key2, ...> with differing trailing arguments:
tname<Key1, Key2, TagA, TagB>
wrong<Key1, Key2, TagA, TagB>
)MESSAGE"));
EXPECT_DEATH( // different mapped
make(tname, tag_t<Key1, Key2>{}, tag_t<TagB, TagA>{}),
stripLeftMargin(R"MESSAGE(
Overloaded unique instance over <Key1, Key2, ...> with differing trailing arguments:
tname<Key1, Key2, TagA, TagB>
tname<Key1, Key2, TagB, TagA>
)MESSAGE"));
}
} // namespace detail
} // namespace folly
......@@ -229,7 +229,7 @@ TEST(SingletonThreadLocalDeathTest, Overload) {
auto exe = fs::executable_path();
auto lib = exe.parent_path() / "singleton_thread_local_overload.so";
auto message = stripLeftMargin(R"MESSAGE(
Overloaded folly::SingletonThreadLocal<int, DeathTag, ...> with differing trailing arguments:
Overloaded unique instance over <int, DeathTag, ...> with differing trailing arguments:
folly::SingletonThreadLocal<int, DeathTag, Make1, DeathTag>
folly::SingletonThreadLocal<int, DeathTag, Make2, DeathTag>
)MESSAGE");
......
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