123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129 |
- // 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 <thread> // NOLINT(build/c++11)
- #include <vector>
- #include "gtest/gtest.h"
- #include "absl/base/attributes.h"
- #include "absl/base/internal/spinlock.h"
- #include "absl/base/macros.h"
- #include "absl/base/thread_annotations.h"
- #include "absl/synchronization/internal/per_thread_sem.h"
- #include "absl/synchronization/mutex.h"
- namespace absl {
- ABSL_NAMESPACE_BEGIN
- namespace base_internal {
- namespace {
- ABSL_CONST_INIT static absl::base_internal::SpinLock map_lock(
- absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY);
- ABSL_CONST_INIT static int num_identities_reused ABSL_GUARDED_BY(map_lock);
- static const void* const kCheckNoIdentity = reinterpret_cast<void*>(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<intptr_t>(&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 = 32;
- for (int iter = 0; iter < kNumLoops; iter++) {
- std::vector<std::thread> 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.
- absl::base_internal::SpinLockHolder l(&map_lock);
- 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<std::thread> 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
- ABSL_NAMESPACE_END
- } // namespace absl
|