proxygen
ThreadLocalTest.cpp File Reference
#include <folly/ThreadLocal.h>
#include <dlfcn.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <array>
#include <atomic>
#include <chrono>
#include <climits>
#include <condition_variable>
#include <map>
#include <memory>
#include <mutex>
#include <set>
#include <thread>
#include <unordered_map>
#include <glog/logging.h>
#include <folly/Memory.h>
#include <folly/experimental/io/FsUtil.h>
#include <folly/portability/GTest.h>
#include <folly/portability/Unistd.h>
#include <folly/synchronization/Baton.h>
#include <folly/synchronization/detail/ThreadCachedInts.h>
#include <folly/system/ThreadId.h>

Go to the source code of this file.

Classes

class  Widget
 
struct  MultiWidget
 
class  SimpleThreadCachedInt
 
struct  HoldsOneTag2
 
struct  folly::threadlocal_detail::PthreadKeyUnregisterTester
 

Namespaces

 folly
 —— Concurrent Priority Queue Implementation ——
 
 folly::threadlocal_detail
 

Macros

#define SHARED_LIBRARY_TEST_NAME   DISABLED_SharedLibrary
 

Functions

 TEST (ThreadLocalPtr, BasicDestructor)
 
 TEST (ThreadLocalPtr, CustomDeleter1)
 
 TEST (ThreadLocalPtr, CustomDeleterOwnershipTransfer)
 
 TEST (ThreadLocalPtr, DefaultDeleterOwnershipTransfer)
 
 TEST (ThreadLocalPtr, resetNull)
 
 TEST (ThreadLocalPtr, TestRelease)
 
 TEST (ThreadLocalPtr, CreateOnThreadExit)
 
 TEST (ThreadLocalPtr, CustomDeleter2)
 
 TEST (ThreadLocal, BasicDestructor)
 
 TEST (ThreadLocal, ReallocDestructor)
 
 TEST (ThreadLocal, SimpleRepeatDestructor)
 
 TEST (ThreadLocal, InterleavedDestructors)
 
 TEST (ThreadLocalPtr, AccessAllThreadsCounter)
 
 TEST (ThreadLocal, resetNull)
 
 TEST (ThreadLocal, Movable1)
 
 TEST (ThreadLocal, Movable2)
 
 TEST (ThreadLocal, TCICreateOnThreadExit)
 
 TEST (ThreadLocal, Stress)
 
 TEST (ThreadLocal, Fork2)
 
 TEST (ThreadLocal, DISABLED_SharedLibrary)
 
 TEST (ThreadLocal, UnregisterClassHasConstExprCtor)
 

Macro Definition Documentation

#define SHARED_LIBRARY_TEST_NAME   DISABLED_SharedLibrary

Definition at line 667 of file ThreadLocalTest.cpp.

Function Documentation

TEST ( ThreadLocalPtr  ,
BasicDestructor   
)

Definition at line 81 of file ThreadLocalTest.cpp.

References EXPECT_EQ, folly::ThreadLocalPtr< T, Tag, AccessMode >::get(), folly::join(), folly::ThreadLocalPtr< T, Tag, AccessMode >::reset(), and Widget::totalVal_.

81  {
84  std::thread([&w]() {
85  w.reset(new Widget());
86  w.get()->val_ += 10;
87  })
88  .join();
90 }
void reset(T *newPtr=nullptr)
Definition: ThreadLocal.h:176
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
static int totalVal_
#define join
TEST ( ThreadLocalPtr  ,
CustomDeleter1   
)

Definition at line 92 of file ThreadLocalTest.cpp.

References Widget::customDeleter(), EXPECT_EQ, folly::ThreadLocalPtr< T, Tag, AccessMode >::get(), folly::join(), folly::ThreadLocalPtr< T, Tag, AccessMode >::reset(), and Widget::totalVal_.

92  {
94  {
96  std::thread([&w]() {
97  w.reset(new Widget(), Widget::customDeleter);
98  w.get()->val_ += 10;
99  })
100  .join();
102  }
104 }
void reset(T *newPtr=nullptr)
Definition: ThreadLocal.h:176
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
static void customDeleter(Widget *w, TLPDestructionMode mode)
static int totalVal_
#define join
TEST ( ThreadLocalPtr  ,
CustomDeleterOwnershipTransfer   
)

Definition at line 106 of file ThreadLocalTest.cpp.

References Widget::customDeleter(), EXPECT_EQ, folly::ThreadLocalPtr< T, Tag, AccessMode >::get(), folly::join(), folly::gen::move, ptr, folly::ThreadLocalPtr< T, Tag, AccessMode >::reset(), folly::THIS_THREAD, and Widget::totalVal_.

106  {
107  Widget::totalVal_ = 0;
108  {
110  auto deleter = [](Widget* ptr) {
111  Widget::customDeleter(ptr, TLPDestructionMode::THIS_THREAD);
112  };
113  std::unique_ptr<Widget, decltype(deleter)> source(new Widget(), deleter);
114  std::thread([&w, &source]() {
115  w.reset(std::move(source));
116  w.get()->val_ += 10;
117  })
118  .join();
120  }
122 }
void * ptr
void reset(T *newPtr=nullptr)
Definition: ThreadLocal.h:176
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
static void customDeleter(Widget *w, TLPDestructionMode mode)
static int totalVal_
#define join
TEST ( ThreadLocalPtr  ,
DefaultDeleterOwnershipTransfer   
)

Definition at line 124 of file ThreadLocalTest.cpp.

References EXPECT_EQ, folly::ThreadLocalPtr< T, Tag, AccessMode >::get(), folly::join(), folly::gen::move, folly::ThreadLocalPtr< T, Tag, AccessMode >::reset(), and Widget::totalVal_.

124  {
125  Widget::totalVal_ = 0;
126  {
128  auto source = std::make_unique<Widget>();
129  std::thread([&w, &source]() {
130  w.reset(std::move(source));
131  w.get()->val_ += 10;
132  })
133  .join();
135  }
137 }
void reset(T *newPtr=nullptr)
Definition: ThreadLocal.h:176
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
static int totalVal_
#define join
TEST ( ThreadLocalPtr  ,
resetNull   
)

Definition at line 139 of file ThreadLocalTest.cpp.

References EXPECT_EQ, EXPECT_FALSE, EXPECT_TRUE, folly::ThreadLocalPtr< T, Tag, AccessMode >::get(), and folly::ThreadLocalPtr< T, Tag, AccessMode >::reset().

139  {
141  EXPECT_FALSE(tl);
142  tl.reset(new int(4));
143  EXPECT_TRUE(static_cast<bool>(tl));
144  EXPECT_EQ(*tl.get(), 4);
145  tl.reset();
146  EXPECT_FALSE(tl);
147 }
void reset(T *newPtr=nullptr)
Definition: ThreadLocal.h:176
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
#define EXPECT_TRUE(condition)
Definition: gtest.h:1859
#define EXPECT_FALSE(condition)
Definition: gtest.h:1862
TEST ( ThreadLocalPtr  ,
TestRelease   
)

Definition at line 149 of file ThreadLocalTest.cpp.

References EXPECT_EQ, folly::ThreadLocalPtr< T, Tag, AccessMode >::get(), folly::join(), folly::ThreadLocalPtr< T, Tag, AccessMode >::release(), folly::ThreadLocalPtr< T, Tag, AccessMode >::reset(), and Widget::totalVal_.

149  {
150  Widget::totalVal_ = 0;
152  std::unique_ptr<Widget> wPtr;
153  std::thread([&w, &wPtr]() {
154  w.reset(new Widget());
155  w.get()->val_ += 10;
156 
157  wPtr.reset(w.release());
158  })
159  .join();
161  wPtr.reset();
163 }
void reset(T *newPtr=nullptr)
Definition: ThreadLocal.h:176
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
static int totalVal_
#define join
TEST ( ThreadLocalPtr  ,
CreateOnThreadExit   
)

Definition at line 165 of file ThreadLocalTest.cpp.

References EXPECT_EQ, folly::ThreadLocal< T, Tag, AccessMode >::get(), folly::join(), ptr, folly::ThreadLocalPtr< T, Tag, AccessMode >::reset(), and Widget::totalVal_.

165  {
166  Widget::totalVal_ = 0;
169 
170  std::thread([&] {
171  tl.reset(new int(1), [&](int* ptr, TLPDestructionMode /* mode */) {
172  delete ptr;
173  // This test ensures Widgets allocated here are not leaked.
174  ++w.get()->val_;
176  ++wl.get()->val_;
177  });
178  })
179  .join();
181 }
void * ptr
void reset(T *newPtr=nullptr)
Definition: ThreadLocal.h:176
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
FOLLY_ALWAYS_INLINE FOLLY_ATTR_VISIBILITY_HIDDEN T * get() const
Definition: ThreadLocal.h:69
static int totalVal_
#define join
TEST ( ThreadLocalPtr  ,
CustomDeleter2   
)

Definition at line 184 of file ThreadLocalTest.cpp.

References Widget::customDeleter(), EXPECT_EQ, folly::ThreadLocalPtr< T, Tag, AccessMode >::get(), folly::lock(), mutex, folly::ThreadLocalPtr< T, Tag, AccessMode >::reset(), folly::pushmi::detail::t, and Widget::totalVal_.

184  {
185  Widget::totalVal_ = 0;
186  std::thread t;
188  std::condition_variable cv;
189  enum class State {
190  START,
191  DONE,
192  EXIT,
193  };
194  State state = State::START;
195  {
197  t = std::thread([&]() {
198  w.reset(new Widget(), Widget::customDeleter);
199  w.get()->val_ += 10;
200 
201  // Notify main thread that we're done
202  {
203  std::unique_lock<std::mutex> lock(mutex);
204  state = State::DONE;
205  cv.notify_all();
206  }
207 
208  // Wait for main thread to allow us to exit
209  {
210  std::unique_lock<std::mutex> lock(mutex);
211  while (state != State::EXIT) {
212  cv.wait(lock);
213  }
214  }
215  });
216 
217  // Wait for main thread to start (and set w.get()->val_)
218  {
219  std::unique_lock<std::mutex> lock(mutex);
220  while (state != State::DONE) {
221  cv.wait(lock);
222  }
223  }
224 
225  // Thread started but hasn't exited yet
227 
228  // Destroy ThreadLocalPtr<Widget> (by letting it go out of scope)
229  }
230 
232 
233  // Allow thread to exit
234  {
235  std::unique_lock<std::mutex> lock(mutex);
236  state = State::EXIT;
237  cv.notify_all();
238  }
239  t.join();
240 
242 }
void reset(T *newPtr=nullptr)
Definition: ThreadLocal.h:176
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
State
See Core for details.
Definition: Core.h:43
auto lock(SynchronizedLocker...lockersIn) -> std::tuple< typename SynchronizedLocker::LockedPtr... >
Definition: Synchronized.h:871
static void customDeleter(Widget *w, TLPDestructionMode mode)
static int totalVal_
std::mutex mutex
state
Definition: http_parser.c:272
TEST ( ThreadLocal  ,
BasicDestructor   
)

Definition at line 244 of file ThreadLocalTest.cpp.

References EXPECT_EQ, folly::join(), and Widget::totalVal_.

244  {
245  Widget::totalVal_ = 0;
247  std::thread([&w]() { w->val_ += 10; }).join();
249 }
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
static int totalVal_
#define join
TEST ( ThreadLocal  ,
ReallocDestructor   
)

Definition at line 252 of file ThreadLocalTest.cpp.

References folly::join(), and MultiWidget::val_.

252  {
254  std::thread([&w]() { w->val_ += 10; }).join();
255 }
#define join
TEST ( ThreadLocal  ,
SimpleRepeatDestructor   
)

Definition at line 257 of file ThreadLocalTest.cpp.

References EXPECT_EQ, and Widget::totalVal_.

257  {
258  Widget::totalVal_ = 0;
259  {
261  w->val_ += 10;
262  }
263  {
265  w->val_ += 10;
266  }
268 }
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
static int totalVal_
TEST ( ThreadLocal  ,
InterleavedDestructors   
)

Definition at line 270 of file ThreadLocalTest.cpp.

References EXPECT_EQ, FOR_EACH_RANGE, g(), i, folly::lock(), mutex, and Widget::totalVal_.

270  {
271  Widget::totalVal_ = 0;
272  std::unique_ptr<ThreadLocal<Widget>> w;
273  int wVersion = 0;
274  const int wVersionMax = 2;
275  int thIter = 0;
277  auto th = std::thread([&]() {
278  int wVersionPrev = 0;
279  while (true) {
280  while (true) {
281  std::lock_guard<std::mutex> g(lock);
282  if (wVersion > wVersionMax) {
283  return;
284  }
285  if (wVersion > wVersionPrev) {
286  // We have a new version of w, so it should be initialized to zero
287  EXPECT_EQ((*w)->val_, 0);
288  break;
289  }
290  }
291  std::lock_guard<std::mutex> g(lock);
292  wVersionPrev = wVersion;
293  (*w)->val_ += 10;
294  ++thIter;
295  }
296  });
297  FOR_EACH_RANGE (i, 0, wVersionMax) {
298  int thIterPrev = 0;
299  {
300  std::lock_guard<std::mutex> g(lock);
301  thIterPrev = thIter;
302  w = std::make_unique<ThreadLocal<Widget>>();
303  ++wVersion;
304  }
305  while (true) {
306  std::lock_guard<std::mutex> g(lock);
307  if (thIter > thIterPrev) {
308  break;
309  }
310  }
311  }
312  {
313  std::lock_guard<std::mutex> g(lock);
314  wVersion = wVersionMax + 1;
315  }
316  th.join();
317  EXPECT_EQ(wVersionMax * 10, Widget::totalVal_);
318 }
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
#define FOR_EACH_RANGE(i, begin, end)
Definition: Foreach.h:313
auto lock(SynchronizedLocker...lockersIn) -> std::tuple< typename SynchronizedLocker::LockedPtr... >
Definition: Synchronized.h:871
static int totalVal_
std::mutex mutex
g_t g(f_t)
TEST ( ThreadLocalPtr  ,
AccessAllThreadsCounter   
)

Definition at line 338 of file ThreadLocalTest.cpp.

References SimpleThreadCachedInt::add(), EXPECT_EQ, i, kNumThreads, fizz::detail::read(), folly::run(), folly::pushmi::detail::t, and threads.

338  {
339  const int kNumThreads = 256;
340  SimpleThreadCachedInt stci[kNumThreads + 1];
341  std::atomic<bool> run(true);
342  std::atomic<int> totalAtomic{0};
343  std::vector<std::thread> threads;
344  // thread i will increment all the thread locals
345  // in the range 0..i
346  for (int i = 0; i < kNumThreads; ++i) {
347  threads.push_back(std::thread([i, // i needs to be captured by value
348  &stci,
349  &run,
350  &totalAtomic]() {
351  for (int j = 0; j <= i; j++) {
352  stci[j].add(1);
353  }
354 
355  totalAtomic.fetch_add(1);
356  while (run.load()) {
357  usleep(100);
358  }
359  }));
360  }
361  while (totalAtomic.load() != kNumThreads) {
362  usleep(100);
363  }
364  for (int i = 0; i <= kNumThreads; i++) {
365  EXPECT_EQ(kNumThreads - i, stci[i].read());
366  }
367  run.store(false);
368  for (auto& t : threads) {
369  t.join();
370  }
371 }
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
static size_t const kNumThreads
std::vector< std::thread::id > threads
static void run(EventBaseManager *ebm, EventBase *eb, folly::Baton<> *stop, const StringPiece &name)
size_t read(T &out, folly::io::Cursor &cursor)
Definition: Types-inl.h:258
TEST ( ThreadLocal  ,
resetNull   
)

Definition at line 373 of file ThreadLocalTest.cpp.

References EXPECT_EQ, folly::ThreadLocal< T, Tag, AccessMode >::get(), and folly::ThreadLocal< T, Tag, AccessMode >::reset().

373  {
374  ThreadLocal<int> tl;
375  tl.reset(new int(4));
376  EXPECT_EQ(*tl.get(), 4);
377  tl.reset();
378  EXPECT_EQ(*tl.get(), 0);
379  tl.reset(new int(5));
380  EXPECT_EQ(*tl.get(), 5);
381 }
void reset(T *newPtr=nullptr)
Definition: ThreadLocal.h:82
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
FOLLY_ALWAYS_INLINE FOLLY_ATTR_VISIBILITY_HIDDEN T * get() const
Definition: ThreadLocal.h:69
TEST ( ThreadLocal  ,
Movable1   
)

Definition at line 391 of file ThreadLocalTest.cpp.

References a, b, and EXPECT_TRUE.

391  {
392  Foo a;
393  Foo b;
394  EXPECT_TRUE(a.tl.get() != b.tl.get());
395 
396  a = Foo();
397  b = Foo();
398  EXPECT_TRUE(a.tl.get() != b.tl.get());
399 }
char b
char a
#define EXPECT_TRUE(condition)
Definition: gtest.h:1859
TEST ( ThreadLocal  ,
Movable2   
)

Definition at line 401 of file ThreadLocalTest.cpp.

References EXPECT_EQ, m, and map().

401  {
402  std::map<int, Foo> map;
403 
404  map[42];
405  map[10];
406  map[23];
407  map[100];
408 
409  std::set<void*> tls;
410  for (auto& m : map) {
411  tls.insert(m.second.tl.get());
412  }
413 
414  // Make sure that we have 4 different instances of *tl
415  EXPECT_EQ(4, tls.size());
416 }
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
static Map map(mapCap)
static map< string, int > m
TEST ( ThreadLocal  ,
TCICreateOnThreadExit   
)

Definition at line 438 of file ThreadLocalTest.cpp.

References check(), data_, folly::getCurrentThreadID(), i, folly::join(), uint64_t, v, and val.

438  {
439  detail::ThreadCachedInts<void> ints;
441 
442  std::thread([&] {
443  // make sure the ints object is created
444  ints.increment(1);
445  // now the widget
446  w->set(&ints);
447  })
448  .join();
449 }
#define join
TEST ( ThreadLocal  ,
Stress   
)

Definition at line 494 of file ThreadLocalTest.cpp.

References folly::ThreadLocal< T, Tag, AccessMode >::accessAllThreads(), ADD_FAILURE, EXPECT_EQ, EXPECT_TRUE, i, k, folly::lock(), mutex, ptr, folly::pushmi::detail::t, folly::TEST(), threads, and folly::value().

494  {
495  static constexpr size_t numFillObjects = 250;
496  std::array<ThreadLocalPtr<FillObject>, numFillObjects> objects;
497 
498  static constexpr size_t numThreads = 32;
499  static constexpr size_t numReps = 20;
500 
501  std::vector<std::thread> threads;
502  threads.reserve(numThreads);
503 
504  for (size_t k = 0; k < numThreads; ++k) {
505  threads.emplace_back([&objects] {
506  for (size_t rep = 0; rep < numReps; ++rep) {
507  for (size_t i = 0; i < objects.size(); ++i) {
508  objects[i].reset(new FillObject(rep * objects.size() + i));
509  std::this_thread::sleep_for(std::chrono::microseconds(100));
510  }
511  for (size_t i = 0; i < objects.size(); ++i) {
512  objects[i]->check();
513  }
514  }
515  });
516  }
517 
518  for (auto& t : threads) {
519  t.join();
520  }
521 
522  EXPECT_EQ(numFillObjects * numThreads * numReps, gDestroyed);
523 }
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
std::vector< std::thread::id > threads
KeyT k
TEST ( ThreadLocal  ,
Fork2   
)

Definition at line 632 of file ThreadLocalTest.cpp.

References ADD_FAILURE, EXPECT_EQ, EXPECT_TRUE, folly::ThreadLocal< T, Tag, AccessMode >::get(), and folly::pushmi::detail::t.

632  {
633  // A thread-local tag that was used in the parent from a *different* thread
634  // (but not the forking thread) would cause the child to hang in a
635  // ThreadLocalPtr's object destructor. Yeah.
637  {
638  // use tag in different thread
639  std::thread t([&p] { p.get(); });
640  t.join();
641  }
642  pid_t pid = fork();
643  if (pid == 0) {
644  {
646  q.get();
647  }
648  _exit(0);
649  } else if (pid > 0) {
650  int status;
651  EXPECT_EQ(pid, waitpid(pid, &status, 0));
652  EXPECT_TRUE(WIFEXITED(status));
653  EXPECT_EQ(0, WEXITSTATUS(status));
654  } else {
655  ADD_FAILURE() << "fork failed";
656  }
657 }
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
FOLLY_ALWAYS_INLINE FOLLY_ATTR_VISIBILITY_HIDDEN T * get() const
Definition: ThreadLocal.h:69
#define EXPECT_TRUE(condition)
Definition: gtest.h:1859
#define ADD_FAILURE()
Definition: gtest.h:1808
TEST ( ThreadLocal  ,
DISABLED_SharedLibrary   
)

Definition at line 672 of file ThreadLocalTest.cpp.

References ASSERT_NE, exe, folly::fs::executable_path(), EXPECT_EQ, lib, folly::Baton< MayBlock, Atom >::post(), useA(), and folly::Baton< MayBlock, Atom >::wait().

672  {
673  auto exe = fs::executable_path();
674  auto lib = exe.parent_path() / "thread_local_test_lib.so";
675  auto handle = dlopen(lib.string().c_str(), RTLD_LAZY);
676  ASSERT_NE(nullptr, handle)
677  << "unable to load " << lib.string() << ": " << dlerror();
678 
679  typedef void (*useA_t)();
680  dlerror();
681  useA_t useA = (useA_t)dlsym(handle, "useA");
682 
683  const char* dlsym_error = dlerror();
684  EXPECT_EQ(nullptr, dlsym_error);
685  ASSERT_NE(nullptr, useA);
686 
687  useA();
688 
689  folly::Baton<> b11, b12, b21, b22;
690 
691  std::thread t1([&]() {
692  useA();
693  b11.post();
694  b12.wait();
695  });
696 
697  std::thread t2([&]() {
698  useA();
699  b21.post();
700  b22.wait();
701  });
702 
703  b11.wait();
704  b21.wait();
705 
706  dlclose(handle);
707 
708  b12.post();
709  b22.post();
710 
711  t1.join();
712  t2.join();
713 }
path executable_path()
Definition: FsUtil.cpp:72
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
void useA()
FOLLY_ALWAYS_INLINE void wait(const WaitOptions &opt=wait_options()) noexcept
Definition: Baton.h:170
void post() noexcept
Definition: Baton.h:123
InlineExecutor exe
Definition: Benchmark.cpp:337
#define ASSERT_NE(val1, val2)
Definition: gtest.h:1960
#define lib
TEST ( ThreadLocal  ,
UnregisterClassHasConstExprCtor   
)

Definition at line 726 of file ThreadLocalTest.cpp.

References SUCCEED, and x.