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>
26 #include <condition_variable>
32 #include <unordered_map>
34 #include <glog/logging.h>
35 #include <gtest/gtest.h>
37 #include <folly/Baton.h>
38 #include <folly/Memory.h>
39 #include <folly/experimental/io/FsUtil.h>
40 #include <folly/portability/Unistd.h>
42 using namespace folly;
51 static void customDeleter(Widget* w, TLPDestructionMode mode) {
52 totalVal_ += (mode == TLPDestructionMode::ALL_THREADS) ? 1000 : 1;
56 int Widget::totalVal_ = 0;
58 TEST(ThreadLocalPtr, BasicDestructor) {
59 Widget::totalVal_ = 0;
60 ThreadLocalPtr<Widget> w;
62 w.reset(new Widget());
65 EXPECT_EQ(10, Widget::totalVal_);
68 TEST(ThreadLocalPtr, CustomDeleter1) {
69 Widget::totalVal_ = 0;
71 ThreadLocalPtr<Widget> w;
73 w.reset(new Widget(), Widget::customDeleter);
76 EXPECT_EQ(11, Widget::totalVal_);
78 EXPECT_EQ(11, Widget::totalVal_);
81 TEST(ThreadLocalPtr, CustomDeleterOwnershipTransfer) {
82 Widget::totalVal_ = 0;
84 ThreadLocalPtr<Widget> w;
85 auto deleter = [](Widget* ptr) {
86 Widget::customDeleter(ptr, TLPDestructionMode::THIS_THREAD);
88 std::unique_ptr<Widget, typeof(deleter)> source(new Widget(), deleter);
89 std::thread([&w, &source]() {
90 w.reset(std::move(source));
93 EXPECT_EQ(11, Widget::totalVal_);
95 EXPECT_EQ(11, Widget::totalVal_);
98 TEST(ThreadLocalPtr, DefaultDeleterOwnershipTransfer) {
99 Widget::totalVal_ = 0;
101 ThreadLocalPtr<Widget> w;
102 auto source = folly::make_unique<Widget>();
103 std::thread([&w, &source]() {
104 w.reset(std::move(source));
107 EXPECT_EQ(10, Widget::totalVal_);
109 EXPECT_EQ(10, Widget::totalVal_);
112 TEST(ThreadLocalPtr, resetNull) {
113 ThreadLocalPtr<int> tl;
115 tl.reset(new int(4));
116 EXPECT_TRUE(static_cast<bool>(tl));
117 EXPECT_EQ(*tl.get(), 4);
122 TEST(ThreadLocalPtr, TestRelease) {
123 Widget::totalVal_ = 0;
124 ThreadLocalPtr<Widget> w;
125 std::unique_ptr<Widget> wPtr;
126 std::thread([&w, &wPtr]() {
127 w.reset(new Widget());
130 wPtr.reset(w.release());
132 EXPECT_EQ(0, Widget::totalVal_);
134 EXPECT_EQ(10, Widget::totalVal_);
137 TEST(ThreadLocalPtr, CreateOnThreadExit) {
138 Widget::totalVal_ = 0;
139 ThreadLocal<Widget> w;
140 ThreadLocalPtr<int> tl;
144 [&](int* ptr, TLPDestructionMode /* mode */) {
146 // This test ensures Widgets allocated here are not leaked.
148 ThreadLocal<Widget> wl;
152 EXPECT_EQ(2, Widget::totalVal_);
155 // Test deleting the ThreadLocalPtr object
156 TEST(ThreadLocalPtr, CustomDeleter2) {
157 Widget::totalVal_ = 0;
160 std::condition_variable cv;
166 State state = State::START;
168 ThreadLocalPtr<Widget> w;
169 t = std::thread([&]() {
170 w.reset(new Widget(), Widget::customDeleter);
173 // Notify main thread that we're done
175 std::unique_lock<std::mutex> lock(mutex);
180 // Wait for main thread to allow us to exit
182 std::unique_lock<std::mutex> lock(mutex);
183 while (state != State::EXIT) {
189 // Wait for main thread to start (and set w.get()->val_)
191 std::unique_lock<std::mutex> lock(mutex);
192 while (state != State::DONE) {
197 // Thread started but hasn't exited yet
198 EXPECT_EQ(0, Widget::totalVal_);
200 // Destroy ThreadLocalPtr<Widget> (by letting it go out of scope)
203 EXPECT_EQ(1010, Widget::totalVal_);
205 // Allow thread to exit
207 std::unique_lock<std::mutex> lock(mutex);
213 EXPECT_EQ(1010, Widget::totalVal_);
216 TEST(ThreadLocal, BasicDestructor) {
217 Widget::totalVal_ = 0;
218 ThreadLocal<Widget> w;
219 std::thread([&w]() { w->val_ += 10; }).join();
220 EXPECT_EQ(10, Widget::totalVal_);
223 TEST(ThreadLocal, SimpleRepeatDestructor) {
224 Widget::totalVal_ = 0;
226 ThreadLocal<Widget> w;
230 ThreadLocal<Widget> w;
233 EXPECT_EQ(20, Widget::totalVal_);
236 TEST(ThreadLocal, InterleavedDestructors) {
237 Widget::totalVal_ = 0;
238 std::unique_ptr<ThreadLocal<Widget>> w;
240 const int wVersionMax = 2;
243 auto th = std::thread([&]() {
244 int wVersionPrev = 0;
247 std::lock_guard<std::mutex> g(lock);
248 if (wVersion > wVersionMax) {
251 if (wVersion > wVersionPrev) {
252 // We have a new version of w, so it should be initialized to zero
253 EXPECT_EQ((*w)->val_, 0);
257 std::lock_guard<std::mutex> g(lock);
258 wVersionPrev = wVersion;
263 FOR_EACH_RANGE(i, 0, wVersionMax) {
266 std::lock_guard<std::mutex> g(lock);
268 w.reset(new ThreadLocal<Widget>());
272 std::lock_guard<std::mutex> g(lock);
273 if (thIter > thIterPrev) {
279 std::lock_guard<std::mutex> g(lock);
280 wVersion = wVersionMax + 1;
283 EXPECT_EQ(wVersionMax * 10, Widget::totalVal_);
286 class SimpleThreadCachedInt {
289 ThreadLocal<int,NewTag> val_;
298 for (const auto& i : val_.accessAllThreads()) {
305 TEST(ThreadLocalPtr, AccessAllThreadsCounter) {
306 const int kNumThreads = 10;
307 SimpleThreadCachedInt stci;
308 std::atomic<bool> run(true);
309 std::atomic<int> totalAtomic(0);
310 std::vector<std::thread> threads;
311 for (int i = 0; i < kNumThreads; ++i) {
312 threads.push_back(std::thread([&,i]() {
314 totalAtomic.fetch_add(1);
315 while (run.load()) { usleep(100); }
318 while (totalAtomic.load() != kNumThreads) { usleep(100); }
319 EXPECT_EQ(kNumThreads, stci.read());
321 for (auto& t : threads) {
326 TEST(ThreadLocal, resetNull) {
328 tl.reset(new int(4));
329 EXPECT_EQ(*tl.get(), 4);
331 EXPECT_EQ(*tl.get(), 0);
332 tl.reset(new int(5));
333 EXPECT_EQ(*tl.get(), 5);
340 folly::ThreadLocal<int, Tag> tl;
344 TEST(ThreadLocal, Movable1) {
347 EXPECT_TRUE(a.tl.get() != b.tl.get());
351 EXPECT_TRUE(a.tl.get() != b.tl.get());
354 TEST(ThreadLocal, Movable2) {
355 std::map<int, Foo> map;
363 for (auto& m : map) {
364 tls.insert(m.second.tl.get());
367 // Make sure that we have 4 different instances of *tl
368 EXPECT_EQ(4, tls.size());
373 constexpr size_t kFillObjectSize = 300;
375 std::atomic<uint64_t> gDestroyed;
378 * Fill a chunk of memory with a unique-ish pattern that includes the thread id
379 * (so deleting one of these from another thread would cause a failure)
381 * Verify it explicitly and on destruction.
385 explicit FillObject(uint64_t idx) : idx_(idx) {
387 for (size_t i = 0; i < kFillObjectSize; ++i) {
394 for (size_t i = 0; i < kFillObjectSize; ++i) {
395 CHECK_EQ(v, data_[i]);
404 uint64_t val() const {
405 return (idx_ << 40) | uint64_t(pthread_self());
409 uint64_t data_[kFillObjectSize];
414 #if FOLLY_HAVE_STD_THIS_THREAD_SLEEP_FOR
415 TEST(ThreadLocal, Stress) {
416 constexpr size_t numFillObjects = 250;
417 std::array<ThreadLocalPtr<FillObject>, numFillObjects> objects;
419 constexpr size_t numThreads = 32;
420 constexpr size_t numReps = 20;
422 std::vector<std::thread> threads;
423 threads.reserve(numThreads);
425 for (size_t i = 0; i < numThreads; ++i) {
426 threads.emplace_back([&objects] {
427 for (size_t rep = 0; rep < numReps; ++rep) {
428 for (size_t i = 0; i < objects.size(); ++i) {
429 objects[i].reset(new FillObject(rep * objects.size() + i));
430 std::this_thread::sleep_for(std::chrono::microseconds(100));
432 for (size_t i = 0; i < objects.size(); ++i) {
439 for (auto& t : threads) {
443 EXPECT_EQ(numFillObjects * numThreads * numReps, gDestroyed);
447 // Yes, threads and fork don't mix
448 // (http://cppwisdom.quora.com/Why-threads-and-fork-dont-mix) but if you're
449 // stupid or desperate enough to try, we shouldn't stand in your way.
453 HoldsOne() : value_(1) { }
454 // Do an actual access to catch the buggy case where this == nullptr
455 int value() const { return value_; }
460 struct HoldsOneTag {};
462 ThreadLocal<HoldsOne, HoldsOneTag> ptr;
466 for (auto& p : ptr.accessAllThreads()) {
474 #ifdef FOLLY_HAVE_PTHREAD_ATFORK
475 TEST(ThreadLocal, Fork) {
476 EXPECT_EQ(1, ptr->value()); // ensure created
477 EXPECT_EQ(1, totalValue());
478 // Spawn a new thread
481 bool started = false;
482 std::condition_variable startedCond;
483 bool stopped = false;
484 std::condition_variable stoppedCond;
486 std::thread t([&] () {
487 EXPECT_EQ(1, ptr->value()); // ensure created
489 std::unique_lock<std::mutex> lock(mutex);
491 startedCond.notify_all();
494 std::unique_lock<std::mutex> lock(mutex);
496 stoppedCond.wait(lock);
502 std::unique_lock<std::mutex> lock(mutex);
504 startedCond.wait(lock);
508 EXPECT_EQ(2, totalValue());
513 int v = totalValue();
515 // exit successfully if v == 1 (one thread)
516 // diagnostic error code otherwise :)
522 } else if (pid > 0) {
525 EXPECT_EQ(pid, waitpid(pid, &status, 0));
526 EXPECT_TRUE(WIFEXITED(status));
527 EXPECT_EQ(0, WEXITSTATUS(status));
529 EXPECT_TRUE(false) << "fork failed";
532 EXPECT_EQ(2, totalValue());
535 std::unique_lock<std::mutex> lock(mutex);
537 stoppedCond.notify_all();
542 EXPECT_EQ(1, totalValue());
546 struct HoldsOneTag2 {};
548 TEST(ThreadLocal, Fork2) {
549 // A thread-local tag that was used in the parent from a *different* thread
550 // (but not the forking thread) would cause the child to hang in a
551 // ThreadLocalPtr's object destructor. Yeah.
552 ThreadLocal<HoldsOne, HoldsOneTag2> p;
554 // use tag in different thread
555 std::thread t([&p] { p.get(); });
561 ThreadLocal<HoldsOne, HoldsOneTag2> q;
565 } else if (pid > 0) {
567 EXPECT_EQ(pid, waitpid(pid, &status, 0));
568 EXPECT_TRUE(WIFEXITED(status));
569 EXPECT_EQ(0, WEXITSTATUS(status));
571 EXPECT_TRUE(false) << "fork failed";
575 TEST(ThreadLocal, SharedLibrary) {
576 auto exe = fs::executable_path();
577 auto lib = exe.parent_path() / "lib_thread_local_test.so";
578 auto handle = dlopen(lib.string().c_str(), RTLD_LAZY);
579 EXPECT_NE(nullptr, handle);
581 typedef void (*useA_t)();
583 useA_t useA = (useA_t) dlsym(handle, "useA");
585 const char *dlsym_error = dlerror();
586 EXPECT_EQ(nullptr, dlsym_error);
590 folly::Baton<> b11, b12, b21, b22;
592 std::thread t1([&]() {
598 std::thread t2([&]() {
616 namespace folly { namespace threadlocal_detail {
617 struct PthreadKeyUnregisterTester {
618 PthreadKeyUnregister p;
619 constexpr PthreadKeyUnregisterTester() = default;
623 TEST(ThreadLocal, UnregisterClassHasConstExprCtor) {
624 folly::threadlocal_detail::PthreadKeyUnregisterTester x;