Austin Schuh | 36244a1 | 2019-09-21 17:52:38 -0700 | [diff] [blame] | 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 | |
| 15 | #include "absl/synchronization/mutex.h" |
| 16 | |
| 17 | #ifdef _WIN32 |
| 18 | #include <windows.h> |
| 19 | #endif |
| 20 | |
| 21 | #include <algorithm> |
| 22 | #include <atomic> |
| 23 | #include <cstdlib> |
| 24 | #include <functional> |
| 25 | #include <memory> |
| 26 | #include <random> |
| 27 | #include <string> |
| 28 | #include <thread> // NOLINT(build/c++11) |
| 29 | #include <vector> |
| 30 | |
| 31 | #include "gtest/gtest.h" |
| 32 | #include "absl/base/attributes.h" |
Austin Schuh | b4691e9 | 2020-12-31 12:37:18 -0800 | [diff] [blame^] | 33 | #include "absl/base/config.h" |
Austin Schuh | 36244a1 | 2019-09-21 17:52:38 -0700 | [diff] [blame] | 34 | #include "absl/base/internal/raw_logging.h" |
| 35 | #include "absl/base/internal/sysinfo.h" |
| 36 | #include "absl/memory/memory.h" |
| 37 | #include "absl/synchronization/internal/thread_pool.h" |
| 38 | #include "absl/time/clock.h" |
| 39 | #include "absl/time/time.h" |
| 40 | |
| 41 | namespace { |
| 42 | |
| 43 | // TODO(dmauro): Replace with a commandline flag. |
| 44 | static constexpr bool kExtendedTest = false; |
| 45 | |
| 46 | std::unique_ptr<absl::synchronization_internal::ThreadPool> CreatePool( |
| 47 | int threads) { |
| 48 | return absl::make_unique<absl::synchronization_internal::ThreadPool>(threads); |
| 49 | } |
| 50 | |
| 51 | std::unique_ptr<absl::synchronization_internal::ThreadPool> |
| 52 | CreateDefaultPool() { |
| 53 | return CreatePool(kExtendedTest ? 32 : 10); |
| 54 | } |
| 55 | |
| 56 | // Hack to schedule a function to run on a thread pool thread after a |
| 57 | // duration has elapsed. |
| 58 | static void ScheduleAfter(absl::synchronization_internal::ThreadPool *tp, |
| 59 | absl::Duration after, |
| 60 | const std::function<void()> &func) { |
| 61 | tp->Schedule([func, after] { |
| 62 | absl::SleepFor(after); |
| 63 | func(); |
| 64 | }); |
| 65 | } |
| 66 | |
| 67 | struct TestContext { |
| 68 | int iterations; |
| 69 | int threads; |
| 70 | int g0; // global 0 |
| 71 | int g1; // global 1 |
| 72 | absl::Mutex mu; |
| 73 | absl::CondVar cv; |
| 74 | }; |
| 75 | |
| 76 | // To test whether the invariant check call occurs |
| 77 | static std::atomic<bool> invariant_checked; |
| 78 | |
| 79 | static bool GetInvariantChecked() { |
| 80 | return invariant_checked.load(std::memory_order_relaxed); |
| 81 | } |
| 82 | |
| 83 | static void SetInvariantChecked(bool new_value) { |
| 84 | invariant_checked.store(new_value, std::memory_order_relaxed); |
| 85 | } |
| 86 | |
| 87 | static void CheckSumG0G1(void *v) { |
| 88 | TestContext *cxt = static_cast<TestContext *>(v); |
| 89 | ABSL_RAW_CHECK(cxt->g0 == -cxt->g1, "Error in CheckSumG0G1"); |
| 90 | SetInvariantChecked(true); |
| 91 | } |
| 92 | |
| 93 | static void TestMu(TestContext *cxt, int c) { |
| 94 | for (int i = 0; i != cxt->iterations; i++) { |
| 95 | absl::MutexLock l(&cxt->mu); |
| 96 | int a = cxt->g0 + 1; |
| 97 | cxt->g0 = a; |
| 98 | cxt->g1--; |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | static void TestTry(TestContext *cxt, int c) { |
| 103 | for (int i = 0; i != cxt->iterations; i++) { |
| 104 | do { |
| 105 | std::this_thread::yield(); |
| 106 | } while (!cxt->mu.TryLock()); |
| 107 | int a = cxt->g0 + 1; |
| 108 | cxt->g0 = a; |
| 109 | cxt->g1--; |
| 110 | cxt->mu.Unlock(); |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | static void TestR20ms(TestContext *cxt, int c) { |
| 115 | for (int i = 0; i != cxt->iterations; i++) { |
| 116 | absl::ReaderMutexLock l(&cxt->mu); |
| 117 | absl::SleepFor(absl::Milliseconds(20)); |
| 118 | cxt->mu.AssertReaderHeld(); |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | static void TestRW(TestContext *cxt, int c) { |
| 123 | if ((c & 1) == 0) { |
| 124 | for (int i = 0; i != cxt->iterations; i++) { |
| 125 | absl::WriterMutexLock l(&cxt->mu); |
| 126 | cxt->g0++; |
| 127 | cxt->g1--; |
| 128 | cxt->mu.AssertHeld(); |
| 129 | cxt->mu.AssertReaderHeld(); |
| 130 | } |
| 131 | } else { |
| 132 | for (int i = 0; i != cxt->iterations; i++) { |
| 133 | absl::ReaderMutexLock l(&cxt->mu); |
| 134 | ABSL_RAW_CHECK(cxt->g0 == -cxt->g1, "Error in TestRW"); |
| 135 | cxt->mu.AssertReaderHeld(); |
| 136 | } |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | struct MyContext { |
| 141 | int target; |
| 142 | TestContext *cxt; |
| 143 | bool MyTurn(); |
| 144 | }; |
| 145 | |
| 146 | bool MyContext::MyTurn() { |
| 147 | TestContext *cxt = this->cxt; |
| 148 | return cxt->g0 == this->target || cxt->g0 == cxt->iterations; |
| 149 | } |
| 150 | |
| 151 | static void TestAwait(TestContext *cxt, int c) { |
| 152 | MyContext mc; |
| 153 | mc.target = c; |
| 154 | mc.cxt = cxt; |
| 155 | absl::MutexLock l(&cxt->mu); |
| 156 | cxt->mu.AssertHeld(); |
| 157 | while (cxt->g0 < cxt->iterations) { |
| 158 | cxt->mu.Await(absl::Condition(&mc, &MyContext::MyTurn)); |
| 159 | ABSL_RAW_CHECK(mc.MyTurn(), "Error in TestAwait"); |
| 160 | cxt->mu.AssertHeld(); |
| 161 | if (cxt->g0 < cxt->iterations) { |
| 162 | int a = cxt->g0 + 1; |
| 163 | cxt->g0 = a; |
| 164 | mc.target += cxt->threads; |
| 165 | } |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | static void TestSignalAll(TestContext *cxt, int c) { |
| 170 | int target = c; |
| 171 | absl::MutexLock l(&cxt->mu); |
| 172 | cxt->mu.AssertHeld(); |
| 173 | while (cxt->g0 < cxt->iterations) { |
| 174 | while (cxt->g0 != target && cxt->g0 != cxt->iterations) { |
| 175 | cxt->cv.Wait(&cxt->mu); |
| 176 | } |
| 177 | if (cxt->g0 < cxt->iterations) { |
| 178 | int a = cxt->g0 + 1; |
| 179 | cxt->g0 = a; |
| 180 | cxt->cv.SignalAll(); |
| 181 | target += cxt->threads; |
| 182 | } |
| 183 | } |
| 184 | } |
| 185 | |
| 186 | static void TestSignal(TestContext *cxt, int c) { |
| 187 | ABSL_RAW_CHECK(cxt->threads == 2, "TestSignal should use 2 threads"); |
| 188 | int target = c; |
| 189 | absl::MutexLock l(&cxt->mu); |
| 190 | cxt->mu.AssertHeld(); |
| 191 | while (cxt->g0 < cxt->iterations) { |
| 192 | while (cxt->g0 != target && cxt->g0 != cxt->iterations) { |
| 193 | cxt->cv.Wait(&cxt->mu); |
| 194 | } |
| 195 | if (cxt->g0 < cxt->iterations) { |
| 196 | int a = cxt->g0 + 1; |
| 197 | cxt->g0 = a; |
| 198 | cxt->cv.Signal(); |
| 199 | target += cxt->threads; |
| 200 | } |
| 201 | } |
| 202 | } |
| 203 | |
| 204 | static void TestCVTimeout(TestContext *cxt, int c) { |
| 205 | int target = c; |
| 206 | absl::MutexLock l(&cxt->mu); |
| 207 | cxt->mu.AssertHeld(); |
| 208 | while (cxt->g0 < cxt->iterations) { |
| 209 | while (cxt->g0 != target && cxt->g0 != cxt->iterations) { |
| 210 | cxt->cv.WaitWithTimeout(&cxt->mu, absl::Seconds(100)); |
| 211 | } |
| 212 | if (cxt->g0 < cxt->iterations) { |
| 213 | int a = cxt->g0 + 1; |
| 214 | cxt->g0 = a; |
| 215 | cxt->cv.SignalAll(); |
| 216 | target += cxt->threads; |
| 217 | } |
| 218 | } |
| 219 | } |
| 220 | |
| 221 | static bool G0GE2(TestContext *cxt) { return cxt->g0 >= 2; } |
| 222 | |
| 223 | static void TestTime(TestContext *cxt, int c, bool use_cv) { |
| 224 | ABSL_RAW_CHECK(cxt->iterations == 1, "TestTime should only use 1 iteration"); |
| 225 | ABSL_RAW_CHECK(cxt->threads > 2, "TestTime should use more than 2 threads"); |
| 226 | const bool kFalse = false; |
| 227 | absl::Condition false_cond(&kFalse); |
| 228 | absl::Condition g0ge2(G0GE2, cxt); |
| 229 | if (c == 0) { |
| 230 | absl::MutexLock l(&cxt->mu); |
| 231 | |
| 232 | absl::Time start = absl::Now(); |
| 233 | if (use_cv) { |
| 234 | cxt->cv.WaitWithTimeout(&cxt->mu, absl::Seconds(1)); |
| 235 | } else { |
| 236 | ABSL_RAW_CHECK(!cxt->mu.AwaitWithTimeout(false_cond, absl::Seconds(1)), |
| 237 | "TestTime failed"); |
| 238 | } |
| 239 | absl::Duration elapsed = absl::Now() - start; |
| 240 | ABSL_RAW_CHECK( |
| 241 | absl::Seconds(0.9) <= elapsed && elapsed <= absl::Seconds(2.0), |
| 242 | "TestTime failed"); |
| 243 | ABSL_RAW_CHECK(cxt->g0 == 1, "TestTime failed"); |
| 244 | |
| 245 | start = absl::Now(); |
| 246 | if (use_cv) { |
| 247 | cxt->cv.WaitWithTimeout(&cxt->mu, absl::Seconds(1)); |
| 248 | } else { |
| 249 | ABSL_RAW_CHECK(!cxt->mu.AwaitWithTimeout(false_cond, absl::Seconds(1)), |
| 250 | "TestTime failed"); |
| 251 | } |
| 252 | elapsed = absl::Now() - start; |
| 253 | ABSL_RAW_CHECK( |
| 254 | absl::Seconds(0.9) <= elapsed && elapsed <= absl::Seconds(2.0), |
| 255 | "TestTime failed"); |
| 256 | cxt->g0++; |
| 257 | if (use_cv) { |
| 258 | cxt->cv.Signal(); |
| 259 | } |
| 260 | |
| 261 | start = absl::Now(); |
| 262 | if (use_cv) { |
| 263 | cxt->cv.WaitWithTimeout(&cxt->mu, absl::Seconds(4)); |
| 264 | } else { |
| 265 | ABSL_RAW_CHECK(!cxt->mu.AwaitWithTimeout(false_cond, absl::Seconds(4)), |
| 266 | "TestTime failed"); |
| 267 | } |
| 268 | elapsed = absl::Now() - start; |
| 269 | ABSL_RAW_CHECK( |
| 270 | absl::Seconds(3.9) <= elapsed && elapsed <= absl::Seconds(6.0), |
| 271 | "TestTime failed"); |
| 272 | ABSL_RAW_CHECK(cxt->g0 >= 3, "TestTime failed"); |
| 273 | |
| 274 | start = absl::Now(); |
| 275 | if (use_cv) { |
| 276 | cxt->cv.WaitWithTimeout(&cxt->mu, absl::Seconds(1)); |
| 277 | } else { |
| 278 | ABSL_RAW_CHECK(!cxt->mu.AwaitWithTimeout(false_cond, absl::Seconds(1)), |
| 279 | "TestTime failed"); |
| 280 | } |
| 281 | elapsed = absl::Now() - start; |
| 282 | ABSL_RAW_CHECK( |
| 283 | absl::Seconds(0.9) <= elapsed && elapsed <= absl::Seconds(2.0), |
| 284 | "TestTime failed"); |
| 285 | if (use_cv) { |
| 286 | cxt->cv.SignalAll(); |
| 287 | } |
| 288 | |
| 289 | start = absl::Now(); |
| 290 | if (use_cv) { |
| 291 | cxt->cv.WaitWithTimeout(&cxt->mu, absl::Seconds(1)); |
| 292 | } else { |
| 293 | ABSL_RAW_CHECK(!cxt->mu.AwaitWithTimeout(false_cond, absl::Seconds(1)), |
| 294 | "TestTime failed"); |
| 295 | } |
| 296 | elapsed = absl::Now() - start; |
| 297 | ABSL_RAW_CHECK(absl::Seconds(0.9) <= elapsed && |
| 298 | elapsed <= absl::Seconds(2.0), "TestTime failed"); |
| 299 | ABSL_RAW_CHECK(cxt->g0 == cxt->threads, "TestTime failed"); |
| 300 | |
| 301 | } else if (c == 1) { |
| 302 | absl::MutexLock l(&cxt->mu); |
| 303 | const absl::Time start = absl::Now(); |
| 304 | if (use_cv) { |
| 305 | cxt->cv.WaitWithTimeout(&cxt->mu, absl::Milliseconds(500)); |
| 306 | } else { |
| 307 | ABSL_RAW_CHECK( |
| 308 | !cxt->mu.AwaitWithTimeout(false_cond, absl::Milliseconds(500)), |
| 309 | "TestTime failed"); |
| 310 | } |
| 311 | const absl::Duration elapsed = absl::Now() - start; |
| 312 | ABSL_RAW_CHECK( |
| 313 | absl::Seconds(0.4) <= elapsed && elapsed <= absl::Seconds(0.9), |
| 314 | "TestTime failed"); |
| 315 | cxt->g0++; |
| 316 | } else if (c == 2) { |
| 317 | absl::MutexLock l(&cxt->mu); |
| 318 | if (use_cv) { |
| 319 | while (cxt->g0 < 2) { |
| 320 | cxt->cv.WaitWithTimeout(&cxt->mu, absl::Seconds(100)); |
| 321 | } |
| 322 | } else { |
| 323 | ABSL_RAW_CHECK(cxt->mu.AwaitWithTimeout(g0ge2, absl::Seconds(100)), |
| 324 | "TestTime failed"); |
| 325 | } |
| 326 | cxt->g0++; |
| 327 | } else { |
| 328 | absl::MutexLock l(&cxt->mu); |
| 329 | if (use_cv) { |
| 330 | while (cxt->g0 < 2) { |
| 331 | cxt->cv.Wait(&cxt->mu); |
| 332 | } |
| 333 | } else { |
| 334 | cxt->mu.Await(g0ge2); |
| 335 | } |
| 336 | cxt->g0++; |
| 337 | } |
| 338 | } |
| 339 | |
| 340 | static void TestMuTime(TestContext *cxt, int c) { TestTime(cxt, c, false); } |
| 341 | |
| 342 | static void TestCVTime(TestContext *cxt, int c) { TestTime(cxt, c, true); } |
| 343 | |
| 344 | static void EndTest(int *c0, int *c1, absl::Mutex *mu, absl::CondVar *cv, |
| 345 | const std::function<void(int)>& cb) { |
| 346 | mu->Lock(); |
| 347 | int c = (*c0)++; |
| 348 | mu->Unlock(); |
| 349 | cb(c); |
| 350 | absl::MutexLock l(mu); |
| 351 | (*c1)++; |
| 352 | cv->Signal(); |
| 353 | } |
| 354 | |
| 355 | // Code common to RunTest() and RunTestWithInvariantDebugging(). |
| 356 | static int RunTestCommon(TestContext *cxt, void (*test)(TestContext *cxt, int), |
| 357 | int threads, int iterations, int operations) { |
| 358 | absl::Mutex mu2; |
| 359 | absl::CondVar cv2; |
| 360 | int c0 = 0; |
| 361 | int c1 = 0; |
| 362 | cxt->g0 = 0; |
| 363 | cxt->g1 = 0; |
| 364 | cxt->iterations = iterations; |
| 365 | cxt->threads = threads; |
| 366 | absl::synchronization_internal::ThreadPool tp(threads); |
| 367 | for (int i = 0; i != threads; i++) { |
| 368 | tp.Schedule(std::bind(&EndTest, &c0, &c1, &mu2, &cv2, |
| 369 | std::function<void(int)>( |
| 370 | std::bind(test, cxt, std::placeholders::_1)))); |
| 371 | } |
| 372 | mu2.Lock(); |
| 373 | while (c1 != threads) { |
| 374 | cv2.Wait(&mu2); |
| 375 | } |
| 376 | mu2.Unlock(); |
| 377 | return cxt->g0; |
| 378 | } |
| 379 | |
| 380 | // Basis for the parameterized tests configured below. |
| 381 | static int RunTest(void (*test)(TestContext *cxt, int), int threads, |
| 382 | int iterations, int operations) { |
| 383 | TestContext cxt; |
| 384 | return RunTestCommon(&cxt, test, threads, iterations, operations); |
| 385 | } |
| 386 | |
| 387 | // Like RunTest(), but sets an invariant on the tested Mutex and |
| 388 | // verifies that the invariant check happened. The invariant function |
| 389 | // will be passed the TestContext* as its arg and must call |
| 390 | // SetInvariantChecked(true); |
| 391 | #if !defined(ABSL_MUTEX_ENABLE_INVARIANT_DEBUGGING_NOT_IMPLEMENTED) |
| 392 | static int RunTestWithInvariantDebugging(void (*test)(TestContext *cxt, int), |
| 393 | int threads, int iterations, |
| 394 | int operations, |
| 395 | void (*invariant)(void *)) { |
| 396 | absl::EnableMutexInvariantDebugging(true); |
| 397 | SetInvariantChecked(false); |
| 398 | TestContext cxt; |
| 399 | cxt.mu.EnableInvariantDebugging(invariant, &cxt); |
| 400 | int ret = RunTestCommon(&cxt, test, threads, iterations, operations); |
| 401 | ABSL_RAW_CHECK(GetInvariantChecked(), "Invariant not checked"); |
| 402 | absl::EnableMutexInvariantDebugging(false); // Restore. |
| 403 | return ret; |
| 404 | } |
| 405 | #endif |
| 406 | |
| 407 | // -------------------------------------------------------- |
| 408 | // Test for fix of bug in TryRemove() |
| 409 | struct TimeoutBugStruct { |
| 410 | absl::Mutex mu; |
| 411 | bool a; |
| 412 | int a_waiter_count; |
| 413 | }; |
| 414 | |
| 415 | static void WaitForA(TimeoutBugStruct *x) { |
| 416 | x->mu.LockWhen(absl::Condition(&x->a)); |
| 417 | x->a_waiter_count--; |
| 418 | x->mu.Unlock(); |
| 419 | } |
| 420 | |
| 421 | static bool NoAWaiters(TimeoutBugStruct *x) { return x->a_waiter_count == 0; } |
| 422 | |
| 423 | // Test that a CondVar.Wait(&mutex) can un-block a call to mutex.Await() in |
| 424 | // another thread. |
| 425 | TEST(Mutex, CondVarWaitSignalsAwait) { |
| 426 | // Use a struct so the lock annotations apply. |
| 427 | struct { |
| 428 | absl::Mutex barrier_mu; |
| 429 | bool barrier ABSL_GUARDED_BY(barrier_mu) = false; |
| 430 | |
| 431 | absl::Mutex release_mu; |
| 432 | bool release ABSL_GUARDED_BY(release_mu) = false; |
| 433 | absl::CondVar released_cv; |
| 434 | } state; |
| 435 | |
| 436 | auto pool = CreateDefaultPool(); |
| 437 | |
| 438 | // Thread A. Sets barrier, waits for release using Mutex::Await, then |
| 439 | // signals released_cv. |
| 440 | pool->Schedule([&state] { |
| 441 | state.release_mu.Lock(); |
| 442 | |
| 443 | state.barrier_mu.Lock(); |
| 444 | state.barrier = true; |
| 445 | state.barrier_mu.Unlock(); |
| 446 | |
| 447 | state.release_mu.Await(absl::Condition(&state.release)); |
| 448 | state.released_cv.Signal(); |
| 449 | state.release_mu.Unlock(); |
| 450 | }); |
| 451 | |
| 452 | state.barrier_mu.LockWhen(absl::Condition(&state.barrier)); |
| 453 | state.barrier_mu.Unlock(); |
| 454 | state.release_mu.Lock(); |
| 455 | // Thread A is now blocked on release by way of Mutex::Await(). |
| 456 | |
| 457 | // Set release. Calling released_cv.Wait() should un-block thread A, |
| 458 | // which will signal released_cv. If not, the test will hang. |
| 459 | state.release = true; |
| 460 | state.released_cv.Wait(&state.release_mu); |
| 461 | state.release_mu.Unlock(); |
| 462 | } |
| 463 | |
| 464 | // Test that a CondVar.WaitWithTimeout(&mutex) can un-block a call to |
| 465 | // mutex.Await() in another thread. |
| 466 | TEST(Mutex, CondVarWaitWithTimeoutSignalsAwait) { |
| 467 | // Use a struct so the lock annotations apply. |
| 468 | struct { |
| 469 | absl::Mutex barrier_mu; |
| 470 | bool barrier ABSL_GUARDED_BY(barrier_mu) = false; |
| 471 | |
| 472 | absl::Mutex release_mu; |
| 473 | bool release ABSL_GUARDED_BY(release_mu) = false; |
| 474 | absl::CondVar released_cv; |
| 475 | } state; |
| 476 | |
| 477 | auto pool = CreateDefaultPool(); |
| 478 | |
| 479 | // Thread A. Sets barrier, waits for release using Mutex::Await, then |
| 480 | // signals released_cv. |
| 481 | pool->Schedule([&state] { |
| 482 | state.release_mu.Lock(); |
| 483 | |
| 484 | state.barrier_mu.Lock(); |
| 485 | state.barrier = true; |
| 486 | state.barrier_mu.Unlock(); |
| 487 | |
| 488 | state.release_mu.Await(absl::Condition(&state.release)); |
| 489 | state.released_cv.Signal(); |
| 490 | state.release_mu.Unlock(); |
| 491 | }); |
| 492 | |
| 493 | state.barrier_mu.LockWhen(absl::Condition(&state.barrier)); |
| 494 | state.barrier_mu.Unlock(); |
| 495 | state.release_mu.Lock(); |
| 496 | // Thread A is now blocked on release by way of Mutex::Await(). |
| 497 | |
| 498 | // Set release. Calling released_cv.Wait() should un-block thread A, |
| 499 | // which will signal released_cv. If not, the test will hang. |
| 500 | state.release = true; |
| 501 | EXPECT_TRUE( |
| 502 | !state.released_cv.WaitWithTimeout(&state.release_mu, absl::Seconds(10))) |
| 503 | << "; Unrecoverable test failure: CondVar::WaitWithTimeout did not " |
| 504 | "unblock the absl::Mutex::Await call in another thread."; |
| 505 | |
| 506 | state.release_mu.Unlock(); |
| 507 | } |
| 508 | |
| 509 | // Test for regression of a bug in loop of TryRemove() |
| 510 | TEST(Mutex, MutexTimeoutBug) { |
| 511 | auto tp = CreateDefaultPool(); |
| 512 | |
| 513 | TimeoutBugStruct x; |
| 514 | x.a = false; |
| 515 | x.a_waiter_count = 2; |
| 516 | tp->Schedule(std::bind(&WaitForA, &x)); |
| 517 | tp->Schedule(std::bind(&WaitForA, &x)); |
| 518 | absl::SleepFor(absl::Seconds(1)); // Allow first two threads to hang. |
| 519 | // The skip field of the second will point to the first because there are |
| 520 | // only two. |
| 521 | |
| 522 | // Now cause a thread waiting on an always-false to time out |
| 523 | // This would deadlock when the bug was present. |
| 524 | bool always_false = false; |
| 525 | x.mu.LockWhenWithTimeout(absl::Condition(&always_false), |
| 526 | absl::Milliseconds(500)); |
| 527 | |
| 528 | // if we get here, the bug is not present. Cleanup the state. |
| 529 | |
| 530 | x.a = true; // wakeup the two waiters on A |
| 531 | x.mu.Await(absl::Condition(&NoAWaiters, &x)); // wait for them to exit |
| 532 | x.mu.Unlock(); |
| 533 | } |
| 534 | |
| 535 | struct CondVarWaitDeadlock : testing::TestWithParam<int> { |
| 536 | absl::Mutex mu; |
| 537 | absl::CondVar cv; |
| 538 | bool cond1 = false; |
| 539 | bool cond2 = false; |
| 540 | bool read_lock1; |
| 541 | bool read_lock2; |
| 542 | bool signal_unlocked; |
| 543 | |
| 544 | CondVarWaitDeadlock() { |
| 545 | read_lock1 = GetParam() & (1 << 0); |
| 546 | read_lock2 = GetParam() & (1 << 1); |
| 547 | signal_unlocked = GetParam() & (1 << 2); |
| 548 | } |
| 549 | |
| 550 | void Waiter1() { |
| 551 | if (read_lock1) { |
| 552 | mu.ReaderLock(); |
| 553 | while (!cond1) { |
| 554 | cv.Wait(&mu); |
| 555 | } |
| 556 | mu.ReaderUnlock(); |
| 557 | } else { |
| 558 | mu.Lock(); |
| 559 | while (!cond1) { |
| 560 | cv.Wait(&mu); |
| 561 | } |
| 562 | mu.Unlock(); |
| 563 | } |
| 564 | } |
| 565 | |
| 566 | void Waiter2() { |
| 567 | if (read_lock2) { |
| 568 | mu.ReaderLockWhen(absl::Condition(&cond2)); |
| 569 | mu.ReaderUnlock(); |
| 570 | } else { |
| 571 | mu.LockWhen(absl::Condition(&cond2)); |
| 572 | mu.Unlock(); |
| 573 | } |
| 574 | } |
| 575 | }; |
| 576 | |
| 577 | // Test for a deadlock bug in Mutex::Fer(). |
| 578 | // The sequence of events that lead to the deadlock is: |
| 579 | // 1. waiter1 blocks on cv in read mode (mu bits = 0). |
| 580 | // 2. waiter2 blocks on mu in either mode (mu bits = kMuWait). |
| 581 | // 3. main thread locks mu, sets cond1, unlocks mu (mu bits = kMuWait). |
| 582 | // 4. main thread signals on cv and this eventually calls Mutex::Fer(). |
| 583 | // Currently Fer wakes waiter1 since mu bits = kMuWait (mutex is unlocked). |
| 584 | // Before the bug fix Fer neither woke waiter1 nor queued it on mutex, |
| 585 | // which resulted in deadlock. |
| 586 | TEST_P(CondVarWaitDeadlock, Test) { |
| 587 | auto waiter1 = CreatePool(1); |
| 588 | auto waiter2 = CreatePool(1); |
| 589 | waiter1->Schedule([this] { this->Waiter1(); }); |
| 590 | waiter2->Schedule([this] { this->Waiter2(); }); |
| 591 | |
| 592 | // Wait while threads block (best-effort is fine). |
| 593 | absl::SleepFor(absl::Milliseconds(100)); |
| 594 | |
| 595 | // Wake condwaiter. |
| 596 | mu.Lock(); |
| 597 | cond1 = true; |
| 598 | if (signal_unlocked) { |
| 599 | mu.Unlock(); |
| 600 | cv.Signal(); |
| 601 | } else { |
| 602 | cv.Signal(); |
| 603 | mu.Unlock(); |
| 604 | } |
| 605 | waiter1.reset(); // "join" waiter1 |
| 606 | |
| 607 | // Wake waiter. |
| 608 | mu.Lock(); |
| 609 | cond2 = true; |
| 610 | mu.Unlock(); |
| 611 | waiter2.reset(); // "join" waiter2 |
| 612 | } |
| 613 | |
| 614 | INSTANTIATE_TEST_SUITE_P(CondVarWaitDeadlockTest, CondVarWaitDeadlock, |
| 615 | ::testing::Range(0, 8), |
| 616 | ::testing::PrintToStringParamName()); |
| 617 | |
| 618 | // -------------------------------------------------------- |
| 619 | // Test for fix of bug in DequeueAllWakeable() |
| 620 | // Bug was that if there was more than one waiting reader |
| 621 | // and all should be woken, the most recently blocked one |
| 622 | // would not be. |
| 623 | |
| 624 | struct DequeueAllWakeableBugStruct { |
| 625 | absl::Mutex mu; |
| 626 | absl::Mutex mu2; // protects all fields below |
| 627 | int unfinished_count; // count of unfinished readers; under mu2 |
| 628 | bool done1; // unfinished_count == 0; under mu2 |
| 629 | int finished_count; // count of finished readers, under mu2 |
| 630 | bool done2; // finished_count == 0; under mu2 |
| 631 | }; |
| 632 | |
| 633 | // Test for regression of a bug in loop of DequeueAllWakeable() |
| 634 | static void AcquireAsReader(DequeueAllWakeableBugStruct *x) { |
| 635 | x->mu.ReaderLock(); |
| 636 | x->mu2.Lock(); |
| 637 | x->unfinished_count--; |
| 638 | x->done1 = (x->unfinished_count == 0); |
| 639 | x->mu2.Unlock(); |
| 640 | // make sure that both readers acquired mu before we release it. |
| 641 | absl::SleepFor(absl::Seconds(2)); |
| 642 | x->mu.ReaderUnlock(); |
| 643 | |
| 644 | x->mu2.Lock(); |
| 645 | x->finished_count--; |
| 646 | x->done2 = (x->finished_count == 0); |
| 647 | x->mu2.Unlock(); |
| 648 | } |
| 649 | |
| 650 | // Test for regression of a bug in loop of DequeueAllWakeable() |
| 651 | TEST(Mutex, MutexReaderWakeupBug) { |
| 652 | auto tp = CreateDefaultPool(); |
| 653 | |
| 654 | DequeueAllWakeableBugStruct x; |
| 655 | x.unfinished_count = 2; |
| 656 | x.done1 = false; |
| 657 | x.finished_count = 2; |
| 658 | x.done2 = false; |
| 659 | x.mu.Lock(); // acquire mu exclusively |
| 660 | // queue two thread that will block on reader locks on x.mu |
| 661 | tp->Schedule(std::bind(&AcquireAsReader, &x)); |
| 662 | tp->Schedule(std::bind(&AcquireAsReader, &x)); |
| 663 | absl::SleepFor(absl::Seconds(1)); // give time for reader threads to block |
| 664 | x.mu.Unlock(); // wake them up |
| 665 | |
| 666 | // both readers should finish promptly |
| 667 | EXPECT_TRUE( |
| 668 | x.mu2.LockWhenWithTimeout(absl::Condition(&x.done1), absl::Seconds(10))); |
| 669 | x.mu2.Unlock(); |
| 670 | |
| 671 | EXPECT_TRUE( |
| 672 | x.mu2.LockWhenWithTimeout(absl::Condition(&x.done2), absl::Seconds(10))); |
| 673 | x.mu2.Unlock(); |
| 674 | } |
| 675 | |
| 676 | struct LockWhenTestStruct { |
| 677 | absl::Mutex mu1; |
| 678 | bool cond = false; |
| 679 | |
| 680 | absl::Mutex mu2; |
| 681 | bool waiting = false; |
| 682 | }; |
| 683 | |
| 684 | static bool LockWhenTestIsCond(LockWhenTestStruct* s) { |
| 685 | s->mu2.Lock(); |
| 686 | s->waiting = true; |
| 687 | s->mu2.Unlock(); |
| 688 | return s->cond; |
| 689 | } |
| 690 | |
| 691 | static void LockWhenTestWaitForIsCond(LockWhenTestStruct* s) { |
| 692 | s->mu1.LockWhen(absl::Condition(&LockWhenTestIsCond, s)); |
| 693 | s->mu1.Unlock(); |
| 694 | } |
| 695 | |
| 696 | TEST(Mutex, LockWhen) { |
| 697 | LockWhenTestStruct s; |
| 698 | |
| 699 | std::thread t(LockWhenTestWaitForIsCond, &s); |
| 700 | s.mu2.LockWhen(absl::Condition(&s.waiting)); |
| 701 | s.mu2.Unlock(); |
| 702 | |
| 703 | s.mu1.Lock(); |
| 704 | s.cond = true; |
| 705 | s.mu1.Unlock(); |
| 706 | |
| 707 | t.join(); |
| 708 | } |
| 709 | |
Austin Schuh | b4691e9 | 2020-12-31 12:37:18 -0800 | [diff] [blame^] | 710 | TEST(Mutex, LockWhenGuard) { |
| 711 | absl::Mutex mu; |
| 712 | int n = 30; |
| 713 | bool done = false; |
| 714 | |
| 715 | // We don't inline the lambda because the conversion is ambiguous in MSVC. |
| 716 | bool (*cond_eq_10)(int *) = [](int *p) { return *p == 10; }; |
| 717 | bool (*cond_lt_10)(int *) = [](int *p) { return *p < 10; }; |
| 718 | |
| 719 | std::thread t1([&mu, &n, &done, cond_eq_10]() { |
| 720 | absl::ReaderMutexLock lock(&mu, absl::Condition(cond_eq_10, &n)); |
| 721 | done = true; |
| 722 | }); |
| 723 | |
| 724 | std::thread t2[10]; |
| 725 | for (std::thread &t : t2) { |
| 726 | t = std::thread([&mu, &n, cond_lt_10]() { |
| 727 | absl::WriterMutexLock lock(&mu, absl::Condition(cond_lt_10, &n)); |
| 728 | ++n; |
| 729 | }); |
| 730 | } |
| 731 | |
| 732 | { |
| 733 | absl::MutexLock lock(&mu); |
| 734 | n = 0; |
| 735 | } |
| 736 | |
| 737 | for (std::thread &t : t2) t.join(); |
| 738 | t1.join(); |
| 739 | |
| 740 | EXPECT_TRUE(done); |
| 741 | EXPECT_EQ(n, 10); |
| 742 | } |
| 743 | |
Austin Schuh | 36244a1 | 2019-09-21 17:52:38 -0700 | [diff] [blame] | 744 | // -------------------------------------------------------- |
| 745 | // The following test requires Mutex::ReaderLock to be a real shared |
| 746 | // lock, which is not the case in all builds. |
| 747 | #if !defined(ABSL_MUTEX_READER_LOCK_IS_EXCLUSIVE) |
| 748 | |
| 749 | // Test for fix of bug in UnlockSlow() that incorrectly decremented the reader |
| 750 | // count when putting a thread to sleep waiting for a false condition when the |
| 751 | // lock was not held. |
| 752 | |
| 753 | // For this bug to strike, we make a thread wait on a free mutex with no |
| 754 | // waiters by causing its wakeup condition to be false. Then the |
| 755 | // next two acquirers must be readers. The bug causes the lock |
| 756 | // to be released when one reader unlocks, rather than both. |
| 757 | |
| 758 | struct ReaderDecrementBugStruct { |
| 759 | bool cond; // to delay first thread (under mu) |
| 760 | int done; // reference count (under mu) |
| 761 | absl::Mutex mu; |
| 762 | |
| 763 | bool waiting_on_cond; // under mu2 |
| 764 | bool have_reader_lock; // under mu2 |
| 765 | bool complete; // under mu2 |
| 766 | absl::Mutex mu2; // > mu |
| 767 | }; |
| 768 | |
| 769 | // L >= mu, L < mu_waiting_on_cond |
| 770 | static bool IsCond(void *v) { |
| 771 | ReaderDecrementBugStruct *x = reinterpret_cast<ReaderDecrementBugStruct *>(v); |
| 772 | x->mu2.Lock(); |
| 773 | x->waiting_on_cond = true; |
| 774 | x->mu2.Unlock(); |
| 775 | return x->cond; |
| 776 | } |
| 777 | |
| 778 | // L >= mu |
| 779 | static bool AllDone(void *v) { |
| 780 | ReaderDecrementBugStruct *x = reinterpret_cast<ReaderDecrementBugStruct *>(v); |
| 781 | return x->done == 0; |
| 782 | } |
| 783 | |
| 784 | // L={} |
| 785 | static void WaitForCond(ReaderDecrementBugStruct *x) { |
| 786 | absl::Mutex dummy; |
| 787 | absl::MutexLock l(&dummy); |
| 788 | x->mu.LockWhen(absl::Condition(&IsCond, x)); |
| 789 | x->done--; |
| 790 | x->mu.Unlock(); |
| 791 | } |
| 792 | |
| 793 | // L={} |
| 794 | static void GetReadLock(ReaderDecrementBugStruct *x) { |
| 795 | x->mu.ReaderLock(); |
| 796 | x->mu2.Lock(); |
| 797 | x->have_reader_lock = true; |
| 798 | x->mu2.Await(absl::Condition(&x->complete)); |
| 799 | x->mu2.Unlock(); |
| 800 | x->mu.ReaderUnlock(); |
| 801 | x->mu.Lock(); |
| 802 | x->done--; |
| 803 | x->mu.Unlock(); |
| 804 | } |
| 805 | |
| 806 | // Test for reader counter being decremented incorrectly by waiter |
| 807 | // with false condition. |
| 808 | TEST(Mutex, MutexReaderDecrementBug) ABSL_NO_THREAD_SAFETY_ANALYSIS { |
| 809 | ReaderDecrementBugStruct x; |
| 810 | x.cond = false; |
| 811 | x.waiting_on_cond = false; |
| 812 | x.have_reader_lock = false; |
| 813 | x.complete = false; |
| 814 | x.done = 2; // initial ref count |
| 815 | |
| 816 | // Run WaitForCond() and wait for it to sleep |
| 817 | std::thread thread1(WaitForCond, &x); |
| 818 | x.mu2.LockWhen(absl::Condition(&x.waiting_on_cond)); |
| 819 | x.mu2.Unlock(); |
| 820 | |
| 821 | // Run GetReadLock(), and wait for it to get the read lock |
| 822 | std::thread thread2(GetReadLock, &x); |
| 823 | x.mu2.LockWhen(absl::Condition(&x.have_reader_lock)); |
| 824 | x.mu2.Unlock(); |
| 825 | |
| 826 | // Get the reader lock ourselves, and release it. |
| 827 | x.mu.ReaderLock(); |
| 828 | x.mu.ReaderUnlock(); |
| 829 | |
| 830 | // The lock should be held in read mode by GetReadLock(). |
| 831 | // If we have the bug, the lock will be free. |
| 832 | x.mu.AssertReaderHeld(); |
| 833 | |
| 834 | // Wake up all the threads. |
| 835 | x.mu2.Lock(); |
| 836 | x.complete = true; |
| 837 | x.mu2.Unlock(); |
| 838 | |
| 839 | // TODO(delesley): turn on analysis once lock upgrading is supported. |
| 840 | // (This call upgrades the lock from shared to exclusive.) |
| 841 | x.mu.Lock(); |
| 842 | x.cond = true; |
| 843 | x.mu.Await(absl::Condition(&AllDone, &x)); |
| 844 | x.mu.Unlock(); |
| 845 | |
| 846 | thread1.join(); |
| 847 | thread2.join(); |
| 848 | } |
| 849 | #endif // !ABSL_MUTEX_READER_LOCK_IS_EXCLUSIVE |
| 850 | |
| 851 | // Test that we correctly handle the situation when a lock is |
| 852 | // held and then destroyed (w/o unlocking). |
Austin Schuh | b4691e9 | 2020-12-31 12:37:18 -0800 | [diff] [blame^] | 853 | #ifdef ABSL_HAVE_THREAD_SANITIZER |
Austin Schuh | 36244a1 | 2019-09-21 17:52:38 -0700 | [diff] [blame] | 854 | // TSAN reports errors when locked Mutexes are destroyed. |
| 855 | TEST(Mutex, DISABLED_LockedMutexDestructionBug) NO_THREAD_SAFETY_ANALYSIS { |
| 856 | #else |
| 857 | TEST(Mutex, LockedMutexDestructionBug) ABSL_NO_THREAD_SAFETY_ANALYSIS { |
| 858 | #endif |
| 859 | for (int i = 0; i != 10; i++) { |
| 860 | // Create, lock and destroy 10 locks. |
| 861 | const int kNumLocks = 10; |
| 862 | auto mu = absl::make_unique<absl::Mutex[]>(kNumLocks); |
| 863 | for (int j = 0; j != kNumLocks; j++) { |
| 864 | if ((j % 2) == 0) { |
| 865 | mu[j].WriterLock(); |
| 866 | } else { |
| 867 | mu[j].ReaderLock(); |
| 868 | } |
| 869 | } |
| 870 | } |
| 871 | } |
| 872 | |
| 873 | // -------------------------------------------------------- |
| 874 | // Test for bug with pattern of readers using a condvar. The bug was that if a |
| 875 | // reader went to sleep on a condition variable while one or more other readers |
| 876 | // held the lock, but there were no waiters, the reader count (held in the |
| 877 | // mutex word) would be lost. (This is because Enqueue() had at one time |
| 878 | // always placed the thread on the Mutex queue. Later (CL 4075610), to |
| 879 | // tolerate re-entry into Mutex from a Condition predicate, Enqueue() was |
| 880 | // changed so that it could also place a thread on a condition-variable. This |
| 881 | // introduced the case where Enqueue() returned with an empty queue, and this |
| 882 | // case was handled incorrectly in one place.) |
| 883 | |
| 884 | static void ReaderForReaderOnCondVar(absl::Mutex *mu, absl::CondVar *cv, |
| 885 | int *running) { |
| 886 | std::random_device dev; |
| 887 | std::mt19937 gen(dev()); |
| 888 | std::uniform_int_distribution<int> random_millis(0, 15); |
| 889 | mu->ReaderLock(); |
| 890 | while (*running == 3) { |
| 891 | absl::SleepFor(absl::Milliseconds(random_millis(gen))); |
| 892 | cv->WaitWithTimeout(mu, absl::Milliseconds(random_millis(gen))); |
| 893 | } |
| 894 | mu->ReaderUnlock(); |
| 895 | mu->Lock(); |
| 896 | (*running)--; |
| 897 | mu->Unlock(); |
| 898 | } |
| 899 | |
| 900 | struct True { |
| 901 | template <class... Args> |
| 902 | bool operator()(Args...) const { |
| 903 | return true; |
| 904 | } |
| 905 | }; |
| 906 | |
| 907 | struct DerivedTrue : True {}; |
| 908 | |
| 909 | TEST(Mutex, FunctorCondition) { |
| 910 | { // Variadic |
| 911 | True f; |
| 912 | EXPECT_TRUE(absl::Condition(&f).Eval()); |
| 913 | } |
| 914 | |
| 915 | { // Inherited |
| 916 | DerivedTrue g; |
| 917 | EXPECT_TRUE(absl::Condition(&g).Eval()); |
| 918 | } |
| 919 | |
| 920 | { // lambda |
| 921 | int value = 3; |
| 922 | auto is_zero = [&value] { return value == 0; }; |
| 923 | absl::Condition c(&is_zero); |
| 924 | EXPECT_FALSE(c.Eval()); |
| 925 | value = 0; |
| 926 | EXPECT_TRUE(c.Eval()); |
| 927 | } |
| 928 | |
| 929 | { // bind |
| 930 | int value = 0; |
| 931 | auto is_positive = std::bind(std::less<int>(), 0, std::cref(value)); |
| 932 | absl::Condition c(&is_positive); |
| 933 | EXPECT_FALSE(c.Eval()); |
| 934 | value = 1; |
| 935 | EXPECT_TRUE(c.Eval()); |
| 936 | } |
| 937 | |
| 938 | { // std::function |
| 939 | int value = 3; |
| 940 | std::function<bool()> is_zero = [&value] { return value == 0; }; |
| 941 | absl::Condition c(&is_zero); |
| 942 | EXPECT_FALSE(c.Eval()); |
| 943 | value = 0; |
| 944 | EXPECT_TRUE(c.Eval()); |
| 945 | } |
| 946 | } |
| 947 | |
| 948 | static bool IntIsZero(int *x) { return *x == 0; } |
| 949 | |
| 950 | // Test for reader waiting condition variable when there are other readers |
| 951 | // but no waiters. |
| 952 | TEST(Mutex, TestReaderOnCondVar) { |
| 953 | auto tp = CreateDefaultPool(); |
| 954 | absl::Mutex mu; |
| 955 | absl::CondVar cv; |
| 956 | int running = 3; |
| 957 | tp->Schedule(std::bind(&ReaderForReaderOnCondVar, &mu, &cv, &running)); |
| 958 | tp->Schedule(std::bind(&ReaderForReaderOnCondVar, &mu, &cv, &running)); |
| 959 | absl::SleepFor(absl::Seconds(2)); |
| 960 | mu.Lock(); |
| 961 | running--; |
| 962 | mu.Await(absl::Condition(&IntIsZero, &running)); |
| 963 | mu.Unlock(); |
| 964 | } |
| 965 | |
| 966 | // -------------------------------------------------------- |
| 967 | struct AcquireFromConditionStruct { |
| 968 | absl::Mutex mu0; // protects value, done |
| 969 | int value; // times condition function is called; under mu0, |
| 970 | bool done; // done with test? under mu0 |
| 971 | absl::Mutex mu1; // used to attempt to mess up state of mu0 |
| 972 | absl::CondVar cv; // so the condition function can be invoked from |
| 973 | // CondVar::Wait(). |
| 974 | }; |
| 975 | |
| 976 | static bool ConditionWithAcquire(AcquireFromConditionStruct *x) { |
| 977 | x->value++; // count times this function is called |
| 978 | |
| 979 | if (x->value == 2 || x->value == 3) { |
| 980 | // On the second and third invocation of this function, sleep for 100ms, |
| 981 | // but with the side-effect of altering the state of a Mutex other than |
| 982 | // than one for which this is a condition. The spec now explicitly allows |
| 983 | // this side effect; previously it did not. it was illegal. |
| 984 | bool always_false = false; |
| 985 | x->mu1.LockWhenWithTimeout(absl::Condition(&always_false), |
| 986 | absl::Milliseconds(100)); |
| 987 | x->mu1.Unlock(); |
| 988 | } |
| 989 | ABSL_RAW_CHECK(x->value < 4, "should not be invoked a fourth time"); |
| 990 | |
| 991 | // We arrange for the condition to return true on only the 2nd and 3rd calls. |
| 992 | return x->value == 2 || x->value == 3; |
| 993 | } |
| 994 | |
| 995 | static void WaitForCond2(AcquireFromConditionStruct *x) { |
| 996 | // wait for cond0 to become true |
| 997 | x->mu0.LockWhen(absl::Condition(&ConditionWithAcquire, x)); |
| 998 | x->done = true; |
| 999 | x->mu0.Unlock(); |
| 1000 | } |
| 1001 | |
| 1002 | // Test for Condition whose function acquires other Mutexes |
| 1003 | TEST(Mutex, AcquireFromCondition) { |
| 1004 | auto tp = CreateDefaultPool(); |
| 1005 | |
| 1006 | AcquireFromConditionStruct x; |
| 1007 | x.value = 0; |
| 1008 | x.done = false; |
| 1009 | tp->Schedule( |
| 1010 | std::bind(&WaitForCond2, &x)); // run WaitForCond2() in a thread T |
| 1011 | // T will hang because the first invocation of ConditionWithAcquire() will |
| 1012 | // return false. |
| 1013 | absl::SleepFor(absl::Milliseconds(500)); // allow T time to hang |
| 1014 | |
| 1015 | x.mu0.Lock(); |
| 1016 | x.cv.WaitWithTimeout(&x.mu0, absl::Milliseconds(500)); // wake T |
| 1017 | // T will be woken because the Wait() will call ConditionWithAcquire() |
| 1018 | // for the second time, and it will return true. |
| 1019 | |
| 1020 | x.mu0.Unlock(); |
| 1021 | |
| 1022 | // T will then acquire the lock and recheck its own condition. |
| 1023 | // It will find the condition true, as this is the third invocation, |
| 1024 | // but the use of another Mutex by the calling function will |
| 1025 | // cause the old mutex implementation to think that the outer |
| 1026 | // LockWhen() has timed out because the inner LockWhenWithTimeout() did. |
| 1027 | // T will then check the condition a fourth time because it finds a |
| 1028 | // timeout occurred. This should not happen in the new |
| 1029 | // implementation that allows the Condition function to use Mutexes. |
| 1030 | |
| 1031 | // It should also succeed, even though the Condition function |
| 1032 | // is being invoked from CondVar::Wait, and thus this thread |
| 1033 | // is conceptually waiting both on the condition variable, and on mu2. |
| 1034 | |
| 1035 | x.mu0.LockWhen(absl::Condition(&x.done)); |
| 1036 | x.mu0.Unlock(); |
| 1037 | } |
| 1038 | |
Austin Schuh | 36244a1 | 2019-09-21 17:52:38 -0700 | [diff] [blame] | 1039 | TEST(Mutex, DeadlockDetector) { |
| 1040 | absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kAbort); |
| 1041 | |
| 1042 | // check that we can call ForgetDeadlockInfo() on a lock with the lock held |
| 1043 | absl::Mutex m1; |
| 1044 | absl::Mutex m2; |
| 1045 | absl::Mutex m3; |
| 1046 | absl::Mutex m4; |
| 1047 | |
| 1048 | m1.Lock(); // m1 gets ID1 |
| 1049 | m2.Lock(); // m2 gets ID2 |
| 1050 | m3.Lock(); // m3 gets ID3 |
| 1051 | m3.Unlock(); |
| 1052 | m2.Unlock(); |
| 1053 | // m1 still held |
| 1054 | m1.ForgetDeadlockInfo(); // m1 loses ID |
| 1055 | m2.Lock(); // m2 gets ID2 |
| 1056 | m3.Lock(); // m3 gets ID3 |
| 1057 | m4.Lock(); // m4 gets ID4 |
| 1058 | m3.Unlock(); |
| 1059 | m2.Unlock(); |
| 1060 | m4.Unlock(); |
| 1061 | m1.Unlock(); |
| 1062 | } |
| 1063 | |
| 1064 | // Bazel has a test "warning" file that programs can write to if the |
| 1065 | // test should pass with a warning. This class disables the warning |
| 1066 | // file until it goes out of scope. |
| 1067 | class ScopedDisableBazelTestWarnings { |
| 1068 | public: |
| 1069 | ScopedDisableBazelTestWarnings() { |
| 1070 | #ifdef _WIN32 |
| 1071 | char file[MAX_PATH]; |
| 1072 | if (GetEnvironmentVariableA(kVarName, file, sizeof(file)) < sizeof(file)) { |
| 1073 | warnings_output_file_ = file; |
| 1074 | SetEnvironmentVariableA(kVarName, nullptr); |
| 1075 | } |
| 1076 | #else |
| 1077 | const char *file = getenv(kVarName); |
| 1078 | if (file != nullptr) { |
| 1079 | warnings_output_file_ = file; |
| 1080 | unsetenv(kVarName); |
| 1081 | } |
| 1082 | #endif |
| 1083 | } |
| 1084 | |
| 1085 | ~ScopedDisableBazelTestWarnings() { |
| 1086 | if (!warnings_output_file_.empty()) { |
| 1087 | #ifdef _WIN32 |
| 1088 | SetEnvironmentVariableA(kVarName, warnings_output_file_.c_str()); |
| 1089 | #else |
| 1090 | setenv(kVarName, warnings_output_file_.c_str(), 0); |
| 1091 | #endif |
| 1092 | } |
| 1093 | } |
| 1094 | |
| 1095 | private: |
| 1096 | static const char kVarName[]; |
| 1097 | std::string warnings_output_file_; |
| 1098 | }; |
| 1099 | const char ScopedDisableBazelTestWarnings::kVarName[] = |
| 1100 | "TEST_WARNINGS_OUTPUT_FILE"; |
| 1101 | |
Austin Schuh | b4691e9 | 2020-12-31 12:37:18 -0800 | [diff] [blame^] | 1102 | #ifdef ABSL_HAVE_THREAD_SANITIZER |
Austin Schuh | 36244a1 | 2019-09-21 17:52:38 -0700 | [diff] [blame] | 1103 | // This test intentionally creates deadlocks to test the deadlock detector. |
| 1104 | TEST(Mutex, DISABLED_DeadlockDetectorBazelWarning) { |
| 1105 | #else |
| 1106 | TEST(Mutex, DeadlockDetectorBazelWarning) { |
| 1107 | #endif |
| 1108 | absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kReport); |
| 1109 | |
| 1110 | // Cause deadlock detection to detect something, if it's |
| 1111 | // compiled in and enabled. But turn off the bazel warning. |
| 1112 | ScopedDisableBazelTestWarnings disable_bazel_test_warnings; |
| 1113 | |
| 1114 | absl::Mutex mu0; |
| 1115 | absl::Mutex mu1; |
| 1116 | bool got_mu0 = mu0.TryLock(); |
| 1117 | mu1.Lock(); // acquire mu1 while holding mu0 |
| 1118 | if (got_mu0) { |
| 1119 | mu0.Unlock(); |
| 1120 | } |
| 1121 | if (mu0.TryLock()) { // try lock shouldn't cause deadlock detector to fire |
| 1122 | mu0.Unlock(); |
| 1123 | } |
| 1124 | mu0.Lock(); // acquire mu0 while holding mu1; should get one deadlock |
| 1125 | // report here |
| 1126 | mu0.Unlock(); |
| 1127 | mu1.Unlock(); |
| 1128 | |
| 1129 | absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kAbort); |
| 1130 | } |
| 1131 | |
| 1132 | // This test is tagged with NO_THREAD_SAFETY_ANALYSIS because the |
| 1133 | // annotation-based static thread-safety analysis is not currently |
| 1134 | // predicate-aware and cannot tell if the two for-loops that acquire and |
| 1135 | // release the locks have the same predicates. |
Austin Schuh | b4691e9 | 2020-12-31 12:37:18 -0800 | [diff] [blame^] | 1136 | TEST(Mutex, DeadlockDetectorStressTest) ABSL_NO_THREAD_SAFETY_ANALYSIS { |
Austin Schuh | 36244a1 | 2019-09-21 17:52:38 -0700 | [diff] [blame] | 1137 | // Stress test: Here we create a large number of locks and use all of them. |
| 1138 | // If a deadlock detector keeps a full graph of lock acquisition order, |
| 1139 | // it will likely be too slow for this test to pass. |
| 1140 | const int n_locks = 1 << 17; |
| 1141 | auto array_of_locks = absl::make_unique<absl::Mutex[]>(n_locks); |
| 1142 | for (int i = 0; i < n_locks; i++) { |
| 1143 | int end = std::min(n_locks, i + 5); |
| 1144 | // acquire and then release locks i, i+1, ..., i+4 |
| 1145 | for (int j = i; j < end; j++) { |
| 1146 | array_of_locks[j].Lock(); |
| 1147 | } |
| 1148 | for (int j = i; j < end; j++) { |
| 1149 | array_of_locks[j].Unlock(); |
| 1150 | } |
| 1151 | } |
| 1152 | } |
| 1153 | |
Austin Schuh | b4691e9 | 2020-12-31 12:37:18 -0800 | [diff] [blame^] | 1154 | #ifdef ABSL_HAVE_THREAD_SANITIZER |
Austin Schuh | 36244a1 | 2019-09-21 17:52:38 -0700 | [diff] [blame] | 1155 | // TSAN reports errors when locked Mutexes are destroyed. |
| 1156 | TEST(Mutex, DISABLED_DeadlockIdBug) NO_THREAD_SAFETY_ANALYSIS { |
| 1157 | #else |
| 1158 | TEST(Mutex, DeadlockIdBug) ABSL_NO_THREAD_SAFETY_ANALYSIS { |
| 1159 | #endif |
| 1160 | // Test a scenario where a cached deadlock graph node id in the |
| 1161 | // list of held locks is not invalidated when the corresponding |
| 1162 | // mutex is deleted. |
| 1163 | absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kAbort); |
| 1164 | // Mutex that will be destroyed while being held |
| 1165 | absl::Mutex *a = new absl::Mutex; |
| 1166 | // Other mutexes needed by test |
| 1167 | absl::Mutex b, c; |
| 1168 | |
| 1169 | // Hold mutex. |
| 1170 | a->Lock(); |
| 1171 | |
| 1172 | // Force deadlock id assignment by acquiring another lock. |
| 1173 | b.Lock(); |
| 1174 | b.Unlock(); |
| 1175 | |
| 1176 | // Delete the mutex. The Mutex destructor tries to remove held locks, |
| 1177 | // but the attempt isn't foolproof. It can fail if: |
| 1178 | // (a) Deadlock detection is currently disabled. |
| 1179 | // (b) The destruction is from another thread. |
| 1180 | // We exploit (a) by temporarily disabling deadlock detection. |
| 1181 | absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kIgnore); |
| 1182 | delete a; |
| 1183 | absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kAbort); |
| 1184 | |
| 1185 | // Now acquire another lock which will force a deadlock id assignment. |
| 1186 | // We should end up getting assigned the same deadlock id that was |
| 1187 | // freed up when "a" was deleted, which will cause a spurious deadlock |
| 1188 | // report if the held lock entry for "a" was not invalidated. |
| 1189 | c.Lock(); |
| 1190 | c.Unlock(); |
| 1191 | } |
Austin Schuh | 36244a1 | 2019-09-21 17:52:38 -0700 | [diff] [blame] | 1192 | |
| 1193 | // -------------------------------------------------------- |
| 1194 | // Test for timeouts/deadlines on condition waits that are specified using |
| 1195 | // absl::Duration and absl::Time. For each waiting function we test with |
| 1196 | // a timeout/deadline that has already expired/passed, one that is infinite |
| 1197 | // and so never expires/passes, and one that will expire/pass in the near |
| 1198 | // future. |
| 1199 | |
| 1200 | static absl::Duration TimeoutTestAllowedSchedulingDelay() { |
| 1201 | // Note: we use a function here because Microsoft Visual Studio fails to |
| 1202 | // properly initialize constexpr static absl::Duration variables. |
| 1203 | return absl::Milliseconds(150); |
| 1204 | } |
| 1205 | |
| 1206 | // Returns true if `actual_delay` is close enough to `expected_delay` to pass |
| 1207 | // the timeouts/deadlines test. Otherwise, logs warnings and returns false. |
| 1208 | ABSL_MUST_USE_RESULT |
| 1209 | static bool DelayIsWithinBounds(absl::Duration expected_delay, |
| 1210 | absl::Duration actual_delay) { |
| 1211 | bool pass = true; |
| 1212 | // Do not allow the observed delay to be less than expected. This may occur |
| 1213 | // in practice due to clock skew or when the synchronization primitives use a |
| 1214 | // different clock than absl::Now(), but these cases should be handled by the |
| 1215 | // the retry mechanism in each TimeoutTest. |
| 1216 | if (actual_delay < expected_delay) { |
| 1217 | ABSL_RAW_LOG(WARNING, |
| 1218 | "Actual delay %s was too short, expected %s (difference %s)", |
| 1219 | absl::FormatDuration(actual_delay).c_str(), |
| 1220 | absl::FormatDuration(expected_delay).c_str(), |
| 1221 | absl::FormatDuration(actual_delay - expected_delay).c_str()); |
| 1222 | pass = false; |
| 1223 | } |
| 1224 | // If the expected delay is <= zero then allow a small error tolerance, since |
| 1225 | // we do not expect context switches to occur during test execution. |
| 1226 | // Otherwise, thread scheduling delays may be substantial in rare cases, so |
| 1227 | // tolerate up to kTimeoutTestAllowedSchedulingDelay of error. |
| 1228 | absl::Duration tolerance = expected_delay <= absl::ZeroDuration() |
| 1229 | ? absl::Milliseconds(10) |
| 1230 | : TimeoutTestAllowedSchedulingDelay(); |
| 1231 | if (actual_delay > expected_delay + tolerance) { |
| 1232 | ABSL_RAW_LOG(WARNING, |
| 1233 | "Actual delay %s was too long, expected %s (difference %s)", |
| 1234 | absl::FormatDuration(actual_delay).c_str(), |
| 1235 | absl::FormatDuration(expected_delay).c_str(), |
| 1236 | absl::FormatDuration(actual_delay - expected_delay).c_str()); |
| 1237 | pass = false; |
| 1238 | } |
| 1239 | return pass; |
| 1240 | } |
| 1241 | |
| 1242 | // Parameters for TimeoutTest, below. |
| 1243 | struct TimeoutTestParam { |
| 1244 | // The file and line number (used for logging purposes only). |
| 1245 | const char *from_file; |
| 1246 | int from_line; |
| 1247 | |
| 1248 | // Should the absolute deadline API based on absl::Time be tested? If false, |
| 1249 | // the relative deadline API based on absl::Duration is tested. |
| 1250 | bool use_absolute_deadline; |
| 1251 | |
| 1252 | // The deadline/timeout used when calling the API being tested |
| 1253 | // (e.g. Mutex::LockWhenWithDeadline). |
| 1254 | absl::Duration wait_timeout; |
| 1255 | |
| 1256 | // The delay before the condition will be set true by the test code. If zero |
| 1257 | // or negative, the condition is set true immediately (before calling the API |
| 1258 | // being tested). Otherwise, if infinite, the condition is never set true. |
| 1259 | // Otherwise a closure is scheduled for the future that sets the condition |
| 1260 | // true. |
| 1261 | absl::Duration satisfy_condition_delay; |
| 1262 | |
| 1263 | // The expected result of the condition after the call to the API being |
| 1264 | // tested. Generally `true` means the condition was true when the API returns, |
| 1265 | // `false` indicates an expected timeout. |
| 1266 | bool expected_result; |
| 1267 | |
| 1268 | // The expected delay before the API under test returns. This is inherently |
| 1269 | // flaky, so some slop is allowed (see `DelayIsWithinBounds` above), and the |
| 1270 | // test keeps trying indefinitely until this constraint passes. |
| 1271 | absl::Duration expected_delay; |
| 1272 | }; |
| 1273 | |
| 1274 | // Print a `TimeoutTestParam` to a debug log. |
| 1275 | std::ostream &operator<<(std::ostream &os, const TimeoutTestParam ¶m) { |
| 1276 | return os << "from: " << param.from_file << ":" << param.from_line |
| 1277 | << " use_absolute_deadline: " |
| 1278 | << (param.use_absolute_deadline ? "true" : "false") |
| 1279 | << " wait_timeout: " << param.wait_timeout |
| 1280 | << " satisfy_condition_delay: " << param.satisfy_condition_delay |
| 1281 | << " expected_result: " |
| 1282 | << (param.expected_result ? "true" : "false") |
| 1283 | << " expected_delay: " << param.expected_delay; |
| 1284 | } |
| 1285 | |
| 1286 | std::string FormatString(const TimeoutTestParam ¶m) { |
| 1287 | std::ostringstream os; |
| 1288 | os << param; |
| 1289 | return os.str(); |
| 1290 | } |
| 1291 | |
| 1292 | // Like `thread::Executor::ScheduleAt` except: |
| 1293 | // a) Delays zero or negative are executed immediately in the current thread. |
| 1294 | // b) Infinite delays are never scheduled. |
| 1295 | // c) Calls this test's `ScheduleAt` helper instead of using `pool` directly. |
| 1296 | static void RunAfterDelay(absl::Duration delay, |
| 1297 | absl::synchronization_internal::ThreadPool *pool, |
| 1298 | const std::function<void()> &callback) { |
| 1299 | if (delay <= absl::ZeroDuration()) { |
| 1300 | callback(); // immediate |
| 1301 | } else if (delay != absl::InfiniteDuration()) { |
| 1302 | ScheduleAfter(pool, delay, callback); |
| 1303 | } |
| 1304 | } |
| 1305 | |
| 1306 | class TimeoutTest : public ::testing::Test, |
| 1307 | public ::testing::WithParamInterface<TimeoutTestParam> {}; |
| 1308 | |
| 1309 | std::vector<TimeoutTestParam> MakeTimeoutTestParamValues() { |
| 1310 | // The `finite` delay is a finite, relatively short, delay. We make it larger |
| 1311 | // than our allowed scheduling delay (slop factor) to avoid confusion when |
| 1312 | // diagnosing test failures. The other constants here have clear meanings. |
| 1313 | const absl::Duration finite = 3 * TimeoutTestAllowedSchedulingDelay(); |
| 1314 | const absl::Duration never = absl::InfiniteDuration(); |
| 1315 | const absl::Duration negative = -absl::InfiniteDuration(); |
| 1316 | const absl::Duration immediate = absl::ZeroDuration(); |
| 1317 | |
| 1318 | // Every test case is run twice; once using the absolute deadline API and once |
| 1319 | // using the relative timeout API. |
| 1320 | std::vector<TimeoutTestParam> values; |
| 1321 | for (bool use_absolute_deadline : {false, true}) { |
| 1322 | // Tests with a negative timeout (deadline in the past), which should |
| 1323 | // immediately return current state of the condition. |
| 1324 | |
| 1325 | // The condition is already true: |
| 1326 | values.push_back(TimeoutTestParam{ |
| 1327 | __FILE__, __LINE__, use_absolute_deadline, |
| 1328 | negative, // wait_timeout |
| 1329 | immediate, // satisfy_condition_delay |
| 1330 | true, // expected_result |
| 1331 | immediate, // expected_delay |
| 1332 | }); |
| 1333 | |
| 1334 | // The condition becomes true, but the timeout has already expired: |
| 1335 | values.push_back(TimeoutTestParam{ |
| 1336 | __FILE__, __LINE__, use_absolute_deadline, |
| 1337 | negative, // wait_timeout |
| 1338 | finite, // satisfy_condition_delay |
| 1339 | false, // expected_result |
| 1340 | immediate // expected_delay |
| 1341 | }); |
| 1342 | |
| 1343 | // The condition never becomes true: |
| 1344 | values.push_back(TimeoutTestParam{ |
| 1345 | __FILE__, __LINE__, use_absolute_deadline, |
| 1346 | negative, // wait_timeout |
| 1347 | never, // satisfy_condition_delay |
| 1348 | false, // expected_result |
| 1349 | immediate // expected_delay |
| 1350 | }); |
| 1351 | |
| 1352 | // Tests with an infinite timeout (deadline in the infinite future), which |
| 1353 | // should only return when the condition becomes true. |
| 1354 | |
| 1355 | // The condition is already true: |
| 1356 | values.push_back(TimeoutTestParam{ |
| 1357 | __FILE__, __LINE__, use_absolute_deadline, |
| 1358 | never, // wait_timeout |
| 1359 | immediate, // satisfy_condition_delay |
| 1360 | true, // expected_result |
| 1361 | immediate // expected_delay |
| 1362 | }); |
| 1363 | |
| 1364 | // The condition becomes true before the (infinite) expiry: |
| 1365 | values.push_back(TimeoutTestParam{ |
| 1366 | __FILE__, __LINE__, use_absolute_deadline, |
| 1367 | never, // wait_timeout |
| 1368 | finite, // satisfy_condition_delay |
| 1369 | true, // expected_result |
| 1370 | finite, // expected_delay |
| 1371 | }); |
| 1372 | |
| 1373 | // Tests with a (small) finite timeout (deadline soon), with the condition |
| 1374 | // becoming true both before and after its expiry. |
| 1375 | |
| 1376 | // The condition is already true: |
| 1377 | values.push_back(TimeoutTestParam{ |
| 1378 | __FILE__, __LINE__, use_absolute_deadline, |
| 1379 | never, // wait_timeout |
| 1380 | immediate, // satisfy_condition_delay |
| 1381 | true, // expected_result |
| 1382 | immediate // expected_delay |
| 1383 | }); |
| 1384 | |
| 1385 | // The condition becomes true before the expiry: |
| 1386 | values.push_back(TimeoutTestParam{ |
| 1387 | __FILE__, __LINE__, use_absolute_deadline, |
| 1388 | finite * 2, // wait_timeout |
| 1389 | finite, // satisfy_condition_delay |
| 1390 | true, // expected_result |
| 1391 | finite // expected_delay |
| 1392 | }); |
| 1393 | |
| 1394 | // The condition becomes true, but the timeout has already expired: |
| 1395 | values.push_back(TimeoutTestParam{ |
| 1396 | __FILE__, __LINE__, use_absolute_deadline, |
| 1397 | finite, // wait_timeout |
| 1398 | finite * 2, // satisfy_condition_delay |
| 1399 | false, // expected_result |
| 1400 | finite // expected_delay |
| 1401 | }); |
| 1402 | |
| 1403 | // The condition never becomes true: |
| 1404 | values.push_back(TimeoutTestParam{ |
| 1405 | __FILE__, __LINE__, use_absolute_deadline, |
| 1406 | finite, // wait_timeout |
| 1407 | never, // satisfy_condition_delay |
| 1408 | false, // expected_result |
| 1409 | finite // expected_delay |
| 1410 | }); |
| 1411 | } |
| 1412 | return values; |
| 1413 | } |
| 1414 | |
| 1415 | // Instantiate `TimeoutTest` with `MakeTimeoutTestParamValues()`. |
| 1416 | INSTANTIATE_TEST_SUITE_P(All, TimeoutTest, |
| 1417 | testing::ValuesIn(MakeTimeoutTestParamValues())); |
| 1418 | |
| 1419 | TEST_P(TimeoutTest, Await) { |
| 1420 | const TimeoutTestParam params = GetParam(); |
| 1421 | ABSL_RAW_LOG(INFO, "Params: %s", FormatString(params).c_str()); |
| 1422 | |
| 1423 | // Because this test asserts bounds on scheduling delays it is flaky. To |
| 1424 | // compensate it loops forever until it passes. Failures express as test |
| 1425 | // timeouts, in which case the test log can be used to diagnose the issue. |
| 1426 | for (int attempt = 1;; ++attempt) { |
| 1427 | ABSL_RAW_LOG(INFO, "Attempt %d", attempt); |
| 1428 | |
| 1429 | absl::Mutex mu; |
| 1430 | bool value = false; // condition value (under mu) |
| 1431 | |
| 1432 | std::unique_ptr<absl::synchronization_internal::ThreadPool> pool = |
| 1433 | CreateDefaultPool(); |
| 1434 | RunAfterDelay(params.satisfy_condition_delay, pool.get(), [&] { |
| 1435 | absl::MutexLock l(&mu); |
| 1436 | value = true; |
| 1437 | }); |
| 1438 | |
| 1439 | absl::MutexLock lock(&mu); |
| 1440 | absl::Time start_time = absl::Now(); |
| 1441 | absl::Condition cond(&value); |
| 1442 | bool result = |
| 1443 | params.use_absolute_deadline |
| 1444 | ? mu.AwaitWithDeadline(cond, start_time + params.wait_timeout) |
| 1445 | : mu.AwaitWithTimeout(cond, params.wait_timeout); |
| 1446 | if (DelayIsWithinBounds(params.expected_delay, absl::Now() - start_time)) { |
| 1447 | EXPECT_EQ(params.expected_result, result); |
| 1448 | break; |
| 1449 | } |
| 1450 | } |
| 1451 | } |
| 1452 | |
| 1453 | TEST_P(TimeoutTest, LockWhen) { |
| 1454 | const TimeoutTestParam params = GetParam(); |
| 1455 | ABSL_RAW_LOG(INFO, "Params: %s", FormatString(params).c_str()); |
| 1456 | |
| 1457 | // Because this test asserts bounds on scheduling delays it is flaky. To |
| 1458 | // compensate it loops forever until it passes. Failures express as test |
| 1459 | // timeouts, in which case the test log can be used to diagnose the issue. |
| 1460 | for (int attempt = 1;; ++attempt) { |
| 1461 | ABSL_RAW_LOG(INFO, "Attempt %d", attempt); |
| 1462 | |
| 1463 | absl::Mutex mu; |
| 1464 | bool value = false; // condition value (under mu) |
| 1465 | |
| 1466 | std::unique_ptr<absl::synchronization_internal::ThreadPool> pool = |
| 1467 | CreateDefaultPool(); |
| 1468 | RunAfterDelay(params.satisfy_condition_delay, pool.get(), [&] { |
| 1469 | absl::MutexLock l(&mu); |
| 1470 | value = true; |
| 1471 | }); |
| 1472 | |
| 1473 | absl::Time start_time = absl::Now(); |
| 1474 | absl::Condition cond(&value); |
| 1475 | bool result = |
| 1476 | params.use_absolute_deadline |
| 1477 | ? mu.LockWhenWithDeadline(cond, start_time + params.wait_timeout) |
| 1478 | : mu.LockWhenWithTimeout(cond, params.wait_timeout); |
| 1479 | mu.Unlock(); |
| 1480 | |
| 1481 | if (DelayIsWithinBounds(params.expected_delay, absl::Now() - start_time)) { |
| 1482 | EXPECT_EQ(params.expected_result, result); |
| 1483 | break; |
| 1484 | } |
| 1485 | } |
| 1486 | } |
| 1487 | |
| 1488 | TEST_P(TimeoutTest, ReaderLockWhen) { |
| 1489 | const TimeoutTestParam params = GetParam(); |
| 1490 | ABSL_RAW_LOG(INFO, "Params: %s", FormatString(params).c_str()); |
| 1491 | |
| 1492 | // Because this test asserts bounds on scheduling delays it is flaky. To |
| 1493 | // compensate it loops forever until it passes. Failures express as test |
| 1494 | // timeouts, in which case the test log can be used to diagnose the issue. |
| 1495 | for (int attempt = 0;; ++attempt) { |
| 1496 | ABSL_RAW_LOG(INFO, "Attempt %d", attempt); |
| 1497 | |
| 1498 | absl::Mutex mu; |
| 1499 | bool value = false; // condition value (under mu) |
| 1500 | |
| 1501 | std::unique_ptr<absl::synchronization_internal::ThreadPool> pool = |
| 1502 | CreateDefaultPool(); |
| 1503 | RunAfterDelay(params.satisfy_condition_delay, pool.get(), [&] { |
| 1504 | absl::MutexLock l(&mu); |
| 1505 | value = true; |
| 1506 | }); |
| 1507 | |
| 1508 | absl::Time start_time = absl::Now(); |
| 1509 | bool result = |
| 1510 | params.use_absolute_deadline |
| 1511 | ? mu.ReaderLockWhenWithDeadline(absl::Condition(&value), |
| 1512 | start_time + params.wait_timeout) |
| 1513 | : mu.ReaderLockWhenWithTimeout(absl::Condition(&value), |
| 1514 | params.wait_timeout); |
| 1515 | mu.ReaderUnlock(); |
| 1516 | |
| 1517 | if (DelayIsWithinBounds(params.expected_delay, absl::Now() - start_time)) { |
| 1518 | EXPECT_EQ(params.expected_result, result); |
| 1519 | break; |
| 1520 | } |
| 1521 | } |
| 1522 | } |
| 1523 | |
| 1524 | TEST_P(TimeoutTest, Wait) { |
| 1525 | const TimeoutTestParam params = GetParam(); |
| 1526 | ABSL_RAW_LOG(INFO, "Params: %s", FormatString(params).c_str()); |
| 1527 | |
| 1528 | // Because this test asserts bounds on scheduling delays it is flaky. To |
| 1529 | // compensate it loops forever until it passes. Failures express as test |
| 1530 | // timeouts, in which case the test log can be used to diagnose the issue. |
| 1531 | for (int attempt = 0;; ++attempt) { |
| 1532 | ABSL_RAW_LOG(INFO, "Attempt %d", attempt); |
| 1533 | |
| 1534 | absl::Mutex mu; |
| 1535 | bool value = false; // condition value (under mu) |
| 1536 | absl::CondVar cv; // signals a change of `value` |
| 1537 | |
| 1538 | std::unique_ptr<absl::synchronization_internal::ThreadPool> pool = |
| 1539 | CreateDefaultPool(); |
| 1540 | RunAfterDelay(params.satisfy_condition_delay, pool.get(), [&] { |
| 1541 | absl::MutexLock l(&mu); |
| 1542 | value = true; |
| 1543 | cv.Signal(); |
| 1544 | }); |
| 1545 | |
| 1546 | absl::MutexLock lock(&mu); |
| 1547 | absl::Time start_time = absl::Now(); |
| 1548 | absl::Duration timeout = params.wait_timeout; |
| 1549 | absl::Time deadline = start_time + timeout; |
| 1550 | while (!value) { |
| 1551 | if (params.use_absolute_deadline ? cv.WaitWithDeadline(&mu, deadline) |
| 1552 | : cv.WaitWithTimeout(&mu, timeout)) { |
| 1553 | break; // deadline/timeout exceeded |
| 1554 | } |
| 1555 | timeout = deadline - absl::Now(); // recompute |
| 1556 | } |
| 1557 | bool result = value; // note: `mu` is still held |
| 1558 | |
| 1559 | if (DelayIsWithinBounds(params.expected_delay, absl::Now() - start_time)) { |
| 1560 | EXPECT_EQ(params.expected_result, result); |
| 1561 | break; |
| 1562 | } |
| 1563 | } |
| 1564 | } |
| 1565 | |
| 1566 | TEST(Mutex, Logging) { |
| 1567 | // Allow user to look at logging output |
| 1568 | absl::Mutex logged_mutex; |
| 1569 | logged_mutex.EnableDebugLog("fido_mutex"); |
| 1570 | absl::CondVar logged_cv; |
| 1571 | logged_cv.EnableDebugLog("rover_cv"); |
| 1572 | logged_mutex.Lock(); |
| 1573 | logged_cv.WaitWithTimeout(&logged_mutex, absl::Milliseconds(20)); |
| 1574 | logged_mutex.Unlock(); |
| 1575 | logged_mutex.ReaderLock(); |
| 1576 | logged_mutex.ReaderUnlock(); |
| 1577 | logged_mutex.Lock(); |
| 1578 | logged_mutex.Unlock(); |
| 1579 | logged_cv.Signal(); |
| 1580 | logged_cv.SignalAll(); |
| 1581 | } |
| 1582 | |
| 1583 | // -------------------------------------------------------- |
| 1584 | |
| 1585 | // Generate the vector of thread counts for tests parameterized on thread count. |
| 1586 | static std::vector<int> AllThreadCountValues() { |
| 1587 | if (kExtendedTest) { |
| 1588 | return {2, 4, 8, 10, 16, 20, 24, 30, 32}; |
| 1589 | } |
| 1590 | return {2, 4, 10}; |
| 1591 | } |
| 1592 | |
| 1593 | // A test fixture parameterized by thread count. |
| 1594 | class MutexVariableThreadCountTest : public ::testing::TestWithParam<int> {}; |
| 1595 | |
| 1596 | // Instantiate the above with AllThreadCountOptions(). |
| 1597 | INSTANTIATE_TEST_SUITE_P(ThreadCounts, MutexVariableThreadCountTest, |
| 1598 | ::testing::ValuesIn(AllThreadCountValues()), |
| 1599 | ::testing::PrintToStringParamName()); |
| 1600 | |
| 1601 | // Reduces iterations by some factor for slow platforms |
| 1602 | // (determined empirically). |
| 1603 | static int ScaleIterations(int x) { |
| 1604 | // ABSL_MUTEX_READER_LOCK_IS_EXCLUSIVE is set in the implementation |
| 1605 | // of Mutex that uses either std::mutex or pthread_mutex_t. Use |
| 1606 | // these as keys to determine the slow implementation. |
| 1607 | #if defined(ABSL_MUTEX_READER_LOCK_IS_EXCLUSIVE) |
| 1608 | return x / 10; |
| 1609 | #else |
| 1610 | return x; |
| 1611 | #endif |
| 1612 | } |
| 1613 | |
| 1614 | TEST_P(MutexVariableThreadCountTest, Mutex) { |
| 1615 | int threads = GetParam(); |
| 1616 | int iterations = ScaleIterations(10000000) / threads; |
| 1617 | int operations = threads * iterations; |
| 1618 | EXPECT_EQ(RunTest(&TestMu, threads, iterations, operations), operations); |
| 1619 | #if !defined(ABSL_MUTEX_ENABLE_INVARIANT_DEBUGGING_NOT_IMPLEMENTED) |
| 1620 | iterations = std::min(iterations, 10); |
| 1621 | operations = threads * iterations; |
| 1622 | EXPECT_EQ(RunTestWithInvariantDebugging(&TestMu, threads, iterations, |
| 1623 | operations, CheckSumG0G1), |
| 1624 | operations); |
| 1625 | #endif |
| 1626 | } |
| 1627 | |
| 1628 | TEST_P(MutexVariableThreadCountTest, Try) { |
| 1629 | int threads = GetParam(); |
| 1630 | int iterations = 1000000 / threads; |
| 1631 | int operations = iterations * threads; |
| 1632 | EXPECT_EQ(RunTest(&TestTry, threads, iterations, operations), operations); |
| 1633 | #if !defined(ABSL_MUTEX_ENABLE_INVARIANT_DEBUGGING_NOT_IMPLEMENTED) |
| 1634 | iterations = std::min(iterations, 10); |
| 1635 | operations = threads * iterations; |
| 1636 | EXPECT_EQ(RunTestWithInvariantDebugging(&TestTry, threads, iterations, |
| 1637 | operations, CheckSumG0G1), |
| 1638 | operations); |
| 1639 | #endif |
| 1640 | } |
| 1641 | |
| 1642 | TEST_P(MutexVariableThreadCountTest, R20ms) { |
| 1643 | int threads = GetParam(); |
| 1644 | int iterations = 100; |
| 1645 | int operations = iterations * threads; |
| 1646 | EXPECT_EQ(RunTest(&TestR20ms, threads, iterations, operations), 0); |
| 1647 | } |
| 1648 | |
| 1649 | TEST_P(MutexVariableThreadCountTest, RW) { |
| 1650 | int threads = GetParam(); |
| 1651 | int iterations = ScaleIterations(20000000) / threads; |
| 1652 | int operations = iterations * threads; |
| 1653 | EXPECT_EQ(RunTest(&TestRW, threads, iterations, operations), operations / 2); |
| 1654 | #if !defined(ABSL_MUTEX_ENABLE_INVARIANT_DEBUGGING_NOT_IMPLEMENTED) |
| 1655 | iterations = std::min(iterations, 10); |
| 1656 | operations = threads * iterations; |
| 1657 | EXPECT_EQ(RunTestWithInvariantDebugging(&TestRW, threads, iterations, |
| 1658 | operations, CheckSumG0G1), |
| 1659 | operations / 2); |
| 1660 | #endif |
| 1661 | } |
| 1662 | |
| 1663 | TEST_P(MutexVariableThreadCountTest, Await) { |
| 1664 | int threads = GetParam(); |
| 1665 | int iterations = ScaleIterations(500000); |
| 1666 | int operations = iterations; |
| 1667 | EXPECT_EQ(RunTest(&TestAwait, threads, iterations, operations), operations); |
| 1668 | } |
| 1669 | |
| 1670 | TEST_P(MutexVariableThreadCountTest, SignalAll) { |
| 1671 | int threads = GetParam(); |
| 1672 | int iterations = 200000 / threads; |
| 1673 | int operations = iterations; |
| 1674 | EXPECT_EQ(RunTest(&TestSignalAll, threads, iterations, operations), |
| 1675 | operations); |
| 1676 | } |
| 1677 | |
| 1678 | TEST(Mutex, Signal) { |
| 1679 | int threads = 2; // TestSignal must use two threads |
| 1680 | int iterations = 200000; |
| 1681 | int operations = iterations; |
| 1682 | EXPECT_EQ(RunTest(&TestSignal, threads, iterations, operations), operations); |
| 1683 | } |
| 1684 | |
| 1685 | TEST(Mutex, Timed) { |
| 1686 | int threads = 10; // Use a fixed thread count of 10 |
| 1687 | int iterations = 1000; |
| 1688 | int operations = iterations; |
| 1689 | EXPECT_EQ(RunTest(&TestCVTimeout, threads, iterations, operations), |
| 1690 | operations); |
| 1691 | } |
| 1692 | |
| 1693 | TEST(Mutex, CVTime) { |
| 1694 | int threads = 10; // Use a fixed thread count of 10 |
| 1695 | int iterations = 1; |
| 1696 | EXPECT_EQ(RunTest(&TestCVTime, threads, iterations, 1), |
| 1697 | threads * iterations); |
| 1698 | } |
| 1699 | |
| 1700 | TEST(Mutex, MuTime) { |
| 1701 | int threads = 10; // Use a fixed thread count of 10 |
| 1702 | int iterations = 1; |
| 1703 | EXPECT_EQ(RunTest(&TestMuTime, threads, iterations, 1), threads * iterations); |
| 1704 | } |
| 1705 | |
| 1706 | } // namespace |