1*74e9b5f2SOlivier Houchard #ifndef CK_EC_TIMEUTIL_H
2*74e9b5f2SOlivier Houchard #define CK_EC_TIMEUTIL_H
3*74e9b5f2SOlivier Houchard #include <ck_cc.h>
4*74e9b5f2SOlivier Houchard #include <ck_ec.h>
5*74e9b5f2SOlivier Houchard #include <ck_limits.h>
6*74e9b5f2SOlivier Houchard #include <ck_stdint.h>
7*74e9b5f2SOlivier Houchard #include <sys/time.h>
8*74e9b5f2SOlivier Houchard
9*74e9b5f2SOlivier Houchard #define TIME_MAX ((time_t)((1ULL << ((sizeof(time_t) * CHAR_BIT) - 1)) - 1))
10*74e9b5f2SOlivier Houchard #define NSEC_MAX ((1000L * 1000 * 1000) - 1)
11*74e9b5f2SOlivier Houchard
12*74e9b5f2SOlivier Houchard /*
13*74e9b5f2SOlivier Houchard * Approximates (nsec * multiplier) >> shift. Clamps to UINT32_MAX on
14*74e9b5f2SOlivier Houchard * overflow.
15*74e9b5f2SOlivier Houchard */
16*74e9b5f2SOlivier Houchard CK_CC_UNUSED static uint32_t
wait_time_scale(uint32_t nsec,uint32_t multiplier,unsigned int shift)17*74e9b5f2SOlivier Houchard wait_time_scale(uint32_t nsec,
18*74e9b5f2SOlivier Houchard uint32_t multiplier,
19*74e9b5f2SOlivier Houchard unsigned int shift)
20*74e9b5f2SOlivier Houchard {
21*74e9b5f2SOlivier Houchard uint64_t temp = (uint64_t)nsec * multiplier;
22*74e9b5f2SOlivier Houchard uint64_t max = (uint64_t)UINT32_MAX << shift;
23*74e9b5f2SOlivier Houchard
24*74e9b5f2SOlivier Houchard if (temp >= max) {
25*74e9b5f2SOlivier Houchard return UINT32_MAX;
26*74e9b5f2SOlivier Houchard }
27*74e9b5f2SOlivier Houchard
28*74e9b5f2SOlivier Houchard return temp >> shift;
29*74e9b5f2SOlivier Houchard }
30*74e9b5f2SOlivier Houchard
31*74e9b5f2SOlivier Houchard
32*74e9b5f2SOlivier Houchard /*
33*74e9b5f2SOlivier Houchard * Returns ts + ns. ns is clamped to at most 1 second. Clamps the
34*74e9b5f2SOlivier Houchard * return value to TIME_MAX, NSEC_MAX on overflow.
35*74e9b5f2SOlivier Houchard *
36*74e9b5f2SOlivier Houchard */
timespec_add_ns(const struct timespec ts,uint32_t ns)37*74e9b5f2SOlivier Houchard CK_CC_UNUSED static struct timespec timespec_add_ns(const struct timespec ts,
38*74e9b5f2SOlivier Houchard uint32_t ns)
39*74e9b5f2SOlivier Houchard {
40*74e9b5f2SOlivier Houchard struct timespec ret = {
41*74e9b5f2SOlivier Houchard .tv_sec = TIME_MAX,
42*74e9b5f2SOlivier Houchard .tv_nsec = NSEC_MAX
43*74e9b5f2SOlivier Houchard };
44*74e9b5f2SOlivier Houchard time_t sec;
45*74e9b5f2SOlivier Houchard uint32_t sum_ns;
46*74e9b5f2SOlivier Houchard
47*74e9b5f2SOlivier Houchard if (ns > (uint32_t)NSEC_MAX) {
48*74e9b5f2SOlivier Houchard if (ts.tv_sec >= TIME_MAX) {
49*74e9b5f2SOlivier Houchard return ret;
50*74e9b5f2SOlivier Houchard }
51*74e9b5f2SOlivier Houchard
52*74e9b5f2SOlivier Houchard ret.tv_sec = ts.tv_sec + 1;
53*74e9b5f2SOlivier Houchard ret.tv_nsec = ts.tv_nsec;
54*74e9b5f2SOlivier Houchard return ret;
55*74e9b5f2SOlivier Houchard }
56*74e9b5f2SOlivier Houchard
57*74e9b5f2SOlivier Houchard sec = ts.tv_sec;
58*74e9b5f2SOlivier Houchard sum_ns = ns + ts.tv_nsec;
59*74e9b5f2SOlivier Houchard if (sum_ns > NSEC_MAX) {
60*74e9b5f2SOlivier Houchard if (sec >= TIME_MAX) {
61*74e9b5f2SOlivier Houchard return ret;
62*74e9b5f2SOlivier Houchard }
63*74e9b5f2SOlivier Houchard
64*74e9b5f2SOlivier Houchard sec++;
65*74e9b5f2SOlivier Houchard sum_ns -= (NSEC_MAX + 1);
66*74e9b5f2SOlivier Houchard }
67*74e9b5f2SOlivier Houchard
68*74e9b5f2SOlivier Houchard ret.tv_sec = sec;
69*74e9b5f2SOlivier Houchard ret.tv_nsec = sum_ns;
70*74e9b5f2SOlivier Houchard return ret;
71*74e9b5f2SOlivier Houchard }
72*74e9b5f2SOlivier Houchard
73*74e9b5f2SOlivier Houchard
74*74e9b5f2SOlivier Houchard /*
75*74e9b5f2SOlivier Houchard * Returns ts + inc. If inc is negative, it is normalized to 0.
76*74e9b5f2SOlivier Houchard * Clamps the return value to TIME_MAX, NSEC_MAX on overflow.
77*74e9b5f2SOlivier Houchard */
timespec_add(const struct timespec ts,const struct timespec inc)78*74e9b5f2SOlivier Houchard CK_CC_UNUSED static struct timespec timespec_add(const struct timespec ts,
79*74e9b5f2SOlivier Houchard const struct timespec inc)
80*74e9b5f2SOlivier Houchard {
81*74e9b5f2SOlivier Houchard /* Initial return value is clamped to infinite future. */
82*74e9b5f2SOlivier Houchard struct timespec ret = {
83*74e9b5f2SOlivier Houchard .tv_sec = TIME_MAX,
84*74e9b5f2SOlivier Houchard .tv_nsec = NSEC_MAX
85*74e9b5f2SOlivier Houchard };
86*74e9b5f2SOlivier Houchard time_t sec;
87*74e9b5f2SOlivier Houchard unsigned long nsec;
88*74e9b5f2SOlivier Houchard
89*74e9b5f2SOlivier Houchard /* Non-positive delta is a no-op. Invalid nsec is another no-op. */
90*74e9b5f2SOlivier Houchard if (inc.tv_sec < 0 || inc.tv_nsec < 0 || inc.tv_nsec > NSEC_MAX) {
91*74e9b5f2SOlivier Houchard return ts;
92*74e9b5f2SOlivier Houchard }
93*74e9b5f2SOlivier Houchard
94*74e9b5f2SOlivier Houchard /* Detect overflow early. */
95*74e9b5f2SOlivier Houchard if (inc.tv_sec > TIME_MAX - ts.tv_sec) {
96*74e9b5f2SOlivier Houchard return ret;
97*74e9b5f2SOlivier Houchard }
98*74e9b5f2SOlivier Houchard
99*74e9b5f2SOlivier Houchard sec = ts.tv_sec + inc.tv_sec;
100*74e9b5f2SOlivier Houchard /* This sum can't overflow if the inputs are valid.*/
101*74e9b5f2SOlivier Houchard nsec = (unsigned long)ts.tv_nsec + inc.tv_nsec;
102*74e9b5f2SOlivier Houchard
103*74e9b5f2SOlivier Houchard if (nsec > NSEC_MAX) {
104*74e9b5f2SOlivier Houchard if (sec >= TIME_MAX) {
105*74e9b5f2SOlivier Houchard return ret;
106*74e9b5f2SOlivier Houchard }
107*74e9b5f2SOlivier Houchard
108*74e9b5f2SOlivier Houchard sec++;
109*74e9b5f2SOlivier Houchard nsec -= (NSEC_MAX + 1);
110*74e9b5f2SOlivier Houchard }
111*74e9b5f2SOlivier Houchard
112*74e9b5f2SOlivier Houchard ret.tv_sec = sec;
113*74e9b5f2SOlivier Houchard ret.tv_nsec = nsec;
114*74e9b5f2SOlivier Houchard return ret;
115*74e9b5f2SOlivier Houchard }
116*74e9b5f2SOlivier Houchard
117*74e9b5f2SOlivier Houchard /* Compares two timespecs. Returns -1 if x < y, 0 if x == y, and 1 if x > y. */
timespec_cmp(const struct timespec x,const struct timespec y)118*74e9b5f2SOlivier Houchard CK_CC_UNUSED static int timespec_cmp(const struct timespec x,
119*74e9b5f2SOlivier Houchard const struct timespec y)
120*74e9b5f2SOlivier Houchard {
121*74e9b5f2SOlivier Houchard if (x.tv_sec != y.tv_sec) {
122*74e9b5f2SOlivier Houchard return (x.tv_sec < y.tv_sec) ? -1 : 1;
123*74e9b5f2SOlivier Houchard }
124*74e9b5f2SOlivier Houchard
125*74e9b5f2SOlivier Houchard if (x.tv_nsec != y.tv_nsec) {
126*74e9b5f2SOlivier Houchard return (x.tv_nsec < y.tv_nsec) ? -1 : 1;
127*74e9b5f2SOlivier Houchard }
128*74e9b5f2SOlivier Houchard
129*74e9b5f2SOlivier Houchard return 0;
130*74e9b5f2SOlivier Houchard }
131*74e9b5f2SOlivier Houchard
132*74e9b5f2SOlivier Houchard /*
133*74e9b5f2SOlivier Houchard * Overwrites now with the current CLOCK_MONOTONIC time, and returns
134*74e9b5f2SOlivier Houchard * true if the current time is greater than or equal to the deadline,
135*74e9b5f2SOlivier Houchard * or the clock is somehow broken.
136*74e9b5f2SOlivier Houchard */
check_deadline(struct timespec * now,const struct ck_ec_ops * ops,const struct timespec deadline)137*74e9b5f2SOlivier Houchard CK_CC_UNUSED static bool check_deadline(struct timespec *now,
138*74e9b5f2SOlivier Houchard const struct ck_ec_ops *ops,
139*74e9b5f2SOlivier Houchard const struct timespec deadline)
140*74e9b5f2SOlivier Houchard {
141*74e9b5f2SOlivier Houchard int r;
142*74e9b5f2SOlivier Houchard
143*74e9b5f2SOlivier Houchard r = ops->gettime(ops, now);
144*74e9b5f2SOlivier Houchard if (r != 0) {
145*74e9b5f2SOlivier Houchard return true;
146*74e9b5f2SOlivier Houchard }
147*74e9b5f2SOlivier Houchard
148*74e9b5f2SOlivier Houchard return timespec_cmp(*now, deadline) >= 0;
149*74e9b5f2SOlivier Houchard }
150*74e9b5f2SOlivier Houchard #endif /* !CK_EC_TIMEUTIL_H */
151