diff options
Diffstat (limited to 'winsup/cygwin/clock.cc')
-rw-r--r-- | winsup/cygwin/clock.cc | 124 |
1 files changed, 118 insertions, 6 deletions
diff --git a/winsup/cygwin/clock.cc b/winsup/cygwin/clock.cc index 2a4e869..5a4109c 100644 --- a/winsup/cygwin/clock.cc +++ b/winsup/cygwin/clock.cc @@ -1,8 +1,11 @@ #include "winsup.h" +#include <unistd.h> +#include <fcntl.h> #include <realtimeapiset.h> #include "pinfo.h" #include "clock.h" #include "miscfuncs.h" +#include "registry.h" static inline LONGLONG system_qpc_tickspersec () @@ -32,22 +35,119 @@ system_tickcount_period () void inline clk_t::init () { - if (!period) - InterlockedExchange64 (&period, system_tickcount_period ()); + InterlockedCompareExchange64 (&period, system_tickcount_period (), 0); } void inline clk_realtime_t::init () { - if (!ticks_per_sec) - InterlockedExchange64 (&ticks_per_sec, system_qpc_tickspersec ()); + InterlockedCompareExchange64 (&ticks_per_sec, system_qpc_tickspersec (), 0); +} + +uint16_t clk_tai_t::leap_secs = 0; +SRWLOCK NO_COPY clk_tai_t::leap_lock = SRWLOCK_INIT; + +/* This is the structured data stored in the REG_BINARY registry value + HKLM\SYSTEM\CurrentControlSet\Control\LeapSecondInformation\LeapSeconds, + just like the binary representation of an array of reg_leap_secs_t. + Source: https://github.com/microsoft/STL/discussions/1624 */ +struct reg_leap_secs_t +{ + uint16_t year; + uint16_t month; + uint16_t day; + uint16_t hour; /* This field is always set to 23, independent of the + hour used when adding this entry via w32tm. Windows + always sets the timestamp of a leap second to + 23:59:59. */ + uint16_t negative; /* bool: 0 = positive, 1 = negative */ + uint16_t unknown; +} reg_leap_secs[32]; /* This is already unlikely high */ + +void inline +clk_tai_t::init () +{ + size_t size = 0; + + /* Avoid a lock/unlock sequence */ + if (leap_secs) + return; + + /* Do this early so we can just return later on. */ + InterlockedCompareExchange64 (&ticks_per_sec, system_qpc_tickspersec (), 0); + + AcquireSRWLockExclusive (&leap_lock); + + /* Some parallel thread was faster? */ + if (leap_secs) + { + ReleaseSRWLockExclusive (&leap_lock); + return; + } + + leap_secs = 37; /* Default: Leap secs since 2017 */ + + reg_key reg (HKEY_LOCAL_MACHINE, KEY_READ, L"SYSTEM", L"CurrentControlSet", + L"Control", L"LeapSecondInformation", NULL); + + /* If the reg key exists, we're on W10 1803 or later. In this case, + we always ignore the file! */ + if (!reg.error ()) + { + if (!NT_SUCCESS (reg.get_binary (L"LeapSeconds", reg_leap_secs, + sizeof reg_leap_secs, size))) + { + ReleaseSRWLockExclusive (&leap_lock); + return; + } + } + /* Windows 8.1 and W10 prior to 1803. When (if) IERS adds new leap secs, + we'll create a file /etc/leapsecs in the same format as the aforementioned + registry value used on newer OSes. */ + else + { + int fd = open ("/etc/leapsecs", O_RDONLY); + + if (fd < 0) + { + ReleaseSRWLockExclusive (&leap_lock); + return; + } + size = (size_t) read (fd, reg_leap_secs, sizeof reg_leap_secs); + if ((ssize_t) size < 0) + size = 0; + close (fd); + } + + size /= sizeof reg_leap_secs[0]; + for (size_t i = 0; i < size; ++i) + { + struct tm tm = { tm_sec: 59, + tm_min: 59, + tm_hour: 23, + tm_mday: reg_leap_secs[i].day, + tm_mon: reg_leap_secs[i].month - 1, + tm_year: reg_leap_secs[i].year - 1900, + tm_wday: 0, + tm_yday: 0, + tm_isdst: 0, + tm_gmtoff: 0, + tm_zone: 0 + }; + /* Future timestamp? We're done. Note that the leap sec is + second 60, therefore <=, not <! */ + if (time (NULL) <= timegm (&tm)) + break; + leap_secs += reg_leap_secs[i].negative ? -1 : 1; + } + + ReleaseSRWLockExclusive (&leap_lock); } void inline clk_monotonic_t::init () { - if (!ticks_per_sec) - InterlockedExchange64 (&ticks_per_sec, system_qpc_tickspersec ()); + InterlockedCompareExchange64 (&ticks_per_sec, system_qpc_tickspersec (), 0); } int @@ -77,6 +177,15 @@ clk_realtime_t::now (clockid_t clockid, struct timespec *ts) } int +clk_tai_t::now (clockid_t clockid, struct timespec *ts) +{ + init (); + clk_realtime_t::now (clockid, ts); + ts->tv_sec += leap_secs; + return 0; +} + +int clk_process_t::now (clockid_t clockid, struct timespec *ts) { pid_t pid = CLOCKID_TO_PID (clockid); @@ -241,6 +350,7 @@ clk_monotonic_t::resolution (struct timespec *ts) static clk_realtime_coarse_t clk_realtime_coarse; static clk_realtime_t clk_realtime; +static clk_tai_t clk_tai; static clk_process_t clk_process; static clk_thread_t clk_thread; static clk_monotonic_t clk_monotonic; @@ -262,6 +372,8 @@ clk_t *cyg_clock[MAX_CLOCKS] = &clk_boottime, &clk_realtime_alarm, &clk_boottime_alarm, + NULL, + &clk_tai, }; clk_t * |