spinlock_test_common.cc
Go to the documentation of this file.
00001 // Copyright 2017 The Abseil Authors.
00002 //
00003 // Licensed under the Apache License, Version 2.0 (the "License");
00004 // you may not use this file except in compliance with the License.
00005 // You may obtain a copy of the License at
00006 //
00007 //      https://www.apache.org/licenses/LICENSE-2.0
00008 //
00009 // Unless required by applicable law or agreed to in writing, software
00010 // distributed under the License is distributed on an "AS IS" BASIS,
00011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00012 // See the License for the specific language governing permissions and
00013 // limitations under the License.
00014 
00015 // A bunch of threads repeatedly hash an array of ints protected by a
00016 // spinlock.  If the spinlock is working properly, all elements of the
00017 // array should be equal at the end of the test.
00018 
00019 #include <cstdint>
00020 #include <limits>
00021 #include <random>
00022 #include <thread>  // NOLINT(build/c++11)
00023 #include <vector>
00024 
00025 #include "gtest/gtest.h"
00026 #include "absl/base/attributes.h"
00027 #include "absl/base/internal/low_level_scheduling.h"
00028 #include "absl/base/internal/scheduling_mode.h"
00029 #include "absl/base/internal/spinlock.h"
00030 #include "absl/base/internal/sysinfo.h"
00031 #include "absl/base/macros.h"
00032 #include "absl/synchronization/blocking_counter.h"
00033 #include "absl/synchronization/notification.h"
00034 
00035 constexpr int32_t kNumThreads = 10;
00036 constexpr int32_t kIters = 1000;
00037 
00038 namespace absl {
00039 namespace base_internal {
00040 
00041 // This is defined outside of anonymous namespace so that it can be
00042 // a friend of SpinLock to access protected methods for testing.
00043 struct SpinLockTest {
00044   static uint32_t EncodeWaitCycles(int64_t wait_start_time,
00045                                    int64_t wait_end_time) {
00046     return SpinLock::EncodeWaitCycles(wait_start_time, wait_end_time);
00047   }
00048   static uint64_t DecodeWaitCycles(uint32_t lock_value) {
00049     return SpinLock::DecodeWaitCycles(lock_value);
00050   }
00051 };
00052 
00053 namespace {
00054 
00055 static constexpr int kArrayLength = 10;
00056 static uint32_t values[kArrayLength];
00057 
00058 static SpinLock static_spinlock(base_internal::kLinkerInitialized);
00059 static SpinLock static_cooperative_spinlock(
00060     base_internal::kLinkerInitialized,
00061     base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL);
00062 static SpinLock static_noncooperative_spinlock(
00063     base_internal::kLinkerInitialized, base_internal::SCHEDULE_KERNEL_ONLY);
00064 
00065 
00066 // Simple integer hash function based on the public domain lookup2 hash.
00067 // http://burtleburtle.net/bob/c/lookup2.c
00068 static uint32_t Hash32(uint32_t a, uint32_t c) {
00069   uint32_t b = 0x9e3779b9UL;  // The golden ratio; an arbitrary value.
00070   a -= b; a -= c; a ^= (c >> 13);
00071   b -= c; b -= a; b ^= (a << 8);
00072   c -= a; c -= b; c ^= (b >> 13);
00073   a -= b; a -= c; a ^= (c >> 12);
00074   b -= c; b -= a; b ^= (a << 16);
00075   c -= a; c -= b; c ^= (b >> 5);
00076   a -= b; a -= c; a ^= (c >> 3);
00077   b -= c; b -= a; b ^= (a << 10);
00078   c -= a; c -= b; c ^= (b >> 15);
00079   return c;
00080 }
00081 
00082 static void TestFunction(int thread_salt, SpinLock* spinlock) {
00083   for (int i = 0; i < kIters; i++) {
00084     SpinLockHolder h(spinlock);
00085     for (int j = 0; j < kArrayLength; j++) {
00086       const int index = (j + thread_salt) % kArrayLength;
00087       values[index] = Hash32(values[index], thread_salt);
00088       std::this_thread::yield();
00089     }
00090   }
00091 }
00092 
00093 static void ThreadedTest(SpinLock* spinlock) {
00094   std::vector<std::thread> threads;
00095   for (int i = 0; i < kNumThreads; ++i) {
00096     threads.push_back(std::thread(TestFunction, i, spinlock));
00097   }
00098   for (auto& thread : threads) {
00099     thread.join();
00100   }
00101 
00102   SpinLockHolder h(spinlock);
00103   for (int i = 1; i < kArrayLength; i++) {
00104     EXPECT_EQ(values[0], values[i]);
00105   }
00106 }
00107 
00108 TEST(SpinLock, StackNonCooperativeDisablesScheduling) {
00109   SpinLock spinlock(base_internal::SCHEDULE_KERNEL_ONLY);
00110   spinlock.Lock();
00111   EXPECT_FALSE(base_internal::SchedulingGuard::ReschedulingIsAllowed());
00112   spinlock.Unlock();
00113 }
00114 
00115 TEST(SpinLock, StaticNonCooperativeDisablesScheduling) {
00116   static_noncooperative_spinlock.Lock();
00117   EXPECT_FALSE(base_internal::SchedulingGuard::ReschedulingIsAllowed());
00118   static_noncooperative_spinlock.Unlock();
00119 }
00120 
00121 TEST(SpinLock, WaitCyclesEncoding) {
00122   // These are implementation details not exported by SpinLock.
00123   const int kProfileTimestampShift = 7;
00124   const int kLockwordReservedShift = 3;
00125   const uint32_t kSpinLockSleeper = 8;
00126 
00127   // We should be able to encode up to (1^kMaxCycleBits - 1) without clamping
00128   // but the lower kProfileTimestampShift will be dropped.
00129   const int kMaxCyclesShift =
00130     32 - kLockwordReservedShift + kProfileTimestampShift;
00131   const uint64_t kMaxCycles = (int64_t{1} << kMaxCyclesShift) - 1;
00132 
00133   // These bits should be zero after encoding.
00134   const uint32_t kLockwordReservedMask = (1 << kLockwordReservedShift) - 1;
00135 
00136   // These bits are dropped when wait cycles are encoded.
00137   const uint64_t kProfileTimestampMask = (1 << kProfileTimestampShift) - 1;
00138 
00139   // Test a bunch of random values
00140   std::default_random_engine generator;
00141   // Shift to avoid overflow below.
00142   std::uniform_int_distribution<uint64_t> time_distribution(
00143       0, std::numeric_limits<uint64_t>::max() >> 4);
00144   std::uniform_int_distribution<uint64_t> cycle_distribution(0, kMaxCycles);
00145 
00146   for (int i = 0; i < 100; i++) {
00147     int64_t start_time = time_distribution(generator);
00148     int64_t cycles = cycle_distribution(generator);
00149     int64_t end_time = start_time + cycles;
00150     uint32_t lock_value = SpinLockTest::EncodeWaitCycles(start_time, end_time);
00151     EXPECT_EQ(0, lock_value & kLockwordReservedMask);
00152     uint64_t decoded = SpinLockTest::DecodeWaitCycles(lock_value);
00153     EXPECT_EQ(0, decoded & kProfileTimestampMask);
00154     EXPECT_EQ(cycles & ~kProfileTimestampMask, decoded);
00155   }
00156 
00157   // Test corner cases
00158   int64_t start_time = time_distribution(generator);
00159   EXPECT_EQ(kSpinLockSleeper,
00160             SpinLockTest::EncodeWaitCycles(start_time, start_time));
00161   EXPECT_EQ(0, SpinLockTest::DecodeWaitCycles(0));
00162   EXPECT_EQ(0, SpinLockTest::DecodeWaitCycles(kLockwordReservedMask));
00163   EXPECT_EQ(kMaxCycles & ~kProfileTimestampMask,
00164             SpinLockTest::DecodeWaitCycles(~kLockwordReservedMask));
00165 
00166   // Check that we cannot produce kSpinLockSleeper during encoding.
00167   int64_t sleeper_cycles =
00168       kSpinLockSleeper << (kProfileTimestampShift - kLockwordReservedShift);
00169   uint32_t sleeper_value =
00170       SpinLockTest::EncodeWaitCycles(start_time, start_time + sleeper_cycles);
00171   EXPECT_NE(sleeper_value, kSpinLockSleeper);
00172 
00173   // Test clamping
00174   uint32_t max_value =
00175     SpinLockTest::EncodeWaitCycles(start_time, start_time + kMaxCycles);
00176   uint64_t max_value_decoded = SpinLockTest::DecodeWaitCycles(max_value);
00177   uint64_t expected_max_value_decoded = kMaxCycles & ~kProfileTimestampMask;
00178   EXPECT_EQ(expected_max_value_decoded, max_value_decoded);
00179 
00180   const int64_t step = (1 << kProfileTimestampShift);
00181   uint32_t after_max_value =
00182     SpinLockTest::EncodeWaitCycles(start_time, start_time + kMaxCycles + step);
00183   uint64_t after_max_value_decoded =
00184       SpinLockTest::DecodeWaitCycles(after_max_value);
00185   EXPECT_EQ(expected_max_value_decoded, after_max_value_decoded);
00186 
00187   uint32_t before_max_value = SpinLockTest::EncodeWaitCycles(
00188       start_time, start_time + kMaxCycles - step);
00189   uint64_t before_max_value_decoded =
00190     SpinLockTest::DecodeWaitCycles(before_max_value);
00191   EXPECT_GT(expected_max_value_decoded, before_max_value_decoded);
00192 }
00193 
00194 TEST(SpinLockWithThreads, StaticSpinLock) {
00195   ThreadedTest(&static_spinlock);
00196 }
00197 TEST(SpinLockWithThreads, StackSpinLock) {
00198   SpinLock spinlock;
00199   ThreadedTest(&spinlock);
00200 }
00201 
00202 TEST(SpinLockWithThreads, StackCooperativeSpinLock) {
00203   SpinLock spinlock(base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL);
00204   ThreadedTest(&spinlock);
00205 }
00206 
00207 TEST(SpinLockWithThreads, StackNonCooperativeSpinLock) {
00208   SpinLock spinlock(base_internal::SCHEDULE_KERNEL_ONLY);
00209   ThreadedTest(&spinlock);
00210 }
00211 
00212 TEST(SpinLockWithThreads, StaticCooperativeSpinLock) {
00213   ThreadedTest(&static_cooperative_spinlock);
00214 }
00215 
00216 TEST(SpinLockWithThreads, StaticNonCooperativeSpinLock) {
00217   ThreadedTest(&static_noncooperative_spinlock);
00218 }
00219 
00220 TEST(SpinLockWithThreads, DoesNotDeadlock) {
00221   struct Helper {
00222     static void NotifyThenLock(Notification* locked, SpinLock* spinlock,
00223                                BlockingCounter* b) {
00224       locked->WaitForNotification();  // Wait for LockThenWait() to hold "s".
00225       b->DecrementCount();
00226       SpinLockHolder l(spinlock);
00227     }
00228 
00229     static void LockThenWait(Notification* locked, SpinLock* spinlock,
00230                              BlockingCounter* b) {
00231       SpinLockHolder l(spinlock);
00232       locked->Notify();
00233       b->Wait();
00234     }
00235 
00236     static void DeadlockTest(SpinLock* spinlock, int num_spinners) {
00237       Notification locked;
00238       BlockingCounter counter(num_spinners);
00239       std::vector<std::thread> threads;
00240 
00241       threads.push_back(
00242           std::thread(Helper::LockThenWait, &locked, spinlock, &counter));
00243       for (int i = 0; i < num_spinners; ++i) {
00244         threads.push_back(
00245             std::thread(Helper::NotifyThenLock, &locked, spinlock, &counter));
00246       }
00247 
00248       for (auto& thread : threads) {
00249         thread.join();
00250       }
00251     }
00252   };
00253 
00254   SpinLock stack_cooperative_spinlock(
00255       base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL);
00256   SpinLock stack_noncooperative_spinlock(base_internal::SCHEDULE_KERNEL_ONLY);
00257   Helper::DeadlockTest(&stack_cooperative_spinlock,
00258                        base_internal::NumCPUs() * 2);
00259   Helper::DeadlockTest(&stack_noncooperative_spinlock,
00260                        base_internal::NumCPUs() * 2);
00261   Helper::DeadlockTest(&static_cooperative_spinlock,
00262                        base_internal::NumCPUs() * 2);
00263   Helper::DeadlockTest(&static_noncooperative_spinlock,
00264                        base_internal::NumCPUs() * 2);
00265 }
00266 
00267 }  // namespace
00268 }  // namespace base_internal
00269 }  // namespace absl


abseil_cpp
Author(s):
autogenerated on Wed Jun 19 2019 19:42:15