// Copyright 2017 The Abseil Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "absl/base/internal/thread_identity.h" #include // NOLINT(build/c++11) #include #include "gtest/gtest.h" #include "absl/base/attributes.h" #include "absl/base/internal/spinlock.h" #include "absl/base/macros.h" #include "absl/synchronization/internal/per_thread_sem.h" #include "absl/synchronization/mutex.h" namespace absl { inline namespace lts_2019_08_08 { namespace base_internal { namespace { // protects num_identities_reused static absl::base_internal::SpinLock map_lock( absl::base_internal::kLinkerInitialized); static int num_identities_reused; static const void* const kCheckNoIdentity = reinterpret_cast(1); static void TestThreadIdentityCurrent(const void* assert_no_identity) { ThreadIdentity* identity; // We have to test this conditionally, because if the test framework relies // on Abseil, then some previous action may have already allocated an // identity. if (assert_no_identity == kCheckNoIdentity) { identity = CurrentThreadIdentityIfPresent(); EXPECT_TRUE(identity == nullptr); } identity = synchronization_internal::GetOrCreateCurrentThreadIdentity(); EXPECT_TRUE(identity != nullptr); ThreadIdentity* identity_no_init; identity_no_init = CurrentThreadIdentityIfPresent(); EXPECT_TRUE(identity == identity_no_init); // Check that per_thread_synch is correctly aligned. EXPECT_EQ(0, reinterpret_cast(&identity->per_thread_synch) % PerThreadSynch::kAlignment); EXPECT_EQ(identity, identity->per_thread_synch.thread_identity()); absl::base_internal::SpinLockHolder l(&map_lock); num_identities_reused++; } TEST(ThreadIdentityTest, BasicIdentityWorks) { // This tests for the main() thread. TestThreadIdentityCurrent(nullptr); } TEST(ThreadIdentityTest, BasicIdentityWorksThreaded) { // Now try the same basic test with multiple threads being created and // destroyed. This makes sure that: // - New threads are created without a ThreadIdentity. // - We re-allocate ThreadIdentity objects from the free-list. // - If a thread implementation chooses to recycle threads, that // correct re-initialization occurs. static const int kNumLoops = 3; static const int kNumThreads = 400; for (int iter = 0; iter < kNumLoops; iter++) { std::vector threads; for (int i = 0; i < kNumThreads; ++i) { threads.push_back( std::thread(TestThreadIdentityCurrent, kCheckNoIdentity)); } for (auto& thread : threads) { thread.join(); } } // We should have recycled ThreadIdentity objects above; while (external) // library threads allocating their own identities may preclude some // reuse, we should have sufficient repetitions to exclude this. EXPECT_LT(kNumThreads, num_identities_reused); } TEST(ThreadIdentityTest, ReusedThreadIdentityMutexTest) { // This test repeatly creates and joins a series of threads, each of // which acquires and releases shared Mutex locks. This verifies // Mutex operations work correctly under a reused // ThreadIdentity. Note that the most likely failure mode of this // test is a crash or deadlock. static const int kNumLoops = 10; static const int kNumThreads = 12; static const int kNumMutexes = 3; static const int kNumLockLoops = 5; Mutex mutexes[kNumMutexes]; for (int iter = 0; iter < kNumLoops; ++iter) { std::vector threads; for (int thread = 0; thread < kNumThreads; ++thread) { threads.push_back(std::thread([&]() { for (int l = 0; l < kNumLockLoops; ++l) { for (int m = 0; m < kNumMutexes; ++m) { MutexLock lock(&mutexes[m]); } } })); } for (auto& thread : threads) { thread.join(); } } } } // namespace } // namespace base_internal } // inline namespace lts_2019_08_08 } // namespace absl