00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include <cstdint>
00020 #include <limits>
00021 #include <random>
00022 #include <thread>
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
00042
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
00067
00068 static uint32_t Hash32(uint32_t a, uint32_t c) {
00069 uint32_t b = 0x9e3779b9UL;
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
00123 const int kProfileTimestampShift = 7;
00124 const int kLockwordReservedShift = 3;
00125 const uint32_t kSpinLockSleeper = 8;
00126
00127
00128
00129 const int kMaxCyclesShift =
00130 32 - kLockwordReservedShift + kProfileTimestampShift;
00131 const uint64_t kMaxCycles = (int64_t{1} << kMaxCyclesShift) - 1;
00132
00133
00134 const uint32_t kLockwordReservedMask = (1 << kLockwordReservedShift) - 1;
00135
00136
00137 const uint64_t kProfileTimestampMask = (1 << kProfileTimestampShift) - 1;
00138
00139
00140 std::default_random_engine generator;
00141
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
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
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
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();
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 }
00268 }
00269 }