mirror of
https://github.com/systemd/systemd.git
synced 2025-01-18 10:04:04 +03:00
Merge pull request #16536 from poettering/time-clock-map-fixes
time-util: clock mapping improvements
This commit is contained in:
commit
3bb4126262
@ -82,43 +82,82 @@ triple_timestamp* triple_timestamp_get(triple_timestamp *ts) {
|
||||
return ts;
|
||||
}
|
||||
|
||||
static usec_t map_clock_usec_internal(usec_t from, usec_t from_base, usec_t to_base) {
|
||||
|
||||
/* Maps the time 'from' between two clocks, based on a common reference point where the first clock
|
||||
* is at 'from_base' and the second clock at 'to_base'. Basically calculates:
|
||||
*
|
||||
* from - from_base + to_base
|
||||
*
|
||||
* But takes care of overflows/underflows and avoids signed operations. */
|
||||
|
||||
if (from >= from_base) { /* In the future */
|
||||
usec_t delta = from - from_base;
|
||||
|
||||
if (to_base >= USEC_INFINITY - delta) /* overflow? */
|
||||
return USEC_INFINITY;
|
||||
|
||||
return to_base + delta;
|
||||
|
||||
} else { /* In the past */
|
||||
usec_t delta = from_base - from;
|
||||
|
||||
if (to_base <= delta) /* underflow? */
|
||||
return 0;
|
||||
|
||||
return to_base - delta;
|
||||
}
|
||||
}
|
||||
|
||||
usec_t map_clock_usec(usec_t from, clockid_t from_clock, clockid_t to_clock) {
|
||||
|
||||
/* Try to avoid any inaccuracy needlessly added in case we convert from effectively the same clock
|
||||
* onto itself */
|
||||
if (map_clock_id(from_clock) == map_clock_id(to_clock))
|
||||
return from;
|
||||
|
||||
/* Keep infinity as is */
|
||||
if (from == USEC_INFINITY)
|
||||
return from;
|
||||
|
||||
return map_clock_usec_internal(from, now(from_clock), now(to_clock));
|
||||
}
|
||||
|
||||
dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
|
||||
int64_t delta;
|
||||
assert(ts);
|
||||
|
||||
if (u == USEC_INFINITY || u <= 0) {
|
||||
if (u == USEC_INFINITY || u == 0) {
|
||||
ts->realtime = ts->monotonic = u;
|
||||
return ts;
|
||||
}
|
||||
|
||||
ts->realtime = u;
|
||||
|
||||
delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
|
||||
ts->monotonic = usec_sub_signed(now(CLOCK_MONOTONIC), delta);
|
||||
|
||||
ts->monotonic = map_clock_usec(u, CLOCK_REALTIME, CLOCK_MONOTONIC);
|
||||
return ts;
|
||||
}
|
||||
|
||||
triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) {
|
||||
int64_t delta;
|
||||
usec_t nowr;
|
||||
|
||||
assert(ts);
|
||||
|
||||
if (u == USEC_INFINITY || u <= 0) {
|
||||
if (u == USEC_INFINITY || u == 0) {
|
||||
ts->realtime = ts->monotonic = ts->boottime = u;
|
||||
return ts;
|
||||
}
|
||||
|
||||
nowr = now(CLOCK_REALTIME);
|
||||
|
||||
ts->realtime = u;
|
||||
delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
|
||||
ts->monotonic = usec_sub_signed(now(CLOCK_MONOTONIC), delta);
|
||||
ts->boottime = clock_boottime_supported() ? usec_sub_signed(now(CLOCK_BOOTTIME), delta) : USEC_INFINITY;
|
||||
ts->monotonic = map_clock_usec_internal(u, nowr, now(CLOCK_MONOTONIC));
|
||||
ts->boottime = clock_boottime_supported() ?
|
||||
map_clock_usec_internal(u, nowr, now(CLOCK_BOOTTIME)) :
|
||||
USEC_INFINITY;
|
||||
|
||||
return ts;
|
||||
}
|
||||
|
||||
dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
|
||||
int64_t delta;
|
||||
assert(ts);
|
||||
|
||||
if (u == USEC_INFINITY) {
|
||||
@ -127,25 +166,28 @@ dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
|
||||
}
|
||||
|
||||
ts->monotonic = u;
|
||||
delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u;
|
||||
ts->realtime = usec_sub_signed(now(CLOCK_REALTIME), delta);
|
||||
|
||||
ts->realtime = map_clock_usec(u, CLOCK_MONOTONIC, CLOCK_REALTIME);
|
||||
return ts;
|
||||
}
|
||||
|
||||
dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) {
|
||||
int64_t delta;
|
||||
clockid_t cid;
|
||||
usec_t nowm;
|
||||
|
||||
if (u == USEC_INFINITY) {
|
||||
ts->realtime = ts->monotonic = USEC_INFINITY;
|
||||
return ts;
|
||||
}
|
||||
|
||||
dual_timestamp_get(ts);
|
||||
delta = (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u;
|
||||
ts->realtime = usec_sub_signed(ts->realtime, delta);
|
||||
ts->monotonic = usec_sub_signed(ts->monotonic, delta);
|
||||
cid = clock_boottime_or_monotonic();
|
||||
nowm = now(cid);
|
||||
|
||||
if (cid == CLOCK_MONOTONIC)
|
||||
ts->monotonic = u;
|
||||
else
|
||||
ts->monotonic = map_clock_usec_internal(u, nowm, now(CLOCK_MONOTONIC));
|
||||
|
||||
ts->realtime = map_clock_usec_internal(u, nowm, now(CLOCK_REALTIME));
|
||||
return ts;
|
||||
}
|
||||
|
||||
|
@ -29,8 +29,8 @@ typedef struct triple_timestamp {
|
||||
usec_t boottime;
|
||||
} triple_timestamp;
|
||||
|
||||
#define USEC_INFINITY ((usec_t) -1)
|
||||
#define NSEC_INFINITY ((nsec_t) -1)
|
||||
#define USEC_INFINITY ((usec_t) UINT64_MAX)
|
||||
#define NSEC_INFINITY ((nsec_t) UINT64_MAX)
|
||||
|
||||
#define MSEC_PER_SEC 1000ULL
|
||||
#define USEC_PER_SEC ((usec_t) 1000000ULL)
|
||||
@ -67,6 +67,8 @@ typedef struct triple_timestamp {
|
||||
usec_t now(clockid_t clock);
|
||||
nsec_t now_nsec(clockid_t clock);
|
||||
|
||||
usec_t map_clock_usec(usec_t from, clockid_t from_clock, clockid_t to_clock);
|
||||
|
||||
dual_timestamp* dual_timestamp_get(dual_timestamp *ts);
|
||||
dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u);
|
||||
dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u);
|
||||
|
@ -346,7 +346,6 @@ static void timer_enter_waiting(Timer *t, bool time_change) {
|
||||
bool found_monotonic = false, found_realtime = false;
|
||||
bool leave_around = false;
|
||||
triple_timestamp ts;
|
||||
dual_timestamp dts;
|
||||
TimerValue *v;
|
||||
Unit *trigger;
|
||||
int r;
|
||||
@ -368,7 +367,7 @@ static void timer_enter_waiting(Timer *t, bool time_change) {
|
||||
continue;
|
||||
|
||||
if (v->base == TIMER_CALENDAR) {
|
||||
usec_t b;
|
||||
usec_t b, rebased;
|
||||
|
||||
/* If we know the last time this was
|
||||
* triggered, schedule the job based relative
|
||||
@ -388,12 +387,14 @@ static void timer_enter_waiting(Timer *t, bool time_change) {
|
||||
if (r < 0)
|
||||
continue;
|
||||
|
||||
/* To make the delay due to RandomizedDelaySec= work even at boot,
|
||||
* if the scheduled time has already passed, set the time when systemd
|
||||
* first started as the scheduled time. */
|
||||
dual_timestamp_from_monotonic(&dts, UNIT(t)->manager->timestamps[MANAGER_TIMESTAMP_USERSPACE].monotonic);
|
||||
if (v->next_elapse < dts.realtime)
|
||||
v->next_elapse = dts.realtime;
|
||||
/* To make the delay due to RandomizedDelaySec= work even at boot, if the scheduled
|
||||
* time has already passed, set the time when systemd first started as the scheduled
|
||||
* time. Note that we base this on the monotonic timestamp of the boot, not the
|
||||
* realtime one, since the wallclock might have been off during boot. */
|
||||
rebased = map_clock_usec(UNIT(t)->manager->timestamps[MANAGER_TIMESTAMP_USERSPACE].monotonic,
|
||||
CLOCK_MONOTONIC, CLOCK_REALTIME);
|
||||
if (v->next_elapse < rebased)
|
||||
v->next_elapse = rebased;
|
||||
|
||||
if (!found_realtime)
|
||||
t->next_elapse_realtime = v->next_elapse;
|
||||
|
@ -483,6 +483,38 @@ static void test_in_utc_timezone(void) {
|
||||
assert_se(unsetenv("TZ") >= 0);
|
||||
}
|
||||
|
||||
static void test_map_clock_usec(void) {
|
||||
usec_t nowr, x, y, z;
|
||||
|
||||
log_info("/* %s */", __func__);
|
||||
nowr = now(CLOCK_REALTIME);
|
||||
|
||||
x = nowr; /* right now */
|
||||
y = map_clock_usec(x, CLOCK_REALTIME, CLOCK_MONOTONIC);
|
||||
z = map_clock_usec(y, CLOCK_MONOTONIC, CLOCK_REALTIME);
|
||||
/* Converting forth and back will introduce inaccuracies, since we cannot query both clocks atomically, but it should be small. Even on the slowest CI smaller than 1h */
|
||||
|
||||
assert_se((z > x ? z - x : x - z) < USEC_PER_HOUR);
|
||||
|
||||
assert_se(nowr < USEC_INFINITY - USEC_PER_DAY*7); /* overflow check */
|
||||
x = nowr + USEC_PER_DAY*7; /* 1 week from now */
|
||||
y = map_clock_usec(x, CLOCK_REALTIME, CLOCK_MONOTONIC);
|
||||
assert_se(y > 0 && y < USEC_INFINITY);
|
||||
z = map_clock_usec(y, CLOCK_MONOTONIC, CLOCK_REALTIME);
|
||||
assert_se(z > 0 && z < USEC_INFINITY);
|
||||
assert_se((z > x ? z - x : x - z) < USEC_PER_HOUR);
|
||||
|
||||
assert_se(nowr > USEC_PER_DAY * 7); /* underflow check */
|
||||
x = nowr - USEC_PER_DAY*7; /* 1 week ago */
|
||||
y = map_clock_usec(x, CLOCK_REALTIME, CLOCK_MONOTONIC);
|
||||
if (y != 0) { /* might underflow if machine is not up long enough for the monotonic clock to be beyond 1w */
|
||||
assert_se(y < USEC_INFINITY);
|
||||
z = map_clock_usec(y, CLOCK_MONOTONIC, CLOCK_REALTIME);
|
||||
assert_se(z > 0 && z < USEC_INFINITY);
|
||||
assert_se((z > x ? z - x : x - z) < USEC_PER_HOUR);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
test_setup_logging(LOG_INFO);
|
||||
|
||||
@ -511,6 +543,7 @@ int main(int argc, char *argv[]) {
|
||||
test_deserialize_dual_timestamp();
|
||||
test_usec_shift_clock();
|
||||
test_in_utc_timezone();
|
||||
test_map_clock_usec();
|
||||
|
||||
/* Ensure time_t is signed */
|
||||
assert_cc((time_t) -1 < (time_t) 1);
|
||||
|
Loading…
x
Reference in New Issue
Block a user