rename hrt -> wtime; emulate POSIX routine with HRT (simplifies timer code)

This was SVN commit r389.
This commit is contained in:
janwas 2004-06-03 18:36:35 +00:00
parent d6e6dd72e9
commit 520e2a1ff6
3 changed files with 319 additions and 135 deletions

View File

@ -1,80 +0,0 @@
// Windows-specific high resolution timer
// Copyright (c) 2003 Jan Wassenberg
//
// 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.
//
// Contact info:
// Jan.Wassenberg@stud.uni-karlsruhe.de
// http://www.stud.uni-karlsruhe.de/~urkt/
#include "types.h"
// possible high resolution timers, in order of preference.
// see source for timer properties + problems.
// used as index into hrt_overrides.
enum HRTImpl
{
// CPU timestamp counter
HRT_TSC,
// Windows QueryPerformanceCounter
HRT_QPC,
// Windows GetTickCount
HRT_GTC,
// there will always be a valid timer in use.
// this is only used with hrt_override_impl.
HRT_NONE,
HRT_NUM_IMPLS
};
// while we do our best to work around timer problems or avoid them if unsafe,
// future requirements and problems may be different:
// allow the user or app to override our decisions (via hrt_override_impl)
enum HRTOverride
{
// allow use of this implementation if available,
// and we can work around its problems
//
// HACK: give it value 0 for easier static data initialization
HRT_DEFAULT = 0,
// override our 'safe to use' recommendation
// set by hrt_override_impl (via command line arg or console function)
HRT_DISABLE,
HRT_FORCE
};
// return ticks since first call.
extern i64 hrt_ticks();
// return seconds since first call.
extern double hrt_time();
// return seconds between start and end timestamps (returned by hrt_ticks).
// negative if end comes before start.
extern double hrt_delta_s(i64 start, i64 end);
// return current timer implementation and its nominal (rated) frequency.
// nominal_freq is never 0.
// implementation only changes after hrt_override_impl.
extern void hrt_query_impl(HRTImpl& impl, i64& nominal_freq);
// override our 'safe to use' decision.
// resets (and chooses another, if applicable) implementation;
// the timer may jump after doing so.
// call with HRT_DEFAULT, HRT_NONE to re-evaluate implementation choice
// after system info becomes available.
extern int hrt_override_impl(HRTOverride ovr, HRTImpl impl);

View File

@ -1,5 +1,5 @@
// Windows-specific high resolution timer
// Copyright (c) 2003 Jan Wassenberg
// Copyright (c) 2004 Jan Wassenberg
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
@ -17,7 +17,6 @@
#include "precompiled.h"
#include "hrt.h"
#include "lib.h"
#include "adts.h"
#include "sysdep/ia32.h"
@ -26,7 +25,9 @@
#include "win_internal.h"
// we no longer use TGT, due to issues on Win9x; GTC is just as good.
// still need the header for the event timer (triggers periodic recalibration)
// (don't want to accelerate the tick rate, because performance will suffer).
// still need the header for the event timer (triggers periodic recalibration).
// don't bother trying to avoid dependency on winmm - FMOD pulls it in anyway.
#include <mmsystem.h>
// not included by win_internal due to its WIN32_LEAN_AND_MEAN define
#ifdef _MSC_VER
@ -40,7 +41,47 @@ static double hrt_freq = -1.0;
// used to rebase the hrt tick values to 0
static i64 hrt_origin = 0;
// possible high resolution timers, in order of preference.
// see source for timer properties + problems.
// used as index into hrt_overrides.
enum HRTImpl
{
// CPU timestamp counter
HRT_TSC,
// Windows QueryPerformanceCounter
HRT_QPC,
// Windows GetTickCount
HRT_GTC,
// there will always be a valid timer in use.
// this is only used with hrt_override_impl.
HRT_NONE,
HRT_NUM_IMPLS
};
static HRTImpl hrt_impl = HRT_NONE;
// while we do our best to work around timer problems or avoid them if unsafe,
// future requirements and problems may be different:
// allow the user or app to override our decisions (via hrt_override_impl)
enum HRTOverride
{
// allow use of this implementation if available,
// and we can work around its problems
//
// HACK: give it value 0 for easier static data initialization
HRT_DEFAULT = 0,
// override our 'safe to use' recommendation
// set by hrt_override_impl (via command line arg or console function)
HRT_DISABLE,
HRT_FORCE
};
static HRTOverride overrides[HRT_NUM_IMPLS];
// HRTImpl enums as index
// HACK: no init needed - static data is zeroed (= HRT_DEFAULT)
@ -262,81 +303,53 @@ static void reset_impl_lk()
}
// multiple entry points, can't use ONCE.
static bool initialized;
static void init_calibration_thread();
// call iff !initialized. lock must be held.
static void init_lk()
static void hrt_init()
{
lock();
static bool initialized = false;
assert(!initialized && "init_lk called more than once!");
initialized = true;
reset_impl_lk();
init_calibration_thread();
initialized = true;
unlock();
}
// return ticks since first call.
i64 hrt_ticks()
static i64 hrt_ticks()
{
lock();
// ugly, but it'll fall-through in common case.
if(!initialized)
goto init;
ready:
{ // VC6 goto fix
lock();
i64 t = ticks_lk();
unlock();
unlock();
return t;
}
// reached from first call if init_lk hasn't been called yet. lock is held.
init:
init_lk();
goto ready;
}
// return seconds since first call.
double hrt_time()
static double hrt_time()
{
lock();
// ugly, but it'll fall-through in common case.
if(!initialized)
goto init;
ready:
{ // VC6 goto fix
// don't implement with hrt_ticks - hrt_freq may
// change, invalidating ticks_lk.
lock();
double t = ticks_lk() / hrt_freq;
unlock();
unlock();
return t;
}
// reached from first call if init_lk hasn't been called yet. lock is held.
init:
init_lk();
goto ready;
}
// return seconds between start and end timestamps (returned by hrt_ticks).
// negative if end comes before start.
double hrt_delta_s(i64 start, i64 end)
static double hrt_delta_s(i64 start, i64 end)
{
// paranoia: reading double may not be atomic.
lock();
lock();
double freq = hrt_freq;
unlock();
unlock();
assert(freq != -1.0 && "hrt_delta_s called before hrt_ticks");
return (end - start) / freq;
@ -348,13 +361,10 @@ double hrt_delta_s(i64 start, i64 end)
// implementation only changes after hrt_override_impl.
//
// may be called before first hrt_ticks / hrt_time, so do init here also.
void hrt_query_impl(HRTImpl& impl, i64& nominal_freq)
static void hrt_query_impl(HRTImpl& impl, i64& nominal_freq)
{
lock();
if(!initialized)
init_lk();
impl = hrt_impl;
nominal_freq = hrt_nominal_freq;
@ -369,7 +379,7 @@ unlock();
// the timer may jump after doing so.
// call with HRT_DEFAULT, HRT_NONE to re-evaluate implementation choice
// after system info becomes available.
int hrt_override_impl(HRTOverride ovr, HRTImpl impl)
static int hrt_override_impl(HRTOverride ovr, HRTImpl impl)
{
if((ovr != HRT_DISABLE && ovr != HRT_FORCE && ovr != HRT_DEFAULT) ||
(impl != HRT_TSC && impl != HRT_QPC && impl != HRT_GTC && impl != HRT_NONE))
@ -389,15 +399,20 @@ unlock();
}
//////////////////////////////////////////////////////////////////////////////
//
// calibration
//
//////////////////////////////////////////////////////////////////////////////
// 'safe' millisecond timer, used to measure HRT freq
static long ms_time()
{
#ifdef _WIN32
return (long)timeGetTime();
return (long)GetTickCount();
#else
return (long)clock();
return (long)(clock() * 1000.0 / CLOCKS_PER_SEC);
#endif
}
@ -495,3 +510,171 @@ static void init_calibration_thread()
#endif
}
//////////////////////////////////////////////////////////////////////////////
//
// wtime wrapper: emulates POSIX functions
//
//////////////////////////////////////////////////////////////////////////////
static const long _1e6 = 1000000;
static const i64 _1e9 = 1000000000;
// return nanoseconds since posix epoch as reported by system time
// only 10 or 15 ms resolution!
static i64 st_time_ns()
{
union
{
FILETIME ft;
i64 i;
}
t;
GetSystemTimeAsFileTime(&t.ft);
// Windows system time is hectonanoseconds since Jan. 1, 1601
return (t.i - 0x019DB1DED53E8000) * 100;
}
// return nanoseconds since posix epoch as reported by HRT
static i64 hrt_time_ns()
{
// use as starting value, because HRT origin is unspecified
static i64 hrt_start;
static i64 st_start;
if(!st_start)
{
hrt_start = hrt_ticks();
st_start = st_time_ns();
}
const double delta_s = hrt_delta_s(hrt_start, hrt_ticks());
const i64 ns = st_start + (i64)(delta_s * _1e9);
return ns;
}
WIN_REGISTER_MODULE(wtime);
static int wtime_init()
{
hrt_init();
// first call latches start time
hrt_time_ns();
return 0;
}
static int wtime_shutdown()
{
return 0;
}
void wtime_reset_impl()
{
hrt_override_impl(HRT_DEFAULT, HRT_NONE);
}
static void sleep_ns(i64 ns)
{
DWORD ms = DWORD(ns / _1e6);
if(ms != 0)
Sleep(ms);
else
{
i64 t0 = hrt_ticks(), t1;
do
t1 = hrt_ticks();
while(hrt_delta_s(t0, t1) * _1e9 < ns);
}
}
int clock_gettime(clockid_t clock, struct timespec* t)
{
#ifndef NDEBUG
if(clock != CLOCK_REALTIME || !t)
{
debug_warn("clock_gettime: invalid clock or t param");
return -1;
}
#endif
const i64 ns = hrt_time_ns();
t->tv_sec = (time_t)(ns / _1e9);
t->tv_nsec = (long) (ns % _1e9);
return 0;
}
int clock_getres(clockid_t clock, struct timespec* res)
{
#ifndef NDEBUG
if(clock != CLOCK_REALTIME || !res)
{
debug_warn("clock_getres: invalid clock or res param");
return -1;
}
#endif
HRTImpl impl;
i64 nominal_freq;
hrt_query_impl(impl, nominal_freq);
res->tv_sec = 0;
res->tv_nsec = (long)(1e9 / nominal_freq);
return 0;
}
int nanosleep(const struct timespec* rqtp, struct timespec* /* rmtp */)
{
i64 ns = rqtp->tv_sec; // make sure we don't overflow
ns *= _1e9;
ns += rqtp->tv_nsec;
sleep_ns(ns);
return 0;
}
int gettimeofday(struct timeval* tv, void* tzp)
{
UNUSED(tzp);
#ifndef NDEBUG
if(!tv)
{
debug_warn("gettimeofday: invalid t param");
return -1;
}
#endif
const long us = (long)(hrt_time_ns() / 1000);
tv->tv_sec = (time_t) (us / _1e6);
tv->tv_usec = (suseconds_t)(us % _1e6);
return 0;
}
uint sleep(uint sec)
{
Sleep(sec * 1000);
return sec;
}
int usleep(useconds_t us)
{
// can't overflow, because us < 1e6
sleep_ns(us * 1000);
return 0;
}

81
source/lib/sysdep/win/wtime.h Executable file
View File

@ -0,0 +1,81 @@
// Windows-specific high resolution timer
// Copyright (c) 2004 Jan Wassenberg
//
// 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.
//
// Contact info:
// Jan.Wassenberg@stud.uni-karlsruhe.de
// http://www.stud.uni-karlsruhe.de/~urkt/
#ifndef WTIME_H__
#define WTIME_H__
#define HAVE_CLOCK_GETTIME
#define HAVE_GETTIMEOFDAY
//
// <sys/types.h>
//
typedef unsigned long useconds_t;
typedef long suseconds_t;
//
// <unistd.h>
//
extern unsigned int sleep(unsigned int sec);
extern int usleep(useconds_t us);
//
// <time.h>
//
typedef enum
{
CLOCK_REALTIME
}
clockid_t;
// BSD gettimeofday
struct timeval
{
time_t tv_sec;
suseconds_t tv_usec;
};
// POSIX realtime clock_*
struct timespec
{
time_t tv_sec;
long tv_nsec;
};
extern int gettimeofday(struct timeval* tv, void* tzp);
extern int nanosleep(const struct timespec* rqtp, struct timespec* rmtp);
extern int clock_gettime(clockid_t clock, struct timespec* ts);
extern int clock_getres(clockid_t clock, struct timespec* res);
// HACK: if _WIN32, the HRT makes its final implementation choice
// in the first calibrate call where cpu_freq and cpu_caps are
// available. provide a routine that makes the choice when called,
// so app code isn't surprised by a timer change, although the HRT
// does try to keep the timer continuous.
extern void wtime_reset_impl();
#endif // #ifndef WTIME_H__