// timing.cpp // // Copyright (C) 2004-2020 MicroNeil Research Corporation. // // This software is released under the MIT license. See LICENSE.TXT. #include #include #include // Platform Specific Includes ////////////////////////////////////////////////// #if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64) #include #endif #include "timing.hpp" namespace codedweller { /////////////////////////////////////////////////////////////////////////////// // 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 //////////////////////////////////////////// #if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64) // 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. return MillisecondsToSleep; // Return the set value. } 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. } void Sleeper::operator()() { // Syntactic sugar - operator() on sleep(); // a sleeper calls 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) : NominalPollTime(MinimumSleeperTime), MaximumPollTime(MinimumSleeperTime) { // 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() const { 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() const { 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. } void Timer::clear() { // Stop, zero elapsed, now. StartTime = StopTime = getLocalRawClock(); // Set the start and stop time RunningFlag = false; // to now. We are NOT running. } 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() const { // Return the elapsed timeofday - msclock AssumedStopTime; // We need to use a StopTime simulation. if(RunningFlag) { // If we are running we must get AssumedStopTime = getLocalRawClock(); // the current time (as if it were stop). } else { // If we are not running we use AssumedStopTime = StopTime; // the actual stop time. } msclock delta = AssumedStopTime - 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() const { // 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, set the duration, start. 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. } } // End namespace codedweller