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