aboutsummaryrefslogtreecommitdiff
path: root/winsup/cygwin/clock.cc
diff options
context:
space:
mode:
Diffstat (limited to 'winsup/cygwin/clock.cc')
-rw-r--r--winsup/cygwin/clock.cc124
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 *