diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in index a232836e1..fb0195fc2 100644 --- a/winsup/cygwin/Makefile.in +++ b/winsup/cygwin/Makefile.in @@ -254,6 +254,7 @@ DLL_OFILES:= \ autoload.o \ base64.o \ bsdlib.o \ + clock.o \ ctype.o \ cxx.o \ cygheap.o \ diff --git a/winsup/cygwin/aio.cc b/winsup/cygwin/aio.cc index 7d5d98299..b245bdd02 100644 --- a/winsup/cygwin/aio.cc +++ b/winsup/cygwin/aio.cc @@ -7,7 +7,6 @@ Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ #include "winsup.h" -#include "hires.h" #include "path.h" #include "fhandler.h" #include "dtable.h" @@ -803,12 +802,12 @@ retry: return -1; } - time0 = ntod.nsecs (); + time0 = get_clock (CLOCK_MONOTONIC)->nsecs (); /* Note wait below is abortable even w/ empty sigmask and infinite timeout */ res = sigtimedwait (&sigmask, &si, timeout ? &to : NULL); if (res == -1) return -1; /* Return with errno set by failed sigtimedwait() */ - time1 = ntod.nsecs (); + time1 = get_clock (CLOCK_MONOTONIC)->nsecs (); /* Adjust timeout to account for time just waited */ time1 -= time0; diff --git a/winsup/cygwin/autoload.cc b/winsup/cygwin/autoload.cc index 4fac3e39c..5a0e400aa 100644 --- a/winsup/cygwin/autoload.cc +++ b/winsup/cygwin/autoload.cc @@ -586,6 +586,14 @@ LoadDLLfunc (GetSystemTimePreciseAsFileTime, 4, kernel32) LoadDLLfuncEx (PrefetchVirtualMemory, 16, kernel32, 1) LoadDLLfunc (SetThreadGroupAffinity, 12, kernel32) +/* MSDN claims these are exported by kernel32.dll, but only + QueryUnbiasedInterruptTime actually is. The others are only + available via KernelBase.dll. */ +LoadDLLfunc (QueryInterruptTime, 4, KernelBase) +LoadDLLfunc (QueryInterruptTimePrecise, 4, KernelBase) +LoadDLLfunc (QueryUnbiasedInterruptTime, 4, KernelBase) +LoadDLLfunc (QueryUnbiasedInterruptTimePrecise, 4, KernelBase) + /* ldap functions are cdecl! */ #pragma push_macro ("mangle") #undef mangle diff --git a/winsup/cygwin/clock.cc b/winsup/cygwin/clock.cc new file mode 100644 index 000000000..99d0a281b --- /dev/null +++ b/winsup/cygwin/clock.cc @@ -0,0 +1,286 @@ +#include "winsup.h" +#include +#include "pinfo.h" +#include "clock.h" +#include "miscfuncs.h" +#include "spinlock.h" + +static LONGLONG +system_qpc_resolution () +{ + LARGE_INTEGER qpf; + + QueryPerformanceFrequency (&qpf); + return qpf.QuadPart; +} + +static LONGLONG +system_tickcount_resolution () +{ + 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 NS100PERSEC / coarsest; +} + +void +clk_t::init () +{ + spinlock spin (inited, 1); + if (!spin) + ticks_per_sec = system_tickcount_resolution (); +} + +void +clk_realtime_t::init () +{ + spinlock spin (inited, 1); + if (!spin) + ticks_per_sec = wincap.has_precise_system_time () + ? system_qpc_resolution () + : system_tickcount_resolution (); +} + +void +clk_monotonic_t::init () +{ + spinlock spin (inited, 1); + if (!spin) + ticks_per_sec = system_qpc_resolution (); +} + +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; + + if (inited <= 0) + 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. */ + LONGLONG now; + + if (inited <= 0) + init (); + now = GetTickCount64 (); + ts->tv_sec = now / ticks_per_sec; + now %= ticks_per_sec; + ts->tv_nsec = (now * NSPERSEC) / ticks_per_sec; + } + 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; + + if (inited <= 0) + 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) +{ + if (inited <= 0) + 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; + +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_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]; +} diff --git a/winsup/cygwin/clock.h b/winsup/cygwin/clock.h new file mode 100644 index 000000000..3c5bd5fbd --- /dev/null +++ b/winsup/cygwin/clock.h @@ -0,0 +1,149 @@ +/* clock.h: Definitions for clock calculations + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#ifndef __CLOCK_H__ +#define __CLOCK_H__ + +#include + +/* Must be a power of 2. */ +#define MAX_CLOCKS (8) + +/* Conversions for per-process and per-thread clocks */ +#define CLOCKID(cid) \ + ((cid) % MAX_CLOCKS) +#define PID_TO_CLOCKID(pid) \ + ((pid) * MAX_CLOCKS + CLOCK_PROCESS_CPUTIME_ID) +#define CLOCKID_TO_PID(cid) \ + (((cid) - CLOCK_PROCESS_CPUTIME_ID) / MAX_CLOCKS) +#define CLOCKID_IS_PROCESS(cid) \ + (CLOCKID(cid) == CLOCK_PROCESS_CPUTIME_ID) +#define THREADID_TO_CLOCKID(tid) \ + ((tid) * MAX_CLOCKS + CLOCK_THREAD_CPUTIME_ID) +#define CLOCKID_TO_THREADID(cid) \ + (((cid) - CLOCK_THREAD_CPUTIME_ID) / MAX_CLOCKS) +#define CLOCKID_IS_THREAD(cid) \ + (CLOCKID(cid) == CLOCK_THREAD_CPUTIME_ID) + +/* Largest delay in ms for sleep and alarm calls. + Allow actual delay to exceed requested delay by 10 s. + Express as multiple of 1000 (i.e. seconds) + max resolution + The tv_sec argument in timeval structures cannot exceed + CLOCK_DELAY_MAX / 1000 - 1, so that adding fractional part + and rounding won't exceed CLOCK_DELAY_MAX */ +#define CLOCK_DELAY_MAX ((((UINT_MAX - 10000) / 1000) * 1000) + 10) + +/* 100ns difference between Windows and UNIX timebase. */ +#define FACTOR (0x19db1ded53e8000LL) +/* # of nanosecs per second. */ +#define NSPERSEC (1000000000LL) +/* # of 100ns intervals per second. */ +#define NS100PERSEC (10000000LL) +/* # of microsecs per second. */ +#define USPERSEC (1000000LL) +/* # of millisecs per second. */ +#define MSPERSEC (1000L) + +class clk_t +{ + protected: + LONG inited; + LONGLONG ticks_per_sec; + virtual void init (); + virtual int now (clockid_t, struct timespec *) = 0; + + public: + int nsecs (clockid_t _id, struct timespec *ts) + { + return now (_id, ts); + } + void resolution (struct timespec *); + + /* shortcuts for non-process/thread clocks */ + void nsecs (struct timespec *ts) { nsecs (0, ts); } + ULONGLONG nsecs () + { + struct timespec ts; + nsecs (&ts); + return (ULONGLONG) ts.tv_sec * NSPERSEC + ts.tv_nsec; + } + LONGLONG n100secs () + { + struct timespec ts; + nsecs (&ts); + return ts.tv_sec * NS100PERSEC + ts.tv_nsec / (NSPERSEC/NS100PERSEC); + } + LONGLONG usecs () + { + struct timespec ts; + nsecs (&ts); + return ts.tv_sec * USPERSEC + ts.tv_nsec / (NSPERSEC/USPERSEC); + } + LONGLONG msecs () + { + struct timespec ts; + nsecs (&ts); + return ts.tv_sec * MSPERSEC + ts.tv_nsec / (NSPERSEC/MSPERSEC); + } +}; + +class clk_realtime_coarse_t : public clk_t +{ + virtual int now (clockid_t, struct timespec *); +}; + +class clk_realtime_t : public clk_t +{ + virtual void init (); + virtual int now (clockid_t, struct timespec *); +}; + +class clk_process_t : public clk_t +{ + virtual int now (clockid_t, struct timespec *); +}; + +class clk_thread_t : public clk_t +{ + virtual int now (clockid_t, struct timespec *); +}; + +class clk_monotonic_t : public clk_t +{ + protected: + virtual void init (); + private: + virtual int now (clockid_t, struct timespec *); +}; + +class clk_monotonic_coarse_t : public clk_t +{ + virtual int now (clockid_t, struct timespec *); +}; + +class clk_boottime_t : public clk_monotonic_t +{ + virtual int now (clockid_t, struct timespec *); +}; + +clk_t *get_clock (clockid_t clk_id); + +/* Compute interval between two timespec timestamps: ts1 = ts1 - ts0. */ +static inline void +ts_diff (const struct timespec &ts0, struct timespec &ts1) +{ + ts1.tv_nsec -= ts0.tv_nsec; + if (ts1.tv_nsec < 0) + { + ts1.tv_nsec += NSPERSEC; + --ts1.tv_sec; + } + ts1.tv_sec -= ts0.tv_sec; +} + +#endif /*__CLOCK_H__*/ diff --git a/winsup/cygwin/cygheap.h b/winsup/cygwin/cygheap.h index abbf9ec07..d8a2e79a4 100644 --- a/winsup/cygwin/cygheap.h +++ b/winsup/cygwin/cygheap.h @@ -6,7 +6,7 @@ This software is a copyrighted work licensed under the terms of the Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ -#include "hires.h" +#include "clock.h" #include "cygheap_malloc.h" #include "pwdgrp.h" diff --git a/winsup/cygwin/fhandler_socket_unix.cc b/winsup/cygwin/fhandler_socket_unix.cc index da83eccee..e71d2a722 100644 --- a/winsup/cygwin/fhandler_socket_unix.cc +++ b/winsup/cygwin/fhandler_socket_unix.cc @@ -24,7 +24,6 @@ #include "fhandler.h" #include "dtable.h" #include "cygheap.h" -#include "hires.h" #include "shared_info.h" #include "ntdll.h" #include "miscfuncs.h" @@ -1269,7 +1268,7 @@ fhandler_socket_unix::wait_pipe_thread (PUNICODE_STRING pipe_name) pwbuf->NameLength = pipe_name->Length; pwbuf->TimeoutSpecified = TRUE; memcpy (pwbuf->Name, pipe_name->Buffer, pipe_name->Length); - stamp = ntod.nsecs (); + stamp = get_clock (CLOCK_MONOTONIC)->n100secs (); do { status = NtFsControlFile (npfsh, evt, NULL, NULL, &io, FSCTL_PIPE_WAIT, @@ -1298,7 +1297,8 @@ fhandler_socket_unix::wait_pipe_thread (PUNICODE_STRING pipe_name) /* Another concurrent connect grabbed the pipe instance under our nose. Fix the timeout value and go waiting again, unless the timeout has passed. */ - pwbuf->Timeout.QuadPart -= (stamp - ntod.nsecs ()) / 100LL; + pwbuf->Timeout.QuadPart -= + stamp - get_clock (CLOCK_MONOTONIC)->n100secs (); if (pwbuf->Timeout.QuadPart >= 0) { status = STATUS_IO_TIMEOUT; diff --git a/winsup/cygwin/hires.h b/winsup/cygwin/hires.h deleted file mode 100644 index 10aed7baf..000000000 --- a/winsup/cygwin/hires.h +++ /dev/null @@ -1,64 +0,0 @@ -/* hires.h: Definitions for hires clock calculations - -This file is part of Cygwin. - -This software is a copyrighted work licensed under the terms of the -Cygwin license. Please consult the file "CYGWIN_LICENSE" for -details. */ - -#ifndef __HIRES_H__ -#define __HIRES_H__ - -#include - -/* Conversions for per-process and per-thread clocks */ -#define PID_TO_CLOCKID(pid) (pid * 8 + CLOCK_PROCESS_CPUTIME_ID) -#define CLOCKID_TO_PID(cid) ((cid - CLOCK_PROCESS_CPUTIME_ID) / 8) -#define CLOCKID_IS_PROCESS(cid) ((cid % 8) == CLOCK_PROCESS_CPUTIME_ID) -#define THREADID_TO_CLOCKID(tid) (tid * 8 + CLOCK_THREAD_CPUTIME_ID) -#define CLOCKID_TO_THREADID(cid) ((cid - CLOCK_THREAD_CPUTIME_ID) / 8) -#define CLOCKID_IS_THREAD(cid) ((cid % 8) == CLOCK_THREAD_CPUTIME_ID) - -/* Largest delay in ms for sleep and alarm calls. - Allow actual delay to exceed requested delay by 10 s. - Express as multiple of 1000 (i.e. seconds) + max resolution - The tv_sec argument in timeval structures cannot exceed - HIRES_DELAY_MAX / 1000 - 1, so that adding fractional part - and rounding won't exceed HIRES_DELAY_MAX */ -#define HIRES_DELAY_MAX ((((UINT_MAX - 10000) / 1000) * 1000) + 10) - -/* 100ns difference between Windows and UNIX timebase. */ -#define FACTOR (0x19db1ded53e8000LL) -/* # of nanosecs per second. */ -#define NSPERSEC (1000000000LL) -/* # of 100ns intervals per second. */ -#define NS100PERSEC (10000000LL) -/* # of microsecs per second. */ -#define USPERSEC (1000000LL) -/* # of millisecs per second. */ -#define MSPERSEC (1000L) - -class hires_ns -{ - LONG inited; - LARGE_INTEGER primed_pc; - double freq; - void prime (); - public: - LONGLONG nsecs (bool monotonic = false); - LONGLONG usecs () {return nsecs () / 1000LL;} - LONGLONG resolution(); -}; - -class hires_ms -{ - public: - LONGLONG nsecs (); - LONGLONG usecs () {return nsecs () / 10LL;} - LONGLONG msecs () {return nsecs () / 10000LL;} - UINT resolution (); -}; - -extern hires_ms gtod; -extern hires_ns ntod; -#endif /*__HIRES_H__*/ diff --git a/winsup/cygwin/include/cygwin/version.h b/winsup/cygwin/include/cygwin/version.h index 56b626963..bb1fa9746 100644 --- a/winsup/cygwin/include/cygwin/version.h +++ b/winsup/cygwin/include/cygwin/version.h @@ -10,8 +10,8 @@ details. */ the Cygwin shared library". This version is used to track important changes to the DLL and is mainly informative in nature. */ -#define CYGWIN_VERSION_DLL_MAJOR 2011 -#define CYGWIN_VERSION_DLL_MINOR 3 +#define CYGWIN_VERSION_DLL_MAJOR 2012 +#define CYGWIN_VERSION_DLL_MINOR 0 /* Major numbers before CYGWIN_VERSION_DLL_EPOCH are incompatible. */ @@ -498,13 +498,15 @@ details. */ 327: Export pthread_tryjoin_np, pthread_timedjoin_np. 328: Export aio_cancel, aio_error, aio_fsync, aio_read, aio_return, aio_suspend, aio_write, lio_listio. - 329: Export sched_getcpu.. + 329: Export sched_getcpu. + 330: Add CLOCK_REALTIME_COARSE, CLOCK_MONOTONIC_RAW, CLOCK_MONOTONIC_COARSE, + CLOCK_BOOTTIME. Note that we forgot to bump the api for ualarm, strtoll, strtoull, sigaltstack, sethostname. */ #define CYGWIN_VERSION_API_MAJOR 0 -#define CYGWIN_VERSION_API_MINOR 329 +#define CYGWIN_VERSION_API_MINOR 330 /* There is also a compatibity version number associated with the shared memory regions. It is incremented when incompatible changes are made to the shared diff --git a/winsup/cygwin/ntdll.h b/winsup/cygwin/ntdll.h index d64de1b9a..8d60ae732 100644 --- a/winsup/cygwin/ntdll.h +++ b/winsup/cygwin/ntdll.h @@ -793,7 +793,8 @@ typedef struct _KUSER_SHARED_DATA KSYSTEM_TIME InterruptTime; BYTE Reserved2[0x2c8]; ULONG DismountCount; - /* A lot more follows... */ + BYTE Reserved3[0xd0]; + UINT64 InterruptTimeBias; } KUSER_SHARED_DATA, *PKUSER_SHARED_DATA; /* Checked on 64 bit. */ diff --git a/winsup/cygwin/sched.cc b/winsup/cygwin/sched.cc index 702d038b0..10168e641 100644 --- a/winsup/cygwin/sched.cc +++ b/winsup/cygwin/sched.cc @@ -12,7 +12,7 @@ #include "miscfuncs.h" #include "cygerrno.h" #include "pinfo.h" -#include "hires.h" +#include "clock.h" /* for getpid */ #include #include diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc index a580ac658..0d82a5914 100644 --- a/winsup/cygwin/select.cc +++ b/winsup/cygwin/select.cc @@ -159,7 +159,7 @@ select (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, int ret = 0; /* Record the current time for later use. */ - LONGLONG start_time = gtod.usecs (); + LONGLONG start_time = get_clock (CLOCK_REALTIME)->usecs (); select_stuff sel; sel.return_on_signal = 0; @@ -210,7 +210,7 @@ select (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, if (us != -1LL && wait_state == select_stuff::select_set_zero) { select_printf ("recalculating us"); - LONGLONG now = gtod.usecs (); + LONGLONG now = get_clock (CLOCK_REALTIME)->usecs (); if (now >= (start_time + us)) { select_printf ("timed out after verification"); diff --git a/winsup/cygwin/signal.cc b/winsup/cygwin/signal.cc index de3e88697..9e6fdc61a 100644 --- a/winsup/cygwin/signal.cc +++ b/winsup/cygwin/signal.cc @@ -76,16 +76,9 @@ clock_nanosleep (clockid_t clk_id, int flags, const struct timespec *rqtp, /* support for CPU-time clocks is optional */ if (CLOCKID_IS_PROCESS (clk_id) || CLOCKID_IS_THREAD (clk_id)) return ENOTSUP; - - switch (clk_id) - { - case CLOCK_REALTIME: - case CLOCK_MONOTONIC: - break; - default: - /* unknown or illegal clock ID */ - return EINVAL; - } + /* All other valid clocks are valid */ + if (clk_id >= MAX_CLOCKS) + return EINVAL; LARGE_INTEGER timeout; @@ -103,14 +96,18 @@ clock_nanosleep (clockid_t clk_id, int flags, const struct timespec *rqtp, || (tp.tv_sec == rqtp->tv_sec && tp.tv_nsec > rqtp->tv_nsec)) return 0; - if (clk_id == CLOCK_REALTIME) - timeout.QuadPart += FACTOR; - else + switch (clk_id) { + case CLOCK_REALTIME_COARSE: + case CLOCK_REALTIME: + timeout.QuadPart += FACTOR; + break; + default: /* other clocks need to be handled with a relative timeout */ timeout.QuadPart -= tp.tv_sec * NS100PERSEC + tp.tv_nsec / (NSPERSEC/NS100PERSEC); timeout.QuadPart *= -1LL; + break; } } else /* !abstime */ diff --git a/winsup/cygwin/strace.cc b/winsup/cygwin/strace.cc index b95b436aa..21c2d6d4a 100644 --- a/winsup/cygwin/strace.cc +++ b/winsup/cygwin/strace.cc @@ -82,8 +82,14 @@ strace::dll_info () int strace::microseconds () { - static hires_ns now NO_COPY; - return (int) now.usecs (); + /* Need a local clock instance because this function is called before + the global constructors of the inferior process have been called. */ + static clk_monotonic_t clock_monotonic; + static LONGLONG process_start NO_COPY; + + if (!process_start) + process_start = clock_monotonic.usecs (); + return (int) (clock_monotonic.usecs () - process_start); } static int __stdcall diff --git a/winsup/cygwin/sysconf.cc b/winsup/cygwin/sysconf.cc index 753febca4..216e5142f 100644 --- a/winsup/cygwin/sysconf.cc +++ b/winsup/cygwin/sysconf.cc @@ -19,7 +19,7 @@ details. */ #include "ntdll.h" #include "tls_pbuf.h" #include "cpuid.h" -#include "hires.h" +#include "clock.h" static long get_open_max (int in) diff --git a/winsup/cygwin/thread.cc b/winsup/cygwin/thread.cc index c47a597be..f0f7b0a5f 100644 --- a/winsup/cygwin/thread.cc +++ b/winsup/cygwin/thread.cc @@ -2566,6 +2566,7 @@ pthread_convert_abstime (clockid_t clock_id, const struct timespec *abstime, / (NSPERSEC/NS100PERSEC); switch (clock_id) { + case CLOCK_REALTIME_COARSE: case CLOCK_REALTIME: timeout->QuadPart += FACTOR; break; @@ -3035,14 +3036,9 @@ pthread_condattr_setclock (pthread_condattr_t *attr, clockid_t clock_id) { if (!pthread_condattr::is_good_object (attr)) return EINVAL; - switch (clock_id) - { - case CLOCK_REALTIME: - case CLOCK_MONOTONIC: - break; - default: - return EINVAL; - } + if (CLOCKID_IS_PROCESS (clock_id) || CLOCKID_IS_THREAD (clock_id) + || clock_id >= MAX_CLOCKS) + return EINVAL; (*attr)->clock_id = clock_id; return 0; } diff --git a/winsup/cygwin/timer.cc b/winsup/cygwin/timer.cc index 9d3d7dc94..e1d78eb49 100644 --- a/winsup/cygwin/timer.cc +++ b/winsup/cygwin/timer.cc @@ -127,7 +127,7 @@ timer_thread (VOID *x) LONG sleep_ms; /* Account for delays in starting thread and sending the signal */ - now = gtod.usecs (); + now = get_clock (tt->clock_id)->usecs (); sleep_us = sleepto_us - now; if (sleep_us > 0) { @@ -232,7 +232,8 @@ timer_tracker::settime (int in_flags, const itimerspec *value, itimerspec *ovalu if (timespec_bad (value->it_value) || timespec_bad (value->it_interval)) __leave; - long long now = in_flags & TIMER_ABSTIME ? 0 : gtod.usecs (); + long long now = in_flags & TIMER_ABSTIME ? + 0 : get_clock (clock_id)->usecs (); lock_timer_tracker here; cancel (); @@ -272,7 +273,7 @@ timer_tracker::gettime (itimerspec *ovalue) else { ovalue->it_interval = it_interval; - long long now = gtod.usecs (); + long long now = get_clock (clock_id)->usecs (); long long left_us = sleepto_us - now; if (left_us < 0) left_us = 0; @@ -317,7 +318,7 @@ timer_create (clockid_t clock_id, struct sigevent *__restrict evp, return -1; } - if (clock_id != CLOCK_REALTIME) + if (clock_id >= MAX_CLOCKS) { set_errno (EINVAL); return -1; @@ -466,8 +467,8 @@ alarm (unsigned int seconds) struct itimerspec newt = {}, oldt; /* alarm cannot fail, but only needs not be correct for arguments < 64k. Truncate */ - if (seconds > (HIRES_DELAY_MAX / 1000 - 1)) - seconds = (HIRES_DELAY_MAX / 1000 - 1); + if (seconds > (CLOCK_DELAY_MAX / 1000 - 1)) + seconds = (CLOCK_DELAY_MAX / 1000 - 1); newt.it_value.tv_sec = seconds; timer_settime ((timer_t) &ttstart, 0, &newt, &oldt); int ret = oldt.it_value.tv_sec + (oldt.it_value.tv_nsec > 0); diff --git a/winsup/cygwin/times.cc b/winsup/cygwin/times.cc index c557e8273..8908d44f1 100644 --- a/winsup/cygwin/times.cc +++ b/winsup/cygwin/times.cc @@ -26,10 +26,6 @@ details. */ #include "ntdll.h" #include "spinlock.h" -hires_ms NO_COPY gtod; - -hires_ns NO_COPY ntod; - static inline void __attribute__ ((always_inline)) get_system_time (PLARGE_INTEGER systime) { @@ -171,10 +167,7 @@ gettimeofday (struct timeval *__restrict tv, void *__restrict tzvp) { struct timezone *tz = (struct timezone *) tzvp; static bool tzflag; - LONGLONG now = gtod.usecs (); - - if (now == (LONGLONG) -1) - return -1; + LONGLONG now = get_clock (CLOCK_REALTIME)->usecs (); tv->tv_sec = now / USPERSEC; tv->tv_usec = now % USPERSEC; @@ -462,135 +455,23 @@ ftime (struct timeb *tp) return 0; } -#define stupid_printf if (cygwin_finished_initializing) debug_printf -void -hires_ns::prime () -{ - spinlock hspin (inited, 1); - if (!hspin) - { - LARGE_INTEGER ifreq; - - /* On XP or later the perf counter functions will always succeed. */ - QueryPerformanceFrequency (&ifreq); - freq = (double) ((double) NSPERSEC / (double) ifreq.QuadPart); - QueryPerformanceCounter (&primed_pc); - } -} - -LONGLONG -hires_ns::nsecs (bool monotonic) -{ - LARGE_INTEGER now; - - if (inited <= 0) - prime (); - QueryPerformanceCounter (&now); - // FIXME: Use round() here? - now.QuadPart = (LONGLONG) (freq * (double) - (now.QuadPart - (monotonic ? 0LL : primed_pc.QuadPart))); - return now.QuadPart; -} - -LONGLONG -hires_ms::nsecs () -{ - LARGE_INTEGER systime; - get_system_time (&systime); - /* Add conversion factor for UNIX vs. Windows base time */ - return systime.QuadPart - FACTOR; -} - extern "C" int clock_gettime (clockid_t clk_id, struct timespec *tp) { - if (CLOCKID_IS_PROCESS (clk_id)) + clk_t *clock = get_clock (clk_id); + + if (!clock) { - pid_t pid = CLOCKID_TO_PID (clk_id); - HANDLE hProcess; - KERNEL_USER_TIMES kut; - int64_t x; - - if (pid == 0) - pid = getpid (); - - 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; - tp->tv_sec = x / NS100PERSEC; - tp->tv_nsec = (x % NS100PERSEC) * (NSPERSEC/NS100PERSEC); - - CloseHandle (hProcess); - return 0; + set_errno (EINVAL); + return -1; } - - if (CLOCKID_IS_THREAD (clk_id)) + __try { - long thr_id = CLOCKID_TO_THREADID (clk_id); - 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; - tp->tv_sec = x / NS100PERSEC; - tp->tv_nsec = (x % NS100PERSEC) * (NSPERSEC/NS100PERSEC); - - CloseHandle (hThread); - return 0; + return clock->nsecs (clk_id, tp); } - - switch (clk_id) - { - case CLOCK_REALTIME: - { - LONGLONG now = gtod.nsecs (); - if (now == (LONGLONG) -1) - return -1; - tp->tv_sec = now / NS100PERSEC; - tp->tv_nsec = (now % NS100PERSEC) * (NSPERSEC / NS100PERSEC); - break; - } - - case CLOCK_MONOTONIC: - { - LONGLONG now = ntod.nsecs (true); - if (now == (LONGLONG) -1) - return -1; - - tp->tv_sec = now / NSPERSEC; - tp->tv_nsec = (now % NSPERSEC); - break; - } - - default: - set_errno (EINVAL); - return -1; - } - - return 0; + __except (EFAULT) {} + __endtry + return -1; } extern "C" int @@ -608,80 +489,45 @@ clock_settime (clockid_t clk_id, const struct timespec *tp) return -1; } - if (clk_id != CLOCK_REALTIME) + if (clk_id != CLOCK_REALTIME_COARSE && clk_id != CLOCK_REALTIME) { set_errno (EINVAL); return -1; } - tv.tv_sec = tp->tv_sec; - tv.tv_usec = tp->tv_nsec / 1000; + __try + { + tv.tv_sec = tp->tv_sec; + tv.tv_usec = tp->tv_nsec / 1000; + } + __except (EFAULT) + { + return -1; + } + __endtry return settimeofday (&tv, NULL); } -static ULONG minperiod; // FIXME: Maintain period after a fork. - -LONGLONG -hires_ns::resolution () -{ - if (inited <= 0) - prime (); - return (freq <= 1.0) ? 1LL : (LONGLONG) freq; -} - -UINT -hires_ms::resolution () -{ - if (!minperiod) - { - ULONG coarsest, finest, actual; - - NtQueryTimerResolution (&coarsest, &finest, &actual); - /* 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. */ - minperiod = coarsest; - } - return minperiod; -} - extern "C" int clock_getres (clockid_t clk_id, struct timespec *tp) { - if (CLOCKID_IS_PROCESS (clk_id) || CLOCKID_IS_THREAD (clk_id)) + clk_t *clock = get_clock (clk_id); + + if (!clock) { - ULONG coarsest, finest, actual; - - NtQueryTimerResolution (&coarsest, &finest, &actual); - tp->tv_sec = coarsest / NS100PERSEC; - tp->tv_nsec = (coarsest % NS100PERSEC) * (NSPERSEC/NS100PERSEC); - return 0; + set_errno (EINVAL); + return -1; } - - switch (clk_id) + __try { - case CLOCK_REALTIME: - { - DWORD period = gtod.resolution (); - tp->tv_sec = period / NS100PERSEC; - tp->tv_nsec = (period % NS100PERSEC) * (NSPERSEC/NS100PERSEC); - break; - } - - case CLOCK_MONOTONIC: - { - LONGLONG period = ntod.resolution (); - tp->tv_sec = period / NSPERSEC; - tp->tv_nsec = period % NSPERSEC; - break; - } - - default: - set_errno (EINVAL); - return -1; + clock->resolution (tp); } - + __except (EFAULT) + { + return -1; + } + __endtry return 0; } diff --git a/winsup/cygwin/wincap.cc b/winsup/cygwin/wincap.cc index 0d20d37db..d5d8ef63c 100644 --- a/winsup/cygwin/wincap.cc +++ b/winsup/cygwin/wincap.cc @@ -32,6 +32,8 @@ wincaps wincap_vista __attribute__((section (".cygwin_dll_common"), shared)) = { has_new_pebteb_region:false, has_broken_whoami:true, has_unprivileged_createsymlink:false, + has_unbiased_interrupt_time:false, + has_precise_interrupt_time:false, }, }; @@ -50,6 +52,8 @@ wincaps wincap_7 __attribute__((section (".cygwin_dll_common"), shared)) = { has_new_pebteb_region:false, has_broken_whoami:true, has_unprivileged_createsymlink:false, + has_unbiased_interrupt_time:true, + has_precise_interrupt_time:false, }, }; @@ -68,6 +72,8 @@ wincaps wincap_8 __attribute__((section (".cygwin_dll_common"), shared)) = { has_new_pebteb_region:false, has_broken_whoami:false, has_unprivileged_createsymlink:false, + has_unbiased_interrupt_time:true, + has_precise_interrupt_time:false, }, }; @@ -86,6 +92,8 @@ wincaps wincap_10 __attribute__((section (".cygwin_dll_common"), shared)) = { has_new_pebteb_region:false, has_broken_whoami:false, has_unprivileged_createsymlink:false, + has_unbiased_interrupt_time:true, + has_precise_interrupt_time:true, }, }; @@ -104,6 +112,8 @@ wincaps wincap_10_1511 __attribute__((section (".cygwin_dll_common"), shared)) = has_new_pebteb_region:true, has_broken_whoami:false, has_unprivileged_createsymlink:false, + has_unbiased_interrupt_time:true, + has_precise_interrupt_time:true, }, }; @@ -122,6 +132,8 @@ wincaps wincap_10_1703 __attribute__((section (".cygwin_dll_common"), shared)) = has_new_pebteb_region:true, has_broken_whoami:false, has_unprivileged_createsymlink:true, + has_unbiased_interrupt_time:true, + has_precise_interrupt_time:true, }, }; diff --git a/winsup/cygwin/wincap.h b/winsup/cygwin/wincap.h index 9dc000056..cc1c6ba99 100644 --- a/winsup/cygwin/wincap.h +++ b/winsup/cygwin/wincap.h @@ -27,6 +27,8 @@ struct wincaps unsigned has_new_pebteb_region : 1; unsigned has_broken_whoami : 1; unsigned has_unprivileged_createsymlink : 1; + unsigned has_unbiased_interrupt_time : 1; + unsigned has_precise_interrupt_time : 1; }; }; @@ -74,6 +76,8 @@ public: bool IMPLEMENT (has_new_pebteb_region) bool IMPLEMENT (has_broken_whoami) bool IMPLEMENT (has_unprivileged_createsymlink) + bool IMPLEMENT (has_unbiased_interrupt_time) + bool IMPLEMENT (has_precise_interrupt_time) #undef IMPLEMENT };