// timing.hpp
//
// Copyright (C) 2004-2009 MicroNeil Research Corporation.

// This program is part of the MicroNeil Research Open Library Project. For
// more information go to http://www.microneil.com/OpenLibrary/index.html
//
// 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 2 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, write to the Free Software Foundation, Inc., 59 Temple
// Place, Suite 330, Boston, MA 02111-1307 USA

// 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.

// 20060404 _M Added Timer::start(msclock startt) for chaining.

// 20060403 _M This "timing" module has been completed and tested on
// win32 (compiled using CodeBlocks and minGW) and on RHES3 (g++).
//
// The bottom line is that this code is perfect for most applications that
// don't need real-time interaction on the win32 platform. That is, for
// any application that can accept 15ms or so of "wiggle" in their timing
// functions. On linux I was able to observe very consistent results with
// variations measured in 1-2ms.
//
// Aynone seeking real-time accuracy on the win32 platform will need to contend
// with all of the landmines in place against that and will need to write more
// ellaborate versions of Timer::getLocalRawClock() and Sleeper::doRawSleep()
// aa appropriate for their application. The existing code should work fine for
// almost all other applications.
//
// This code was written with that in mind to some extent. That is why all of
// the timing functions are measured in milliseconds rather than microseconds
// or something smaller. Milliseconds are convenient for polling delays,
// communications timeouts, measuring database application performance, and
// other similar tasks. For that purpose - this timing module is just fine :-)

// 20060323 _M Rewrote this module from a combination of previous
// bits and pieces. This module will provide classes that abstract
// timing functions for use in GNU projects on *nix and win32 systems.

#ifndef MNR_timing
#define MNR_timing

// Introduce the standard namespace ///////////////////////////////////////////

using namespace std;

///////////////////////////////////////////////////////////////////////////////
// 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.
*/

#endif // End MNR_timing once-only switch.
