// timing.hpp // // Copyright (C) 2004-2020 MicroNeil Research Corporation. // // This software is released under the MIT license. See LICENSE.TXT. // // The purpose of this module is to abstract timing functions for // cross platform C++ development usning GNU compilers in *nix and // win32 environments (minGW). Timing resolution is in milliseconds // throughout to provide consistency and reasonable expectations. #pragma once namespace codedweller { /////////////////////////////////////////////////////////////////////////////// // class Sleeper - An object that remembers how long it is supposed to sleep. // This allows an application to create "standard" sleep timers that can be // established at the top of the code (easy to find) and reused. /////////////////////////////////////////////////////////////////////////////// static const int MinimumSleeperTime = 1; // Minimum msec allowed. static const int MaximumSleeperTime = 2000000000; // Maximum msec allowed. class Sleeper { private: int MillisecondsToSleep; // How long to sleep. void doRawSleep(int x); // Abstracted local sleep function. public: class BadSleeperValue {}; // Exception for bad values. Sleeper(); // Constructed empty - set to zero. Sleeper(int x); // Constructed with a value. int setMillisecondsToSleep(int x); // Safe way to set the vlaue. int getMillisecondsToSleep(); // Safe way to get the value. void sleep(); // Here's where we snooze if we can. void sleep(int x); // Shortcut - set the time and then sleep. void operator()(); }; /* Sleeper Documentation... ** ** Sleeper.Sleeper() ** Constructs a Sleeper with a zero value. ** ** Sleeper.Sleeper(int x) ** Constructs a Sleeper to "snooze" for x milliseconds. ** ** Sleeper.setMillisecondsToSleep(int x) ** Sets the sleep time for the Sleeper and returns the time set. ** If the value is out of range then the Sleeper::BadSleeperValue will be thrown. ** ** Sleeper.getMillisecondsToSleep() ** Returns the current MillisecondsToSleep. ** ** Sleeper.sleep() ** Goes to sleep for MillisecondsToSleep. If MillisecondsToSleep has not been set ** then the function throws Sleeper::BadSleeperVlaue. ** ** Sleeper.sleep(int x) ** First sets MillisecondsToSleep, then goes to sleep. If x is too big or too small ** then the method throws Sleeper::BadSleeperValue. */ /////////////////////////////////////////////////////////////////////////////// // class PollTimer - An object to pause during polling processes where the // time between polls is expanded according to a Fibonacci sequence. This // allows self organizing automata to relax a bit when a particular process // is taking a long time so that the resources used in the polling process are // reduced if the system is under load - The idea is to prevent the polling // process from loading the system when there are many nodes poling, yet to // allow for a rapid response when there are few or when the answer we're // waiting for is ready quickly. We use a Fibonacci expansion because it is // a natural spiral. /////////////////////////////////////////////////////////////////////////////// class PollTimer { private: Sleeper mySleeper; // We need a sleeper to do this. int NominalPollTime; // Normal poll delay in msec. int MaximumPollTime; // Maximum poll delay in msec. bool LimitReached; // Why not use unsigned int everywhere? Because sometimes libraries use // int for their Sleep() functions... so we calculate with unsigned ints, // but we use ints for inputs to keep things sane. Wierd bugs show up if // signed ints overflow in clock_t values -- this learned by experience. unsigned int FibA; // One poll delay ago. unsigned int FibB; // Two poll delays ago. // FibA and FibB are used to generate the fibonacci expansion. The current // delay will always be the sum of the previous two delays assuming that // there was always a first delay of 1 x Nominal Poll time. This results // in an expansion like this: 1,2,3,5,8,13,21,34,... public: class BadPollTimerValue {}; // Exception for bad values. PollTimer(int Nom, int Max); // Construct with nominal and max delays. int setNominalPollTime(int Nom); // Set the Nominal Poll Time. int setMaximumPollTime(int Max); // Set the Maximum Poll Time. void reset(); // Reset the spiral. int pause(); // Pause between polls. }; /* PollTimer Documentation... ** ** PollTimer(nominal_delay, maximum_delay) ** Constructs a PollTimer and sets it's basic parameters. If the parameters are ** out of range then BadPollTimerValue will be thrown. ** ** setNiminalPollTime(Nom) ** Sets the nominal (base unit) poll delay time. Throws BadPollTimerValue if ** the value is out of range. ** ** setMaximumPollTime(Max) ** Sets the maximum (upper limit) poll delay. If the value is out of range then ** BadPollTimerValue is thrown. ** ** reset() ** Resets the current poll delay to the nominal delay. The next call to pause() ** will sleep for the nominal delay. This method would normally be called when ** a poll cycle turns up some work to do so that subsequent poll delays will be ** short - leading to a responsive system. ** ** pause() ** Calling this method will cause the current thread to sleep for the current ** poll delay time. Subsquent calls to pause will cause longer sleep times ** according to a natural spiral. An intervening call to reset() will shorten ** the delay times again. This method returns the number of milliseconds ** paused on this pass. */ /////////////////////////////////////////////////////////////////////////////// // class Timer - This one acts much like a stop watch with millisecond // resolution. The time is based on wall-clock time using gettimeofday() or // GetSystemTimeAsFileTime depending on the OS. /////////////////////////////////////////////////////////////////////////////// typedef unsigned long long int msclock; // 64 bit int used for measuring time in ms. static msclock EPOCH_DELTA_IN_USEC = 11644473600000000ULL; // Microseconds difference between epochs. static msclock EPOCH_DELTA_IN_MSEC = EPOCH_DELTA_IN_USEC / 1000; // Milliseconds difference between epochs. class Timer { private: bool RunningFlag; // True if clock is running. msclock StartTime; // TimeOfDay at start. msclock StopTime; // TimeOfDay at stop or check. msclock getLocalRawClock() const; // Derives unix epoch ms from local clock. public: Timer(); // Construct and start the Timer. Timer(msclock startt); // Constructs and starts from a specific moment. void clear(); // Stop and set elapsed time to zero at now. msclock start(); // Re(set) the Start time to this moment. msclock start(msclock startt); // Re(set) the Start time to startt. msclock getStartClock(); // Return the unix epoch start clock. bool isRunning(); // Return true if the clock is running. msclock getElapsedTime() const; // get milliseconds since Timer start. msclock stop(); // Stop the Timer. msclock getStopClock(); // Return the unix epoch stop clock. double getElapsedSeconds()const; // Get floating point elapsed seconds. bool isUnixBased(); // True if base clock is unix/posix. msclock toWindowsEpoch(msclock unixt); // Converts unix t to windows t. msclock toUnixEpoch(msclock win32t); // Converts windows t to unix t. }; /* Timer Documentation... ** ** All raw clock values are returned as 64 bit unsigned integers using a special ** type - msclock. Conversions are done using microsecond accuracy. ** ** Timer() ** Creates a new timer and starts the clock at this moment. ** ** Timer(msclock startt) ** Creates a new timer and starts the clock at a specific moment. This can be ** used to start one clock precisely when another one ends as in: ** new Timer B(A.stop()); ** ** getLocalRawClock() ** This method uses slightly different code depending upon whether the system ** is a unix box or win32. In both cases the function determines the local time ** value as a 64bit integer with millisecond resolution using the unix epoch of ** Jan 1, 1970. ** ** start() ** This method starts or restarts the Timer's clock at this moment. ** The msclock value for the start clock is returned. ** ** start(msclock startt) ** This method starts or restarts the Timer's clock at the time specified ** int startt. This is used for chaining operations such as B.start(A.stop()) ** ** getStartClock() ** This method returns the start clock value. ** ** isRunning() ** Returns true if the clock is running. ** ** getElapsedTime() ** This method returns the elapsed time in milliseconds. ** If the clock is running this value will be different each time it is called. ** ** stop() ** This method stops the clock and returns the stop clock value. If this method ** is called more than once then the stop clock is reset to the current time and ** that time is returned. ** ** getStopClock() ** This method returns the stop clock value. If the Timer is still running ** then the result is the same as calling getElapsedTime(). If the clock is ** not running then the time the clock was last stopped is returned. ** ** getElapsedSeconds() ** Returns the elapsed time as a floating point number with millisecond ** resolution. ** ** isUnixBased() ** Returns true if the raw clock values are being derived from a unix/posix OS. ** ** toWindowsEpoch(msclock unixt) ** Converts unixt to a windows value by adding the epoch delta. ** ** toUnixEpoch(msclock win32t) ** Converts win32t to a unix value by subtracting the epoch delta. */ //////////////////////////////////////////////////////////////////////////////// // class ScopeTimer - Runs a Timer while ScopeTimer is in scope. //////////////////////////////////////////////////////////////////////////////// class ScopeTimer { // Runs a timer when in scope. private: Timer& myTimer; // This is the timer to run. public: ScopeTimer(Timer& T) : myTimer(T) { myTimer.start(); } // The Timer starts at construction. ~ScopeTimer() { myTimer.stop(); } // The Timer stops at destruction. }; /////////////////////////////////////////////////////////////////////////////// // class Timeout - This one uses a Timer to establish a timeout value. /////////////////////////////////////////////////////////////////////////////// class Timeout { private: Timer myTimer; // We need a timer to do this. msclock myDuration; // Milliseconds before timout expires. public: class BadTimeoutValue {}; // If the value is bad throw this. Timeout(msclock duration); // Create and set the duration. msclock setDuration(msclock duration); // Set/Change the duration in milliseconds. msclock getDuration(); // Return the current duration in milliseconds. msclock restart(); // Restart the timeout timer. msclock getElapsedTime(); // Get elapsed milliseconds. msclock getRemainingTime(); // Get remaining milliseconds. bool isExpired(); // Return true if time is up. }; /* Timeout Documentation... ** ** Timeout(int duration) ** Creates a Timout timer and sets the duration in milliseconds. ** ** setDuration(int duration) ** Sets or changes the duration of the timeout timer. ** The Timout is NOT reset by this method. This allows you to change ** the timeout on the fly. ** ** restart() ** Restarts the timeout timer. ** ** getElapsedTime() ** Returns the number of milliseconds elapsed since the Timout was created ** or reset. ** ** getRemainingTime() ** Returns the number of milliseconds remaining before time is up. ** ** isExpired() ** Returns true if time is up. */ } // End namespace codedweller