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