// ---------------------------------------------------------------------- // File: ClockWrapper.hh // Author: Georgios Bitzes - CERN // ---------------------------------------------------------------------- /************************************************************************ * quarkdb - a redis-like highly available key-value store * * Copyright (C) 2016 CERN/Switzerland * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* ************************************************************************/ #ifndef QUARKDB_CLOCK_WRAPPER_HH #define QUARKDB_CLOCK_WRAPPER_HH #include #include namespace quarkdb { //------------------------------------------------------------------------------ // A template to wrap any std::clock and provide faking abilities. In a fake // clock, time only changes when asked explicitly with advance() or set(). // // - A faked steady clock can only be advanced. // - A faked non-steady clock can be set to any value. // // If faking is de-activated, now() dispatches to the underlying type's // T::now(). // // Example usage: // SteadyClock realClock; // realClock.advance( .. ) --> has no effects // realClock.now() --> std::chrono::steady_clock::now() // // SteadyClock fakeClock(true); // fakeClock.advance(std::chrono::seconds(1)); // fakeClock.now() --> 1 seconds from the beginning of time, which is // indicated as a default-constructed T::time_point. //------------------------------------------------------------------------------ template class ClockWrapper { public: //---------------------------------------------------------------------------- // Forward underlying clock's definitions for rep, period, duration, and // time_point. //---------------------------------------------------------------------------- using rep = typename T::rep; using period = typename T::period; using duration = typename T::duration; using time_point = typename T::time_point; //---------------------------------------------------------------------------- // Forward is_steady //---------------------------------------------------------------------------- static constexpr bool is_steady = T::is_steady; //---------------------------------------------------------------------------- // Constructor - specify if we're faking time, or not. // When faking time, we're starting from a defult-constructed time_point. //---------------------------------------------------------------------------- ClockWrapper(bool fake = false) : faking(fake) {} //---------------------------------------------------------------------------- // Are we faking time? //---------------------------------------------------------------------------- bool fake() const { return faking; } //---------------------------------------------------------------------------- // Get current time. //---------------------------------------------------------------------------- time_point now() const { if(faking) { std::unique_lock lock(mtx); return fakeTime; } return T::now(); } //---------------------------------------------------------------------------- // Advance time - available in all clocks. Has no effect on now() if we // aren't faking time in this object. //---------------------------------------------------------------------------- template void advance(Dur duration) { std::unique_lock lock(mtx); fakeTime += duration; } //---------------------------------------------------------------------------- // Set time to specified timepoint - available only in non-steady clocks. // For steady-clocks, use advance. // // Has no effect on now() if we aren't faking time in this object. //---------------------------------------------------------------------------- void set(time_point point) { if(!is_steady) { std::unique_lock lock(mtx); fakeTime = point; } } private: bool faking; mutable std::mutex mtx; time_point fakeTime; }; using SteadyClock = ClockWrapper; using SystemClock = ClockWrapper; } #endif