blob: 7c2d421ab024d5d8958ecd7c0db080ce943fc6f8 [file] [log] [blame] [edit]
#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__