time-util: add triple timestamp object

We already have a double timestamp object that we use whenever we need both a
MONOTONIC and a REALTIME timestamp taken and stored. With this change we
also add a triple timestamp object that in addition stores a BOOTTIME
timestamp, which is useful for a few usecases.

Note that we keep dual_timestamp around, as it is useful in many cases where
triple_timestamp is not, in particular because retrieving the monotonic and
realtime timestamps is much cheaper on Linux that getting the boottime
timestamp.
This commit is contained in:
Lennart Poettering 2016-05-30 22:08:21 +02:00
parent b2bb19bbda
commit fe624c4c07
2 changed files with 98 additions and 1 deletions

View file

@ -87,6 +87,16 @@ dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
return ts;
}
triple_timestamp* triple_timestamp_get(triple_timestamp *ts) {
assert(ts);
ts->realtime = now(CLOCK_REALTIME);
ts->monotonic = now(CLOCK_MONOTONIC);
ts->boottime = clock_boottime_supported() ? now(CLOCK_BOOTTIME) : USEC_INFINITY;
return ts;
}
dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
int64_t delta;
assert(ts);
@ -104,6 +114,24 @@ dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
return ts;
}
triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) {
int64_t delta;
assert(ts);
if (u == USEC_INFINITY || u <= 0) {
ts->realtime = ts->monotonic = ts->boottime = u;
return ts;
}
ts->realtime = u;
delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
ts->monotonic = usec_sub(now(CLOCK_MONOTONIC), delta);
ts->boottime = clock_boottime_supported() ? usec_sub(now(CLOCK_BOOTTIME), delta) : USEC_INFINITY;
return ts;
}
dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
int64_t delta;
assert(ts);
@ -136,6 +164,26 @@ dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, us
return ts;
}
usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock) {
switch (clock) {
case CLOCK_REALTIME:
case CLOCK_REALTIME_ALARM:
return ts->realtime;
case CLOCK_MONOTONIC:
return ts->monotonic;
case CLOCK_BOOTTIME:
case CLOCK_BOOTTIME_ALARM:
return ts->boottime;
default:
return USEC_INFINITY;
}
}
usec_t timespec_load(const struct timespec *ts) {
assert(ts);
@ -1107,6 +1155,30 @@ clockid_t clock_boottime_or_monotonic(void) {
return CLOCK_MONOTONIC;
}
bool clock_supported(clockid_t clock) {
struct timespec ts;
switch (clock) {
case CLOCK_MONOTONIC:
case CLOCK_REALTIME:
return true;
case CLOCK_BOOTTIME:
return clock_boottime_supported();
case CLOCK_BOOTTIME_ALARM:
if (!clock_boottime_supported())
return false;
/* fall through, after checking the cached value for CLOCK_BOOTTIME. */
default:
/* For everything else, check properly */
return clock_gettime(clock, &ts) >= 0;
}
}
int get_timezone(char **tz) {
_cleanup_free_ char *t = NULL;
const char *e;

View file

@ -39,6 +39,12 @@ typedef struct dual_timestamp {
usec_t monotonic;
} dual_timestamp;
typedef struct triple_timestamp {
usec_t realtime;
usec_t monotonic;
usec_t boottime;
} triple_timestamp;
#define USEC_INFINITY ((usec_t) -1)
#define NSEC_INFINITY ((nsec_t) -1)
@ -69,7 +75,8 @@ typedef struct dual_timestamp {
#define TIME_T_MAX (time_t)((UINTMAX_C(1) << ((sizeof(time_t) << 3) - 1)) - 1)
#define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) { 0ULL, 0ULL })
#define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) {})
#define TRIPLE_TIMESTAMP_NULL ((struct triple_timestamp) {})
usec_t now(clockid_t clock);
nsec_t now_nsec(clockid_t clock);
@ -79,11 +86,28 @@ dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u);
dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u);
dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u);
triple_timestamp* triple_timestamp_get(triple_timestamp *ts);
triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u);
#define DUAL_TIMESTAMP_HAS_CLOCK(clock) \
IN_SET(clock, CLOCK_REALTIME, CLOCK_REALTIME_ALARM, CLOCK_MONOTONIC)
#define TRIPLE_TIMESTAMP_HAS_CLOCK(clock) \
IN_SET(clock, CLOCK_REALTIME, CLOCK_REALTIME_ALARM, CLOCK_MONOTONIC, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM)
static inline bool dual_timestamp_is_set(dual_timestamp *ts) {
return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) ||
(ts->monotonic > 0 && ts->monotonic != USEC_INFINITY));
}
static inline bool triple_timestamp_is_set(triple_timestamp *ts) {
return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) ||
(ts->monotonic > 0 && ts->monotonic != USEC_INFINITY) ||
(ts->boottime > 0 && ts->boottime != USEC_INFINITY));
}
usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock);
usec_t timespec_load(const struct timespec *ts) _pure_;
struct timespec *timespec_store(struct timespec *ts, usec_t u);
@ -113,6 +137,7 @@ int get_timezones(char ***l);
bool timezone_is_valid(const char *name);
bool clock_boottime_supported(void);
bool clock_supported(clockid_t clock);
clockid_t clock_boottime_or_monotonic(void);
#define xstrftime(buf, fmt, tm) \