1 // Copyright 2022 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/allocator/dispatcher/tls.h"
6
7 #if USE_LOCAL_TLS_EMULATION()
8 #include <algorithm>
9 #include <array>
10 #include <cstddef>
11 #include <functional>
12 #include <mutex>
13 #include <thread>
14 #include <unordered_set>
15 #include <utility>
16
17 #include "testing/gmock/include/gmock/gmock.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19
20 using ::testing::_;
21 using ::testing::InSequence;
22 using ::testing::NiceMock;
23 using ::testing::NotNull;
24 using ::testing::Return;
25 using ::testing::ReturnNull;
26
27 namespace base::allocator::dispatcher {
28 namespace {
29 struct DataToStore {
30 int data_int = 0;
31 float data_float = 0.0;
32 size_t data_size_t = 0;
33 double data_double = 0.0;
34 };
35
36 struct AllocatorMockBase {
AllocatorMockBasebase::allocator::dispatcher::__anon4b6f41660111::AllocatorMockBase37 AllocatorMockBase() {
38 ON_CALL(*this, AllocateMemory(_)).WillByDefault([](size_t size_in_bytes) {
39 return malloc(size_in_bytes);
40 });
41 ON_CALL(*this, FreeMemoryForTesting(_, _))
42 .WillByDefault([](void* pointer_to_allocated, size_t size_in_bytes) {
43 free(pointer_to_allocated);
44 return true;
45 });
46 }
47
48 MOCK_METHOD(void*, AllocateMemory, (size_t size_in_bytes), ());
49 MOCK_METHOD(bool,
50 FreeMemoryForTesting,
51 (void* pointer_to_allocated, size_t size_in_bytes),
52 ());
53 };
54
55 struct TLSSystemMockBase {
TLSSystemMockBasebase::allocator::dispatcher::__anon4b6f41660111::TLSSystemMockBase56 TLSSystemMockBase() {
57 ON_CALL(*this, Setup(_)).WillByDefault(Return(true));
58 ON_CALL(*this, TearDownForTesting()).WillByDefault(Return(true));
59 ON_CALL(*this, SetThreadSpecificData(_)).WillByDefault(Return(true));
60 }
61
62 MOCK_METHOD(
63 bool,
64 Setup,
65 (internal::OnThreadTerminationFunction thread_termination_function),
66 ());
67 MOCK_METHOD(bool, TearDownForTesting, (), ());
68 MOCK_METHOD(void*, GetThreadSpecificData, (), ());
69 MOCK_METHOD(bool, SetThreadSpecificData, (void* data), ());
70 };
71
72 using AllocatorMock = NiceMock<AllocatorMockBase>;
73 using TLSSystemMock = NiceMock<TLSSystemMockBase>;
74
75 template <typename T, typename Allocator, typename TLSSystem>
76 ThreadLocalStorage<T,
77 std::reference_wrapper<Allocator>,
78 std::reference_wrapper<TLSSystem>,
79 0,
80 true>
CreateThreadLocalStorage(Allocator & allocator,TLSSystem & tlsSystem)81 CreateThreadLocalStorage(Allocator& allocator, TLSSystem& tlsSystem) {
82 return {std::ref(allocator), std::ref(tlsSystem)};
83 }
84
85 template <typename T>
86 ThreadLocalStorage<T,
87 internal::DefaultAllocator,
88 internal::DefaultTLSSystem,
89 0,
90 true>
CreateThreadLocalStorage()91 CreateThreadLocalStorage() {
92 return {};
93 }
94
95 } // namespace
96
97 struct BaseThreadLocalStorageTest : public ::testing::Test {};
98
TEST_F(BaseThreadLocalStorageTest,VerifyDataIsIndependentBetweenDifferentSUTs)99 TEST_F(BaseThreadLocalStorageTest,
100 VerifyDataIsIndependentBetweenDifferentSUTs) {
101 auto sut_1 = CreateThreadLocalStorage<DataToStore>();
102 auto sut_2 = CreateThreadLocalStorage<DataToStore>();
103
104 EXPECT_NE(sut_1.GetThreadLocalData(), sut_2.GetThreadLocalData());
105 }
106
TEST_F(BaseThreadLocalStorageTest,VerifyDistinctEntriesForEachThread)107 TEST_F(BaseThreadLocalStorageTest, VerifyDistinctEntriesForEachThread) {
108 auto sut = CreateThreadLocalStorage<DataToStore>();
109 using TLSType = decltype(sut);
110
111 std::array<std::thread, 2 * TLSType::ItemsPerChunk> threads;
112 std::mutex thread_worker_mutex;
113 std::condition_variable thread_counter_cv;
114 std::atomic_uint32_t thread_counter{0};
115 std::unordered_set<void*> stored_object_addresses;
116
117 std::mutex threads_can_finish_mutex;
118 std::condition_variable threads_can_finish_cv;
119 std::atomic_bool threads_can_finish{false};
120
121 for (auto& t : threads) {
122 t = std::thread{[&] {
123 {
124 std::lock_guard<std::mutex> lock(thread_worker_mutex);
125 stored_object_addresses.insert(sut.GetThreadLocalData());
126 ++thread_counter;
127 thread_counter_cv.notify_one();
128 }
129
130 {
131 std::unique_lock<std::mutex> lock(threads_can_finish_mutex);
132 threads_can_finish_cv.wait(lock,
133 [&] { return threads_can_finish.load(); });
134 }
135 }};
136 }
137
138 {
139 std::unique_lock<std::mutex> lock(thread_worker_mutex);
140 thread_counter_cv.wait(
141 lock, [&] { return thread_counter.load() == threads.size(); });
142 }
143
144 {
145 std::unique_lock<std::mutex> lock(threads_can_finish_mutex);
146 threads_can_finish = true;
147 threads_can_finish_cv.notify_all();
148 }
149
150 for (auto& t : threads) {
151 t.join();
152 }
153
154 EXPECT_EQ(stored_object_addresses.size(), threads.size());
155 }
156
TEST_F(BaseThreadLocalStorageTest,VerifyEntriesAreReusedForNewThreads)157 TEST_F(BaseThreadLocalStorageTest, VerifyEntriesAreReusedForNewThreads) {
158 auto sut = CreateThreadLocalStorage<DataToStore>();
159 using TLSType = decltype(sut);
160
161 std::unordered_set<void*> stored_object_addresses;
162
163 for (size_t thread_count = 0; thread_count < (2 * TLSType::ItemsPerChunk);
164 ++thread_count) {
165 auto thread = std::thread{
166 [&] { stored_object_addresses.insert(sut.GetThreadLocalData()); }};
167
168 thread.join();
169 }
170
171 EXPECT_EQ(stored_object_addresses.size(), 1ul);
172 }
173
TEST_F(BaseThreadLocalStorageTest,VerifyDataIsSameWithinEachThread)174 TEST_F(BaseThreadLocalStorageTest, VerifyDataIsSameWithinEachThread) {
175 auto sut = CreateThreadLocalStorage<DataToStore>();
176 using TLSType = decltype(sut);
177
178 std::array<std::thread, 2 * TLSType::ItemsPerChunk> threads;
179
180 for (auto& t : threads) {
181 t = std::thread{[&] {
182 EXPECT_EQ(sut.GetThreadLocalData(), sut.GetThreadLocalData());
183 std::this_thread::sleep_for(std::chrono::milliseconds(100));
184 // Check once again to verify the data doesn't change in the course of a
185 // thread's lifetime.
186 EXPECT_EQ(sut.GetThreadLocalData(), sut.GetThreadLocalData());
187 }};
188 }
189
190 for (auto& t : threads) {
191 t.join();
192 }
193 }
194
TEST_F(BaseThreadLocalStorageTest,VerifySetupTeardownSequence)195 TEST_F(BaseThreadLocalStorageTest, VerifySetupTeardownSequence) {
196 AllocatorMock allocator_mock;
197 TLSSystemMock tlsSystem_mock;
198
199 InSequence execution_sequence;
200
201 EXPECT_CALL(allocator_mock, AllocateMemory(_))
202 .WillOnce([](size_t size_in_bytes) { return malloc(size_in_bytes); });
203 EXPECT_CALL(tlsSystem_mock, Setup(NotNull())).WillOnce(Return(true));
204 EXPECT_CALL(tlsSystem_mock, TearDownForTesting()).WillOnce(Return(true));
205 EXPECT_CALL(allocator_mock, FreeMemoryForTesting(_, _))
206 .WillOnce([](void* pointer_to_allocated, size_t size_in_bytes) {
207 free(pointer_to_allocated);
208 return true;
209 });
210
211 auto sut =
212 CreateThreadLocalStorage<DataToStore>(allocator_mock, tlsSystem_mock);
213 }
214
TEST_F(BaseThreadLocalStorageTest,VerifyAllocatorIsUsed)215 TEST_F(BaseThreadLocalStorageTest, VerifyAllocatorIsUsed) {
216 AllocatorMock allocator_mock;
217 TLSSystemMock tlsSystem_mock;
218
219 EXPECT_CALL(allocator_mock, AllocateMemory(_))
220 .WillOnce([](size_t size_in_bytes) { return malloc(size_in_bytes); });
221
222 EXPECT_CALL(allocator_mock, FreeMemoryForTesting(_, _))
223 .WillOnce([](void* pointer_to_allocated, size_t size_in_bytes) {
224 free(pointer_to_allocated);
225 return true;
226 });
227
228 auto sut =
229 CreateThreadLocalStorage<DataToStore>(allocator_mock, tlsSystem_mock);
230 }
231
TEST_F(BaseThreadLocalStorageTest,VerifyAllocatorIsUsedForMultipleChunks)232 TEST_F(BaseThreadLocalStorageTest, VerifyAllocatorIsUsedForMultipleChunks) {
233 AllocatorMock allocator_mock;
234 TLSSystemMock tlsSystem_mock;
235
236 constexpr auto number_of_chunks = 3;
237
238 EXPECT_CALL(allocator_mock, AllocateMemory(_))
239 .Times(number_of_chunks)
240 .WillRepeatedly(
241 [](size_t size_in_bytes) { return malloc(size_in_bytes); });
242
243 EXPECT_CALL(allocator_mock, FreeMemoryForTesting(_, _))
244 .Times(number_of_chunks)
245 .WillRepeatedly([](void* pointer_to_allocated, size_t size_in_bytes) {
246 free(pointer_to_allocated);
247 return true;
248 });
249
250 auto sut =
251 CreateThreadLocalStorage<DataToStore>(allocator_mock, tlsSystem_mock);
252
253 std::array<std::thread,
254 (number_of_chunks - 1) * decltype(sut)::ItemsPerChunk + 1>
255 threads;
256 std::mutex thread_worker_mutex;
257 std::condition_variable thread_counter_cv;
258 std::atomic_uint32_t thread_counter{0};
259 std::unordered_set<void*> stored_object_addresses;
260
261 std::mutex threads_can_finish_mutex;
262 std::condition_variable threads_can_finish_cv;
263 std::atomic_bool threads_can_finish{false};
264
265 for (auto& t : threads) {
266 t = std::thread{[&] {
267 sut.GetThreadLocalData();
268
269 {
270 std::lock_guard<std::mutex> lock(thread_worker_mutex);
271 ++thread_counter;
272 thread_counter_cv.notify_one();
273 }
274
275 {
276 std::unique_lock<std::mutex> lock(threads_can_finish_mutex);
277 threads_can_finish_cv.wait(lock,
278 [&] { return threads_can_finish.load(); });
279 }
280 }};
281 }
282
283 {
284 std::unique_lock<std::mutex> lock(thread_worker_mutex);
285 thread_counter_cv.wait(
286 lock, [&] { return thread_counter.load() == threads.size(); });
287 }
288
289 {
290 std::unique_lock<std::mutex> lock(threads_can_finish_mutex);
291 threads_can_finish = true;
292 threads_can_finish_cv.notify_all();
293 }
294
295 for (auto& t : threads) {
296 t.join();
297 }
298 }
299
TEST_F(BaseThreadLocalStorageTest,VerifyTLSSystemIsUsed)300 TEST_F(BaseThreadLocalStorageTest, VerifyTLSSystemIsUsed) {
301 AllocatorMock allocator_mock;
302 TLSSystemMock tlsSystem_mock;
303
304 InSequence execution_sequence;
305
306 EXPECT_CALL(tlsSystem_mock, Setup(NotNull())).WillOnce(Return(true));
307 EXPECT_CALL(tlsSystem_mock, GetThreadSpecificData())
308 .WillOnce(Return(nullptr));
309 EXPECT_CALL(tlsSystem_mock, SetThreadSpecificData(NotNull()));
310 EXPECT_CALL(tlsSystem_mock, TearDownForTesting())
311 .Times(1)
312 .WillOnce(Return(true));
313
314 auto sut =
315 CreateThreadLocalStorage<DataToStore>(allocator_mock, tlsSystem_mock);
316
317 sut.GetThreadLocalData();
318 }
319
320 #if defined(GTEST_HAS_DEATH_TEST)
321 struct BaseThreadLocalStorageDeathTest : public ::testing::Test {};
322
TEST_F(BaseThreadLocalStorageDeathTest,VerifyDeathIfAllocationFails)323 TEST_F(BaseThreadLocalStorageDeathTest, VerifyDeathIfAllocationFails) {
324 auto f = [] {
325 AllocatorMock allocator_mock;
326 TLSSystemMock tlsSystem_mock;
327
328 // Setup all expectations here. If we're setting them up in the parent
329 // process, they will fail because the parent doesn't execute any test.
330 EXPECT_CALL(allocator_mock, AllocateMemory(_)).WillOnce(ReturnNull());
331
332 CreateThreadLocalStorage<DataToStore>(allocator_mock, tlsSystem_mock);
333 };
334
335 EXPECT_DEATH(f(), "");
336 }
337
TEST_F(BaseThreadLocalStorageDeathTest,VerifyDeathIfFreeFails)338 TEST_F(BaseThreadLocalStorageDeathTest, VerifyDeathIfFreeFails) {
339 auto f = [] {
340 AllocatorMock allocator_mock;
341 TLSSystemMock tlsSystem_mock;
342
343 // Setup all expectations here. If we're setting them up in the parent
344 // process, they will fail because the parent doesn't execute any test.
345 EXPECT_CALL(allocator_mock, FreeMemoryForTesting(_, _))
346 .WillOnce([](void* allocated_memory, size_t size_in_bytes) {
347 free(allocated_memory);
348 return false;
349 });
350
351 CreateThreadLocalStorage<DataToStore>(allocator_mock, tlsSystem_mock);
352 };
353
354 EXPECT_DEATH(f(), "");
355 }
356
TEST_F(BaseThreadLocalStorageDeathTest,VerifyDeathIfTLSSetupFails)357 TEST_F(BaseThreadLocalStorageDeathTest, VerifyDeathIfTLSSetupFails) {
358 auto f = [] {
359 AllocatorMock allocator_mock;
360 TLSSystemMock tlsSystem_mock;
361
362 // Setup all expectations here. If we're setting them up in the parent
363 // process, they will fail because the parent doesn't execute any test.
364 EXPECT_CALL(tlsSystem_mock, Setup(_)).WillOnce(Return(false));
365 EXPECT_CALL(tlsSystem_mock, GetThreadSpecificData()).Times(0);
366 EXPECT_CALL(tlsSystem_mock, SetThreadSpecificData(_)).Times(0);
367 EXPECT_CALL(tlsSystem_mock, TearDownForTesting()).Times(0);
368
369 CreateThreadLocalStorage<DataToStore>(allocator_mock, tlsSystem_mock);
370 };
371
372 EXPECT_DEATH(f(), "");
373 }
374
TEST_F(BaseThreadLocalStorageDeathTest,VerifyDeathIfStoringTLSDataFails)375 TEST_F(BaseThreadLocalStorageDeathTest, VerifyDeathIfStoringTLSDataFails) {
376 auto f = [] {
377 AllocatorMock allocator_mock;
378 TLSSystemMock tlsSystem_mock;
379
380 // Setup all expectations here. If we're setting them up in the parent
381 // process, they will fail because the parent doesn't execute any test.
382 EXPECT_CALL(tlsSystem_mock, SetThreadSpecificData(_))
383 .Times(1)
384 .WillOnce(Return(false));
385 EXPECT_CALL(tlsSystem_mock, TearDownForTesting()).Times(0);
386
387 CreateThreadLocalStorage<DataToStore>(allocator_mock, tlsSystem_mock)
388 .GetThreadLocalData();
389 };
390
391 EXPECT_DEATH(f(), "");
392 }
393
TEST_F(BaseThreadLocalStorageDeathTest,VerifyDeathIfTLSTeardownFails)394 TEST_F(BaseThreadLocalStorageDeathTest, VerifyDeathIfTLSTeardownFails) {
395 auto f = [] {
396 AllocatorMock allocator_mock;
397 TLSSystemMock tlsSystem_mock;
398
399 // Setup all expectations here. If we're setting them up in the parent
400 // process, they will fail because the parent doesn't execute any test.
401 EXPECT_CALL(tlsSystem_mock, Setup(_)).WillOnce(Return(true));
402 EXPECT_CALL(tlsSystem_mock, TearDownForTesting()).WillOnce(Return(false));
403
404 CreateThreadLocalStorage<DataToStore>(allocator_mock, tlsSystem_mock);
405 };
406
407 EXPECT_DEATH(f(), "");
408 }
409 #endif // GTEST_HAS_DEATH_TEST
410
411 struct BasePThreadTLSSystemTest : public ::testing::Test {
SetUpbase::allocator::dispatcher::BasePThreadTLSSystemTest412 void SetUp() override { thread_termination_counter = 0; }
413
414 protected:
ThreadTerminationFunctionbase::allocator::dispatcher::BasePThreadTLSSystemTest415 static void ThreadTerminationFunction(void*) { ++thread_termination_counter; }
416
417 static std::atomic<size_t> thread_termination_counter;
418 };
419
420 std::atomic<size_t> BasePThreadTLSSystemTest::thread_termination_counter{0};
421
TEST_F(BasePThreadTLSSystemTest,VerifySetupNTeardownSequence)422 TEST_F(BasePThreadTLSSystemTest, VerifySetupNTeardownSequence) {
423 internal::PThreadTLSSystem sut;
424
425 for (size_t idx = 0; idx < 5; ++idx) {
426 EXPECT_TRUE(sut.Setup(nullptr));
427 EXPECT_TRUE(sut.TearDownForTesting());
428 }
429 }
430
TEST_F(BasePThreadTLSSystemTest,VerifyThreadTerminationFunctionIsCalled)431 TEST_F(BasePThreadTLSSystemTest, VerifyThreadTerminationFunctionIsCalled) {
432 std::array<std::thread, 10> threads;
433
434 internal::PThreadTLSSystem sut;
435 sut.Setup(&ThreadTerminationFunction);
436
437 for (auto& t : threads) {
438 t = std::thread{[&] {
439 int x = 0;
440 ASSERT_TRUE(sut.SetThreadSpecificData(&x));
441 }};
442 }
443
444 for (auto& t : threads) {
445 t.join();
446 }
447
448 sut.TearDownForTesting();
449
450 EXPECT_EQ(threads.size(), thread_termination_counter);
451 }
452
TEST_F(BasePThreadTLSSystemTest,VerifyGetWithoutSetReturnsNull)453 TEST_F(BasePThreadTLSSystemTest, VerifyGetWithoutSetReturnsNull) {
454 internal::PThreadTLSSystem sut;
455 sut.Setup(nullptr);
456
457 EXPECT_EQ(nullptr, sut.GetThreadSpecificData());
458
459 sut.TearDownForTesting();
460 }
461
TEST_F(BasePThreadTLSSystemTest,VerifyGetAfterTeardownReturnsNull)462 TEST_F(BasePThreadTLSSystemTest, VerifyGetAfterTeardownReturnsNull) {
463 internal::PThreadTLSSystem sut;
464 sut.Setup(nullptr);
465 sut.SetThreadSpecificData(this);
466 sut.TearDownForTesting();
467
468 EXPECT_EQ(sut.GetThreadSpecificData(), nullptr);
469 }
470
TEST_F(BasePThreadTLSSystemTest,VerifyGetAfterTeardownReturnsNullThreaded)471 TEST_F(BasePThreadTLSSystemTest, VerifyGetAfterTeardownReturnsNullThreaded) {
472 std::array<std::thread, 50> threads;
473
474 std::mutex thread_worker_mutex;
475 std::condition_variable thread_counter_cv;
476 std::atomic_uint32_t thread_counter{0};
477
478 std::mutex threads_can_finish_mutex;
479 std::condition_variable threads_can_finish_cv;
480 std::atomic_bool threads_can_finish{false};
481
482 internal::PThreadTLSSystem sut;
483 ASSERT_TRUE(sut.Setup(nullptr));
484
485 for (auto& t : threads) {
486 t = std::thread{[&] {
487 // Set some thread specific data. At this stage retrieving the data must
488 // return the pointer that was originally set.
489 int x = 0;
490 ASSERT_TRUE(sut.SetThreadSpecificData(&x));
491 ASSERT_EQ(sut.GetThreadSpecificData(), &x);
492
493 // Notify the main thread that one more test thread has started.
494 {
495 std::lock_guard<std::mutex> lock(thread_worker_mutex);
496 ++thread_counter;
497 thread_counter_cv.notify_one();
498 }
499
500 // Wait for the main thread to notify about teardown of the sut.
501 {
502 std::unique_lock<std::mutex> lock(threads_can_finish_mutex);
503 threads_can_finish_cv.wait(lock,
504 [&] { return threads_can_finish.load(); });
505 }
506
507 // After teardown, thread local data must be nullptr for all threads.
508 EXPECT_EQ(sut.GetThreadSpecificData(), nullptr);
509 }};
510 }
511
512 // Wait for notification from threads that they started and passed the initial
513 // check.
514 {
515 std::unique_lock<std::mutex> lock(thread_worker_mutex);
516 thread_counter_cv.wait(
517 lock, [&] { return thread_counter.load() == threads.size(); });
518 }
519
520 ASSERT_TRUE(sut.TearDownForTesting());
521
522 // Notify all threads that the subject under test was torn down and they can
523 // proceed.
524 {
525 std::unique_lock<std::mutex> lock(threads_can_finish_mutex);
526 threads_can_finish = true;
527 threads_can_finish_cv.notify_all();
528 }
529
530 for (auto& t : threads) {
531 t.join();
532 }
533 }
534
TEST_F(BasePThreadTLSSystemTest,VerifyGetSetSequence)535 TEST_F(BasePThreadTLSSystemTest, VerifyGetSetSequence) {
536 std::array<std::thread, 50> threads;
537
538 internal::PThreadTLSSystem sut;
539 sut.Setup(nullptr);
540
541 for (auto& t : threads) {
542 t = std::thread{[&] {
543 int x = 0;
544 EXPECT_TRUE(sut.SetThreadSpecificData(&x));
545 EXPECT_EQ(&x, sut.GetThreadSpecificData());
546 }};
547 }
548
549 for (auto& t : threads) {
550 t.join();
551 }
552
553 sut.TearDownForTesting();
554 }
555
556 #if DCHECK_IS_ON()
TEST_F(BasePThreadTLSSystemTest,VerifyGetWithoutSetupReturnsNull)557 TEST_F(BasePThreadTLSSystemTest, VerifyGetWithoutSetupReturnsNull) {
558 internal::PThreadTLSSystem sut;
559
560 EXPECT_EQ(sut.GetThreadSpecificData(), nullptr);
561 }
562
TEST_F(BasePThreadTLSSystemTest,VerifyStoreWithoutSetupFails)563 TEST_F(BasePThreadTLSSystemTest, VerifyStoreWithoutSetupFails) {
564 internal::PThreadTLSSystem sut;
565
566 EXPECT_FALSE(sut.SetThreadSpecificData(this));
567 }
568 #endif
569
570 #if defined(GTEST_HAS_DEATH_TEST) && DCHECK_IS_ON()
571 struct BasePThreadTLSSystemDeathTest : public ::testing::Test {};
572
TEST_F(BasePThreadTLSSystemDeathTest,VerifyDeathIfSetupTwice)573 TEST_F(BasePThreadTLSSystemDeathTest, VerifyDeathIfSetupTwice) {
574 internal::PThreadTLSSystem sut;
575
576 EXPECT_TRUE(sut.Setup(nullptr));
577 EXPECT_DEATH(sut.Setup(nullptr), "");
578 }
579
TEST_F(BasePThreadTLSSystemDeathTest,VerifyDeathIfTearDownWithoutSetup)580 TEST_F(BasePThreadTLSSystemDeathTest, VerifyDeathIfTearDownWithoutSetup) {
581 internal::PThreadTLSSystem sut;
582
583 EXPECT_DEATH(sut.TearDownForTesting(), "");
584 }
585 #endif
586 } // namespace base::allocator::dispatcher
587
588 #endif // USE_LOCAL_TLS_EMULATION()