forked from 0ad/0ad
janwas
d802b73d94
wdbg: cleanup, improve exception catcher (previously potentially failed if __try block came in non-main thread). required since wstartup no longer commandeers the entry point. winit, wstartup: update documentation This was SVN commit r5141.
166 lines
4.0 KiB
C++
166 lines
4.0 KiB
C++
/**
|
|
* =========================================================================
|
|
* File : wtime.cpp
|
|
* Project : 0 A.D.
|
|
* Description : emulate POSIX time functionality on Windows.
|
|
* =========================================================================
|
|
*/
|
|
|
|
// license: GPL; see lib/license.txt
|
|
|
|
// note: clock_gettime et al. have been removed. callers should use the
|
|
// WHRT directly, rather than needlessly translating s -> ns -> s,
|
|
// which costs time and accuracy.
|
|
|
|
#include "precompiled.h"
|
|
#include "wtime.h"
|
|
|
|
#include "wposix_internal.h"
|
|
#include "lib/sysdep/cpu.h" // cpu_i64FromDouble
|
|
#include "lib/sysdep/win/whrt/whrt.h"
|
|
|
|
WINIT_REGISTER_MAIN_INIT(wtime_Init); // whrt -> wtime
|
|
|
|
// NT system time and FILETIME are hectonanoseconds since Jan. 1, 1601 UTC.
|
|
// SYSTEMTIME is a struct containing month, year, etc.
|
|
|
|
static const long _1e3 = 1000;
|
|
static const long _1e6 = 1000000;
|
|
static const long _1e7 = 10000000;
|
|
static const i64 _1e9 = 1000000000;
|
|
|
|
|
|
//
|
|
// FILETIME -> time_t routines; used by wposix filetime_to_time_t wrapper.
|
|
//
|
|
|
|
// hectonanoseconds between Windows and POSIX epoch
|
|
static const u64 posix_epoch_hns = 0x019DB1DED53E8000;
|
|
|
|
// this function avoids the pitfall of casting FILETIME* to u64*,
|
|
// which is not safe due to differing alignment guarantees!
|
|
// on some platforms, that would result in an exception.
|
|
static u64 u64_from_FILETIME(const FILETIME* ft)
|
|
{
|
|
return u64_from_u32(ft->dwHighDateTime, ft->dwLowDateTime);
|
|
}
|
|
|
|
|
|
// convert UTC FILETIME to seconds-since-1970 UTC:
|
|
// we just have to subtract POSIX epoch and scale down to units of seconds.
|
|
//
|
|
// used by wfilesystem.
|
|
//
|
|
// note: RtlTimeToSecondsSince1970 isn't officially documented,
|
|
// so don't use that.
|
|
time_t wtime_utc_filetime_to_time_t(FILETIME* ft)
|
|
{
|
|
u64 hns = u64_from_FILETIME(ft);
|
|
u64 s = (hns - posix_epoch_hns) / _1e7;
|
|
return (time_t)(s & 0xFFFFFFFF);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// system clock at startup [nanoseconds since POSIX epoch]
|
|
// note: the HRT starts at 0; any increase by the time we get here
|
|
// just makes our notion of the start time more accurate)
|
|
static i64 stInitial_ns;
|
|
|
|
static void LatchInitialSystemTime()
|
|
{
|
|
FILETIME ft;
|
|
GetSystemTimeAsFileTime(&ft);
|
|
const u64 hns = u64_from_FILETIME(&ft);
|
|
stInitial_ns = (hns - posix_epoch_hns) * 100;
|
|
}
|
|
|
|
// return nanoseconds since POSIX epoch.
|
|
// algorithm: add current HRT value to the startup system time
|
|
static i64 CurrentSystemTime_ns()
|
|
{
|
|
const i64 ns = stInitial_ns + cpu_i64FromDouble(whrt_Time() * _1e9);
|
|
return ns;
|
|
}
|
|
|
|
static timespec TimespecFromNs(i64 ns)
|
|
{
|
|
timespec ts;
|
|
ts.tv_sec = (time_t)((ns / _1e9) & 0xFFFFFFFF);
|
|
ts.tv_nsec = (long)(ns % _1e9);
|
|
return ts;
|
|
}
|
|
|
|
static uint MsFromTimespec(const timespec& ts)
|
|
{
|
|
i64 ms = ts.tv_sec; // avoid overflow
|
|
ms *= _1e3;
|
|
ms += ts.tv_nsec / _1e6;
|
|
return ms;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
int clock_gettime(clockid_t clock, struct timespec* ts)
|
|
{
|
|
debug_assert(clock == CLOCK_REALTIME || clock == CLOCK_MONOTONIC);
|
|
|
|
const i64 ns = CurrentSystemTime_ns();
|
|
*ts = TimespecFromNs(ns);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int clock_getres(clockid_t clock, struct timespec* ts)
|
|
{
|
|
debug_assert(clock == CLOCK_REALTIME || clock == CLOCK_MONOTONIC);
|
|
|
|
const i64 ns = cpu_i64FromDouble(whrt_Resolution() * 1e9);
|
|
*ts = TimespecFromNs(ns);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int nanosleep(const struct timespec* rqtp, struct timespec* /* rmtp */)
|
|
{
|
|
const DWORD ms = (DWORD)MsFromTimespec(*rqtp);
|
|
if(ms)
|
|
Sleep(ms);
|
|
return 0;
|
|
}
|
|
|
|
|
|
uint sleep(uint sec)
|
|
{
|
|
// warn if overflow would result (it would be insane to ask for
|
|
// such lengthy sleep timeouts, but still)
|
|
debug_assert(sec < std::numeric_limits<uint>::max()/1000);
|
|
|
|
const DWORD ms = sec * 1000;
|
|
if(ms)
|
|
Sleep(ms);
|
|
return sec;
|
|
}
|
|
|
|
|
|
int usleep(useconds_t us)
|
|
{
|
|
debug_assert(us < _1e6);
|
|
|
|
const DWORD ms = us/1000;
|
|
if(ms)
|
|
Sleep(ms);
|
|
return 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static LibError wtime_Init()
|
|
{
|
|
LatchInitialSystemTime();
|
|
return INFO::OK;
|
|
}
|