xref: /freebsd-src/sys/contrib/ck/src/ck_ec_timeutil.h (revision 74e9b5f29ad0056bbe11a30c91dfa0705fa19cd5)
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