1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <folly/ThreadLocal.h>
18 
19 #ifndef _WIN32
20 #include <dlfcn.h>
21 #include <sys/wait.h>
22 #endif
23 
24 #include <sys/types.h>
25 
26 #include <array>
27 #include <atomic>
28 #include <chrono>
29 #include <climits>
30 #include <condition_variable>
31 #include <map>
32 #include <memory>
33 #include <mutex>
34 #include <set>
35 #include <thread>
36 #include <unordered_map>
37 
38 #include <boost/thread/barrier.hpp>
39 #include <glog/logging.h>
40 
41 #include <folly/Memory.h>
42 #include <folly/experimental/io/FsUtil.h>
43 #include <folly/portability/GTest.h>
44 #include <folly/portability/Unistd.h>
45 #include <folly/synchronization/Baton.h>
46 #include <folly/synchronization/detail/ThreadCachedInts.h>
47 #include <folly/system/ThreadId.h>
48 
49 using namespace folly;
50 
51 struct Widget {
52   static int totalVal_;
53   static int totalMade_;
54   int val_;
WidgetWidget55   Widget() : val_(0) { totalMade_++; }
~WidgetWidget56   ~Widget() { totalVal_ += val_; }
57 
customDeleterWidget58   static void customDeleter(Widget* w, TLPDestructionMode mode) {
59     totalVal_ += (mode == TLPDestructionMode::ALL_THREADS) ? 1000 : 1;
60     delete w;
61   }
62 };
63 int Widget::totalVal_ = 0;
64 int Widget::totalMade_ = 0;
65 
66 struct MultiWidget {
67   int val_{0};
68   MultiWidget() = default;
~MultiWidgetMultiWidget69   ~MultiWidget() {
70     // force a reallocation in the destructor by
71     // allocating more than elementsCapacity
72 
73     using TL = ThreadLocal<size_t>;
74     using TLMeta = threadlocal_detail::static_meta_of<TL>::type;
75     auto const numElements = TLMeta::instance().elementsCapacity() + 1;
76     std::vector<ThreadLocal<size_t>> elems(numElements);
77     for (auto& t : elems) {
78       *t += 1;
79     }
80   }
81 };
82 
TEST(ThreadLocalPtr,BasicDestructor)83 TEST(ThreadLocalPtr, BasicDestructor) {
84   Widget::totalVal_ = 0;
85   ThreadLocalPtr<Widget> w;
86   std::thread([&w]() {
87     w.reset(new Widget());
88     w.get()->val_ += 10;
89   }).join();
90   EXPECT_EQ(10, Widget::totalVal_);
91 }
92 
TEST(ThreadLocalPtr,CustomDeleter1)93 TEST(ThreadLocalPtr, CustomDeleter1) {
94   Widget::totalVal_ = 0;
95   {
96     ThreadLocalPtr<Widget> w;
97     std::thread([&w]() {
98       w.reset(new Widget(), Widget::customDeleter);
99       w.get()->val_ += 10;
100     }).join();
101     EXPECT_EQ(11, Widget::totalVal_);
102   }
103   EXPECT_EQ(11, Widget::totalVal_);
104 }
105 
TEST(ThreadLocalPtr,CustomDeleterOwnershipTransfer)106 TEST(ThreadLocalPtr, CustomDeleterOwnershipTransfer) {
107   Widget::totalVal_ = 0;
108   {
109     ThreadLocalPtr<Widget> w;
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     }).join();
118     EXPECT_EQ(11, Widget::totalVal_);
119   }
120   EXPECT_EQ(11, Widget::totalVal_);
121 }
122 
TEST(ThreadLocalPtr,DefaultDeleterOwnershipTransfer)123 TEST(ThreadLocalPtr, DefaultDeleterOwnershipTransfer) {
124   Widget::totalVal_ = 0;
125   {
126     ThreadLocalPtr<Widget> w;
127     auto source = std::make_unique<Widget>();
128     std::thread([&w, &source]() {
129       w.reset(std::move(source));
130       w.get()->val_ += 10;
131     }).join();
132     EXPECT_EQ(10, Widget::totalVal_);
133   }
134   EXPECT_EQ(10, Widget::totalVal_);
135 }
136 
TEST(ThreadLocalPtr,resetNull)137 TEST(ThreadLocalPtr, resetNull) {
138   ThreadLocalPtr<int> tl;
139   EXPECT_FALSE(tl);
140   tl.reset(new int(4));
141   EXPECT_TRUE(static_cast<bool>(tl));
142   EXPECT_EQ(*tl.get(), 4);
143   tl.reset();
144   EXPECT_FALSE(tl);
145 }
146 
TEST(ThreadLocalPtr,TestRelease)147 TEST(ThreadLocalPtr, TestRelease) {
148   Widget::totalVal_ = 0;
149   ThreadLocalPtr<Widget> w;
150   std::unique_ptr<Widget> wPtr;
151   std::thread([&w, &wPtr]() {
152     w.reset(new Widget());
153     w.get()->val_ += 10;
154 
155     wPtr.reset(w.release());
156   }).join();
157   EXPECT_EQ(0, Widget::totalVal_);
158   wPtr.reset();
159   EXPECT_EQ(10, Widget::totalVal_);
160 }
161 
TEST(ThreadLocalPtr,CreateOnThreadExit)162 TEST(ThreadLocalPtr, CreateOnThreadExit) {
163   Widget::totalVal_ = 0;
164   ThreadLocal<Widget> w;
165   ThreadLocalPtr<int> tl;
166 
167   std::thread([&] {
168     tl.reset(new int(1), [&](int* ptr, TLPDestructionMode /* mode */) {
169       delete ptr;
170       // This test ensures Widgets allocated here are not leaked.
171       ++w.get()->val_;
172       ThreadLocal<Widget> wl;
173       ++wl.get()->val_;
174     });
175   }).join();
176   EXPECT_EQ(2, Widget::totalVal_);
177 }
178 
179 // Test deleting the ThreadLocalPtr object
TEST(ThreadLocalPtr,CustomDeleter2)180 TEST(ThreadLocalPtr, CustomDeleter2) {
181   Widget::totalVal_ = 0;
182   std::thread t;
183   std::mutex mutex;
184   std::condition_variable cv;
185   enum class State {
186     START,
187     DONE,
188     EXIT,
189   };
190   State state = State::START;
191   {
192     ThreadLocalPtr<Widget> w;
193     t = std::thread([&]() {
194       w.reset(new Widget(), Widget::customDeleter);
195       w.get()->val_ += 10;
196 
197       // Notify main thread that we're done
198       {
199         std::unique_lock<std::mutex> lock(mutex);
200         state = State::DONE;
201         cv.notify_all();
202       }
203 
204       // Wait for main thread to allow us to exit
205       {
206         std::unique_lock<std::mutex> lock(mutex);
207         while (state != State::EXIT) {
208           cv.wait(lock);
209         }
210       }
211     });
212 
213     // Wait for main thread to start (and set w.get()->val_)
214     {
215       std::unique_lock<std::mutex> lock(mutex);
216       while (state != State::DONE) {
217         cv.wait(lock);
218       }
219     }
220 
221     // Thread started but hasn't exited yet
222     EXPECT_EQ(0, Widget::totalVal_);
223 
224     // Destroy ThreadLocalPtr<Widget> (by letting it go out of scope)
225   }
226 
227   EXPECT_EQ(1010, Widget::totalVal_);
228 
229   // Allow thread to exit
230   {
231     std::unique_lock<std::mutex> lock(mutex);
232     state = State::EXIT;
233     cv.notify_all();
234   }
235   t.join();
236 
237   EXPECT_EQ(1010, Widget::totalVal_);
238 }
239 
TEST(ThreadLocal,GetWithoutCreateUncreated)240 TEST(ThreadLocal, GetWithoutCreateUncreated) {
241   Widget::totalVal_ = 0;
242   Widget::totalMade_ = 0;
243   ThreadLocal<Widget> w;
244   std::thread([&w]() {
245     auto ptr = w.getIfExist();
246     if (ptr) {
247       ptr->val_++;
248     }
249   }).join();
250   EXPECT_EQ(0, Widget::totalMade_);
251 }
252 
TEST(ThreadLocal,GetWithoutCreateGets)253 TEST(ThreadLocal, GetWithoutCreateGets) {
254   Widget::totalVal_ = 0;
255   Widget::totalMade_ = 0;
256   ThreadLocal<Widget> w;
257   std::thread([&w]() {
258     w->val_++;
259     auto ptr = w.getIfExist();
260     if (ptr) {
261       ptr->val_++;
262     }
263   }).join();
264   EXPECT_EQ(1, Widget::totalMade_);
265   EXPECT_EQ(2, Widget::totalVal_);
266 }
267 
TEST(ThreadLocal,BasicDestructor)268 TEST(ThreadLocal, BasicDestructor) {
269   Widget::totalVal_ = 0;
270   ThreadLocal<Widget> w;
271   std::thread([&w]() { w->val_ += 10; }).join();
272   EXPECT_EQ(10, Widget::totalVal_);
273 }
274 
275 // this should force a realloc of the ElementWrapper array
TEST(ThreadLocal,ReallocDestructor)276 TEST(ThreadLocal, ReallocDestructor) {
277   ThreadLocal<MultiWidget> w;
278   std::thread([&w]() { w->val_ += 10; }).join();
279 }
280 
TEST(ThreadLocal,SimpleRepeatDestructor)281 TEST(ThreadLocal, SimpleRepeatDestructor) {
282   Widget::totalVal_ = 0;
283   {
284     ThreadLocal<Widget> w;
285     w->val_ += 10;
286   }
287   {
288     ThreadLocal<Widget> w;
289     w->val_ += 10;
290   }
291   EXPECT_EQ(20, Widget::totalVal_);
292 }
293 
TEST(ThreadLocal,InterleavedDestructors)294 TEST(ThreadLocal, InterleavedDestructors) {
295   Widget::totalVal_ = 0;
296   std::unique_ptr<ThreadLocal<Widget>> w;
297   int wVersion = 0;
298   const int wVersionMax = 2;
299   int thIter = 0;
300   std::mutex lock;
301   auto th = std::thread([&]() {
302     int wVersionPrev = 0;
303     while (true) {
304       while (true) {
305         std::lock_guard<std::mutex> g(lock);
306         if (wVersion > wVersionMax) {
307           return;
308         }
309         if (wVersion > wVersionPrev) {
310           // We have a new version of w, so it should be initialized to zero
311           EXPECT_EQ((*w)->val_, 0);
312           break;
313         }
314       }
315       std::lock_guard<std::mutex> g(lock);
316       wVersionPrev = wVersion;
317       (*w)->val_ += 10;
318       ++thIter;
319     }
320   });
321   FOR_EACH_RANGE (i, 0, wVersionMax) {
322     int thIterPrev = 0;
323     {
324       std::lock_guard<std::mutex> g(lock);
325       thIterPrev = thIter;
326       w = std::make_unique<ThreadLocal<Widget>>();
327       ++wVersion;
328     }
329     while (true) {
330       std::lock_guard<std::mutex> g(lock);
331       if (thIter > thIterPrev) {
332         break;
333       }
334     }
335   }
336   {
337     std::lock_guard<std::mutex> g(lock);
338     wVersion = wVersionMax + 1;
339   }
340   th.join();
341   EXPECT_EQ(wVersionMax * 10, Widget::totalVal_);
342 }
343 
344 class SimpleThreadCachedInt {
345   class NewTag;
346   ThreadLocal<int, NewTag> val_;
347 
348  public:
add(int val)349   void add(int val) { *val_ += val; }
350 
read()351   int read() {
352     int ret = 0;
353     for (const auto& i : val_.accessAllThreads()) {
354       ret += i;
355     }
356     return ret;
357   }
358 };
359 
TEST(ThreadLocalPtr,AccessAllThreadsCounter)360 TEST(ThreadLocalPtr, AccessAllThreadsCounter) {
361   const int kNumThreads = 256;
362   SimpleThreadCachedInt stci[kNumThreads + 1];
363   std::atomic<bool> run(true);
364   std::atomic<int> totalAtomic{0};
365   std::vector<std::thread> threads;
366   // thread i will increment all the thread locals
367   // in the range 0..i
368   for (int i = 0; i < kNumThreads; ++i) {
369     threads.push_back(std::thread([i, // i needs to be captured by value
370                                    &stci,
371                                    &run,
372                                    &totalAtomic]() {
373       for (int j = 0; j <= i; j++) {
374         stci[j].add(1);
375       }
376 
377       totalAtomic.fetch_add(1);
378       while (run.load()) {
379         usleep(100);
380       }
381     }));
382   }
383   while (totalAtomic.load() != kNumThreads) {
384     usleep(100);
385   }
386   for (int i = 0; i <= kNumThreads; i++) {
387     EXPECT_EQ(kNumThreads - i, stci[i].read());
388   }
389   run.store(false);
390   for (auto& t : threads) {
391     t.join();
392   }
393 }
394 
TEST(ThreadLocal,resetNull)395 TEST(ThreadLocal, resetNull) {
396   ThreadLocal<int> tl;
397   tl.reset(new int(4));
398   EXPECT_EQ(*tl.get(), 4);
399   tl.reset();
400   EXPECT_EQ(*tl.get(), 0);
401   tl.reset(new int(5));
402   EXPECT_EQ(*tl.get(), 5);
403 }
404 
405 namespace {
406 struct Tag {};
407 
408 struct Foo {
409   folly::ThreadLocal<int, Tag> tl;
410 };
411 } // namespace
412 
TEST(ThreadLocal,Movable1)413 TEST(ThreadLocal, Movable1) {
414   Foo a;
415   Foo b;
416   EXPECT_TRUE(a.tl.get() != b.tl.get());
417 
418   a = Foo();
419   b = Foo();
420   EXPECT_TRUE(a.tl.get() != b.tl.get());
421 }
422 
TEST(ThreadLocal,Movable2)423 TEST(ThreadLocal, Movable2) {
424   std::map<int, Foo> map;
425 
426   map[42];
427   map[10];
428   map[23];
429   map[100];
430 
431   std::set<void*> tls;
432   for (auto& m : map) {
433     tls.insert(m.second.tl.get());
434   }
435 
436   // Make sure that we have 4 different instances of *tl
437   EXPECT_EQ(4, tls.size());
438 }
439 
440 namespace {
441 class ThreadCachedIntWidget {
442  public:
ThreadCachedIntWidget()443   ThreadCachedIntWidget() {}
444 
~ThreadCachedIntWidget()445   ~ThreadCachedIntWidget() {
446     if (ints_) {
447       ints_->increment(0);
448     }
449   }
450 
set(detail::ThreadCachedInts<void> * ints)451   void set(detail::ThreadCachedInts<void>* ints) { ints_ = ints; }
452 
453  private:
454   detail::ThreadCachedInts<void>* ints_{nullptr};
455 };
456 } // namespace
457 
TEST(ThreadLocal,TCICreateOnThreadExit)458 TEST(ThreadLocal, TCICreateOnThreadExit) {
459   detail::ThreadCachedInts<void> ints;
460   ThreadLocal<ThreadCachedIntWidget> w;
461 
462   std::thread([&] {
463     // make sure the ints object is created
464     ints.increment(1);
465     // now the widget
466     w->set(&ints);
467   }).join();
468 }
469 
470 namespace {
471 
472 constexpr size_t kFillObjectSize = 300;
473 
474 std::atomic<uint64_t> gDestroyed;
475 
476 /**
477  * Fill a chunk of memory with a unique-ish pattern that includes the thread id
478  * (so deleting one of these from another thread would cause a failure)
479  *
480  * Verify it explicitly and on destruction.
481  */
482 class FillObject {
483  public:
FillObject(uint64_t idx)484   explicit FillObject(uint64_t idx) : idx_(idx) {
485     uint64_t v = val();
486     for (size_t i = 0; i < kFillObjectSize; ++i) {
487       data_[i] = v;
488     }
489   }
490 
check()491   void check() {
492     uint64_t v = val();
493     for (size_t i = 0; i < kFillObjectSize; ++i) {
494       CHECK_EQ(v, data_[i]);
495     }
496   }
497 
~FillObject()498   ~FillObject() { ++gDestroyed; }
499 
500  private:
val() const501   uint64_t val() const { return (idx_ << 40) | folly::getCurrentThreadID(); }
502 
503   uint64_t idx_;
504   uint64_t data_[kFillObjectSize];
505 };
506 
507 } // namespace
508 
TEST(ThreadLocal,Stress)509 TEST(ThreadLocal, Stress) {
510   static constexpr size_t numFillObjects = 250;
511   std::array<ThreadLocalPtr<FillObject>, numFillObjects> objects;
512 
513   static constexpr size_t numThreads = 32;
514   static constexpr size_t numReps = 20;
515 
516   std::vector<std::thread> threads;
517   threads.reserve(numThreads);
518 
519   for (size_t k = 0; k < numThreads; ++k) {
520     threads.emplace_back([&objects] {
521       for (size_t rep = 0; rep < numReps; ++rep) {
522         for (size_t i = 0; i < objects.size(); ++i) {
523           objects[i].reset(new FillObject(rep * objects.size() + i));
524           std::this_thread::sleep_for(std::chrono::microseconds(100));
525         }
526         for (size_t i = 0; i < objects.size(); ++i) {
527           objects[i]->check();
528         }
529       }
530     });
531   }
532 
533   for (auto& t : threads) {
534     t.join();
535   }
536 
537   EXPECT_EQ(numFillObjects * numThreads * numReps, gDestroyed);
538 }
539 
540 struct StressAccessTag {};
541 using TLPInt = ThreadLocalPtr<int, Tag>;
542 
tlpIntCustomDeleter(int * p,TLPDestructionMode)543 static void tlpIntCustomDeleter(int* p, TLPDestructionMode /*unused*/) {
544   delete p;
545 }
546 
547 template <typename Op, typename Check>
StresAccessTest(Op op,Check check)548 void StresAccessTest(Op op, Check check) {
549   static constexpr size_t kNumThreads = 16;
550   static constexpr size_t kNumLoops = 10000;
551 
552   TLPInt ptr;
553   ptr.reset(new int(0));
554   std::atomic<bool> running{true};
555 
556   boost::barrier barrier(kNumThreads + 1);
557 
558   std::vector<std::thread> threads;
559 
560   for (size_t k = 0; k < kNumThreads; ++k) {
561     threads.emplace_back([&] {
562       ptr.reset(new int(1));
563 
564       barrier.wait();
565 
566       while (running.load()) {
567         op(ptr);
568       }
569     });
570   }
571 
572   // wait for the threads to be up and running
573   barrier.wait();
574 
575   for (size_t n = 0; n < kNumLoops; n++) {
576     int sum = 0;
577     auto accessor = ptr.accessAllThreads();
578     for (auto& i : accessor) {
579       sum += i;
580     }
581 
582     check(sum, kNumThreads);
583   }
584 
585   running.store(false);
586   for (auto& t : threads) {
587     t.join();
588   }
589 }
590 
TEST(ThreadLocal,StressAccessReset)591 TEST(ThreadLocal, StressAccessReset) {
592   StresAccessTest(
593       [](TLPInt& ptr) { ptr.reset(new int(1)); },
594       [](size_t sum, size_t numThreads) { EXPECT_EQ(sum, numThreads); });
595 }
596 
TEST(ThreadLocal,StressAccessResetDeleter)597 TEST(ThreadLocal, StressAccessResetDeleter) {
598   StresAccessTest(
599       [](TLPInt& ptr) { ptr.reset(new int(1), tlpIntCustomDeleter); },
600       [](size_t sum, size_t numThreads) { EXPECT_EQ(sum, numThreads); });
601 }
602 
TEST(ThreadLocal,StressAccessRelease)603 TEST(ThreadLocal, StressAccessRelease) {
604   StresAccessTest(
605       [](TLPInt& ptr) {
606         auto* p = ptr.release();
607         delete p;
608         ptr.reset(new int(1));
609       },
610       [](size_t sum, size_t numThreads) { EXPECT_LE(sum, numThreads); });
611 }
612 
613 // Yes, threads and fork don't mix
614 // (http://cppwisdom.quora.com/Why-threads-and-fork-dont-mix) but if you're
615 // stupid or desperate enough to try, we shouldn't stand in your way.
616 namespace {
617 class HoldsOne {
618  public:
HoldsOne()619   HoldsOne() : value_(1) {}
620   // Do an actual access to catch the buggy case where this == nullptr
value() const621   int value() const { return value_; }
622 
623  private:
624   int value_;
625 };
626 
627 struct HoldsOneTag {};
628 
629 ThreadLocal<HoldsOne, HoldsOneTag> ptr;
630 
totalValue()631 int totalValue() {
632   int value = 0;
633   for (auto& p : ptr.accessAllThreads()) {
634     value += p.value();
635   }
636   return value;
637 }
638 
639 } // namespace
640 
641 #ifdef FOLLY_HAVE_PTHREAD_ATFORK
TEST(ThreadLocal,Fork)642 TEST(ThreadLocal, Fork) {
643   EXPECT_EQ(1, ptr->value()); // ensure created
644   EXPECT_EQ(1, totalValue());
645   // Spawn a new thread
646 
647   std::mutex mutex;
648   bool started = false;
649   std::condition_variable startedCond;
650   bool stopped = false;
651   std::condition_variable stoppedCond;
652 
653   std::thread t([&]() {
654     EXPECT_EQ(1, ptr->value()); // ensure created
655     {
656       std::unique_lock<std::mutex> lock(mutex);
657       started = true;
658       startedCond.notify_all();
659     }
660     {
661       std::unique_lock<std::mutex> lock(mutex);
662       while (!stopped) {
663         stoppedCond.wait(lock);
664       }
665     }
666   });
667 
668   {
669     std::unique_lock<std::mutex> lock(mutex);
670     while (!started) {
671       startedCond.wait(lock);
672     }
673   }
674 
675   EXPECT_EQ(2, totalValue());
676 
677   pid_t pid = fork();
678   if (pid == 0) {
679     // in child
680     int v = totalValue();
681 
682     // exit successfully if v == 1 (one thread)
683     // diagnostic error code otherwise :)
684     switch (v) {
685       case 1:
686         _exit(0);
687       case 0:
688         _exit(1);
689     }
690     _exit(2);
691   } else if (pid > 0) {
692     // in parent
693     int status;
694     EXPECT_EQ(pid, waitpid(pid, &status, 0));
695     EXPECT_TRUE(WIFEXITED(status));
696     EXPECT_EQ(0, WEXITSTATUS(status));
697   } else {
698     ADD_FAILURE() << "fork failed";
699   }
700 
701   EXPECT_EQ(2, totalValue());
702 
703   {
704     std::unique_lock<std::mutex> lock(mutex);
705     stopped = true;
706     stoppedCond.notify_all();
707   }
708 
709   t.join();
710 
711   EXPECT_EQ(1, totalValue());
712 }
713 #endif
714 
715 #ifndef _WIN32
716 struct HoldsOneTag2 {};
717 
TEST(ThreadLocal,Fork2)718 TEST(ThreadLocal, Fork2) {
719   // A thread-local tag that was used in the parent from a *different* thread
720   // (but not the forking thread) would cause the child to hang in a
721   // ThreadLocalPtr's object destructor. Yeah.
722   ThreadLocal<HoldsOne, HoldsOneTag2> p;
723   {
724     // use tag in different thread
725     std::thread t([&p] { p.get(); });
726     t.join();
727   }
728   pid_t pid = fork();
729   if (pid == 0) {
730     {
731       ThreadLocal<HoldsOne, HoldsOneTag2> q;
732       q.get();
733     }
734     _exit(0);
735   } else if (pid > 0) {
736     int status;
737     EXPECT_EQ(pid, waitpid(pid, &status, 0));
738     EXPECT_TRUE(WIFEXITED(status));
739     EXPECT_EQ(0, WEXITSTATUS(status));
740   } else {
741     ADD_FAILURE() << "fork failed";
742   }
743 }
744 
745 // Disable the SharedLibrary test when using any sanitizer. Otherwise, the
746 // dlopen'ed code would end up running without e.g., ASAN-initialized data
747 // structures and failing right away.
748 //
749 // We also cannot run this test unless folly was compiled with PIC support,
750 // since we cannot build thread_local_test_lib.so without PIC.
751 #if defined FOLLY_SANITIZE_ADDRESS || defined FOLLY_SANITIZE_THREAD || \
752     !defined FOLLY_SUPPORT_SHARED_LIBRARY
753 #define SHARED_LIBRARY_TEST_NAME DISABLED_SharedLibrary
754 #else
755 #define SHARED_LIBRARY_TEST_NAME SharedLibrary
756 #endif
757 
TEST(ThreadLocal,SHARED_LIBRARY_TEST_NAME)758 TEST(ThreadLocal, SHARED_LIBRARY_TEST_NAME) {
759   auto exe = fs::executable_path();
760   auto lib = exe.parent_path() / "thread_local_test_lib.so";
761   auto handle = dlopen(lib.string().c_str(), RTLD_LAZY);
762   ASSERT_NE(nullptr, handle)
763       << "unable to load " << lib.string() << ": " << dlerror();
764 
765   typedef void (*useA_t)();
766   dlerror();
767   useA_t useA = (useA_t)dlsym(handle, "useA");
768 
769   const char* dlsym_error = dlerror();
770   EXPECT_EQ(nullptr, dlsym_error);
771   ASSERT_NE(nullptr, useA);
772 
773   useA();
774 
775   folly::Baton<> b11, b12, b21, b22;
776 
777   std::thread t1([&]() {
778     useA();
779     b11.post();
780     b12.wait();
781   });
782 
783   std::thread t2([&]() {
784     useA();
785     b21.post();
786     b22.wait();
787   });
788 
789   b11.wait();
790   b21.wait();
791 
792   dlclose(handle);
793 
794   b12.post();
795   b22.post();
796 
797   t1.join();
798   t2.join();
799 }
800 
801 #endif
802 
803 namespace folly {
804 namespace threadlocal_detail {
805 struct PthreadKeyUnregisterTester {
806   PthreadKeyUnregister p;
807   constexpr PthreadKeyUnregisterTester() = default;
808 };
809 } // namespace threadlocal_detail
810 } // namespace folly
811 
TEST(ThreadLocal,UnregisterClassHasConstExprCtor)812 TEST(ThreadLocal, UnregisterClassHasConstExprCtor) {
813   folly::threadlocal_detail::PthreadKeyUnregisterTester x;
814   // yep!
815   SUCCEED();
816 }
817