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 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 */ 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 */ 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. */ 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 */ 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