folly/test/ThreadLocalTest.cpp: avoid shadowing warnings
[folly.git] / folly / test / ThreadLocalTest.cpp
1 /*
2  * Copyright 2016 Facebook, Inc.
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 <condition_variable>
30 #include <limits.h>
31 #include <map>
32 #include <mutex>
33 #include <set>
34 #include <thread>
35 #include <unordered_map>
36
37 #include <glog/logging.h>
38
39 #include <folly/Baton.h>
40 #include <folly/Memory.h>
41 #include <folly/experimental/io/FsUtil.h>
42 #include <folly/portability/GTest.h>
43 #include <folly/portability/Unistd.h>
44
45 using namespace folly;
46
47 struct Widget {
48   static int totalVal_;
49   int val_;
50   ~Widget() {
51     totalVal_ += val_;
52   }
53
54   static void customDeleter(Widget* w, TLPDestructionMode mode) {
55     totalVal_ += (mode == TLPDestructionMode::ALL_THREADS) ? 1000 : 1;
56     delete w;
57   }
58 };
59 int Widget::totalVal_ = 0;
60
61 TEST(ThreadLocalPtr, BasicDestructor) {
62   Widget::totalVal_ = 0;
63   ThreadLocalPtr<Widget> w;
64   std::thread([&w]() {
65       w.reset(new Widget());
66       w.get()->val_ += 10;
67     }).join();
68   EXPECT_EQ(10, Widget::totalVal_);
69 }
70
71 TEST(ThreadLocalPtr, CustomDeleter1) {
72   Widget::totalVal_ = 0;
73   {
74     ThreadLocalPtr<Widget> w;
75     std::thread([&w]() {
76         w.reset(new Widget(), Widget::customDeleter);
77         w.get()->val_ += 10;
78       }).join();
79     EXPECT_EQ(11, Widget::totalVal_);
80   }
81   EXPECT_EQ(11, Widget::totalVal_);
82 }
83
84 TEST(ThreadLocalPtr, CustomDeleterOwnershipTransfer) {
85   Widget::totalVal_ = 0;
86   {
87     ThreadLocalPtr<Widget> w;
88     auto deleter = [](Widget* ptr) {
89       Widget::customDeleter(ptr, TLPDestructionMode::THIS_THREAD);
90     };
91     std::unique_ptr<Widget, decltype(deleter)> source(new Widget(), deleter);
92     std::thread([&w, &source]() {
93       w.reset(std::move(source));
94       w.get()->val_ += 10;
95     }).join();
96     EXPECT_EQ(11, Widget::totalVal_);
97   }
98   EXPECT_EQ(11, Widget::totalVal_);
99 }
100
101 TEST(ThreadLocalPtr, DefaultDeleterOwnershipTransfer) {
102   Widget::totalVal_ = 0;
103   {
104     ThreadLocalPtr<Widget> w;
105     auto source = folly::make_unique<Widget>();
106     std::thread([&w, &source]() {
107       w.reset(std::move(source));
108       w.get()->val_ += 10;
109     }).join();
110     EXPECT_EQ(10, Widget::totalVal_);
111   }
112   EXPECT_EQ(10, Widget::totalVal_);
113 }
114
115 TEST(ThreadLocalPtr, resetNull) {
116   ThreadLocalPtr<int> tl;
117   EXPECT_FALSE(tl);
118   tl.reset(new int(4));
119   EXPECT_TRUE(static_cast<bool>(tl));
120   EXPECT_EQ(*tl.get(), 4);
121   tl.reset();
122   EXPECT_FALSE(tl);
123 }
124
125 TEST(ThreadLocalPtr, TestRelease) {
126   Widget::totalVal_ = 0;
127   ThreadLocalPtr<Widget> w;
128   std::unique_ptr<Widget> wPtr;
129   std::thread([&w, &wPtr]() {
130       w.reset(new Widget());
131       w.get()->val_ += 10;
132
133       wPtr.reset(w.release());
134     }).join();
135   EXPECT_EQ(0, Widget::totalVal_);
136   wPtr.reset();
137   EXPECT_EQ(10, Widget::totalVal_);
138 }
139
140 TEST(ThreadLocalPtr, CreateOnThreadExit) {
141   Widget::totalVal_ = 0;
142   ThreadLocal<Widget> w;
143   ThreadLocalPtr<int> tl;
144
145   std::thread([&] {
146     tl.reset(new int(1),
147              [&](int* ptr, TLPDestructionMode /* mode */) {
148                delete ptr;
149                // This test ensures Widgets allocated here are not leaked.
150                ++w.get()->val_;
151                ThreadLocal<Widget> wl;
152                ++wl.get()->val_;
153              });
154   }).join();
155   EXPECT_EQ(2, Widget::totalVal_);
156 }
157
158 // Test deleting the ThreadLocalPtr object
159 TEST(ThreadLocalPtr, CustomDeleter2) {
160   Widget::totalVal_ = 0;
161   std::thread t;
162   std::mutex mutex;
163   std::condition_variable cv;
164   enum class State {
165     START,
166     DONE,
167     EXIT
168   };
169   State state = State::START;
170   {
171     ThreadLocalPtr<Widget> w;
172     t = std::thread([&]() {
173         w.reset(new Widget(), Widget::customDeleter);
174         w.get()->val_ += 10;
175
176         // Notify main thread that we're done
177         {
178           std::unique_lock<std::mutex> lock(mutex);
179           state = State::DONE;
180           cv.notify_all();
181         }
182
183         // Wait for main thread to allow us to exit
184         {
185           std::unique_lock<std::mutex> lock(mutex);
186           while (state != State::EXIT) {
187             cv.wait(lock);
188           }
189         }
190     });
191
192     // Wait for main thread to start (and set w.get()->val_)
193     {
194       std::unique_lock<std::mutex> lock(mutex);
195       while (state != State::DONE) {
196         cv.wait(lock);
197       }
198     }
199
200     // Thread started but hasn't exited yet
201     EXPECT_EQ(0, Widget::totalVal_);
202
203     // Destroy ThreadLocalPtr<Widget> (by letting it go out of scope)
204   }
205
206   EXPECT_EQ(1010, Widget::totalVal_);
207
208   // Allow thread to exit
209   {
210     std::unique_lock<std::mutex> lock(mutex);
211     state = State::EXIT;
212     cv.notify_all();
213   }
214   t.join();
215
216   EXPECT_EQ(1010, Widget::totalVal_);
217 }
218
219 TEST(ThreadLocal, BasicDestructor) {
220   Widget::totalVal_ = 0;
221   ThreadLocal<Widget> w;
222   std::thread([&w]() { w->val_ += 10; }).join();
223   EXPECT_EQ(10, Widget::totalVal_);
224 }
225
226 TEST(ThreadLocal, SimpleRepeatDestructor) {
227   Widget::totalVal_ = 0;
228   {
229     ThreadLocal<Widget> w;
230     w->val_ += 10;
231   }
232   {
233     ThreadLocal<Widget> w;
234     w->val_ += 10;
235   }
236   EXPECT_EQ(20, Widget::totalVal_);
237 }
238
239 TEST(ThreadLocal, InterleavedDestructors) {
240   Widget::totalVal_ = 0;
241   std::unique_ptr<ThreadLocal<Widget>> w;
242   int wVersion = 0;
243   const int wVersionMax = 2;
244   int thIter = 0;
245   std::mutex lock;
246   auto th = std::thread([&]() {
247     int wVersionPrev = 0;
248     while (true) {
249       while (true) {
250         std::lock_guard<std::mutex> g(lock);
251         if (wVersion > wVersionMax) {
252           return;
253         }
254         if (wVersion > wVersionPrev) {
255           // We have a new version of w, so it should be initialized to zero
256           EXPECT_EQ((*w)->val_, 0);
257           break;
258         }
259       }
260       std::lock_guard<std::mutex> g(lock);
261       wVersionPrev = wVersion;
262       (*w)->val_ += 10;
263       ++thIter;
264     }
265   });
266   FOR_EACH_RANGE(i, 0, wVersionMax) {
267     int thIterPrev = 0;
268     {
269       std::lock_guard<std::mutex> g(lock);
270       thIterPrev = thIter;
271       w.reset(new ThreadLocal<Widget>());
272       ++wVersion;
273     }
274     while (true) {
275       std::lock_guard<std::mutex> g(lock);
276       if (thIter > thIterPrev) {
277         break;
278       }
279     }
280   }
281   {
282     std::lock_guard<std::mutex> g(lock);
283     wVersion = wVersionMax + 1;
284   }
285   th.join();
286   EXPECT_EQ(wVersionMax * 10, Widget::totalVal_);
287 }
288
289 class SimpleThreadCachedInt {
290
291   class NewTag;
292   ThreadLocal<int,NewTag> val_;
293
294  public:
295   void add(int val) {
296     *val_ += val;
297   }
298
299   int read() {
300     int ret = 0;
301     for (const auto& i : val_.accessAllThreads()) {
302       ret += i;
303     }
304     return ret;
305   }
306 };
307
308 TEST(ThreadLocalPtr, AccessAllThreadsCounter) {
309   const int kNumThreads = 10;
310   SimpleThreadCachedInt stci;
311   std::atomic<bool> run(true);
312   std::atomic<int> totalAtomic(0);
313   std::vector<std::thread> threads;
314   for (int i = 0; i < kNumThreads; ++i) {
315     threads.push_back(std::thread([&,i]() {
316       stci.add(1);
317       totalAtomic.fetch_add(1);
318       while (run.load()) { usleep(100); }
319     }));
320   }
321   while (totalAtomic.load() != kNumThreads) { usleep(100); }
322   EXPECT_EQ(kNumThreads, stci.read());
323   run.store(false);
324   for (auto& t : threads) {
325     t.join();
326   }
327 }
328
329 TEST(ThreadLocal, resetNull) {
330   ThreadLocal<int> tl;
331   tl.reset(new int(4));
332   EXPECT_EQ(*tl.get(), 4);
333   tl.reset();
334   EXPECT_EQ(*tl.get(), 0);
335   tl.reset(new int(5));
336   EXPECT_EQ(*tl.get(), 5);
337 }
338
339 namespace {
340 struct Tag {};
341
342 struct Foo {
343   folly::ThreadLocal<int, Tag> tl;
344 };
345 }  // namespace
346
347 TEST(ThreadLocal, Movable1) {
348   Foo a;
349   Foo b;
350   EXPECT_TRUE(a.tl.get() != b.tl.get());
351
352   a = Foo();
353   b = Foo();
354   EXPECT_TRUE(a.tl.get() != b.tl.get());
355 }
356
357 TEST(ThreadLocal, Movable2) {
358   std::map<int, Foo> map;
359
360   map[42];
361   map[10];
362   map[23];
363   map[100];
364
365   std::set<void*> tls;
366   for (auto& m : map) {
367     tls.insert(m.second.tl.get());
368   }
369
370   // Make sure that we have 4 different instances of *tl
371   EXPECT_EQ(4, tls.size());
372 }
373
374 namespace {
375
376 constexpr size_t kFillObjectSize = 300;
377
378 std::atomic<uint64_t> gDestroyed;
379
380 /**
381  * Fill a chunk of memory with a unique-ish pattern that includes the thread id
382  * (so deleting one of these from another thread would cause a failure)
383  *
384  * Verify it explicitly and on destruction.
385  */
386 class FillObject {
387  public:
388   explicit FillObject(uint64_t idx) : idx_(idx) {
389     uint64_t v = val();
390     for (size_t i = 0; i < kFillObjectSize; ++i) {
391       data_[i] = v;
392     }
393   }
394
395   void check() {
396     uint64_t v = val();
397     for (size_t i = 0; i < kFillObjectSize; ++i) {
398       CHECK_EQ(v, data_[i]);
399     }
400   }
401
402   ~FillObject() {
403     ++gDestroyed;
404   }
405
406  private:
407   uint64_t val() const {
408     return (idx_ << 40) | uint64_t(pthread_self());
409   }
410
411   uint64_t idx_;
412   uint64_t data_[kFillObjectSize];
413 };
414
415 }  // namespace
416
417 #if FOLLY_HAVE_STD_THIS_THREAD_SLEEP_FOR
418 TEST(ThreadLocal, Stress) {
419   constexpr size_t numFillObjects = 250;
420   std::array<ThreadLocalPtr<FillObject>, numFillObjects> objects;
421
422   constexpr size_t numThreads = 32;
423   constexpr size_t numReps = 20;
424
425   std::vector<std::thread> threads;
426   threads.reserve(numThreads);
427
428   for (size_t k = 0; k < numThreads; ++k) {
429     threads.emplace_back([&objects] {
430       for (size_t rep = 0; rep < numReps; ++rep) {
431         for (size_t i = 0; i < objects.size(); ++i) {
432           objects[i].reset(new FillObject(rep * objects.size() + i));
433           std::this_thread::sleep_for(std::chrono::microseconds(100));
434         }
435         for (size_t i = 0; i < objects.size(); ++i) {
436           objects[i]->check();
437         }
438       }
439     });
440   }
441
442   for (auto& t : threads) {
443     t.join();
444   }
445
446   EXPECT_EQ(numFillObjects * numThreads * numReps, gDestroyed);
447 }
448 #endif
449
450 // Yes, threads and fork don't mix
451 // (http://cppwisdom.quora.com/Why-threads-and-fork-dont-mix) but if you're
452 // stupid or desperate enough to try, we shouldn't stand in your way.
453 namespace {
454 class HoldsOne {
455  public:
456   HoldsOne() : value_(1) { }
457   // Do an actual access to catch the buggy case where this == nullptr
458   int value() const { return value_; }
459  private:
460   int value_;
461 };
462
463 struct HoldsOneTag {};
464
465 ThreadLocal<HoldsOne, HoldsOneTag> ptr;
466
467 int totalValue() {
468   int value = 0;
469   for (auto& p : ptr.accessAllThreads()) {
470     value += p.value();
471   }
472   return value;
473 }
474
475 }  // namespace
476
477 #ifdef FOLLY_HAVE_PTHREAD_ATFORK
478 TEST(ThreadLocal, Fork) {
479   EXPECT_EQ(1, ptr->value());  // ensure created
480   EXPECT_EQ(1, totalValue());
481   // Spawn a new thread
482
483   std::mutex mutex;
484   bool started = false;
485   std::condition_variable startedCond;
486   bool stopped = false;
487   std::condition_variable stoppedCond;
488
489   std::thread t([&] () {
490     EXPECT_EQ(1, ptr->value());  // ensure created
491     {
492       std::unique_lock<std::mutex> lock(mutex);
493       started = true;
494       startedCond.notify_all();
495     }
496     {
497       std::unique_lock<std::mutex> lock(mutex);
498       while (!stopped) {
499         stoppedCond.wait(lock);
500       }
501     }
502   });
503
504   {
505     std::unique_lock<std::mutex> lock(mutex);
506     while (!started) {
507       startedCond.wait(lock);
508     }
509   }
510
511   EXPECT_EQ(2, totalValue());
512
513   pid_t pid = fork();
514   if (pid == 0) {
515     // in child
516     int v = totalValue();
517
518     // exit successfully if v == 1 (one thread)
519     // diagnostic error code otherwise :)
520     switch (v) {
521     case 1: _exit(0);
522     case 0: _exit(1);
523     }
524     _exit(2);
525   } else if (pid > 0) {
526     // in parent
527     int status;
528     EXPECT_EQ(pid, waitpid(pid, &status, 0));
529     EXPECT_TRUE(WIFEXITED(status));
530     EXPECT_EQ(0, WEXITSTATUS(status));
531   } else {
532     EXPECT_TRUE(false) << "fork failed";
533   }
534
535   EXPECT_EQ(2, totalValue());
536
537   {
538     std::unique_lock<std::mutex> lock(mutex);
539     stopped = true;
540     stoppedCond.notify_all();
541   }
542
543   t.join();
544
545   EXPECT_EQ(1, totalValue());
546 }
547 #endif
548
549 #ifndef _WIN32
550 struct HoldsOneTag2 {};
551
552 TEST(ThreadLocal, Fork2) {
553   // A thread-local tag that was used in the parent from a *different* thread
554   // (but not the forking thread) would cause the child to hang in a
555   // ThreadLocalPtr's object destructor. Yeah.
556   ThreadLocal<HoldsOne, HoldsOneTag2> p;
557   {
558     // use tag in different thread
559     std::thread t([&p] { p.get(); });
560     t.join();
561   }
562   pid_t pid = fork();
563   if (pid == 0) {
564     {
565       ThreadLocal<HoldsOne, HoldsOneTag2> q;
566       q.get();
567     }
568     _exit(0);
569   } else if (pid > 0) {
570     int status;
571     EXPECT_EQ(pid, waitpid(pid, &status, 0));
572     EXPECT_TRUE(WIFEXITED(status));
573     EXPECT_EQ(0, WEXITSTATUS(status));
574   } else {
575     EXPECT_TRUE(false) << "fork failed";
576   }
577 }
578
579 // Elide this test when using any sanitizer. Otherwise, the dlopen'ed code
580 // would end up running without e.g., ASAN-initialized data structures and
581 // failing right away.
582 #if !defined FOLLY_SANITIZE_ADDRESS && !defined UNDEFINED_SANITIZER && \
583     !defined FOLLY_SANITIZE_THREAD
584
585 TEST(ThreadLocal, SharedLibrary) {
586   auto exe = fs::executable_path();
587   auto lib = exe.parent_path() / "lib_thread_local_test.so";
588   auto handle = dlopen(lib.string().c_str(), RTLD_LAZY);
589   EXPECT_NE(nullptr, handle);
590
591   typedef void (*useA_t)();
592   dlerror();
593   useA_t useA = (useA_t) dlsym(handle, "useA");
594
595   const char *dlsym_error = dlerror();
596   EXPECT_EQ(nullptr, dlsym_error);
597
598   useA();
599
600   folly::Baton<> b11, b12, b21, b22;
601
602   std::thread t1([&]() {
603       useA();
604       b11.post();
605       b12.wait();
606     });
607
608   std::thread t2([&]() {
609       useA();
610       b21.post();
611       b22.wait();
612     });
613
614   b11.wait();
615   b21.wait();
616
617   dlclose(handle);
618
619   b12.post();
620   b22.post();
621
622   t1.join();
623   t2.join();
624 }
625
626 #endif
627 #endif
628
629 namespace folly { namespace threadlocal_detail {
630 struct PthreadKeyUnregisterTester {
631   PthreadKeyUnregister p;
632   constexpr PthreadKeyUnregisterTester() = default;
633 };
634 }}
635
636 TEST(ThreadLocal, UnregisterClassHasConstExprCtor) {
637   folly::threadlocal_detail::PthreadKeyUnregisterTester x;
638   // yep!
639   SUCCEED();
640 }