1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2024 Oxide Computer Company 14 */ 15 16 /* 17 * Test the various pthreads related clock based locking routines. These all 18 * attempt to take some form of lock and utilize a timeout that can be specified 19 * in terms of a given clock (i.e. CLOCK_REALTIME and CLOCK_HIGHRES). In 20 * particular we want to cover: 21 * 22 * - Invalid clock sources 23 * - Invalid timeouts ignored when acquired 24 * - Invalid timeouts caught when used 25 * - We can successfully get an ETIMEDOUT and that time has advanced at least 26 * that much 27 */ 28 29 #include <err.h> 30 #include <stdlib.h> 31 #include <pthread.h> 32 #include <sys/debug.h> 33 #include <sys/sysmacros.h> 34 #include <stdbool.h> 35 #include <errno.h> 36 #include <string.h> 37 38 #include "clock_lock.h" 39 40 /* 41 * This is a generic 100ms timeout that we can use. We use it for some tests 42 * that require an absolute timeout that is in the future but we don't want to 43 * bother computing. 44 */ 45 const struct timespec clock_to_100ms = { 0, MSEC2NSEC(100) }; 46 47 /* 48 * A series of invalid clocks. The first is usable for both relative and 49 * absolute operations. The others which use negative times should only fail for 50 * the relative operations at this time. 51 */ 52 const struct timespec clock_to_invns = { 0, NANOSEC * 2 }; 53 const struct timespec clock_to_invnegs = { -12345, 0 }; 54 const struct timespec clock_to_invnegns = { 100, -0x23 }; 55 56 void 57 clock_rel_to_abs(clockid_t clock, const struct timespec *restrict rel, 58 struct timespec *restrict abs) 59 { 60 if (clock_gettime(clock, abs) != 0) { 61 err(EXIT_FAILURE, "failed to get absolute time for clock %d", 62 clock); 63 } 64 65 abs->tv_nsec += rel->tv_nsec; 66 abs->tv_sec += rel->tv_sec; 67 if (abs->tv_nsec > NANOSEC) { 68 abs->tv_sec += abs->tv_nsec / NANOSEC; 69 abs->tv_nsec %= NANOSEC; 70 } 71 } 72 73 bool 74 clock_abs_after(clockid_t clock, const struct timespec *to) 75 { 76 struct timespec now; 77 78 if (clock_gettime(clock, &now) != 0) { 79 err(EXIT_FAILURE, "failed to get absolute time for clock %d", 80 clock); 81 } 82 83 if (now.tv_sec > to->tv_sec) 84 return (true); 85 86 return (now.tv_sec == to->tv_sec && now.tv_nsec > to->tv_nsec); 87 } 88 89 bool 90 clock_rel_after(clockid_t clock, const struct timespec *start, 91 const struct timespec *to) 92 { 93 struct timespec now, absto; 94 95 if (clock_gettime(clock, &now) != 0) { 96 err(EXIT_FAILURE, "failed to get absolute time for clock %d", 97 clock); 98 } 99 100 absto.tv_nsec = start->tv_nsec + to->tv_nsec; 101 absto.tv_sec = start->tv_sec + to->tv_sec; 102 if (absto.tv_nsec > NANOSEC) { 103 absto.tv_sec += absto.tv_nsec / NANOSEC; 104 absto.tv_nsec %= NANOSEC; 105 } 106 107 if (now.tv_sec > absto.tv_sec) 108 return (true); 109 110 return (now.tv_sec == absto.tv_sec && now.tv_nsec > absto.tv_nsec); 111 } 112 113 typedef struct { 114 const clock_test_t *cthr_test; 115 void *cthr_prim; 116 bool cthr_ret; 117 } clock_test_thr_t; 118 119 static void * 120 clock_test_thr(void *arg) 121 { 122 clock_test_thr_t *thr = arg; 123 thr->cthr_ret = thr->cthr_test->ct_test(thr->cthr_test, 124 thr->cthr_prim); 125 return (NULL); 126 } 127 128 static bool 129 clock_test_one(const clock_test_t *test) 130 { 131 void *prim; 132 bool ret; 133 134 test->ct_ops->lo_create(test->ct_desc, &prim); 135 136 /* 137 * If the test requires that the lock be held in some way, then we spawn 138 * the test to run in another thread to avoid any issues with recursive 139 * actions. Otherwise we let it run locally. 140 */ 141 if (test->ct_enter) { 142 clock_test_thr_t thr_test; 143 pthread_t thr; 144 int pret; 145 146 test->ct_ops->lo_lock(prim); 147 thr_test.cthr_test = test; 148 thr_test.cthr_prim = prim; 149 thr_test.cthr_ret = false; 150 151 if ((pret = pthread_create(&thr, NULL, clock_test_thr, 152 &thr_test)) != 0) { 153 errc(EXIT_FAILURE, pret, "TEST FAILED: %s: internal " 154 "error creating test thread", test->ct_desc); 155 } 156 157 if ((pret = pthread_join(thr, NULL)) != 0) { 158 errc(EXIT_FAILURE, pret, "TEST FAILED: %s: internal " 159 "error joining test thread", test->ct_desc); 160 } 161 ret = thr_test.cthr_ret; 162 test->ct_ops->lo_unlock(prim); 163 } else { 164 ret = test->ct_test(test, prim); 165 } 166 167 test->ct_ops->lo_destroy(prim); 168 169 if (ret) { 170 (void) printf("TEST PASSED: %s\n", test->ct_desc); 171 } 172 173 return (ret); 174 } 175 176 int 177 main(void) 178 { 179 int ret = EXIT_SUCCESS; 180 181 for (size_t i = 0; i < clock_mutex_ntests; i++) { 182 if (!clock_test_one(&clock_mutex_tests[i])) { 183 ret = EXIT_FAILURE; 184 } 185 } 186 187 for (size_t i = 0; i < clock_rwlock_ntests; i++) { 188 if (!clock_test_one(&clock_rwlock_tests[i])) { 189 ret = EXIT_FAILURE; 190 } 191 } 192 193 for (size_t i = 0; i < clock_sem_ntests; i++) { 194 if (!clock_test_one(&clock_sem_tests[i])) { 195 ret = EXIT_FAILURE; 196 } 197 } 198 199 for (size_t i = 0; i < clock_cond_ntests; i++) { 200 if (!clock_test_one(&clock_cond_tests[i])) { 201 ret = EXIT_FAILURE; 202 } 203 } 204 205 206 return (ret); 207 } 208