304 lines
7.3 KiB
C++
304 lines
7.3 KiB
C++
#include "winsup.h"
|
|
#include <realtimeapiset.h>
|
|
#include "pinfo.h"
|
|
#include "clock.h"
|
|
#include "miscfuncs.h"
|
|
#include "spinlock.h"
|
|
|
|
static inline LONGLONG
|
|
system_qpc_tickspersec ()
|
|
{
|
|
LARGE_INTEGER qpf;
|
|
|
|
/* ticks per sec */
|
|
QueryPerformanceFrequency (&qpf);
|
|
return qpf.QuadPart;
|
|
}
|
|
|
|
static inline LONGLONG
|
|
system_tickcount_period ()
|
|
{
|
|
ULONG coarsest = 0, finest, actual;
|
|
|
|
if (!coarsest)
|
|
{
|
|
/* The actual resolution of the OS timer is a system-wide setting which
|
|
can be changed any time, by any process. The only fixed value we
|
|
can rely on is the coarsest value. */
|
|
NtQueryTimerResolution (&coarsest, &finest, &actual);
|
|
}
|
|
return coarsest;
|
|
}
|
|
|
|
void inline
|
|
clk_t::init ()
|
|
{
|
|
if (!period)
|
|
InterlockedExchange64 (&period, system_tickcount_period ());
|
|
}
|
|
|
|
void inline
|
|
clk_realtime_t::init ()
|
|
{
|
|
if (wincap.has_precise_system_time ())
|
|
{
|
|
if (!ticks_per_sec)
|
|
InterlockedExchange64 (&ticks_per_sec, system_qpc_tickspersec ());
|
|
}
|
|
else if (!period)
|
|
InterlockedExchange64 (&period, system_tickcount_period ());
|
|
}
|
|
|
|
void inline
|
|
clk_monotonic_t::init ()
|
|
{
|
|
if (!ticks_per_sec)
|
|
InterlockedExchange64 (&ticks_per_sec, system_qpc_tickspersec ());
|
|
}
|
|
|
|
int
|
|
clk_realtime_coarse_t::now (clockid_t clockid, struct timespec *ts)
|
|
{
|
|
LARGE_INTEGER now;
|
|
|
|
GetSystemTimeAsFileTime ((LPFILETIME) &now);
|
|
/* Add conversion factor for UNIX vs. Windows base time */
|
|
now.QuadPart -= FACTOR;
|
|
ts->tv_sec = now.QuadPart / NS100PERSEC;
|
|
ts->tv_nsec = (now.QuadPart % NS100PERSEC) * (NSPERSEC/NS100PERSEC);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
clk_realtime_t::now (clockid_t clockid, struct timespec *ts)
|
|
{
|
|
LARGE_INTEGER now;
|
|
|
|
wincap.has_precise_system_time ()
|
|
? GetSystemTimePreciseAsFileTime ((LPFILETIME) &now)
|
|
: GetSystemTimeAsFileTime ((LPFILETIME) &now);
|
|
/* Add conversion factor for UNIX vs. Windows base time */
|
|
now.QuadPart -= FACTOR;
|
|
ts->tv_sec = now.QuadPart / NS100PERSEC;
|
|
ts->tv_nsec = (now.QuadPart % NS100PERSEC) * (NSPERSEC/NS100PERSEC);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
clk_process_t::now (clockid_t clockid, struct timespec *ts)
|
|
{
|
|
pid_t pid = CLOCKID_TO_PID (clockid);
|
|
HANDLE hProcess;
|
|
KERNEL_USER_TIMES kut;
|
|
int64_t x;
|
|
|
|
if (pid == 0)
|
|
pid = myself->pid;
|
|
|
|
pinfo p (pid);
|
|
if (!p || !p->exists ())
|
|
{
|
|
set_errno (EINVAL);
|
|
return -1;
|
|
}
|
|
|
|
hProcess = OpenProcess (PROCESS_QUERY_LIMITED_INFORMATION, 0,
|
|
p->dwProcessId);
|
|
NtQueryInformationProcess (hProcess, ProcessTimes,
|
|
&kut, sizeof kut, NULL);
|
|
|
|
x = kut.KernelTime.QuadPart + kut.UserTime.QuadPart;
|
|
ts->tv_sec = x / NS100PERSEC;
|
|
ts->tv_nsec = (x % NS100PERSEC) * (NSPERSEC/NS100PERSEC);
|
|
CloseHandle (hProcess);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
clk_thread_t::now (clockid_t clockid, struct timespec *ts)
|
|
{
|
|
long thr_id = CLOCKID_TO_THREADID (clockid);
|
|
HANDLE hThread;
|
|
KERNEL_USER_TIMES kut;
|
|
int64_t x;
|
|
|
|
if (thr_id == 0)
|
|
thr_id = pthread::self ()->getsequence_np ();
|
|
|
|
hThread = OpenThread (THREAD_QUERY_LIMITED_INFORMATION, 0, thr_id);
|
|
if (!hThread)
|
|
{
|
|
set_errno (EINVAL);
|
|
return -1;
|
|
}
|
|
|
|
NtQueryInformationThread (hThread, ThreadTimes,
|
|
&kut, sizeof kut, NULL);
|
|
|
|
x = kut.KernelTime.QuadPart + kut.UserTime.QuadPart;
|
|
ts->tv_sec = x / NS100PERSEC;
|
|
ts->tv_nsec = (x % NS100PERSEC) * (NSPERSEC/NS100PERSEC);
|
|
CloseHandle (hThread);
|
|
return 0;
|
|
}
|
|
|
|
extern "C" void WINAPI QueryUnbiasedInterruptTimePrecise (PULONGLONG);
|
|
extern "C" void WINAPI QueryInterruptTimePrecise (PULONGLONG);
|
|
|
|
int
|
|
clk_monotonic_t::now (clockid_t clockid, struct timespec *ts)
|
|
{
|
|
if (wincap.has_precise_interrupt_time ())
|
|
{
|
|
/* Suspend time not taken into account, as on Linux */
|
|
ULONGLONG now;
|
|
|
|
QueryUnbiasedInterruptTimePrecise (&now);
|
|
ts->tv_sec = now / NS100PERSEC;
|
|
now %= NS100PERSEC;
|
|
ts->tv_nsec = now * (NSPERSEC/NS100PERSEC);
|
|
}
|
|
else
|
|
{
|
|
/* https://stackoverflow.com/questions/24330496. Skip rounding since
|
|
its almost always wrong when working with timestamps */
|
|
UINT64 bias;
|
|
LARGE_INTEGER now;
|
|
struct timespec bts;
|
|
|
|
init ();
|
|
do
|
|
{
|
|
bias = SharedUserData.InterruptTimeBias;
|
|
QueryPerformanceCounter(&now);
|
|
}
|
|
while (bias != SharedUserData.InterruptTimeBias);
|
|
/* Convert perf counter to timespec */
|
|
ts->tv_sec = now.QuadPart / ticks_per_sec;
|
|
now.QuadPart %= ticks_per_sec;
|
|
ts->tv_nsec = (now.QuadPart * NSPERSEC) / ticks_per_sec;
|
|
/* Convert bias to timespec */
|
|
bts.tv_sec = bias / NS100PERSEC;
|
|
bias %= NS100PERSEC;
|
|
bts.tv_nsec = bias * (NSPERSEC/NS100PERSEC);
|
|
/* Subtract bias from perf */
|
|
ts_diff (bts, *ts);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
clk_monotonic_coarse_t::now (clockid_t clockid, struct timespec *ts)
|
|
{
|
|
if (wincap.has_unbiased_interrupt_time ())
|
|
{
|
|
/* Suspend time not taken into account, as on Linux */
|
|
ULONGLONG now;
|
|
|
|
QueryUnbiasedInterruptTime (&now);
|
|
ts->tv_sec = now / NS100PERSEC;
|
|
now %= NS100PERSEC;
|
|
ts->tv_nsec = now * (NSPERSEC/NS100PERSEC);
|
|
}
|
|
else
|
|
{
|
|
/* Vista-only: GetTickCount64 is biased but it's coarse and monotonic. */
|
|
ULONGLONG now;
|
|
|
|
now = GetTickCount64 (); /* Returns ms since boot */
|
|
ts->tv_sec = now / MSPERSEC;
|
|
now %= MSPERSEC;
|
|
ts->tv_nsec = now * (NSPERSEC/MSPERSEC);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
clk_boottime_t::now (clockid_t clockid, struct timespec *ts)
|
|
{
|
|
/* Suspend time taken into account, as on Linux */
|
|
if (wincap.has_precise_interrupt_time ())
|
|
{
|
|
ULONGLONG now;
|
|
|
|
QueryInterruptTimePrecise (&now);
|
|
ts->tv_sec = now / NS100PERSEC;
|
|
now %= NS100PERSEC;
|
|
ts->tv_nsec = now * (NSPERSEC/NS100PERSEC);
|
|
}
|
|
else
|
|
{
|
|
LARGE_INTEGER now;
|
|
|
|
init ();
|
|
QueryPerformanceCounter (&now);
|
|
ts->tv_sec = now.QuadPart / ticks_per_sec;
|
|
now.QuadPart %= ticks_per_sec;
|
|
ts->tv_nsec = (now.QuadPart * NSPERSEC) / ticks_per_sec;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
clk_t::resolution (struct timespec *ts)
|
|
{
|
|
init ();
|
|
ts->tv_sec = 0;
|
|
ts->tv_nsec = period * (NSPERSEC/NS100PERSEC);
|
|
}
|
|
|
|
void
|
|
clk_realtime_t::resolution (struct timespec *ts)
|
|
{
|
|
init ();
|
|
ts->tv_sec = 0;
|
|
if (wincap.has_precise_system_time ())
|
|
ts->tv_nsec = NSPERSEC / ticks_per_sec;
|
|
else
|
|
ts->tv_nsec = period * (NSPERSEC/NS100PERSEC);
|
|
}
|
|
|
|
void
|
|
clk_monotonic_t::resolution (struct timespec *ts)
|
|
{
|
|
init ();
|
|
ts->tv_sec = 0;
|
|
ts->tv_nsec = NSPERSEC / ticks_per_sec;
|
|
}
|
|
|
|
static clk_realtime_coarse_t clk_realtime_coarse;
|
|
static clk_realtime_t clk_realtime;
|
|
static clk_process_t clk_process;
|
|
static clk_thread_t clk_thread;
|
|
static clk_monotonic_t clk_monotonic;
|
|
static clk_monotonic_t clk_monotonic_raw; /* same as clk_monotonic */
|
|
static clk_monotonic_coarse_t clk_monotonic_coarse;
|
|
static clk_boottime_t clk_boottime;
|
|
static clk_realtime_t clk_realtime_alarm; /* same as clk_realtime */
|
|
static clk_boottime_t clk_boottime_alarm; /* same as clk_boottime_t */
|
|
|
|
clk_t *cyg_clock[MAX_CLOCKS] =
|
|
{
|
|
&clk_realtime_coarse,
|
|
&clk_realtime,
|
|
&clk_process,
|
|
&clk_thread,
|
|
&clk_monotonic,
|
|
&clk_monotonic_raw,
|
|
&clk_monotonic_coarse,
|
|
&clk_boottime,
|
|
&clk_realtime_alarm,
|
|
&clk_boottime_alarm,
|
|
};
|
|
|
|
clk_t *
|
|
get_clock (clockid_t clk_id)
|
|
{
|
|
extern clk_t *cyg_clock[MAX_CLOCKS];
|
|
clockid_t clockid = CLOCKID (clk_id);
|
|
if (clk_id >= MAX_CLOCKS
|
|
&& clockid != CLOCK_PROCESS_CPUTIME_ID
|
|
&& clockid != CLOCK_THREAD_CPUTIME_ID)
|
|
return NULL;
|
|
return cyg_clock[clockid];
|
|
}
|