summaryrefslogtreecommitdiff
path: root/absl/base/internal/thread_identity_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'absl/base/internal/thread_identity_test.cc')
-rw-r--r--absl/base/internal/thread_identity_test.cc124
1 files changed, 124 insertions, 0 deletions
diff --git a/absl/base/internal/thread_identity_test.cc b/absl/base/internal/thread_identity_test.cc
new file mode 100644
index 00000000..a2b053d9
--- /dev/null
+++ b/absl/base/internal/thread_identity_test.cc
@@ -0,0 +1,124 @@
+// 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
+//
+// http://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/internal/spinlock.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