| // Copyright 2026 The Abseil Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "absl/time/simulated_clock.h" |
| |
| #include <cstddef> |
| #include <cstdint> |
| #include <memory> |
| #include <thread> // NOLINT(build/c++11) |
| #include <vector> |
| |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| #include "absl/base/internal/raw_logging.h" |
| #include "absl/base/thread_annotations.h" |
| #include "absl/random/random.h" |
| #include "absl/synchronization/blocking_counter.h" |
| #include "absl/synchronization/mutex.h" |
| #include "absl/synchronization/notification.h" |
| #include "absl/time/clock.h" |
| #include "absl/time/clock_interface.h" |
| #include "absl/time/time.h" |
| |
| namespace { |
| |
| constexpr absl::Duration kShortPause = absl::Milliseconds(20); |
| constexpr absl::Duration kLongPause = absl::Milliseconds(1000); |
| |
| #ifdef _MSC_VER |
| // As of 2026-01-29, multithreaded tests on MSVC are too flaky. |
| const char* kSkipFlakyReason = |
| "Skipping this timing test because it is too flaky"; |
| #else |
| const char* kSkipFlakyReason = nullptr; |
| #endif |
| |
| TEST(SimulatedClock, TimeInitializedToZero) { |
| absl::SimulatedClock simclock; |
| EXPECT_EQ(absl::UnixEpoch(), simclock.TimeNow()); |
| } |
| |
| TEST(SimulatedClock, NowSetTime) { |
| absl::SimulatedClock simclock; |
| absl::Time now = simclock.TimeNow(); |
| |
| now += absl::Seconds(123); |
| simclock.SetTime(now); |
| EXPECT_EQ(now, simclock.TimeNow()); |
| |
| now += absl::Seconds(123); |
| simclock.SetTime(now); |
| EXPECT_EQ(now, simclock.TimeNow()); |
| |
| now += absl::ZeroDuration(); |
| simclock.SetTime(now); |
| EXPECT_EQ(now, simclock.TimeNow()); |
| } |
| |
| TEST(SimulatedClock, NowAdvanceTime) { |
| absl::SimulatedClock simclock; |
| absl::Time now = simclock.TimeNow(); |
| |
| simclock.AdvanceTime(absl::Seconds(123)); |
| now += absl::Seconds(123); |
| EXPECT_EQ(now, simclock.TimeNow()); |
| |
| simclock.AdvanceTime(absl::Seconds(123)); |
| now += absl::Seconds(123); |
| EXPECT_EQ(now, simclock.TimeNow()); |
| |
| simclock.AdvanceTime(absl::ZeroDuration()); |
| now += absl::ZeroDuration(); |
| EXPECT_EQ(now, simclock.TimeNow()); |
| } |
| |
| void SleepAndNotify(absl::Clock* clock, absl::Duration sleep_secs, |
| absl::Notification* note) { |
| clock->Sleep(sleep_secs); |
| note->Notify(); |
| } |
| |
| TEST(SimulatedClock, Sleep_SetToSleepTime) { |
| if (kSkipFlakyReason != nullptr) { |
| GTEST_SKIP() << kSkipFlakyReason; |
| } |
| |
| absl::SimulatedClock simclock; |
| absl::Notification note; |
| |
| std::thread tr(SleepAndNotify, &simclock, absl::Seconds(123), ¬e); |
| |
| // wait for SleepAndNotify() to block |
| absl::SleepFor(kLongPause); |
| simclock.SetTime(absl::FromUnixSeconds(122)); |
| // give Sleep() the opportunity to fail |
| absl::SleepFor(kShortPause); |
| EXPECT_FALSE(note.HasBeenNotified()); |
| simclock.SetTime(absl::FromUnixSeconds(122 + 1)); |
| // wait for Sleep() to return |
| absl::SleepFor(kLongPause); |
| EXPECT_TRUE(note.HasBeenNotified()); |
| note.WaitForNotification(); // in case the expectation fails |
| tr.join(); |
| } |
| |
| TEST(SimulatedClock, SleepAdvanceToSleepTime) { |
| if (kSkipFlakyReason != nullptr) { |
| GTEST_SKIP() << kSkipFlakyReason; |
| } |
| |
| absl::SimulatedClock simclock; |
| absl::Notification note; |
| |
| std::thread tr(SleepAndNotify, &simclock, absl::Seconds(123), ¬e); |
| // wait for SleepAndNotify() to block |
| absl::SleepFor(kLongPause); |
| simclock.AdvanceTime(absl::Seconds(122)); |
| // give Sleep() the opportunity to fail |
| absl::SleepFor(kShortPause); |
| EXPECT_FALSE(note.HasBeenNotified()); |
| simclock.AdvanceTime(absl::Seconds(1)); |
| // wait for Sleep() to return |
| absl::SleepFor(kLongPause); |
| EXPECT_TRUE(note.HasBeenNotified()); |
| note.WaitForNotification(); // in case the expectation fails |
| tr.join(); |
| } |
| |
| TEST(SimulatedClock, SleepSetPastSleepTime) { |
| if (kSkipFlakyReason != nullptr) { |
| GTEST_SKIP() << kSkipFlakyReason; |
| } |
| |
| absl::SimulatedClock simclock; |
| absl::Notification note; |
| |
| std::thread tr(SleepAndNotify, &simclock, absl::Seconds(123), ¬e); |
| // wait for SleepAndNotify() to block |
| absl::SleepFor(kLongPause); |
| simclock.SetTime(absl::FromUnixSeconds(122)); |
| // give Sleep() the opportunity to fail |
| absl::SleepFor(kShortPause); |
| EXPECT_FALSE(note.HasBeenNotified()); |
| simclock.SetTime(absl::FromUnixSeconds(122 + 2)); |
| // wait for Sleep() to return |
| absl::SleepFor(kLongPause); |
| EXPECT_TRUE(note.HasBeenNotified()); |
| note.WaitForNotification(); // in case the expectation fails |
| tr.join(); |
| } |
| |
| TEST(SimulatedClock, SleepAdvancePastSleepTime) { |
| if (kSkipFlakyReason != nullptr) { |
| GTEST_SKIP() << kSkipFlakyReason; |
| } |
| |
| absl::SimulatedClock simclock; |
| absl::Notification note; |
| |
| std::thread tr(SleepAndNotify, &simclock, absl::Seconds(123), ¬e); |
| // wait for SleepAndNotify() to block |
| absl::SleepFor(kLongPause); |
| simclock.AdvanceTime(absl::Seconds(122)); |
| // give Sleep() the opportunity to fail |
| absl::SleepFor(kShortPause); |
| EXPECT_FALSE(note.HasBeenNotified()); |
| simclock.AdvanceTime(absl::Seconds(2)); |
| // wait for Sleep() to return |
| absl::SleepFor(kLongPause); |
| EXPECT_TRUE(note.HasBeenNotified()); |
| note.WaitForNotification(); // in case the expectation fails |
| tr.join(); |
| } |
| |
| TEST(SimulatedClock, SleepZeroSleepTime) { |
| if (kSkipFlakyReason != nullptr) { |
| GTEST_SKIP() << kSkipFlakyReason; |
| } |
| |
| absl::SimulatedClock simclock; |
| absl::Notification note; |
| |
| std::thread tr(SleepAndNotify, &simclock, absl::ZeroDuration(), ¬e); |
| // wait for SleepAndNotify() to ping note |
| absl::SleepFor(kLongPause); |
| EXPECT_TRUE(note.HasBeenNotified()); |
| note.WaitForNotification(); // in case the expectation fails |
| tr.join(); |
| } |
| |
| void SleepUntilAndNotify(absl::Clock* clock, absl::Time wakeup_time, |
| absl::Notification* note) { |
| clock->SleepUntil(wakeup_time); |
| note->Notify(); |
| } |
| |
| TEST(SimulatedClock, SleepUntilSetToSleepTime) { |
| if (kSkipFlakyReason != nullptr) { |
| GTEST_SKIP() << kSkipFlakyReason; |
| } |
| |
| absl::SimulatedClock simclock; |
| absl::Notification note; |
| |
| simclock.SetTime(absl::FromUnixSeconds(123)); |
| std::thread tr(SleepUntilAndNotify, &simclock, |
| absl::UnixEpoch() + absl::Seconds(246), ¬e); |
| // wait for SleepUntilAndNotify() to block |
| absl::SleepFor(kLongPause); |
| simclock.SetTime(absl::FromUnixSeconds(123 + 122)); |
| // give SleepUntil() the opportunity to fail |
| absl::SleepFor(kShortPause); |
| EXPECT_FALSE(note.HasBeenNotified()); |
| simclock.SetTime(absl::FromUnixSeconds(123 + 122 + 1)); |
| absl::Time start = absl::Now(); |
| note.WaitForNotification(); // SleepUntilAndNotify() should ping note |
| EXPECT_GE(absl::Milliseconds(50), absl::Now() - start); |
| tr.join(); |
| } |
| |
| TEST(SimulatedClock, SleepUntilAdvanceToSleepTime) { |
| if (kSkipFlakyReason != nullptr) { |
| GTEST_SKIP() << kSkipFlakyReason; |
| } |
| |
| absl::SimulatedClock simclock; |
| absl::Notification note; |
| |
| simclock.AdvanceTime(absl::Seconds(123)); |
| std::thread tr(SleepUntilAndNotify, &simclock, |
| absl::UnixEpoch() + absl::Seconds(246), ¬e); |
| // wait for SleepUntilAndNotify() to block |
| absl::SleepFor(kLongPause); |
| simclock.AdvanceTime(absl::Seconds(122)); |
| // give SleepUntil() the opportunity to fail |
| absl::SleepFor(kShortPause); |
| EXPECT_FALSE(note.HasBeenNotified()); |
| simclock.AdvanceTime(absl::Seconds(1)); |
| absl::Time start = absl::Now(); |
| note.WaitForNotification(); // SleepUntilAndNotify() should ping note |
| EXPECT_GE(absl::Milliseconds(70), absl::Now() - start); |
| tr.join(); |
| } |
| |
| TEST(SimulatedClock, SleepUntilSetPastSleepTime) { |
| if (kSkipFlakyReason != nullptr) { |
| GTEST_SKIP() << kSkipFlakyReason; |
| } |
| |
| absl::SimulatedClock simclock; |
| absl::Notification note; |
| |
| simclock.SetTime(absl::FromUnixSeconds(123)); |
| std::thread tr(SleepUntilAndNotify, &simclock, |
| absl::UnixEpoch() + absl::Seconds(246), ¬e); |
| // wait for SleepUntilAndNotify() to block |
| absl::SleepFor(kLongPause); |
| simclock.SetTime(absl::FromUnixSeconds(123 + 122)); |
| // give SleepUntil() the opportunity to fail |
| absl::SleepFor(kShortPause); |
| EXPECT_FALSE(note.HasBeenNotified()); |
| simclock.SetTime(absl::FromUnixSeconds(123 + 122 + 2)); |
| // wait for SleepUntilAndNotify() to ping note |
| absl::SleepFor(kLongPause); |
| EXPECT_TRUE(note.HasBeenNotified()); |
| note.WaitForNotification(); // in case the expectation fails |
| tr.join(); |
| } |
| |
| TEST(SimulatedClock, SleepUntilAdvancePastSleepTime) { |
| if (kSkipFlakyReason != nullptr) { |
| GTEST_SKIP() << kSkipFlakyReason; |
| } |
| |
| absl::SimulatedClock simclock; |
| absl::Notification note; |
| |
| simclock.AdvanceTime(absl::Seconds(123)); |
| std::thread tr(SleepUntilAndNotify, &simclock, |
| absl::UnixEpoch() + absl::Seconds(246), ¬e); |
| // wait for SleepUntilAndNotify() to block |
| absl::SleepFor(kLongPause); |
| simclock.AdvanceTime(absl::Seconds(122)); |
| // give SleepUntil() the opportunity to fail |
| absl::SleepFor(kShortPause); |
| EXPECT_FALSE(note.HasBeenNotified()); |
| simclock.AdvanceTime(absl::Seconds(2)); |
| // wait for SleepUntilAndNotify() to ping note |
| absl::SleepFor(kLongPause); |
| EXPECT_TRUE(note.HasBeenNotified()); |
| note.WaitForNotification(); // in case the expectation fails |
| tr.join(); |
| } |
| |
| TEST(SimulatedClock, SleepUntilTimeAlreadyPassed) { |
| if (kSkipFlakyReason != nullptr) { |
| GTEST_SKIP() << kSkipFlakyReason; |
| } |
| |
| absl::SimulatedClock simclock; |
| absl::Notification note; |
| |
| simclock.AdvanceTime(absl::Seconds(123)); |
| std::thread tr(SleepUntilAndNotify, &simclock, |
| absl::UnixEpoch() + absl::Seconds(123), ¬e); |
| // wait for SleepUntilAndNotify() to ping note |
| absl::SleepFor(kLongPause); |
| EXPECT_TRUE(note.HasBeenNotified()); |
| note.WaitForNotification(); // in case the expectation fails |
| tr.join(); |
| } |
| |
| void AwaitWithDeadlineAndNotify(absl::Clock* clock, absl::Mutex* mu, |
| absl::Condition* cond, absl::Time wakeup_time, |
| absl::Notification* note, bool* return_val) { |
| mu->lock_shared(); |
| *return_val = clock->AwaitWithDeadline(mu, *cond, wakeup_time); |
| mu->unlock_shared(); |
| note->Notify(); |
| } |
| |
| TEST(SimulatedClock, AwaitWithDeadlineConditionInitiallyTrue) { |
| absl::SimulatedClock simclock; |
| absl::Mutex mu; |
| bool f = true; |
| absl::Condition cond(&f); |
| absl::MutexLock lock(mu); |
| ASSERT_TRUE(simclock.AwaitWithDeadline(&mu, cond, absl::InfiniteFuture())); |
| } |
| |
| TEST(SimulatedClock, AwaitWithDeadlineConditionInitiallyFalse) { |
| if (kSkipFlakyReason != nullptr) { |
| GTEST_SKIP() << kSkipFlakyReason; |
| } |
| |
| absl::SimulatedClock simclock; |
| absl::Mutex mu; |
| bool f = false; |
| absl::Condition cond(&f); |
| absl::Notification note; |
| bool return_val; |
| |
| std::thread tr(AwaitWithDeadlineAndNotify, &simclock, &mu, &cond, |
| absl::UnixEpoch() + absl::Seconds(123), ¬e, &return_val); |
| // wait for AwaitWithDeadline...() to block |
| absl::SleepFor(kShortPause); |
| EXPECT_FALSE(note.HasBeenNotified()); |
| mu.lock(); |
| f = true; |
| mu.unlock(); |
| // wait for AwaitWithDeadline...() to ping note |
| absl::SleepFor(kLongPause); |
| EXPECT_TRUE(note.HasBeenNotified()); |
| EXPECT_TRUE(return_val); |
| note.WaitForNotification(); // in case the expectation fails |
| tr.join(); |
| } |
| |
| TEST(SimulatedClock, AwaitWithDeadlineDeadlinePassed) { |
| if (kSkipFlakyReason != nullptr) { |
| GTEST_SKIP() << kSkipFlakyReason; |
| } |
| |
| absl::SimulatedClock simclock; |
| absl::Mutex mu; |
| bool f = false; |
| absl::Condition cond(&f); |
| absl::Notification note; |
| bool return_val; |
| |
| std::thread tr(AwaitWithDeadlineAndNotify, &simclock, &mu, &cond, |
| absl::UnixEpoch() + absl::Seconds(123), ¬e, &return_val); |
| // wait for AwaitWithDeadline...() to block |
| absl::SleepFor(kLongPause); |
| EXPECT_FALSE(note.HasBeenNotified()); |
| simclock.AdvanceTime(absl::Seconds(124)); |
| // wait for AwaitWithDeadline...() to ping note |
| absl::SleepFor(kLongPause); |
| EXPECT_TRUE(note.HasBeenNotified()); |
| EXPECT_FALSE(return_val); |
| note.WaitForNotification(); // in case the expectation fails |
| tr.join(); |
| } |
| |
| TEST(SimulatedClock, AwaitWithDeadlineDeadlineAlreadyPassed) { |
| if (kSkipFlakyReason != nullptr) { |
| GTEST_SKIP() << kSkipFlakyReason; |
| } |
| |
| absl::SimulatedClock simclock; |
| absl::Mutex mu; |
| bool f = false; |
| absl::Condition cond(&f); |
| absl::Notification note; |
| bool return_val; |
| |
| std::thread tr(AwaitWithDeadlineAndNotify, &simclock, &mu, &cond, |
| absl::UnixEpoch(), ¬e, &return_val); |
| // wait for AwaitWithDeadline...() to ping note |
| absl::SleepFor(kLongPause); |
| EXPECT_TRUE(note.HasBeenNotified()); |
| EXPECT_FALSE(return_val); |
| note.WaitForNotification(); // in case the expectation fails |
| tr.join(); |
| } |
| |
| void RacerMakesConditionTrue(absl::Notification* start_note, absl::Mutex* mu, |
| bool* f, absl::BlockingCounter* threads_done) { |
| start_note->WaitForNotification(); |
| absl::SleepFor(absl::Milliseconds(1)); |
| mu->lock(); |
| *f = true; |
| mu->unlock(); |
| threads_done->DecrementCount(); |
| } |
| |
| void RacerAdvancesTime(absl::Notification* start_note, |
| absl::SimulatedClock* simclock, absl::Duration d, |
| absl::BlockingCounter* threads_done) { |
| start_note->WaitForNotification(); |
| absl::SleepFor(absl::Milliseconds(1)); |
| simclock->AdvanceTime(d); |
| threads_done->DecrementCount(); |
| } |
| |
| TEST(SimulatedClock, SimultaneousConditionTrueAndDeadline) { |
| absl::SimulatedClock simclock; |
| for (int iteration = 0; iteration < 100; ++iteration) { |
| auto mu = std::make_unique<absl::Mutex>(); |
| bool f = false; |
| absl::Condition cond(&f); |
| absl::Notification note_start; |
| absl::BlockingCounter threads_done(2); |
| std::thread tr1(RacerMakesConditionTrue, ¬e_start, mu.get(), &f, |
| &threads_done); |
| std::thread tr2(RacerAdvancesTime, ¬e_start, &simclock, |
| absl::Seconds(20), &threads_done); |
| note_start.Notify(); |
| mu->lock(); |
| absl::Time deadline = simclock.TimeNow() + absl::Seconds(10); |
| simclock.AwaitWithDeadline(mu.get(), cond, deadline); |
| EXPECT_TRUE(f || (simclock.TimeNow() >= deadline)); |
| if (f) { |
| // RacerMakesConditionTrue has unlocked mu and AwaitWithDeadline has |
| // returned, so it is safe to destruct mu. Do so while RacerAdvancesTime |
| // is possibly still running in an attempt to catch simclock holding on |
| // to a reference to mu and using it after AwaitWithDeadline returns. |
| mu->unlock(); |
| mu = nullptr; |
| } else { |
| mu->unlock(); |
| } |
| threads_done.Wait(); |
| tr1.join(); |
| tr2.join(); |
| } |
| } |
| |
| void RacerDeletesClock(absl::Mutex* mu, absl::Notification* start_note, |
| absl::Clock* clock, |
| absl::BlockingCounter* threads_done) { |
| start_note->WaitForNotification(); |
| // mu is acquired temporarily to make sure that AwaitWithDeadline() in |
| // SimultaneousConditionTrueAndDestruction has blocked. |
| mu->lock(); |
| mu->unlock(); |
| absl::SleepFor(absl::Milliseconds(1)); |
| delete clock; |
| threads_done->DecrementCount(); |
| } |
| |
| TEST(SimulatedClock, SimultaneousConditionTrueAndDestruction) { |
| for (int iteration = 0; iteration < 100; ++iteration) { |
| absl::Clock* clock = new absl::SimulatedClock(); |
| absl::Mutex mu; |
| bool f = false; |
| absl::Condition cond(&f); |
| absl::Notification note_start; |
| absl::BlockingCounter threads_done(2); |
| std::thread tr1([¬e_start, &mu, &f, &threads_done] { |
| RacerMakesConditionTrue(¬e_start, &mu, &f, &threads_done); |
| }); |
| std::thread tr2([&mu, ¬e_start, clock, &threads_done] { |
| RacerDeletesClock(&mu, ¬e_start, clock, &threads_done); |
| }); |
| mu.lock(); |
| note_start.Notify(); |
| absl::Time deadline = absl::UnixEpoch() + absl::Seconds(100000); |
| clock->AwaitWithDeadline(&mu, cond, deadline); |
| mu.unlock(); |
| threads_done.Wait(); |
| tr1.join(); |
| tr2.join(); |
| } |
| } |
| |
| class SimulatedClockTorturer { |
| public: |
| SimulatedClockTorturer(absl::SimulatedClock* simclock, int num_threads, |
| int num_iterations) |
| : simclock_(simclock), |
| num_threads_(num_threads), |
| num_iterations_(num_iterations), |
| num_flags_(2 * num_threads), |
| mutex_and_flag_(static_cast<size_t>(num_flags_)) {} |
| |
| // Implements a torture test. |
| // |
| // This method uses several groups of: |
| // SimulatedClock |
| // Mutex protected flag |
| // It starts several threads that call AwaitWithDeadline() and several |
| // threads that call AdvanceTime() or toggle flag values. |
| void DoTorture() { |
| // The threads calling AwaitWithDeadline() have a separate BlockingCounter |
| // than the threads calling AdvanceTime()/toggling flags, since the former |
| // would be deadlocked if all the threads that might unblock them had |
| // already finished. |
| absl::Notification go; |
| absl::BlockingCounter await_threads_done(num_threads_); |
| absl::Notification signal_threads_should_exit; |
| absl::BlockingCounter signal_threads_done(num_threads_); |
| std::vector<std::thread> trs; |
| for (int i = 0; i < num_threads_; ++i) { |
| trs.emplace_back(&SimulatedClockTorturer::AwaitRandomly, this, &go, |
| &await_threads_done); |
| } |
| for (int i = 0; i < num_threads_; ++i) { |
| trs.emplace_back(&SimulatedClockTorturer::SignalRandomly, this, &go, |
| &signal_threads_should_exit, &signal_threads_done); |
| } |
| go.Notify(); |
| await_threads_done.Wait(); |
| signal_threads_should_exit.Notify(); |
| signal_threads_done.Wait(); |
| for (auto& thread : trs) { |
| thread.join(); |
| } |
| } |
| |
| private: |
| // Randomly call AwaitWithDeadline() for num_iterations_ times. |
| void AwaitRandomly(absl::Notification* go, |
| absl::BlockingCounter* threads_done) { |
| go->WaitForNotification(); |
| |
| absl::BitGen gen; |
| for (int i = 0; i < num_iterations_; ++i) { |
| auto& [mu, f] = mutex_and_flag_[absl::Uniform<size_t>(gen, size_t{0}, |
| static_cast<size_t>(num_flags_))]; |
| absl::MutexLock lock(mu); |
| absl::Time deadline = simclock_->TimeNow() + absl::Seconds(1); |
| simclock_->AwaitWithDeadline(&mu, absl::Condition(&f), deadline); |
| ABSL_RAW_CHECK(f || simclock_->TimeNow() >= deadline, ""); |
| } |
| |
| threads_done->DecrementCount(); |
| } |
| |
| // Randomly call AdvanceTime() or toggle a flag value until notified to |
| // stop. |
| void SignalRandomly(absl::Notification* go, absl::Notification* should_exit, |
| absl::BlockingCounter* threads_done) { |
| go->WaitForNotification(); |
| |
| absl::BitGen gen; |
| while (!should_exit->HasBeenNotified()) { |
| int action = absl::Uniform<int>(gen, 0, num_flags_ + 1); |
| if (action < num_flags_) { |
| // Change a flag value. |
| auto& [mutex, flag] = mutex_and_flag_[static_cast<size_t>(action)]; |
| absl::MutexLock lock(mutex); |
| flag = !flag; |
| } else { |
| // Advance time. |
| simclock_->AdvanceTime(absl::Seconds(1)); |
| } |
| } |
| |
| threads_done->DecrementCount(); |
| } |
| |
| absl::SimulatedClock* simclock_; |
| int num_threads_; |
| int num_iterations_; |
| int num_flags_; |
| |
| struct MutexAndFlag { |
| absl::Mutex mutex; |
| bool flag ABSL_GUARDED_BY(mutex) = false; |
| }; |
| std::vector<MutexAndFlag> mutex_and_flag_; |
| }; |
| |
| TEST(SimulatedClock, Torture) { |
| absl::SimulatedClock simclock; |
| constexpr int kNumThreads = 10; |
| constexpr int kNumIterations = 1000; |
| SimulatedClockTorturer torturer(&simclock, kNumThreads, kNumIterations); |
| torturer.DoTorture(); |
| } |
| |
| } // namespace |