// timing.cpp
//
// Copyright (C) 2006 MicroNeil Research Corporation.
//
// See the corresponding .hpp file for descriptions and history.
#include <time.h>
#include <sys/time.h>
#include <errno.h>
// Platform Specific Includes //////////////////////////////////////////////////
#ifdef WIN32
#include <windows.h>
#endif
#include "timing.hpp"
// 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. This also
// helps keep sleeper values within range to avoid weird timing problems.
///////////////////////////////////////////////////////////////////////////////
// Abstracted doRawSleep() function ////////////////////////////////////////////
#ifdef WIN32
// In a WIN32 environment Sleep() is defined and it works in milliseconds so
// we will use that for doRawSleep(). It's important to note that under normal
// circumstances win32 Sleep() may be off by quite a bit (15ms or so) due to
// how timing is done in the OS. There are ways around this, but they are
// sometimes complex - so here I've left things basic. If more precise win32
// timing is needed then this method can be recoded using a workaround that is
// appropriate to the application.
void Sleeper::doRawSleep(int x) {
Sleep(x); // Use windows Sleep()
}
#else
// If we are not in a win32 environment then we're likely on a posix/unix system
// or at least we have the standard posix/unix time functions so we'll redefine
// absSleep to use nanosleep();
void Sleeper::doRawSleep(int x) {
struct timespec sleeptime; // How much sleeping to do.
struct timespec remaining; // How much sleeping remains.
int result; // The latest result.
remaining.tv_sec = x/1000; // Divide ms by 1000 to get secs.
remaining.tv_nsec = (x%1000)*1000000; // Multiply the remaining msecs to get nsecs.
do { // Just in case we get interruped...
sleeptime.tv_sec = remaining.tv_sec; // Get our sleep time from the
sleeptime.tv_nsec = remaining.tv_nsec; // remaining time.
result = nanosleep(&sleeptime,&remaining); // Call nanosleep and get the remaining time.
} while(0>result && EINTR==errno); // If we were interrupted sleep some more.
}
#endif
Sleeper::Sleeper() // Constructed empty we set our
:MillisecondsToSleep(0) { // sleep time to zero.
}
Sleeper::Sleeper(int x) { // Constructed with a value we
setMillisecondsToSleep(x); // set the sleep time or throw.
}
int Sleeper::setMillisecondsToSleep(int x) { // Safe way to set the vlaue.
if(x < MinimumSleeperTime ||
x > MaximumSleeperTime) // If it's not a good time value
throw BadSleeperValue(); // then throw the exception.
MillisecondsToSleep = x; // If it is good - set it.
}
int Sleeper::getMillisecondsToSleep() { // Safe way to get the value.
return MillisecondsToSleep; // Send back the value.
}
void Sleeper::sleep() { // Here's where we snooze.
if(MillisecondsToSleep > 0) { // If we have a good snooze
doRawSleep(MillisecondsToSleep); // value then go to Sleep().
} else { // If the value is not good
throw BadSleeperValue(); // throw an exception.
}
}
void Sleeper::sleep(int x) { // Reset the sleep time then sleep.
setMillisecondsToSleep(x); // Set the sleep time.
sleep(); // Sleep.
}
///////////////////////////////////////////////////////////////////////////////
// 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.
///////////////////////////////////////////////////////////////////////////////
PollTimer::PollTimer(int Nom, int Max) { // Construction requires a
setNominalPollTime(Nom); // nominal delay to use and
setMaximumPollTime(Max); // a maximum delay to allow.
}
int PollTimer::setNominalPollTime(int Nom) { // Set the Nominal Poll Time.
if(Nom < MinimumSleeperTime || // Check the low and high
Nom > MaximumSleeperTime) // limits and throw an
throw BadPollTimerValue(); // exception if we need to.
// If the value is good then
NominalPollTime = Nom; // remember it.
if(MaximumPollTime < NominalPollTime) // Make sure the Maximum poll
MaximumPollTime = NominalPollTime; // time is > the Nominal time.
reset(); // Reset due to the change.
return NominalPollTime; // Return the new value.
}
int PollTimer::setMaximumPollTime(int Max) { // Set the Maximum Poll Time.
if(Max < MinimumSleeperTime || // Check the low and high
Max > MaximumSleeperTime) // limits and throw an
throw BadPollTimerValue(); // exception if we need to.
// If the value is good then
MaximumPollTime = Max; // remember it.
if(MaximumPollTime < NominalPollTime) // Make sure the Maximum poll
MaximumPollTime = NominalPollTime; // time is >= the Nominal time.
reset(); // Reset due to the change.
return MaximumPollTime; // Return the new value.
}
void PollTimer::reset() { // Reset the spiral.
FibA = NominalPollTime; // Assume our starting event.
FibB = 0; // Assume no other events.
LimitReached=false; // Reset our limit watcher.
}
int PollTimer::pause() { // Pause between polls.
int SleepThisTime = MaximumPollTime; // Assume we're at out limit for now.
if(LimitReached) { // If actually are at our limit then
mySleeper.sleep(SleepThisTime); // use the current value.
} else { // If we are still expanding then
SleepThisTime = FibA+FibB; // Calculate the time to use and
if(SleepThisTime >= MaximumPollTime) { // check it against the limit. If
SleepThisTime = MaximumPollTime; // we reached the limit, us that value
LimitReached = true; // and set the flag.
} else { // If we haven't reached the limit yet
FibB=FibA; // then shift our events and remember
FibA=SleepThisTime; // this one to build our spiral.
}
mySleeper.sleep(SleepThisTime); // Take a nap.
} // Then FIRE THE MISSILES!
return SleepThisTime; // Tell the caller how long we slept.
}
///////////////////////////////////////////////////////////////////////////////
// class Timer - This one acts much like a stop watch with millisecond
// resolution. The time is based on wall-clock time using gettimeofday().
///////////////////////////////////////////////////////////////////////////////
#ifdef WIN32
// Here is the win32 version of getLocalRawClock()
#define TimerIsUnixBased (false)
msclock Timer::getLocalRawClock() {
FILETIME t; // We need a FILETIME structure.
msclock c; // We need a place to calculate our value.
GetSystemTimeAsFileTime(&t); // Grab the system time.
c = (unsigned long long int) t.dwHighDateTime << 32LL; // Put full seconds into the high order bits.
c |= t.dwLowDateTime; // Put 100ns ticks into the low order bits.
c /= 10000; // Divide 100ns ticks by 10K to get ms.
c -= EPOCH_DELTA_IN_MSEC; // Correct for the epoch difference.
return c; // Return the result.
}
#else
// Here is the unix/posix version of getLocalRawClock()
#define TimerIsUnixBased (true)
msclock Timer::getLocalRawClock() {
struct timeval t; // We need a timval structure.
msclock c; // We need a place to calculate our value.
gettimeofday(&t,NULL); // Grab the system time.
c = t.tv_sec * 1000; // Put the full seconds in as milliseconds.
c += t.tv_usec / 1000; // Add the microseconds as milliseconds.
return c; // Return the milliseconds.
}
#endif
Timer::Timer() { // Construct by resetting the
start(); // clocks by using start();
}
Timer::Timer(msclock startt): // Construct a timer from a specific time.
RunningFlag(true), // Set the running flag,
StartTime(startt), // the start time and
StopTime(startt) { // the stop time clock to startt.
}
msclock Timer::start() { // (re) Start the timer at this moment.
return start(getLocalRawClock()); // start() using the current raw clock.
}
msclock Timer::start(msclock startt) { // (re) Start a timer at startt.
StartTime = StopTime = startt; // Set the start and end clocks.
RunningFlag = true; // Set the running flag to true.
return StartTime; // Return the start clock.
}
msclock Timer::getStartClock() { return StartTime; } // Return the start clock value.
bool Timer::isRunning() { return RunningFlag; } // Return the running state.
msclock Timer::getElapsedTime() { // Return the elapsed timeofday -
if(RunningFlag) // if we are running we must get
StopTime = getLocalRawClock(); // the current time (as if it were stop).
msclock delta = StopTime - StartTime; // Calculate the difference.
return delta; // That's our result.
}
msclock Timer::stop() { // Stop the timer.
StopTime = getLocalRawClock(); // Grab the time and then stop
RunningFlag=false; // the clock.
return StopTime; // Return the time we stopped.
}
msclock Timer::getStopClock() { return StopTime; } // Return the stop clock value.
double Timer::getElapsedSeconds() { // Calculate the elapsed seconds.
msclock e = getElapsedTime(); // Get the elapsed time in msecs.
double secs = (double) e / 1000.0; // Calculate seconds from msecs.
return secs;
}
bool Timer::isUnixBased() { return TimerIsUnixBased; } // Is this timer unix based?
msclock Timer::toWindowsEpoch(msclock unixt) { // Convert a unix based msclock to win32 based.
return (unixt + EPOCH_DELTA_IN_MSEC); // Going this way we add the epoch delta.
}
msclock Timer::toUnixEpoch(msclock win32t) { // Convert a win32 based msclock to a unix based.
return (win32t - EPOCH_DELTA_IN_MSEC); // Going this way we subtract the epoch delta.
}
///////////////////////////////////////////////////////////////////////////////
// class Timeout - This one uses a Timer to establish a timeout value.
///////////////////////////////////////////////////////////////////////////////
Timeout::Timeout(msclock duration):myDuration(duration) {} // Create and set the duration.
msclock Timeout::setDuration(msclock duration) { // Set/Change the duration in milliseconds.
myDuration = duration; // (re) Set the duration.
return myDuration; // Return the current (new) duration.
}
msclock Timeout::getDuration() { // Return the current duration.
return myDuration;
}
msclock Timeout::restart() { // Restart the timeout timer.
return myTimer.start(); // Restart the clock and return the time.
}
msclock Timeout::getElapsedTime() { // Get elapsed milliseconds.
return myTimer.getElapsedTime(); // Return the elapsed time.
}
msclock Timeout::getRemainingTime() { // Get remaining milliseconds.
msclock remaining = 0ULL; // Assume we're expired to start.
msclock elapsed = myTimer.getElapsedTime(); // Get the elapsed time.
if(elapsed < myDuration) { // If there is still time then
remaining = myDuration - elapsed; // calculate what is left.
}
return remaining; // Return what we found.
}
bool Timeout::isExpired() { // Return true if time is up.
return (!(myTimer.getElapsedTime() < myDuration)); // Check the elapsed time against myDuration.
}