2 * Copyright 2016 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>
20 #include <sys/types.h>
27 #include <condition_variable>
33 #include <unordered_map>
35 #include <glog/logging.h>
36 #include <gtest/gtest.h>
38 #include <folly/Baton.h>
39 #include <folly/experimental/io/FsUtil.h>
41 using namespace folly;
50 static void customDeleter(Widget* w, TLPDestructionMode mode) {
51 totalVal_ += (mode == TLPDestructionMode::ALL_THREADS) * 1000;
55 int Widget::totalVal_ = 0;
57 TEST(ThreadLocalPtr, BasicDestructor) {
58 Widget::totalVal_ = 0;
59 ThreadLocalPtr<Widget> w;
61 w.reset(new Widget());
64 EXPECT_EQ(10, Widget::totalVal_);
67 TEST(ThreadLocalPtr, CustomDeleter1) {
68 Widget::totalVal_ = 0;
70 ThreadLocalPtr<Widget> w;
72 w.reset(new Widget(), Widget::customDeleter);
75 EXPECT_EQ(10, Widget::totalVal_);
77 EXPECT_EQ(10, Widget::totalVal_);
80 TEST(ThreadLocalPtr, resetNull) {
81 ThreadLocalPtr<int> tl;
84 EXPECT_TRUE(static_cast<bool>(tl));
85 EXPECT_EQ(*tl.get(), 4);
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());
98 wPtr.reset(w.release());
100 EXPECT_EQ(0, Widget::totalVal_);
102 EXPECT_EQ(10, Widget::totalVal_);
105 TEST(ThreadLocalPtr, CreateOnThreadExit) {
106 Widget::totalVal_ = 0;
107 ThreadLocal<Widget> w;
108 ThreadLocalPtr<int> tl;
112 [&](int* ptr, TLPDestructionMode /* mode */) {
114 // This test ensures Widgets allocated here are not leaked.
116 ThreadLocal<Widget> wl;
120 EXPECT_EQ(2, Widget::totalVal_);
123 // Test deleting the ThreadLocalPtr object
124 TEST(ThreadLocalPtr, CustomDeleter2) {
125 Widget::totalVal_ = 0;
128 std::condition_variable cv;
134 State state = State::START;
136 ThreadLocalPtr<Widget> w;
137 t = std::thread([&]() {
138 w.reset(new Widget(), Widget::customDeleter);
141 // Notify main thread that we're done
143 std::unique_lock<std::mutex> lock(mutex);
148 // Wait for main thread to allow us to exit
150 std::unique_lock<std::mutex> lock(mutex);
151 while (state != State::EXIT) {
157 // Wait for main thread to start (and set w.get()->val_)
159 std::unique_lock<std::mutex> lock(mutex);
160 while (state != State::DONE) {
165 // Thread started but hasn't exited yet
166 EXPECT_EQ(0, Widget::totalVal_);
168 // Destroy ThreadLocalPtr<Widget> (by letting it go out of scope)
171 EXPECT_EQ(1010, Widget::totalVal_);
173 // Allow thread to exit
175 std::unique_lock<std::mutex> lock(mutex);
181 EXPECT_EQ(1010, Widget::totalVal_);
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_);
191 TEST(ThreadLocal, SimpleRepeatDestructor) {
192 Widget::totalVal_ = 0;
194 ThreadLocal<Widget> w;
198 ThreadLocal<Widget> w;
201 EXPECT_EQ(20, Widget::totalVal_);
204 TEST(ThreadLocal, InterleavedDestructors) {
205 Widget::totalVal_ = 0;
206 std::unique_ptr<ThreadLocal<Widget>> w;
208 const int wVersionMax = 2;
211 auto th = std::thread([&]() {
212 int wVersionPrev = 0;
215 std::lock_guard<std::mutex> g(lock);
216 if (wVersion > wVersionMax) {
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);
225 std::lock_guard<std::mutex> g(lock);
226 wVersionPrev = wVersion;
231 FOR_EACH_RANGE(i, 0, wVersionMax) {
234 std::lock_guard<std::mutex> g(lock);
236 w.reset(new ThreadLocal<Widget>());
240 std::lock_guard<std::mutex> g(lock);
241 if (thIter > thIterPrev) {
247 std::lock_guard<std::mutex> g(lock);
248 wVersion = wVersionMax + 1;
251 EXPECT_EQ(wVersionMax * 10, Widget::totalVal_);
254 class SimpleThreadCachedInt {
257 ThreadLocal<int,NewTag> val_;
266 for (const auto& i : val_.accessAllThreads()) {
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]() {
282 totalAtomic.fetch_add(1);
283 while (run.load()) { usleep(100); }
286 while (totalAtomic.load() != kNumThreads) { usleep(100); }
287 EXPECT_EQ(kNumThreads, stci.read());
289 for (auto& t : threads) {
294 TEST(ThreadLocal, resetNull) {
296 tl.reset(new int(4));
297 EXPECT_EQ(*tl.get(), 4);
299 EXPECT_EQ(*tl.get(), 0);
300 tl.reset(new int(5));
301 EXPECT_EQ(*tl.get(), 5);
308 folly::ThreadLocal<int, Tag> tl;
312 TEST(ThreadLocal, Movable1) {
315 EXPECT_TRUE(a.tl.get() != b.tl.get());
319 EXPECT_TRUE(a.tl.get() != b.tl.get());
322 TEST(ThreadLocal, Movable2) {
323 std::map<int, Foo> map;
331 for (auto& m : map) {
332 tls.insert(m.second.tl.get());
335 // Make sure that we have 4 different instances of *tl
336 EXPECT_EQ(4, tls.size());
341 constexpr size_t kFillObjectSize = 300;
343 std::atomic<uint64_t> gDestroyed;
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)
349 * Verify it explicitly and on destruction.
353 explicit FillObject(uint64_t idx) : idx_(idx) {
355 for (size_t i = 0; i < kFillObjectSize; ++i) {
362 for (size_t i = 0; i < kFillObjectSize; ++i) {
363 CHECK_EQ(v, data_[i]);
372 uint64_t val() const {
373 return (idx_ << 40) | uint64_t(pthread_self());
377 uint64_t data_[kFillObjectSize];
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;
387 constexpr size_t numThreads = 32;
388 constexpr size_t numReps = 20;
390 std::vector<std::thread> threads;
391 threads.reserve(numThreads);
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));
400 for (size_t i = 0; i < objects.size(); ++i) {
407 for (auto& t : threads) {
411 EXPECT_EQ(numFillObjects * numThreads * numReps, gDestroyed);
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.
421 HoldsOne() : value_(1) { }
422 // Do an actual access to catch the buggy case where this == nullptr
423 int value() const { return value_; }
428 struct HoldsOneTag {};
430 ThreadLocal<HoldsOne, HoldsOneTag> ptr;
434 for (auto& p : ptr.accessAllThreads()) {
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
449 bool started = false;
450 std::condition_variable startedCond;
451 bool stopped = false;
452 std::condition_variable stoppedCond;
454 std::thread t([&] () {
455 EXPECT_EQ(1, ptr->value()); // ensure created
457 std::unique_lock<std::mutex> lock(mutex);
459 startedCond.notify_all();
462 std::unique_lock<std::mutex> lock(mutex);
464 stoppedCond.wait(lock);
470 std::unique_lock<std::mutex> lock(mutex);
472 startedCond.wait(lock);
476 EXPECT_EQ(2, totalValue());
481 int v = totalValue();
483 // exit successfully if v == 1 (one thread)
484 // diagnostic error code otherwise :)
490 } else if (pid > 0) {
493 EXPECT_EQ(pid, waitpid(pid, &status, 0));
494 EXPECT_TRUE(WIFEXITED(status));
495 EXPECT_EQ(0, WEXITSTATUS(status));
497 EXPECT_TRUE(false) << "fork failed";
500 EXPECT_EQ(2, totalValue());
503 std::unique_lock<std::mutex> lock(mutex);
505 stoppedCond.notify_all();
510 EXPECT_EQ(1, totalValue());
514 struct HoldsOneTag2 {};
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;
522 // use tag in different thread
523 std::thread t([&p] { p.get(); });
529 ThreadLocal<HoldsOne, HoldsOneTag2> q;
533 } else if (pid > 0) {
535 EXPECT_EQ(pid, waitpid(pid, &status, 0));
536 EXPECT_TRUE(WIFEXITED(status));
537 EXPECT_EQ(0, WEXITSTATUS(status));
539 EXPECT_TRUE(false) << "fork failed";
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);
549 typedef void (*useA_t)();
551 useA_t useA = (useA_t) dlsym(handle, "useA");
553 const char *dlsym_error = dlerror();
554 EXPECT_EQ(nullptr, dlsym_error);
558 folly::Baton<> b11, b12, b21, b22;
560 std::thread t1([&]() {
566 std::thread t2([&]() {
584 namespace folly { namespace threadlocal_detail {
585 struct PthreadKeyUnregisterTester {
586 PthreadKeyUnregister p;
587 constexpr PthreadKeyUnregisterTester() = default;
591 TEST(ThreadLocal, UnregisterClassHasConstExprCtor) {
592 folly::threadlocal_detail::PthreadKeyUnregisterTester x;