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