1 1.1 christos /* $NetBSD: mutex_test.c,v 1.2 2025/01/26 16:25:49 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 #include <isc/atomic.h> 29 1.1 christos #include <isc/file.h> 30 1.1 christos #include <isc/mem.h> 31 1.1 christos #include <isc/mutex.h> 32 1.1 christos #include <isc/os.h> 33 1.1 christos #include <isc/pause.h> 34 1.1 christos #include <isc/result.h> 35 1.1 christos #include <isc/stdio.h> 36 1.1 christos #include <isc/thread.h> 37 1.1 christos #include <isc/time.h> 38 1.1 christos #include <isc/util.h> 39 1.1 christos 40 1.1 christos #include <tests/isc.h> 41 1.1 christos 42 1.1 christos static unsigned int loops = 100; 43 1.1 christos static unsigned int delay_loop = 1; 44 1.1 christos 45 1.1 christos static int 46 1.1 christos setup_env(void **unused __attribute__((__unused__))) { 47 1.1 christos char *env = getenv("ISC_BENCHMARK_LOOPS"); 48 1.1 christos if (env != NULL) { 49 1.1 christos loops = atoi(env); 50 1.1 christos } 51 1.1 christos assert_int_not_equal(loops, 0); 52 1.1 christos 53 1.1 christos env = getenv("ISC_BENCHMARK_DELAY"); 54 1.1 christos if (env != NULL) { 55 1.1 christos delay_loop = atoi(env); 56 1.1 christos } 57 1.1 christos assert_int_not_equal(delay_loop, 0); 58 1.1 christos 59 1.1 christos return 0; 60 1.1 christos } 61 1.1 christos 62 1.1 christos ISC_RUN_TEST_IMPL(isc_mutex) { 63 1.1 christos isc_mutex_t lock; 64 1.1 christos 65 1.1 christos isc_mutex_init(&lock); 66 1.1 christos 67 1.1 christos for (size_t i = 0; i < loops; i++) { 68 1.1 christos isc_mutex_lock(&lock); 69 1.1 christos isc_pause_n(delay_loop); 70 1.1 christos isc_mutex_unlock(&lock); 71 1.1 christos } 72 1.1 christos 73 1.1 christos isc_mutex_destroy(&lock); 74 1.1 christos } 75 1.1 christos 76 1.1 christos #define ITERS 20 77 1.1 christos 78 1.1 christos #define DC 200 79 1.1 christos #define CNT_MIN 800 80 1.1 christos #define CNT_MAX 1600 81 1.1 christos 82 1.1 christos static size_t shared_counter = 0; 83 1.1 christos static size_t expected_counter = SIZE_MAX; 84 1.1 christos static isc_mutex_t lock; 85 1.1 christos static pthread_mutex_t mutex; 86 1.1 christos 87 1.1 christos static void * 88 1.1 christos pthread_mutex_thread(void *arg) { 89 1.1 christos size_t cont = *(size_t *)arg; 90 1.1 christos 91 1.1 christos for (size_t i = 0; i < loops; i++) { 92 1.1 christos pthread_mutex_lock(&mutex); 93 1.1 christos size_t v = shared_counter; 94 1.1 christos isc_pause_n(delay_loop); 95 1.1 christos shared_counter = v + 1; 96 1.1 christos pthread_mutex_unlock(&mutex); 97 1.1 christos isc_pause_n(cont); 98 1.1 christos } 99 1.1 christos 100 1.1 christos return NULL; 101 1.1 christos } 102 1.1 christos 103 1.1 christos static void * 104 1.1 christos isc_mutex_thread(void *arg) { 105 1.1 christos size_t cont = *(size_t *)arg; 106 1.1 christos 107 1.1 christos for (size_t i = 0; i < loops; i++) { 108 1.1 christos isc_mutex_lock(&lock); 109 1.1 christos size_t v = shared_counter; 110 1.1 christos isc_pause_n(delay_loop); 111 1.1 christos shared_counter = v + 1; 112 1.1 christos isc_mutex_unlock(&lock); 113 1.1 christos isc_pause_n(cont); 114 1.1 christos } 115 1.1 christos 116 1.1 christos return NULL; 117 1.1 christos } 118 1.1 christos 119 1.1 christos ISC_RUN_TEST_IMPL(isc_mutex_benchmark) { 120 1.1 christos isc_thread_t *threads = isc_mem_cget(mctx, workers, sizeof(*threads)); 121 1.1 christos isc_time_t ts1, ts2; 122 1.1 christos double t; 123 1.1 christos int dc; 124 1.1 christos size_t cont; 125 1.1 christos int r; 126 1.1 christos 127 1.1 christos memset(threads, 0, sizeof(*threads) * workers); 128 1.1 christos 129 1.1 christos expected_counter = ITERS * workers * loops * 130 1.1 christos ((CNT_MAX - CNT_MIN) / DC + 1); 131 1.1 christos 132 1.1 christos /* PTHREAD MUTEX */ 133 1.1 christos 134 1.1 christos r = pthread_mutex_init(&mutex, NULL); 135 1.1 christos assert_int_not_equal(r, -1); 136 1.1 christos 137 1.1 christos ts1 = isc_time_now_hires(); 138 1.1 christos 139 1.1 christos shared_counter = 0; 140 1.1 christos dc = DC; 141 1.1 christos for (size_t l = 0; l < ITERS; l++) { 142 1.1 christos for (cont = (dc > 0) ? CNT_MIN : CNT_MAX; 143 1.1 christos cont <= CNT_MAX && cont >= CNT_MIN; cont += dc) 144 1.1 christos { 145 1.1 christos for (size_t i = 0; i < workers; i++) { 146 1.1 christos isc_thread_create(pthread_mutex_thread, &cont, 147 1.1 christos &threads[i]); 148 1.1 christos } 149 1.1 christos for (size_t i = 0; i < workers; i++) { 150 1.1 christos isc_thread_join(threads[i], NULL); 151 1.1 christos } 152 1.1 christos } 153 1.1 christos dc = -dc; 154 1.1 christos } 155 1.1 christos assert_int_equal(shared_counter, expected_counter); 156 1.1 christos 157 1.1 christos ts2 = isc_time_now_hires(); 158 1.1 christos 159 1.1 christos t = isc_time_microdiff(&ts2, &ts1); 160 1.1 christos 161 1.1 christos printf("[ TIME ] isc_mutex_benchmark: %zu pthread_mutex " 162 1.1 christos "loops in " 163 1.1 christos "%u threads, %2.3f seconds, %2.3f calls/second\n", 164 1.1 christos shared_counter, workers, t / 1000000.0, 165 1.1 christos shared_counter / (t / 1000000.0)); 166 1.1 christos 167 1.1 christos r = pthread_mutex_destroy(&mutex); 168 1.1 christos assert_int_not_equal(r, -1); 169 1.1 christos 170 1.1 christos /* ISC MUTEX */ 171 1.1 christos 172 1.1 christos isc_mutex_init(&lock); 173 1.1 christos 174 1.1 christos ts1 = isc_time_now_hires(); 175 1.1 christos 176 1.1 christos dc = DC; 177 1.1 christos shared_counter = 0; 178 1.1 christos for (size_t l = 0; l < ITERS; l++) { 179 1.1 christos for (cont = (dc > 0) ? CNT_MIN : CNT_MAX; 180 1.1 christos cont <= CNT_MAX && cont >= CNT_MIN; cont += dc) 181 1.1 christos { 182 1.1 christos for (size_t i = 0; i < workers; i++) { 183 1.1 christos isc_thread_create(isc_mutex_thread, &cont, 184 1.1 christos &threads[i]); 185 1.1 christos } 186 1.1 christos for (size_t i = 0; i < workers; i++) { 187 1.1 christos isc_thread_join(threads[i], NULL); 188 1.1 christos } 189 1.1 christos } 190 1.1 christos dc = -dc; 191 1.1 christos } 192 1.1 christos assert_int_equal(shared_counter, expected_counter); 193 1.1 christos 194 1.1 christos ts2 = isc_time_now_hires(); 195 1.1 christos 196 1.1 christos t = isc_time_microdiff(&ts2, &ts1); 197 1.1 christos 198 1.1 christos printf("[ TIME ] isc_mutex_benchmark: %zu isc_mutex loops " 199 1.1 christos "in %u " 200 1.1 christos "threads, %2.3f seconds, %2.3f calls/second\n", 201 1.1 christos shared_counter, workers, t / 1000000.0, 202 1.1 christos shared_counter / (t / 1000000.0)); 203 1.1 christos 204 1.1 christos isc_mutex_destroy(&lock); 205 1.1 christos 206 1.1 christos isc_mem_cput(mctx, threads, workers, sizeof(*threads)); 207 1.1 christos } 208 1.1 christos 209 1.1 christos ISC_TEST_LIST_START 210 1.1 christos 211 1.1 christos ISC_TEST_ENTRY(isc_mutex) 212 1.1 christos #if !defined(__SANITIZE_THREAD__) 213 1.1 christos ISC_TEST_ENTRY(isc_mutex_benchmark) 214 1.1 christos #endif /* __SANITIZE_THREAD__ */ 215 1.1 christos 216 1.1 christos ISC_TEST_LIST_END 217 1.1 christos 218 1.1 christos ISC_TEST_MAIN_CUSTOM(setup_env, NULL) 219