ThreadLocalTest.cpp 16 KB

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