X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Ftiming%2Ftimestamp.cxx;h=2189d643a8ec65c5313d2903ea6cbabd417982ce;hb=423eba373389684061fc0a8c7b78292f10011ed7;hp=cdafa0a2297dd5a76b4bd365833660ff74e0588b;hpb=3583c1333977345964cbf4829f983cf7e2ff4a84;p=simgear.git diff --git a/simgear/timing/timestamp.cxx b/simgear/timing/timestamp.cxx index cdafa0a2..2189d643 100644 --- a/simgear/timing/timestamp.cxx +++ b/simgear/timing/timestamp.cxx @@ -19,7 +19,7 @@ // // 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // $Id$ @@ -28,32 +28,23 @@ # include #endif -#ifdef HAVE_WINDOWS_H -# include -#endif - #include -#ifdef SG_HAVE_STD_INCLUDES -# include -#else -# include -#endif +#include +#include #ifdef HAVE_SYS_TIMEB_H # include // for ftime() and struct timeb #endif #ifdef HAVE_UNISTD_H -# include // for gettimeofday() +# include // for gettimeofday() and the _POSIX_TIMERS define #endif #ifdef HAVE_SYS_TIME_H # include // for get/setitimer, gettimeofday, struct timeval #endif -// -dw- want to use metrowerks time.h -#ifdef macintosh +#if defined(_POSIX_TIMERS) && (0 < _POSIX_TIMERS) # include -# include #endif #ifdef WIN32 @@ -67,58 +58,239 @@ #include "timestamp.hxx" +#if defined(_POSIX_TIMERS) && (0 < _POSIX_TIMERS) +static clockid_t getClockId() +{ +#if defined(_POSIX_MONOTONIC_CLOCK) + static clockid_t clockid = CLOCK_MONOTONIC; + static bool firstTime = true; + if (!firstTime) + return clockid; + + firstTime = false; + // For the first time test if the monotonic clock is available. + // If so use this, if not use the realtime clock. + struct timespec ts; + if (-1 == clock_gettime(clockid, &ts) && errno == EINVAL) + clockid = CLOCK_REALTIME; + return clockid; +#else + return CLOCK_REALTIME; +#endif +} + +#endif void SGTimeStamp::stamp() { -#if defined( WIN32 ) && !defined(__CYGWIN__) +#ifdef _WIN32 unsigned int t; t = timeGetTime(); - seconds = 0; - usec = t * 1000; + _sec = t / 1000; + _nsec = ( t - ( _sec * 1000 ) ) * 1000 * 1000; +#elif defined(_POSIX_TIMERS) && (0 < _POSIX_TIMERS) + struct timespec ts; + clock_gettime(getClockId(), &ts); + _sec = ts.tv_sec; + _nsec = ts.tv_nsec; #elif defined( HAVE_GETTIMEOFDAY ) struct timeval current; - struct timezone tz; - // sg_timestamp currtime; - gettimeofday(¤t, &tz); - seconds = current.tv_sec; - usec = current.tv_usec; + gettimeofday(¤t, NULL); + _sec = current.tv_sec; + _nsec = current.tv_usec * 1000; #elif defined( HAVE_GETLOCALTIME ) SYSTEMTIME current; GetLocalTime(¤t); - seconds = current.wSecond; - usec = current.wMilliseconds * 1000; + _sec = current.wSecond; + _nsec = current.wMilliseconds * 1000 * 1000; #elif defined( HAVE_FTIME ) struct timeb current; ftime(¤t); - seconds = current.time; - usec = current.millitm * 1000; -// -dw- uses time manager -#elif defined( macintosh ) - UnsignedWide ms; - Microseconds(&ms); - - seconds = ms.lo / 1000000; - usec = ms.lo - ( seconds * 1000000 ); + _sec = current.time; + _nsec = current.millitm * 1000 * 1000; #else # error Port me #endif } -// increment the time stamp by the number of microseconds (usec) -SGTimeStamp operator + (const SGTimeStamp& t, const long& m) { -#if defined( WIN32 ) && !defined(__CYGWIN__) - return SGTimeStamp( 0, t.usec + m ); +// sleep based timing loop. +// +// Calling sleep, even usleep() on linux is less accurate than +// we like, but it does free up the cpu for other tasks during +// the sleep so it is desirable. Because of the way sleep() +// is implemented in consumer operating systems like windows +// and linux, you almost always sleep a little longer than the +// requested amount. +// +// To combat the problem of sleeping too long, we calculate the +// desired wait time and shorten it by 2000us (2ms) to avoid +// [hopefully] over-sleep'ing. The 2ms value was arrived at +// via experimentation. We follow this up at the end with a +// simple busy-wait loop to get the final pause timing exactly +// right. +// +// Assuming we don't oversleep by more than 2000us, this +// should be a reasonable compromise between sleep based +// waiting, and busy waiting. +// +// Usually posix timer resolutions are low enough that we +// could just leave this to the operating system today. +// The day where the busy loop was introduced in flightgear, +// the usual kernels still had just about 10ms (=HZ for +// the timer tick) accuracy which is too bad to catch 60Hz... +bool SGTimeStamp::sleepUntil(const SGTimeStamp& abstime) +{ +#if defined(_POSIX_TIMERS) && (0 < _POSIX_TIMERS) + SGTimeStamp abstimeForSleep = abstime; + + // Always undersleep by resolution of the clock + struct timespec ts; + if (-1 != clock_getres(getClockId(), &ts)) { + abstimeForSleep -= SGTimeStamp::fromSecNSec(ts.tv_sec, ts.tv_nsec); + } else { + abstimeForSleep -= SGTimeStamp::fromSecMSec(0, 2); + } + + ts.tv_sec = abstimeForSleep._sec; + ts.tv_nsec = abstimeForSleep._nsec; + for (;;) { + int ret = clock_nanosleep(getClockId(), TIMER_ABSTIME, &ts, NULL); + if (-1 == ret && errno != EINTR) + return false; + if (ret == 0) + break; + } + + // The busy loop for the rest + SGTimeStamp currentTime; + do { + currentTime.stamp(); + } while (currentTime < abstime); + + return true; + +#elif defined _WIN32 + + SGTimeStamp currentTime; + currentTime.stamp(); + if (abstime <= currentTime) + return true; + + SGTimeStamp abstimeForSleep = abstime - SGTimeStamp::fromSecMSec(0, 2); + for (;abstimeForSleep < currentTime;) { + SGTimeStamp timeDiff = abstimeForSleep - currentTime; + if (timeDiff < SGTimeStamp::fromSecMSec(0, 1)) + break; + // Don't know, but may be win32 has something better today?? + Sleep(static_cast(timeDiff.toMSecs())); + + currentTime.stamp(); + } + + // Follow by a busy loop + while (currentTime < abstime) { + currentTime.stamp(); + } + + return true; + #else - return SGTimeStamp( t.seconds + ( t.usec + m ) / 1000000, - ( t.usec + m ) % 1000000 ); + + SGTimeStamp currentTime; + currentTime.stamp(); + if (abstime <= currentTime) + return true; + + SGTimeStamp abstimeForSleep = abstime - SGTimeStamp::fromSecMSec(0, 2); + for (;abstimeForSleep < currentTime;) { + SGTimeStamp timeDiff = abstimeForSleep - currentTime; + if (timeDiff < SGTimeStamp::fromSecUSec(0, 1)) + break; + // Its documented that some systems bail out on usleep for longer than 1s + // since we recheck the current time anyway just wait for + // less than a second multiple times + bool truncated = false; + if (SGTimeStamp::fromSec(1) < timeDiff) { + timeDiff = SGTimeStamp::fromSec(1); + truncated = true; + } + int ret = usleep(static_cast(timeDiff.toUSecs())); + if (-1 == ret && errno != EINTR) + return false; + if (ret == 0 && !truncated) + break; + + currentTime.stamp(); + } + + // Follow by a busy loop + while (currentTime < abstime) { + currentTime.stamp(); + } + + return true; + #endif } -// difference between time stamps in microseconds (usec) -long operator - (const SGTimeStamp& a, const SGTimeStamp& b) +bool SGTimeStamp::sleepFor(const SGTimeStamp& reltime) { -#if defined( WIN32 ) && !defined(__CYGWIN__) - return a.usec - b.usec; +#if defined(_POSIX_TIMERS) && (0 < _POSIX_TIMERS) + struct timespec ts; + ts.tv_sec = reltime._sec; + ts.tv_nsec = reltime._nsec; + for (;;) { + struct timespec rem; + int ret = clock_nanosleep(getClockId(), 0, &ts, &rem); + if (-1 == ret && errno != EINTR) + return false; + if (ret == 0) + break; + // Use the remainder for the next cycle. + ts = rem; + } + return true; +#elif defined _WIN32 + if (reltime < SGTimeStamp::fromSecMSec(0, 1)) + return true; + // Don't know, but may be win32 has something better today?? + Sleep(static_cast(reltime.toMSecs())); + return true; #else - return 1000000 * (a.seconds - b.seconds) + (a.usec - b.usec); + SGTimeStamp abstime; + abstime.stamp(); + abstime += reltime; + + SGTimeStamp currentTime; + currentTime.stamp(); + for (;abstime < currentTime;) { + SGTimeStamp timeDiff = abstime - currentTime; + if (timeDiff < SGTimeStamp::fromSecUSec(0, 1)) + break; + // Its documented that some systems bail out on usleep for longer than 1s + // since we recheck the current time anyway just wait for + // less than a second multiple times + bool truncated = false; + if (SGTimeStamp::fromSec(1) < timeDiff) { + timeDiff = SGTimeStamp::fromSec(1); + truncated = true; + } + int ret = usleep(static_cast(timeDiff.toUSecs())); + if (-1 == ret && errno != EINTR) + return false; + if (ret == 0 && !truncated) + break; + + currentTime.stamp(); + } + + return true; #endif } + +int SGTimeStamp::elapsedMSec() const +{ + SGTimeStamp now; + now.stamp(); + + return static_cast((now - *this).toMSecs()); +}