This isn’t a question necessarily about kotlinx-da...
# kotlinx-datetime
k
This isn’t a question necessarily about kotlinx-datetime, but I expect this to be the best place to ask. How can I detect when `Clock.System`’s underlying data provider has abruptly changed? I’m trying to mitigate against time drift in one of my projects, and knowing when a NTP sync occurs would be useful. Trying to search for this online has been fairly fruitless. In my project Cardiologist, I want to cancel and re-run a suspending
delay
when an NTP sync occurs.
k
I guess one way might be to monitor the difference between
System.currentTimeMillis()
and
System.nanoTime()
.
d
You can launch a separate thread/coroutine like
Copy code
var lastObservedInstant = Clock.System.now()
while (true) {
  delay(1.seconds)
  val newInstant = Clock.System.now()
  val duration = newInstant - lastObservedInstant
  if (/* duration differs too much from one second */) {
    notifyAboutDrift()
  }
}
👀 1
k
The problem is NNTP may change the system clock very gradually, e.g. by a few nanoseconds every second over the next few hours until the right time is achieved. This kind of check won't catch that.
🤔 1
k
Perhaps a better way to phrase the question is "how does the cron utility deal with time drift?" Scheduling a job that occurs the next month surely has to deal with NTP sync. I've read the cron works by calculating the amount of time a thread should sleep until the next job, so it's a conceptually similar implementation. I just haven't been able to dig up how it's actually done
k
> "cron works by calculating the amount of time a thread should sleep" But conversely, think about how the internals of the operating system implements "sleep". I think it typically calculates the scheduled wakeup time and puts the thread in a non-runnable queue until that time comes around.
k
Do you have a source on that claim? I'm pretty sure
sleep
uses monotonic time.
``` POSIX.1 specifies that nanosleep() should measure time against
the CLOCK_REALTIME clock. However, Linux measures the time using
the CLOCK_MONOTONIC clock. This probably does not matter, since
the POSIX.1 specification for clock_settime(2) says that
discontinuous changes in CLOCK_REALTIME should not affect
nanosleep():
Setting the value of the CLOCK_REALTIME clock via
clock_settime(2) shall have no effect on threads that are
blocked waiting for a relative time service based upon
this clock, including the nanosleep() function; ...
Consequently, these time services shall expire when the
requested relative interval elapses, independently of the
new or old value of the clock.```
https://www.man7.org/linux/man-pages/man2/nanosleep.2.html
k
That was from memory of reading some textbook, but this "scheduled wakeup time" could be based on the monotonic clock and indeed it is in POSIX.
k
Right, since monotonic time is always increasing then utilities using
sleep
would also have to contend with NTP sync if they're trying to work against real time
Looks like I might want to take inspiration from here, but I am a bit hesitant considering this isn't a reference implementation of cron. https://github.com/cronie-crond/cronie/blob/adbd221f2ee1f12cad4924fb5a53f88346de885c/src/cron.c#L349-L480
Looks like it's checking for time drift in one minute increments
Copy code
int timeDiff;
		enum timejump wakeupKind;

		/* ... wait for the time (in minutes) to change ... */
		do {
			cron_sleep(timeRunning + 1, &database);
			set_time(FALSE);
		} while (!got_sigintterm && clockTime == timeRunning);
		if (got_sigintterm)
			break;
		timeRunning = clockTime;

		/*
		 * Calculate how the current time differs from our virtual
		 * clock.  Classify the change into one of 4 cases.
		 */
		timeDiff = timeRunning - virtualTime;
		check_orphans(&database);
So looks like @Dmitry Khalanskiy [JB]’s suggestion is the way to go!