126 lines
		
	
	
	
		
			4.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			126 lines
		
	
	
	
		
			4.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
// 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/synchronization/internal/per_thread_sem.h"
 | 
						|
#include "absl/synchronization/mutex.h"
 | 
						|
 | 
						|
namespace absl {
 | 
						|
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<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 = 400;
 | 
						|
  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.
 | 
						|
  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
 | 
						|
}  // namespace absl
 |