1 1.3 christos /* $NetBSD: spinlock_test.c,v 1.3 2026/04/08 00:16:17 christos Exp $ */ 2 1.1 christos 3 1.1 christos /* 4 1.1 christos * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 1.1 christos * 6 1.1 christos * SPDX-License-Identifier: MPL-2.0 7 1.1 christos * 8 1.1 christos * This Source Code Form is subject to the terms of the Mozilla Public 9 1.1 christos * License, v. 2.0. If a copy of the MPL was not distributed with this 10 1.1 christos * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 1.1 christos * 12 1.1 christos * See the COPYRIGHT file distributed with this work for additional 13 1.1 christos * information regarding copyright ownership. 14 1.1 christos */ 15 1.1 christos 16 1.1 christos #include <fcntl.h> 17 1.1 christos #include <inttypes.h> 18 1.1 christos #include <sched.h> /* IWYU pragma: keep */ 19 1.1 christos #include <setjmp.h> 20 1.1 christos #include <stdarg.h> 21 1.1 christos #include <stddef.h> 22 1.1 christos #include <stdlib.h> 23 1.1 christos #include <unistd.h> 24 1.1 christos 25 1.1 christos #define UNIT_TESTING 26 1.1 christos #include <cmocka.h> 27 1.1 christos 28 1.1 christos #ifdef HAVE_PTHREAD_SPIN_INIT 29 1.1 christos #define HAD_PTHREAD_SPIN_INIT 1 30 1.1 christos #undef HAVE_PTHREAD_SPIN_INIT 31 1.1 christos #endif 32 1.1 christos 33 1.1 christos #include <isc/atomic.h> 34 1.1 christos #include <isc/file.h> 35 1.1 christos #include <isc/mem.h> 36 1.1 christos #include <isc/os.h> 37 1.1 christos #include <isc/pause.h> 38 1.1 christos #include <isc/result.h> 39 1.1 christos #include <isc/spinlock.h> 40 1.1 christos #include <isc/stdio.h> 41 1.1 christos #include <isc/thread.h> 42 1.1 christos #include <isc/time.h> 43 1.1 christos #include <isc/util.h> 44 1.1 christos 45 1.1 christos #include <tests/isc.h> 46 1.1 christos 47 1.1 christos static unsigned int loops = 100; 48 1.1 christos static unsigned int delay_loop = 1; 49 1.1 christos 50 1.1 christos static int 51 1.1 christos setup_env(void **unused __attribute__((__unused__))) { 52 1.1 christos char *env = getenv("ISC_BENCHMARK_LOOPS"); 53 1.1 christos if (env != NULL) { 54 1.1 christos loops = atoi(env); 55 1.1 christos } 56 1.1 christos assert_int_not_equal(loops, 0); 57 1.1 christos 58 1.1 christos env = getenv("ISC_BENCHMARK_DELAY"); 59 1.1 christos if (env != NULL) { 60 1.1 christos delay_loop = atoi(env); 61 1.1 christos } 62 1.1 christos assert_int_not_equal(delay_loop, 0); 63 1.1 christos 64 1.1 christos return 0; 65 1.1 christos } 66 1.1 christos 67 1.1 christos ISC_RUN_TEST_IMPL(isc_spinlock) { 68 1.1 christos isc_spinlock_t lock; 69 1.1 christos 70 1.1 christos isc_spinlock_init(&lock); 71 1.1 christos 72 1.1 christos for (size_t i = 0; i < loops; i++) { 73 1.1 christos isc_spinlock_lock(&lock); 74 1.1 christos isc_pause_n(delay_loop); 75 1.1 christos isc_spinlock_unlock(&lock); 76 1.1 christos } 77 1.1 christos 78 1.1 christos isc_spinlock_destroy(&lock); 79 1.1 christos } 80 1.1 christos 81 1.1 christos #define ITERS 20 82 1.1 christos 83 1.1 christos #define DC 200 84 1.1 christos #define CNT_MIN 800 85 1.1 christos #define CNT_MAX 1600 86 1.1 christos 87 1.3 christos #if !defined(__SANITIZE_THREAD__) 88 1.1 christos static size_t shared_counter = 0; 89 1.1 christos static size_t expected_counter = SIZE_MAX; 90 1.1 christos 91 1.1 christos #if HAD_PTHREAD_SPIN_INIT 92 1.1 christos static pthread_spinlock_t spin; 93 1.1 christos 94 1.1 christos static void * 95 1.1 christos pthread_spin_thread(void *arg) { 96 1.1 christos size_t cont = *(size_t *)arg; 97 1.1 christos 98 1.1 christos for (size_t i = 0; i < loops; i++) { 99 1.1 christos pthread_spin_lock(&spin); 100 1.1 christos size_t v = shared_counter; 101 1.1 christos isc_pause_n(delay_loop); 102 1.1 christos shared_counter = v + 1; 103 1.1 christos pthread_spin_unlock(&spin); 104 1.1 christos isc_pause_n(cont); 105 1.1 christos } 106 1.1 christos 107 1.1 christos return NULL; 108 1.1 christos } 109 1.1 christos #endif 110 1.1 christos 111 1.1 christos static isc_spinlock_t lock; 112 1.1 christos 113 1.1 christos static void * 114 1.1 christos isc_spinlock_thread(void *arg) { 115 1.1 christos size_t cont = *(size_t *)arg; 116 1.1 christos 117 1.1 christos for (size_t i = 0; i < loops; i++) { 118 1.1 christos isc_spinlock_lock(&lock); 119 1.1 christos size_t v = shared_counter; 120 1.1 christos isc_pause_n(delay_loop); 121 1.1 christos shared_counter = v + 1; 122 1.1 christos isc_spinlock_unlock(&lock); 123 1.1 christos isc_pause_n(cont); 124 1.1 christos } 125 1.1 christos 126 1.1 christos return NULL; 127 1.1 christos } 128 1.1 christos 129 1.1 christos ISC_RUN_TEST_IMPL(isc_spinlock_benchmark) { 130 1.1 christos isc_thread_t *threads = isc_mem_cget(mctx, workers, sizeof(*threads)); 131 1.1 christos isc_time_t ts1, ts2; 132 1.1 christos double t; 133 1.1 christos int dc; 134 1.1 christos size_t cont; 135 1.1 christos 136 1.1 christos memset(threads, 0, sizeof(*threads) * workers); 137 1.1 christos 138 1.1 christos expected_counter = ITERS * workers * loops * 139 1.1 christos ((CNT_MAX - CNT_MIN) / DC + 1); 140 1.1 christos 141 1.1 christos /* PTHREAD SPINLOCK */ 142 1.1 christos 143 1.1 christos #if HAD_PTHREAD_SPIN_INIT 144 1.1 christos int r = pthread_spin_init(&spin, PTHREAD_PROCESS_PRIVATE); 145 1.1 christos assert_int_not_equal(r, -1); 146 1.1 christos 147 1.1 christos ts1 = isc_time_now_hires(); 148 1.1 christos 149 1.1 christos shared_counter = 0; 150 1.1 christos dc = DC; 151 1.1 christos for (size_t l = 0; l < ITERS; l++) { 152 1.1 christos for (cont = (dc > 0) ? CNT_MIN : CNT_MAX; 153 1.1 christos cont <= CNT_MAX && cont >= CNT_MIN; cont += dc) 154 1.1 christos { 155 1.1 christos for (size_t i = 0; i < workers; i++) { 156 1.1 christos isc_thread_create(pthread_spin_thread, &cont, 157 1.1 christos &threads[i]); 158 1.1 christos } 159 1.1 christos for (size_t i = 0; i < workers; i++) { 160 1.1 christos isc_thread_join(threads[i], NULL); 161 1.1 christos } 162 1.1 christos } 163 1.1 christos dc = -dc; 164 1.1 christos } 165 1.1 christos assert_int_equal(shared_counter, expected_counter); 166 1.1 christos 167 1.1 christos ts2 = isc_time_now_hires(); 168 1.1 christos 169 1.1 christos t = isc_time_microdiff(&ts2, &ts1); 170 1.1 christos 171 1.1 christos printf("[ TIME ] isc_spinlock_benchmark: %zu pthread_spin " 172 1.1 christos "loops in " 173 1.1 christos "%u threads, %2.3f seconds, %2.3f calls/second\n", 174 1.1 christos shared_counter, workers, t / 1000000.0, 175 1.1 christos shared_counter / (t / 1000000.0)); 176 1.1 christos 177 1.1 christos r = pthread_spin_destroy(&spin); 178 1.1 christos assert_int_not_equal(r, -1); 179 1.1 christos #endif 180 1.1 christos 181 1.1 christos /* ISC SPINLOCK */ 182 1.1 christos 183 1.1 christos isc_spinlock_init(&lock); 184 1.1 christos 185 1.1 christos ts1 = isc_time_now_hires(); 186 1.1 christos 187 1.1 christos dc = DC; 188 1.1 christos shared_counter = 0; 189 1.1 christos for (size_t l = 0; l < ITERS; l++) { 190 1.1 christos for (cont = (dc > 0) ? CNT_MIN : CNT_MAX; 191 1.1 christos cont <= CNT_MAX && cont >= CNT_MIN; cont += dc) 192 1.1 christos { 193 1.1 christos for (size_t i = 0; i < workers; i++) { 194 1.1 christos isc_thread_create(isc_spinlock_thread, &cont, 195 1.1 christos &threads[i]); 196 1.1 christos } 197 1.1 christos for (size_t i = 0; i < workers; i++) { 198 1.1 christos isc_thread_join(threads[i], NULL); 199 1.1 christos } 200 1.1 christos } 201 1.1 christos dc = -dc; 202 1.1 christos } 203 1.1 christos assert_int_equal(shared_counter, expected_counter); 204 1.1 christos 205 1.1 christos ts2 = isc_time_now_hires(); 206 1.1 christos 207 1.1 christos t = isc_time_microdiff(&ts2, &ts1); 208 1.1 christos 209 1.1 christos printf("[ TIME ] isc_spinlock_benchmark: %zu isc_spinlock loops " 210 1.1 christos "in %u " 211 1.1 christos "threads, %2.3f seconds, %2.3f calls/second\n", 212 1.1 christos shared_counter, workers, t / 1000000.0, 213 1.1 christos shared_counter / (t / 1000000.0)); 214 1.1 christos 215 1.1 christos isc_spinlock_destroy(&lock); 216 1.1 christos 217 1.1 christos isc_mem_cput(mctx, threads, workers, sizeof(*threads)); 218 1.1 christos } 219 1.3 christos #endif 220 1.1 christos 221 1.1 christos ISC_TEST_LIST_START 222 1.1 christos 223 1.1 christos ISC_TEST_ENTRY(isc_spinlock) 224 1.1 christos #if !defined(__SANITIZE_THREAD__) 225 1.1 christos ISC_TEST_ENTRY(isc_spinlock_benchmark) 226 1.1 christos #endif /* __SANITIZE_THREAD__ */ 227 1.1 christos 228 1.1 christos ISC_TEST_LIST_END 229 1.1 christos 230 1.1 christos ISC_TEST_MAIN_CUSTOM(setup_env, NULL) 231