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