2 * Copyright 2017 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include <folly/ThreadLocal.h>
24 #include <sys/types.h>
30 #include <condition_variable>
36 #include <unordered_map>
38 #include <glog/logging.h>
40 #include <folly/Baton.h>
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/system/ThreadId.h>
47 using namespace folly;
56 static void customDeleter(Widget* w, TLPDestructionMode mode) {
57 totalVal_ += (mode == TLPDestructionMode::ALL_THREADS) ? 1000 : 1;
61 int Widget::totalVal_ = 0;
63 TEST(ThreadLocalPtr, BasicDestructor) {
64 Widget::totalVal_ = 0;
65 ThreadLocalPtr<Widget> w;
67 w.reset(new Widget());
70 EXPECT_EQ(10, Widget::totalVal_);
73 TEST(ThreadLocalPtr, CustomDeleter1) {
74 Widget::totalVal_ = 0;
76 ThreadLocalPtr<Widget> w;
78 w.reset(new Widget(), Widget::customDeleter);
81 EXPECT_EQ(11, Widget::totalVal_);
83 EXPECT_EQ(11, Widget::totalVal_);
86 TEST(ThreadLocalPtr, CustomDeleterOwnershipTransfer) {
87 Widget::totalVal_ = 0;
89 ThreadLocalPtr<Widget> w;
90 auto deleter = [](Widget* ptr) {
91 Widget::customDeleter(ptr, TLPDestructionMode::THIS_THREAD);
93 std::unique_ptr<Widget, decltype(deleter)> source(new Widget(), deleter);
94 std::thread([&w, &source]() {
95 w.reset(std::move(source));
98 EXPECT_EQ(11, Widget::totalVal_);
100 EXPECT_EQ(11, Widget::totalVal_);
103 TEST(ThreadLocalPtr, DefaultDeleterOwnershipTransfer) {
104 Widget::totalVal_ = 0;
106 ThreadLocalPtr<Widget> w;
107 auto source = std::make_unique<Widget>();
108 std::thread([&w, &source]() {
109 w.reset(std::move(source));
112 EXPECT_EQ(10, Widget::totalVal_);
114 EXPECT_EQ(10, Widget::totalVal_);
117 TEST(ThreadLocalPtr, resetNull) {
118 ThreadLocalPtr<int> tl;
120 tl.reset(new int(4));
121 EXPECT_TRUE(static_cast<bool>(tl));
122 EXPECT_EQ(*tl.get(), 4);
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());
135 wPtr.reset(w.release());
137 EXPECT_EQ(0, Widget::totalVal_);
139 EXPECT_EQ(10, Widget::totalVal_);
142 TEST(ThreadLocalPtr, CreateOnThreadExit) {
143 Widget::totalVal_ = 0;
144 ThreadLocal<Widget> w;
145 ThreadLocalPtr<int> tl;
149 [&](int* ptr, TLPDestructionMode /* mode */) {
151 // This test ensures Widgets allocated here are not leaked.
153 ThreadLocal<Widget> wl;
157 EXPECT_EQ(2, Widget::totalVal_);
160 // Test deleting the ThreadLocalPtr object
161 TEST(ThreadLocalPtr, CustomDeleter2) {
162 Widget::totalVal_ = 0;
165 std::condition_variable cv;
171 State state = State::START;
173 ThreadLocalPtr<Widget> w;
174 t = std::thread([&]() {
175 w.reset(new Widget(), Widget::customDeleter);
178 // Notify main thread that we're done
180 std::unique_lock<std::mutex> lock(mutex);
185 // Wait for main thread to allow us to exit
187 std::unique_lock<std::mutex> lock(mutex);
188 while (state != State::EXIT) {
194 // Wait for main thread to start (and set w.get()->val_)
196 std::unique_lock<std::mutex> lock(mutex);
197 while (state != State::DONE) {
202 // Thread started but hasn't exited yet
203 EXPECT_EQ(0, Widget::totalVal_);
205 // Destroy ThreadLocalPtr<Widget> (by letting it go out of scope)
208 EXPECT_EQ(1010, Widget::totalVal_);
210 // Allow thread to exit
212 std::unique_lock<std::mutex> lock(mutex);
218 EXPECT_EQ(1010, Widget::totalVal_);
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_);
228 TEST(ThreadLocal, SimpleRepeatDestructor) {
229 Widget::totalVal_ = 0;
231 ThreadLocal<Widget> w;
235 ThreadLocal<Widget> w;
238 EXPECT_EQ(20, Widget::totalVal_);
241 TEST(ThreadLocal, InterleavedDestructors) {
242 Widget::totalVal_ = 0;
243 std::unique_ptr<ThreadLocal<Widget>> w;
245 const int wVersionMax = 2;
248 auto th = std::thread([&]() {
249 int wVersionPrev = 0;
252 std::lock_guard<std::mutex> g(lock);
253 if (wVersion > wVersionMax) {
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);
262 std::lock_guard<std::mutex> g(lock);
263 wVersionPrev = wVersion;
268 FOR_EACH_RANGE(i, 0, wVersionMax) {
271 std::lock_guard<std::mutex> g(lock);
273 w = std::make_unique<ThreadLocal<Widget>>();
277 std::lock_guard<std::mutex> g(lock);
278 if (thIter > thIterPrev) {
284 std::lock_guard<std::mutex> g(lock);
285 wVersion = wVersionMax + 1;
288 EXPECT_EQ(wVersionMax * 10, Widget::totalVal_);
291 class SimpleThreadCachedInt {
294 ThreadLocal<int,NewTag> val_;
303 for (const auto& i : val_.accessAllThreads()) {
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([&]() {
319 totalAtomic.fetch_add(1);
325 while (totalAtomic.load() != kNumThreads) { usleep(100); }
326 EXPECT_EQ(kNumThreads, stci.read());
328 for (auto& t : threads) {
333 TEST(ThreadLocal, resetNull) {
335 tl.reset(new int(4));
336 EXPECT_EQ(*tl.get(), 4);
338 EXPECT_EQ(*tl.get(), 0);
339 tl.reset(new int(5));
340 EXPECT_EQ(*tl.get(), 5);
347 folly::ThreadLocal<int, Tag> tl;
351 TEST(ThreadLocal, Movable1) {
354 EXPECT_TRUE(a.tl.get() != b.tl.get());
358 EXPECT_TRUE(a.tl.get() != b.tl.get());
361 TEST(ThreadLocal, Movable2) {
362 std::map<int, Foo> map;
370 for (auto& m : map) {
371 tls.insert(m.second.tl.get());
374 // Make sure that we have 4 different instances of *tl
375 EXPECT_EQ(4, tls.size());
380 constexpr size_t kFillObjectSize = 300;
382 std::atomic<uint64_t> gDestroyed;
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)
388 * Verify it explicitly and on destruction.
392 explicit FillObject(uint64_t idx) : idx_(idx) {
394 for (size_t i = 0; i < kFillObjectSize; ++i) {
401 for (size_t i = 0; i < kFillObjectSize; ++i) {
402 CHECK_EQ(v, data_[i]);
411 uint64_t val() const {
412 return (idx_ << 40) | folly::getCurrentThreadID();
416 uint64_t data_[kFillObjectSize];
421 TEST(ThreadLocal, Stress) {
422 static constexpr size_t numFillObjects = 250;
423 std::array<ThreadLocalPtr<FillObject>, numFillObjects> objects;
425 static constexpr size_t numThreads = 32;
426 static constexpr size_t numReps = 20;
428 std::vector<std::thread> threads;
429 threads.reserve(numThreads);
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));
438 for (size_t i = 0; i < objects.size(); ++i) {
445 for (auto& t : threads) {
449 EXPECT_EQ(numFillObjects * numThreads * numReps, gDestroyed);
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.
458 HoldsOne() : value_(1) { }
459 // Do an actual access to catch the buggy case where this == nullptr
460 int value() const { return value_; }
465 struct HoldsOneTag {};
467 ThreadLocal<HoldsOne, HoldsOneTag> ptr;
471 for (auto& p : ptr.accessAllThreads()) {
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
486 bool started = false;
487 std::condition_variable startedCond;
488 bool stopped = false;
489 std::condition_variable stoppedCond;
491 std::thread t([&] () {
492 EXPECT_EQ(1, ptr->value()); // ensure created
494 std::unique_lock<std::mutex> lock(mutex);
496 startedCond.notify_all();
499 std::unique_lock<std::mutex> lock(mutex);
501 stoppedCond.wait(lock);
507 std::unique_lock<std::mutex> lock(mutex);
509 startedCond.wait(lock);
513 EXPECT_EQ(2, totalValue());
518 int v = totalValue();
520 // exit successfully if v == 1 (one thread)
521 // diagnostic error code otherwise :)
527 } else if (pid > 0) {
530 EXPECT_EQ(pid, waitpid(pid, &status, 0));
531 EXPECT_TRUE(WIFEXITED(status));
532 EXPECT_EQ(0, WEXITSTATUS(status));
534 ADD_FAILURE() << "fork failed";
537 EXPECT_EQ(2, totalValue());
540 std::unique_lock<std::mutex> lock(mutex);
542 stoppedCond.notify_all();
547 EXPECT_EQ(1, totalValue());
552 struct HoldsOneTag2 {};
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;
560 // use tag in different thread
561 std::thread t([&p] { p.get(); });
567 ThreadLocal<HoldsOne, HoldsOneTag2> q;
571 } else if (pid > 0) {
573 EXPECT_EQ(pid, waitpid(pid, &status, 0));
574 EXPECT_TRUE(WIFEXITED(status));
575 EXPECT_EQ(0, WEXITSTATUS(status));
577 ADD_FAILURE() << "fork failed";
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
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);
593 typedef void (*useA_t)();
595 useA_t useA = (useA_t) dlsym(handle, "useA");
597 const char *dlsym_error = dlerror();
598 EXPECT_EQ(nullptr, dlsym_error);
602 folly::Baton<> b11, b12, b21, b22;
604 std::thread t1([&]() {
610 std::thread t2([&]() {
631 namespace folly { namespace threadlocal_detail {
632 struct PthreadKeyUnregisterTester {
633 PthreadKeyUnregister p;
634 constexpr PthreadKeyUnregisterTester() = default;
636 } // namespace threadlocal_detail
639 TEST(ThreadLocal, UnregisterClassHasConstExprCtor) {
640 folly::threadlocal_detail::PthreadKeyUnregisterTester x;