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