| #ifndef UTIL_TIME_CLOCK_H__ |
| #define UTIL_TIME_CLOCK_H__ |
| |
| #include <cstdint> |
| #include <map> |
| #include <memory> |
| #include <optional> |
| |
| #include "absl/base/attributes.h" |
| #include "absl/base/nullability.h" |
| #include "absl/base/thread_annotations.h" |
| #include "absl/synchronization/mutex.h" |
| #include "absl/time/time.h" |
| |
| namespace util { |
| |
| // An abstract interface representing a Clock, which is an object that can |
| // tell you the current time, sleep, and wait for a condition variable. |
| // |
| // This interface allows decoupling code that uses time from the code that |
| // creates a point in time. You can use this to your advantage by injecting |
| // Clocks into interfaces rather than having implementations call absl::Now() |
| // directly. |
| // |
| // The Clock::RealClock() function returns a pointer (that you do not own) |
| // to the global realtime clock. |
| // |
| // Example: |
| // |
| // bool IsWeekend(Clock* clock) { |
| // absl::Time now = clock->TimeNow(); |
| // // ... code to check if 'now' is a weekend. |
| // } |
| // |
| // // Production code. |
| // IsWeekend(Clock::RealClock()); |
| // |
| // // Test code: |
| // MyTestClock test_clock(SATURDAY); |
| // IsWeekend(&test_clock); |
| // |
| class Clock { |
| public: |
| // Returns a pointer to the global realtime clock. The caller does not |
| // own the returned pointer and should not delete it. The returned clock |
| // is thread-safe. |
| static Clock* RealClock(); |
| |
| virtual ~Clock(); |
| |
| // Returns the current time. |
| virtual absl::Time TimeNow() = 0; |
| |
| // Sleeps for the specified duration. |
| virtual void Sleep(absl::Duration d) = 0; |
| |
| // Sleeps until the specified time. |
| virtual void SleepUntil(absl::Time wakeup_time) = 0; |
| |
| // Returns when cond is true or the deadline has passed. Returns true iff |
| // cond holds when returning. |
| // |
| // Requires *mu to be held at least in shared mode. It will be held when |
| // evaluating cond, and upon return, but it may be released and reacquired |
| // in the meantime. |
| // |
| // This method is similar to mu->AwaitWithDeadline() except that the |
| // latter only works with real-time deadlines. This call works properly |
| // with simulated time if invoked on a simulated clock. |
| virtual bool AwaitWithDeadline(absl::Mutex* mu, const absl::Condition& cond, |
| absl::Time deadline) = 0; |
| }; |
| |
| // A simulated clock is a concrete Clock implementation that does not "tick" |
| // on its own. Time is advanced by explicit calls to the AdvanceTime() or |
| // SetTime() functions. |
| // |
| // Example: |
| // SimulatedClock sim_clock; |
| // absl::Time now = sim_clock.TimeNow(); |
| // // now == absl::UnixEpoch() |
| // |
| // now = sim_clock.TimeNow(); |
| // // now == absl::UnixEpoch() (still) |
| // |
| // sim_clock.AdvanceTime(absl::Seconds(3)); |
| // now = sim_clock.TimeNow(); |
| // // now == absl::UnixEpoch() + absl::Seconds(3) |
| // |
| // This code is thread-safe. |
| class SimulatedClock : public Clock { |
| public: |
| explicit SimulatedClock(absl::Time t); |
| SimulatedClock() : SimulatedClock(absl::UnixEpoch()) {} |
| |
| // The destructor should be called only if all Sleep(), etc. and |
| // AdvanceTime() calls have completed. The code does its best to let |
| // any pending calls finish gracefully, but there are no guarantees. |
| ~SimulatedClock() override; |
| |
| // Returns the simulated time. |
| absl::Time TimeNow() override; |
| |
| // Sleeps until the specified duration has elapsed according to this clock. |
| void Sleep(absl::Duration d) override; |
| |
| // Sleeps until the specified wakeup_time. |
| void SleepUntil(absl::Time wakeup_time) override; |
| |
| // Sets the simulated time to the argument. Wakes up any threads whose |
| // sleeps have now expired. Returns the number of woken threads. |
| int64_t SetTime(absl::Time t); |
| |
| // Advances the simulated time by the specified duration. Wakes up any |
| // threads whose sleeps have now expired. Returns the number of woken threads. |
| int64_t AdvanceTime(absl::Duration d); |
| |
| // Blocks until the condition is true or until the simulated clock is |
| // advanced to or beyond the wakeup time (or both). |
| bool AwaitWithDeadline(absl::Mutex* mu, const absl::Condition& cond, |
| absl::Time deadline) override |
| ABSL_SHARED_LOCKS_REQUIRED(mu); |
| |
| // Some tests work by generating an unknown number of sleep/await calls |
| // while reaching a known, quiescent state---making it impossible to pass |
| // acceptable call counts to WaitUntilThreadsAsleep()---but then complete |
| // by generating a fixed number of additional sleep/await calls. In such |
| // cases use CatchUpThreadsAsleep() when in the known state, and then, |
| // after initiating the completion phase, use WaitUntilThreadsAsleep() |
| // with the number of extra sleep/await calls needed to finish the test. |
| void CatchUpThreadsAsleep(); |
| |
| // Returns the earliest wakeup time. |
| std::optional<absl::Time> GetEarliestWakeupTime() const; |
| |
| // Waits until the given number of additional Sleep(), SleepUntil(), or |
| // AwaitWithDeadline() calls, over the number previously waited for, have |
| // been initiated. |
| // |
| // Avoid this function; if dependencies' thread structures change, this |
| // function will return a different result. Instead, use explicit |
| // synchronization or testing::testvalue to determine when worker threads are |
| // done. |
| // |
| // For testing::testvalue, do the following in your prod code: |
| // |
| // ``` |
| // testing::testvalue::Adjust("WorkDone", static_cast<void*>(nullptr)); |
| // ``` |
| // |
| // And wait in your test using this: |
| // |
| // ``` |
| // absl::Notification work_done; |
| // testing::testvalue::ScopedSetCallback<void> cb( |
| // "WorkDone", [&](void*) { work_done.Notify(); }); |
| // clock.AdvanceTime(absl::Hours(1)); |
| // work_done.WaitForNotification(); |
| // ``` |
| ABSL_DEPRECATED("use explicit synchronization or testing::testvalue instead") |
| void WaitUntilThreadsAsleep(int num_calls); |
| |
| private: |
| template <class T> |
| int64_t UpdateTime(const T& now_updater) ABSL_LOCKS_EXCLUDED(lock_); |
| |
| class WakeUpInfo; |
| typedef std::multimap<absl::Time, std::shared_ptr<WakeUpInfo> > WaiterList; |
| |
| mutable absl::Mutex lock_; |
| absl::Time now_ ABSL_GUARDED_BY(lock_); |
| WaiterList* waiters_ ABSL_GUARDED_BY(lock_); |
| int64_t num_await_calls_ ABSL_GUARDED_BY(lock_); |
| int64_t last_num_await_calls_ ABSL_GUARDED_BY(lock_); |
| }; |
| |
| } // namespace util |
| |
| #endif // UTIL_TIME_CLOCK_H__ |