spinlock_test_common.cc 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. // Copyright 2017 The Abseil Authors.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // https://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // A bunch of threads repeatedly hash an array of ints protected by a
  15. // spinlock. If the spinlock is working properly, all elements of the
  16. // array should be equal at the end of the test.
  17. #include <cstdint>
  18. #include <limits>
  19. #include <random>
  20. #include <thread> // NOLINT(build/c++11)
  21. #include <type_traits>
  22. #include <vector>
  23. #include "gtest/gtest.h"
  24. #include "absl/base/attributes.h"
  25. #include "absl/base/config.h"
  26. #include "absl/base/internal/low_level_scheduling.h"
  27. #include "absl/base/internal/scheduling_mode.h"
  28. #include "absl/base/internal/spinlock.h"
  29. #include "absl/base/internal/sysinfo.h"
  30. #include "absl/base/macros.h"
  31. #include "absl/synchronization/blocking_counter.h"
  32. #include "absl/synchronization/notification.h"
  33. constexpr int32_t kNumThreads = 10;
  34. constexpr int32_t kIters = 1000;
  35. namespace absl {
  36. ABSL_NAMESPACE_BEGIN
  37. namespace base_internal {
  38. // This is defined outside of anonymous namespace so that it can be
  39. // a friend of SpinLock to access protected methods for testing.
  40. struct SpinLockTest {
  41. static uint32_t EncodeWaitCycles(int64_t wait_start_time,
  42. int64_t wait_end_time) {
  43. return SpinLock::EncodeWaitCycles(wait_start_time, wait_end_time);
  44. }
  45. static uint64_t DecodeWaitCycles(uint32_t lock_value) {
  46. return SpinLock::DecodeWaitCycles(lock_value);
  47. }
  48. };
  49. namespace {
  50. static constexpr int kArrayLength = 10;
  51. static uint32_t values[kArrayLength];
  52. ABSL_CONST_INIT static SpinLock static_cooperative_spinlock(
  53. absl::kConstInit, base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL);
  54. ABSL_CONST_INIT static SpinLock static_noncooperative_spinlock(
  55. absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY);
  56. // Simple integer hash function based on the public domain lookup2 hash.
  57. // http://burtleburtle.net/bob/c/lookup2.c
  58. static uint32_t Hash32(uint32_t a, uint32_t c) {
  59. uint32_t b = 0x9e3779b9UL; // The golden ratio; an arbitrary value.
  60. a -= b; a -= c; a ^= (c >> 13);
  61. b -= c; b -= a; b ^= (a << 8);
  62. c -= a; c -= b; c ^= (b >> 13);
  63. a -= b; a -= c; a ^= (c >> 12);
  64. b -= c; b -= a; b ^= (a << 16);
  65. c -= a; c -= b; c ^= (b >> 5);
  66. a -= b; a -= c; a ^= (c >> 3);
  67. b -= c; b -= a; b ^= (a << 10);
  68. c -= a; c -= b; c ^= (b >> 15);
  69. return c;
  70. }
  71. static void TestFunction(int thread_salt, SpinLock* spinlock) {
  72. for (int i = 0; i < kIters; i++) {
  73. SpinLockHolder h(spinlock);
  74. for (int j = 0; j < kArrayLength; j++) {
  75. const int index = (j + thread_salt) % kArrayLength;
  76. values[index] = Hash32(values[index], thread_salt);
  77. std::this_thread::yield();
  78. }
  79. }
  80. }
  81. static void ThreadedTest(SpinLock* spinlock) {
  82. std::vector<std::thread> threads;
  83. threads.reserve(kNumThreads);
  84. for (int i = 0; i < kNumThreads; ++i) {
  85. threads.push_back(std::thread(TestFunction, i, spinlock));
  86. }
  87. for (auto& thread : threads) {
  88. thread.join();
  89. }
  90. SpinLockHolder h(spinlock);
  91. for (int i = 1; i < kArrayLength; i++) {
  92. EXPECT_EQ(values[0], values[i]);
  93. }
  94. }
  95. #ifndef ABSL_HAVE_THREAD_SANITIZER
  96. static_assert(std::is_trivially_destructible<SpinLock>(), "");
  97. #endif
  98. TEST(SpinLock, StackNonCooperativeDisablesScheduling) {
  99. SpinLock spinlock(base_internal::SCHEDULE_KERNEL_ONLY);
  100. spinlock.Lock();
  101. EXPECT_FALSE(base_internal::SchedulingGuard::ReschedulingIsAllowed());
  102. spinlock.Unlock();
  103. }
  104. TEST(SpinLock, StaticNonCooperativeDisablesScheduling) {
  105. static_noncooperative_spinlock.Lock();
  106. EXPECT_FALSE(base_internal::SchedulingGuard::ReschedulingIsAllowed());
  107. static_noncooperative_spinlock.Unlock();
  108. }
  109. TEST(SpinLock, WaitCyclesEncoding) {
  110. // These are implementation details not exported by SpinLock.
  111. const int kProfileTimestampShift = 7;
  112. const int kLockwordReservedShift = 3;
  113. const uint32_t kSpinLockSleeper = 8;
  114. // We should be able to encode up to (1^kMaxCycleBits - 1) without clamping
  115. // but the lower kProfileTimestampShift will be dropped.
  116. const int kMaxCyclesShift =
  117. 32 - kLockwordReservedShift + kProfileTimestampShift;
  118. const uint64_t kMaxCycles = (int64_t{1} << kMaxCyclesShift) - 1;
  119. // These bits should be zero after encoding.
  120. const uint32_t kLockwordReservedMask = (1 << kLockwordReservedShift) - 1;
  121. // These bits are dropped when wait cycles are encoded.
  122. const uint64_t kProfileTimestampMask = (1 << kProfileTimestampShift) - 1;
  123. // Test a bunch of random values
  124. std::default_random_engine generator;
  125. // Shift to avoid overflow below.
  126. std::uniform_int_distribution<uint64_t> time_distribution(
  127. 0, std::numeric_limits<uint64_t>::max() >> 4);
  128. std::uniform_int_distribution<uint64_t> cycle_distribution(0, kMaxCycles);
  129. for (int i = 0; i < 100; i++) {
  130. int64_t start_time = time_distribution(generator);
  131. int64_t cycles = cycle_distribution(generator);
  132. int64_t end_time = start_time + cycles;
  133. uint32_t lock_value = SpinLockTest::EncodeWaitCycles(start_time, end_time);
  134. EXPECT_EQ(0, lock_value & kLockwordReservedMask);
  135. uint64_t decoded = SpinLockTest::DecodeWaitCycles(lock_value);
  136. EXPECT_EQ(0, decoded & kProfileTimestampMask);
  137. EXPECT_EQ(cycles & ~kProfileTimestampMask, decoded);
  138. }
  139. // Test corner cases
  140. int64_t start_time = time_distribution(generator);
  141. EXPECT_EQ(kSpinLockSleeper,
  142. SpinLockTest::EncodeWaitCycles(start_time, start_time));
  143. EXPECT_EQ(0, SpinLockTest::DecodeWaitCycles(0));
  144. EXPECT_EQ(0, SpinLockTest::DecodeWaitCycles(kLockwordReservedMask));
  145. EXPECT_EQ(kMaxCycles & ~kProfileTimestampMask,
  146. SpinLockTest::DecodeWaitCycles(~kLockwordReservedMask));
  147. // Check that we cannot produce kSpinLockSleeper during encoding.
  148. int64_t sleeper_cycles =
  149. kSpinLockSleeper << (kProfileTimestampShift - kLockwordReservedShift);
  150. uint32_t sleeper_value =
  151. SpinLockTest::EncodeWaitCycles(start_time, start_time + sleeper_cycles);
  152. EXPECT_NE(sleeper_value, kSpinLockSleeper);
  153. // Test clamping
  154. uint32_t max_value =
  155. SpinLockTest::EncodeWaitCycles(start_time, start_time + kMaxCycles);
  156. uint64_t max_value_decoded = SpinLockTest::DecodeWaitCycles(max_value);
  157. uint64_t expected_max_value_decoded = kMaxCycles & ~kProfileTimestampMask;
  158. EXPECT_EQ(expected_max_value_decoded, max_value_decoded);
  159. const int64_t step = (1 << kProfileTimestampShift);
  160. uint32_t after_max_value =
  161. SpinLockTest::EncodeWaitCycles(start_time, start_time + kMaxCycles + step);
  162. uint64_t after_max_value_decoded =
  163. SpinLockTest::DecodeWaitCycles(after_max_value);
  164. EXPECT_EQ(expected_max_value_decoded, after_max_value_decoded);
  165. uint32_t before_max_value = SpinLockTest::EncodeWaitCycles(
  166. start_time, start_time + kMaxCycles - step);
  167. uint64_t before_max_value_decoded =
  168. SpinLockTest::DecodeWaitCycles(before_max_value);
  169. EXPECT_GT(expected_max_value_decoded, before_max_value_decoded);
  170. }
  171. TEST(SpinLockWithThreads, StackSpinLock) {
  172. SpinLock spinlock;
  173. ThreadedTest(&spinlock);
  174. }
  175. TEST(SpinLockWithThreads, StackCooperativeSpinLock) {
  176. SpinLock spinlock(base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL);
  177. ThreadedTest(&spinlock);
  178. }
  179. TEST(SpinLockWithThreads, StackNonCooperativeSpinLock) {
  180. SpinLock spinlock(base_internal::SCHEDULE_KERNEL_ONLY);
  181. ThreadedTest(&spinlock);
  182. }
  183. TEST(SpinLockWithThreads, StaticCooperativeSpinLock) {
  184. ThreadedTest(&static_cooperative_spinlock);
  185. }
  186. TEST(SpinLockWithThreads, StaticNonCooperativeSpinLock) {
  187. ThreadedTest(&static_noncooperative_spinlock);
  188. }
  189. TEST(SpinLockWithThreads, DoesNotDeadlock) {
  190. struct Helper {
  191. static void NotifyThenLock(Notification* locked, SpinLock* spinlock,
  192. BlockingCounter* b) {
  193. locked->WaitForNotification(); // Wait for LockThenWait() to hold "s".
  194. b->DecrementCount();
  195. SpinLockHolder l(spinlock);
  196. }
  197. static void LockThenWait(Notification* locked, SpinLock* spinlock,
  198. BlockingCounter* b) {
  199. SpinLockHolder l(spinlock);
  200. locked->Notify();
  201. b->Wait();
  202. }
  203. static void DeadlockTest(SpinLock* spinlock, int num_spinners) {
  204. Notification locked;
  205. BlockingCounter counter(num_spinners);
  206. std::vector<std::thread> threads;
  207. threads.push_back(
  208. std::thread(Helper::LockThenWait, &locked, spinlock, &counter));
  209. for (int i = 0; i < num_spinners; ++i) {
  210. threads.push_back(
  211. std::thread(Helper::NotifyThenLock, &locked, spinlock, &counter));
  212. }
  213. for (auto& thread : threads) {
  214. thread.join();
  215. }
  216. }
  217. };
  218. SpinLock stack_cooperative_spinlock(
  219. base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL);
  220. SpinLock stack_noncooperative_spinlock(base_internal::SCHEDULE_KERNEL_ONLY);
  221. Helper::DeadlockTest(&stack_cooperative_spinlock,
  222. base_internal::NumCPUs() * 2);
  223. Helper::DeadlockTest(&stack_noncooperative_spinlock,
  224. base_internal::NumCPUs() * 2);
  225. Helper::DeadlockTest(&static_cooperative_spinlock,
  226. base_internal::NumCPUs() * 2);
  227. Helper::DeadlockTest(&static_noncooperative_spinlock,
  228. base_internal::NumCPUs() * 2);
  229. }
  230. } // namespace
  231. } // namespace base_internal
  232. ABSL_NAMESPACE_END
  233. } // namespace absl