0ad/source/lib/sysdep/win/wposix/wtime.cpp

171 lines
4.1 KiB
C++
Raw Normal View History

/**
* =========================================================================
* 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"
#pragma SECTION_PRE_LIBC(M) // late; dependent on whrt
WIN_REGISTER_FUNC(wtime_Init);
#pragma FORCE_INCLUDE(wtime_Init)
#pragma SECTION_RESTORE
// 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;
}