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

Let SingletonThreadLocal::accessAllThreads give the right type

Summary: [Folly] Let `SingletonThreadLocal::accessAllThreads` give the right type.

Reviewed By: andriigrynenko

Differential Revision: D10474930

fbshipit-source-id: 3fbe2e8ef6ace7d40d774f66b830ae40d810ef9d
parent 2233b5c6
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <folly/ScopeGuard.h> #include <folly/ScopeGuard.h>
#include <folly/ThreadLocal.h> #include <folly/ThreadLocal.h>
#include <folly/detail/Iterators.h>
#include <folly/detail/Singleton.h> #include <folly/detail/Singleton.h>
#include <folly/functional/Invoke.h> #include <folly/functional/Invoke.h>
...@@ -183,9 +184,52 @@ class SingletonThreadLocal : private detail::SingletonThreadLocalBase { ...@@ -183,9 +184,52 @@ class SingletonThreadLocal : private detail::SingletonThreadLocalBase {
#endif #endif
} }
class Accessor {
private:
using Inner = typename WrapperTL::Accessor;
using IteratorBase = typename Inner::Iterator;
using IteratorTag = std::bidirectional_iterator_tag;
Inner inner_;
explicit Accessor(Inner inner) noexcept : inner_(std::move(inner)) {}
public:
friend class SingletonThreadLocal<T, Tag, Make, TLTag>;
class Iterator
: public detail::
IteratorAdaptor<Iterator, IteratorBase, T, IteratorTag> {
private:
using Super =
detail::IteratorAdaptor<Iterator, IteratorBase, T, IteratorTag>;
using Super::Super;
public:
friend class Accessor;
T& dereference() const {
return const_cast<Iterator*>(this)->base()->object;
}
};
Accessor(const Accessor&) = delete;
Accessor& operator=(const Accessor&) = delete;
Accessor(Accessor&&) = default;
Accessor& operator=(Accessor&&) = default;
Iterator begin() const {
return Iterator(inner_.begin());
}
Iterator end() const {
return Iterator(inner_.end());
}
};
// Must use a unique Tag, takes a lock that is one per Tag // Must use a unique Tag, takes a lock that is one per Tag
static typename WrapperTL::Accessor accessAllThreads() { static Accessor accessAllThreads() {
return getWrapperTL().accessAllThreads(); return Accessor(getWrapperTL().accessAllThreads());
} }
}; };
......
...@@ -22,6 +22,8 @@ ...@@ -22,6 +22,8 @@
#include <unordered_set> #include <unordered_set>
#include <vector> #include <vector>
#include <boost/thread/barrier.hpp>
#include <folly/SingletonThreadLocal.h> #include <folly/SingletonThreadLocal.h>
#include <folly/String.h> #include <folly/String.h>
#include <folly/Synchronized.h> #include <folly/Synchronized.h>
...@@ -186,6 +188,42 @@ TEST(SingletonThreadLocalTest, Reused) { ...@@ -186,6 +188,42 @@ TEST(SingletonThreadLocalTest, Reused) {
} }
} }
TEST(SingletonThreadLocalTest, AccessAllThreads) {
struct Tag {};
struct Obj {
size_t value;
};
using STL = SingletonThreadLocal<Obj, Tag>;
constexpr size_t n_threads = 4;
boost::barrier barrier{n_threads + 1};
std::vector<std::thread> threads{n_threads};
// setup
std::atomic<size_t> count{0};
for (auto& thread : threads) {
thread = std::thread([&] {
STL::get().value = ++count;
barrier.wait();
barrier.wait();
});
}
barrier.wait();
EXPECT_EQ(n_threads, count) << "sanity";
// expectations
size_t sum = 0;
for (auto& obj : STL::accessAllThreads()) { // explicitly auto
sum += obj.value;
}
EXPECT_EQ((n_threads * (n_threads + 1) / 2), sum);
// cleanup
barrier.wait();
for (auto& thread : threads) {
thread.join();
}
}
#ifndef _WIN32 #ifndef _WIN32
TEST(SingletonThreadLocalDeathTest, Overload) { TEST(SingletonThreadLocalDeathTest, Overload) {
auto exe = fs::executable_path(); auto exe = fs::executable_path();
......
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