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

Enforce SingletonThreadLocal uniqueness

Summary: [Folly] Enforce `SingletonThreadLocal` uniqueness per type and tag. Forbid duplicate uses with differing make/tltag.

Reviewed By: andriigrynenko

Differential Revision: D13213730

fbshipit-source-id: f051ac10667ee394545b56816646a721a68e33e5
parent 19292278
/*
* Copyright 2016-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/SingletonThreadLocal.h>
#include <cstdlib>
#include <iostream>
#include <folly/Demangle.h>
namespace folly {
namespace detail {
SingletonThreadLocalBase::UniqueBase::UniqueBase(
Ref type,
Ref tag,
Ref make,
Ref tltag,
Value& value) {
if (!value.init) {
value.init = true;
value.make = &make;
value.tltag = &tltag;
}
if (*value.make == make && *value.tltag == tltag) {
return;
}
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();
}
} // namespace detail
} // namespace folly
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
#pragma once #pragma once
#include <typeinfo>
#include <boost/intrusive/list.hpp> #include <boost/intrusive/list.hpp>
#include <folly/ScopeGuard.h> #include <folly/ScopeGuard.h>
...@@ -25,6 +27,35 @@ ...@@ -25,6 +27,35 @@
namespace folly { 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(TypeTuple<T, Tag, Make, TLTag>)
: UniqueBase(
typeid(T),
typeid(Tag),
typeid(Make),
typeid(TLTag),
detail::createGlobal<Value, TypeTuple<T, Tag, UniqueBase>>()) {}
UniqueBase(Ref type, Ref tag, Ref make, Ref tltag, Value& value);
};
};
} // namespace detail
/// SingletonThreadLocal /// SingletonThreadLocal
/// ///
/// Useful for a per-thread leaky-singleton model in libraries and applications. /// Useful for a per-thread leaky-singleton model in libraries and applications.
...@@ -65,8 +96,13 @@ template < ...@@ -65,8 +96,13 @@ template <
std::is_same<Tag, detail::DefaultTag>::value, std::is_same<Tag, detail::DefaultTag>::value,
void, void,
Tag>>> Tag>>>
class SingletonThreadLocal { class SingletonThreadLocal : private detail::SingletonThreadLocalBase {
private: private:
struct Unique final : UniqueBase {
Unique() : UniqueBase(detail::TypeTuple<T, Tag, Make, TLTag>{}) {}
};
static Unique unique;
struct Wrapper; struct Wrapper;
using NodeBase = boost::intrusive::list_base_hook< using NodeBase = boost::intrusive::list_base_hook<
...@@ -128,6 +164,7 @@ class SingletonThreadLocal { ...@@ -128,6 +164,7 @@ class SingletonThreadLocal {
#ifdef FOLLY_TLS #ifdef FOLLY_TLS
FOLLY_NOINLINE static T& getSlow(Wrapper*& cache) { FOLLY_NOINLINE static T& getSlow(Wrapper*& cache) {
(void)unique; // force the object not to be thrown out as unused
static thread_local Wrapper** check = &cache; static thread_local Wrapper** check = &cache;
CHECK_EQ(check, &cache) << "inline function static thread_local merging"; CHECK_EQ(check, &cache) << "inline function static thread_local merging";
static thread_local bool stale; static thread_local bool stale;
...@@ -152,6 +189,10 @@ class SingletonThreadLocal { ...@@ -152,6 +189,10 @@ class SingletonThreadLocal {
} }
}; };
template <typename T, typename Tag, typename Make, typename TLTag>
typename SingletonThreadLocal<T, Tag, Make, TLTag>::Unique
SingletonThreadLocal<T, Tag, Make, TLTag>::unique;
} // namespace folly } // namespace folly
/// FOLLY_DECLARE_REUSED /// FOLLY_DECLARE_REUSED
......
...@@ -52,5 +52,8 @@ struct DefaultMake { ...@@ -52,5 +52,8 @@ struct DefaultMake {
} }
}; };
template <typename...>
struct TypeTuple {};
} // namespace detail } // namespace detail
} // namespace folly } // namespace folly
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <folly/CPortability.h> #include <folly/CPortability.h>
#include <folly/Likely.h> #include <folly/Likely.h>
#include <folly/detail/Singleton.h>
namespace folly { namespace folly {
namespace detail { namespace detail {
...@@ -34,16 +35,13 @@ class StaticSingletonManager { ...@@ -34,16 +35,13 @@ class StaticSingletonManager {
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(TypePair<T, Tag>); auto const& key = typeid(TypeTuple<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);
} }
private: private:
template <typename A, typename B>
struct TypePair {};
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*>;
......
...@@ -14,12 +14,18 @@ ...@@ -14,12 +14,18 @@
* limitations under the License. * limitations under the License.
*/ */
#ifndef _WIN32
#include <dlfcn.h>
#endif
#include <thread> #include <thread>
#include <unordered_set> #include <unordered_set>
#include <vector> #include <vector>
#include <folly/SingletonThreadLocal.h> #include <folly/SingletonThreadLocal.h>
#include <folly/String.h>
#include <folly/Synchronized.h> #include <folly/Synchronized.h>
#include <folly/experimental/io/FsUtil.h>
#include <folly/portability/GTest.h> #include <folly/portability/GTest.h>
using namespace folly; using namespace folly;
...@@ -179,3 +185,16 @@ TEST(SingletonThreadLocalTest, Reused) { ...@@ -179,3 +185,16 @@ TEST(SingletonThreadLocalTest, Reused) {
EXPECT_EQ(i == 0u ? "hello" : "", data); EXPECT_EQ(i == 0u ? "hello" : "", data);
} }
} }
#ifndef _WIN32
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:
folly::SingletonThreadLocal<int, DeathTag, Make1, DeathTag>
folly::SingletonThreadLocal<int, DeathTag, Make2, DeathTag>
)MESSAGE");
EXPECT_DEATH(dlopen(lib.string().c_str(), RTLD_LAZY), message);
}
#endif
/*
* Copyright 2016-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/SingletonThreadLocal.h>
using namespace folly;
namespace folly {
using Make = detail::DefaultMake<int>;
} // namespace folly
struct Make1 : Make {};
struct Make2 : Make {};
struct DeathTag {};
int stl_get_sum() {
auto& i1 = SingletonThreadLocal<int, DeathTag, Make1>::get();
auto& i2 = SingletonThreadLocal<int, DeathTag, Make2>::get();
return i1 + i2;
}
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