1 1.1 christos // SPDX-FileCopyrightText: 2009 Mathieu Desnoyers <mathieu.desnoyers (at) efficios.com> 2 1.1 christos // 3 1.1 christos // SPDX-License-Identifier: GPL-2.0-or-later 4 1.1 christos 5 1.1 christos /* 6 1.1 christos * Userspace RCU library - test program 7 1.1 christos */ 8 1.1 christos 9 1.1 christos #include <stdio.h> 10 1.1 christos #include <pthread.h> 11 1.1 christos #include <stdlib.h> 12 1.1 christos #include <string.h> 13 1.1 christos #include <sys/types.h> 14 1.1 christos #include <sys/wait.h> 15 1.1 christos #include <unistd.h> 16 1.1 christos #include <stdio.h> 17 1.1 christos #include <errno.h> 18 1.1 christos 19 1.1 christos #include <urcu/arch.h> 20 1.1 christos #include <urcu/assert.h> 21 1.1 christos #include <urcu/tls-compat.h> 22 1.1 christos #include "thread-id.h" 23 1.1 christos 24 1.1 christos /* hardcoded number of CPUs */ 25 1.1 christos #define NR_CPUS 16384 26 1.1 christos 27 1.1 christos #ifndef DYNAMIC_LINK_TEST 28 1.1 christos #define _LGPL_SOURCE 29 1.1 christos #endif 30 1.1 christos #include <urcu.h> 31 1.1 christos 32 1.1 christos struct test_array { 33 1.1 christos int a; 34 1.1 christos }; 35 1.1 christos 36 1.1 christos /* 37 1.1 christos * static rwlock initializer is broken on Cygwin. Use runtime 38 1.1 christos * initialization. 39 1.1 christos */ 40 1.1 christos pthread_rwlock_t lock; 41 1.1 christos 42 1.1 christos static unsigned long wdelay; 43 1.1 christos 44 1.1 christos static volatile struct test_array test_array = { 8 }; 45 1.1 christos 46 1.1 christos static unsigned long duration; 47 1.1 christos 48 1.1 christos /* read-side C.S. duration, in loops */ 49 1.1 christos static unsigned long rduration; 50 1.1 christos 51 1.1 christos /* write-side C.S. duration, in loops */ 52 1.1 christos static unsigned long wduration; 53 1.1 christos 54 1.1 christos static inline void loop_sleep(unsigned long loops) 55 1.1 christos { 56 1.1 christos while (loops-- != 0) 57 1.1 christos caa_cpu_relax(); 58 1.1 christos } 59 1.1 christos 60 1.1 christos static int verbose_mode; 61 1.1 christos 62 1.1 christos #define printf_verbose(fmt, args...) \ 63 1.1 christos do { \ 64 1.1 christos if (verbose_mode) \ 65 1.1 christos printf(fmt, args); \ 66 1.1 christos } while (0) 67 1.1 christos 68 1.1 christos static unsigned int cpu_affinities[NR_CPUS]; 69 1.1 christos static unsigned int next_aff = 0; 70 1.1 christos static int use_affinity = 0; 71 1.1 christos 72 1.1 christos pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; 73 1.1 christos 74 1.1 christos static void set_affinity(void) 75 1.1 christos { 76 1.1 christos #ifdef HAVE_SCHED_SETAFFINITY 77 1.1 christos cpu_set_t mask; 78 1.1 christos int cpu, ret; 79 1.1 christos #endif /* HAVE_SCHED_SETAFFINITY */ 80 1.1 christos 81 1.1 christos if (!use_affinity) 82 1.1 christos return; 83 1.1 christos 84 1.1 christos #ifdef HAVE_SCHED_SETAFFINITY 85 1.1 christos ret = pthread_mutex_lock(&affinity_mutex); 86 1.1 christos if (ret) { 87 1.1 christos perror("Error in pthread mutex lock"); 88 1.1 christos exit(-1); 89 1.1 christos } 90 1.1 christos cpu = cpu_affinities[next_aff++]; 91 1.1 christos ret = pthread_mutex_unlock(&affinity_mutex); 92 1.1 christos if (ret) { 93 1.1 christos perror("Error in pthread mutex unlock"); 94 1.1 christos exit(-1); 95 1.1 christos } 96 1.1 christos 97 1.1 christos CPU_ZERO(&mask); 98 1.1 christos CPU_SET(cpu, &mask); 99 1.1 christos sched_setaffinity(0, sizeof(mask), &mask); 100 1.1 christos #endif /* HAVE_SCHED_SETAFFINITY */ 101 1.1 christos } 102 1.1 christos 103 1.1 christos static DEFINE_URCU_TLS(unsigned long long, nr_writes); 104 1.1 christos static DEFINE_URCU_TLS(unsigned long long, nr_reads); 105 1.1 christos 106 1.1 christos static unsigned int nr_readers; 107 1.1 christos static unsigned int nr_writers; 108 1.1 christos 109 1.1 christos pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER; 110 1.1 christos 111 1.1 christos static 112 1.1 christos void *thr_reader(void *_count) 113 1.1 christos { 114 1.1 christos unsigned long long *count = _count; 115 1.1 christos 116 1.1 christos printf_verbose("thread_begin %s, tid %lu\n", 117 1.1 christos "reader", urcu_get_thread_id()); 118 1.1 christos 119 1.1 christos set_affinity(); 120 1.1 christos 121 1.1 christos wait_until_go(); 122 1.1 christos 123 1.1 christos for (;;) { 124 1.1 christos int a, ret; 125 1.1 christos 126 1.1 christos ret = pthread_rwlock_rdlock(&lock); 127 1.1 christos if (ret) { 128 1.1 christos fprintf(stderr, "reader pthread_rwlock_rdlock: %s\n", strerror(ret)); 129 1.1 christos abort(); 130 1.1 christos } 131 1.1 christos 132 1.1 christos a = test_array.a; 133 1.1 christos urcu_posix_assert(a == 8); 134 1.1 christos if (caa_unlikely(rduration)) 135 1.1 christos loop_sleep(rduration); 136 1.1 christos 137 1.1 christos ret = pthread_rwlock_unlock(&lock); 138 1.1 christos if (ret) { 139 1.1 christos fprintf(stderr, "reader pthread_rwlock_unlock: %s\n", strerror(ret)); 140 1.1 christos abort(); 141 1.1 christos } 142 1.1 christos 143 1.1 christos URCU_TLS(nr_reads)++; 144 1.1 christos if (caa_unlikely(!test_duration_read())) 145 1.1 christos break; 146 1.1 christos } 147 1.1 christos 148 1.1 christos *count = URCU_TLS(nr_reads); 149 1.1 christos 150 1.1 christos printf_verbose("thread_end %s, tid %lu, count %llu\n", 151 1.1 christos "reader", urcu_get_thread_id(), *count); 152 1.1 christos return ((void*)1); 153 1.1 christos 154 1.1 christos } 155 1.1 christos 156 1.1 christos static 157 1.1 christos void *thr_writer(void *_count) 158 1.1 christos { 159 1.1 christos unsigned long long *count = _count; 160 1.1 christos 161 1.1 christos printf_verbose("thread_begin %s, tid %lu\n", 162 1.1 christos "writer", urcu_get_thread_id()); 163 1.1 christos 164 1.1 christos set_affinity(); 165 1.1 christos 166 1.1 christos wait_until_go(); 167 1.1 christos 168 1.1 christos for (;;) { 169 1.1 christos int ret; 170 1.1 christos 171 1.1 christos ret = pthread_rwlock_wrlock(&lock); 172 1.1 christos if (ret) { 173 1.1 christos fprintf(stderr, "writer pthread_rwlock_wrlock: %s\n", strerror(ret)); 174 1.1 christos abort(); 175 1.1 christos } 176 1.1 christos 177 1.1 christos test_array.a = 0; 178 1.1 christos test_array.a = 8; 179 1.1 christos if (caa_unlikely(wduration)) 180 1.1 christos loop_sleep(wduration); 181 1.1 christos 182 1.1 christos ret = pthread_rwlock_unlock(&lock); 183 1.1 christos if (ret) { 184 1.1 christos fprintf(stderr, "writer pthread_rwlock_unlock: %s\n", strerror(ret)); 185 1.1 christos abort(); 186 1.1 christos } 187 1.1 christos 188 1.1 christos URCU_TLS(nr_writes)++; 189 1.1 christos if (caa_unlikely(!test_duration_write())) 190 1.1 christos break; 191 1.1 christos if (caa_unlikely(wdelay)) 192 1.1 christos loop_sleep(wdelay); 193 1.1 christos } 194 1.1 christos *count = URCU_TLS(nr_writes); 195 1.1 christos 196 1.1 christos printf_verbose("thread_end %s, tid %lu, count %llu\n", 197 1.1 christos "writer", urcu_get_thread_id(), *count); 198 1.1 christos return ((void*)2); 199 1.1 christos } 200 1.1 christos 201 1.1 christos static 202 1.1 christos void show_usage(char **argv) 203 1.1 christos { 204 1.1 christos printf("Usage : %s nr_readers nr_writers duration (s) <OPTIONS>\n", 205 1.1 christos argv[0]); 206 1.1 christos printf("OPTIONS:\n"); 207 1.1 christos printf(" [-d delay] (writer period (us))\n"); 208 1.1 christos printf(" [-c duration] (reader C.S. duration (in loops))\n"); 209 1.1 christos printf(" [-e duration] (writer C.S. duration (in loops))\n"); 210 1.1 christos printf(" [-v] (verbose output)\n"); 211 1.1 christos printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); 212 1.1 christos printf("\n"); 213 1.1 christos } 214 1.1 christos 215 1.1 christos int main(int argc, char **argv) 216 1.1 christos { 217 1.1 christos int err; 218 1.1 christos pthread_t *tid_reader, *tid_writer; 219 1.1 christos void *tret; 220 1.1 christos unsigned long long *count_reader, *count_writer; 221 1.1 christos unsigned long long tot_reads = 0, tot_writes = 0; 222 1.1 christos int i, a; 223 1.1 christos unsigned int i_thr; 224 1.1 christos 225 1.1 christos if (argc < 4) { 226 1.1 christos show_usage(argv); 227 1.1 christos return -1; 228 1.1 christos } 229 1.1 christos cmm_smp_mb(); 230 1.1 christos 231 1.1 christos err = sscanf(argv[1], "%u", &nr_readers); 232 1.1 christos if (err != 1) { 233 1.1 christos show_usage(argv); 234 1.1 christos return -1; 235 1.1 christos } 236 1.1 christos 237 1.1 christos err = sscanf(argv[2], "%u", &nr_writers); 238 1.1 christos if (err != 1) { 239 1.1 christos show_usage(argv); 240 1.1 christos return -1; 241 1.1 christos } 242 1.1 christos 243 1.1 christos err = sscanf(argv[3], "%lu", &duration); 244 1.1 christos if (err != 1) { 245 1.1 christos show_usage(argv); 246 1.1 christos return -1; 247 1.1 christos } 248 1.1 christos 249 1.1 christos for (i = 4; i < argc; i++) { 250 1.1 christos if (argv[i][0] != '-') 251 1.1 christos continue; 252 1.1 christos switch (argv[i][1]) { 253 1.1 christos case 'a': 254 1.1 christos if (argc < i + 2) { 255 1.1 christos show_usage(argv); 256 1.1 christos return -1; 257 1.1 christos } 258 1.1 christos a = atoi(argv[++i]); 259 1.1 christos cpu_affinities[next_aff++] = a; 260 1.1 christos use_affinity = 1; 261 1.1 christos printf_verbose("Adding CPU %d affinity\n", a); 262 1.1 christos break; 263 1.1 christos case 'c': 264 1.1 christos if (argc < i + 2) { 265 1.1 christos show_usage(argv); 266 1.1 christos return -1; 267 1.1 christos } 268 1.1 christos rduration = atol(argv[++i]); 269 1.1 christos break; 270 1.1 christos case 'd': 271 1.1 christos if (argc < i + 2) { 272 1.1 christos show_usage(argv); 273 1.1 christos return -1; 274 1.1 christos } 275 1.1 christos wdelay = atol(argv[++i]); 276 1.1 christos break; 277 1.1 christos case 'e': 278 1.1 christos if (argc < i + 2) { 279 1.1 christos show_usage(argv); 280 1.1 christos return -1; 281 1.1 christos } 282 1.1 christos wduration = atol(argv[++i]); 283 1.1 christos break; 284 1.1 christos case 'v': 285 1.1 christos verbose_mode = 1; 286 1.1 christos break; 287 1.1 christos } 288 1.1 christos } 289 1.1 christos 290 1.1 christos printf_verbose("running test for %lu seconds, %u readers, %u writers.\n", 291 1.1 christos duration, nr_readers, nr_writers); 292 1.1 christos printf_verbose("Writer delay : %lu loops.\n", wdelay); 293 1.1 christos printf_verbose("Writer duration : %lu loops.\n", wduration); 294 1.1 christos printf_verbose("Reader duration : %lu loops.\n", rduration); 295 1.1 christos printf_verbose("thread %-6s, tid %lu\n", 296 1.1 christos "main", urcu_get_thread_id()); 297 1.1 christos 298 1.1 christos err = pthread_rwlock_init(&lock, NULL); 299 1.1 christos if (err != 0) { 300 1.1 christos fprintf(stderr, "pthread_rwlock_init: (%d) %s\n", err, strerror(err)); 301 1.1 christos exit(1); 302 1.1 christos } 303 1.1 christos 304 1.1 christos tid_reader = calloc(nr_readers, sizeof(*tid_reader)); 305 1.1 christos tid_writer = calloc(nr_writers, sizeof(*tid_writer)); 306 1.1 christos count_reader = calloc(nr_readers, sizeof(*count_reader)); 307 1.1 christos count_writer = calloc(nr_writers, sizeof(*count_writer)); 308 1.1 christos 309 1.1 christos next_aff = 0; 310 1.1 christos 311 1.1 christos for (i_thr = 0; i_thr < nr_readers; i_thr++) { 312 1.1 christos err = pthread_create(&tid_reader[i_thr], NULL, thr_reader, 313 1.1 christos &count_reader[i_thr]); 314 1.1 christos if (err != 0) 315 1.1 christos exit(1); 316 1.1 christos } 317 1.1 christos for (i_thr = 0; i_thr < nr_writers; i_thr++) { 318 1.1 christos err = pthread_create(&tid_writer[i_thr], NULL, thr_writer, 319 1.1 christos &count_writer[i_thr]); 320 1.1 christos if (err != 0) 321 1.1 christos exit(1); 322 1.1 christos } 323 1.1 christos 324 1.1 christos test_for(duration); 325 1.1 christos 326 1.1 christos for (i_thr = 0; i_thr < nr_readers; i_thr++) { 327 1.1 christos err = pthread_join(tid_reader[i_thr], &tret); 328 1.1 christos if (err != 0) 329 1.1 christos exit(1); 330 1.1 christos tot_reads += count_reader[i_thr]; 331 1.1 christos } 332 1.1 christos for (i_thr = 0; i_thr < nr_writers; i_thr++) { 333 1.1 christos err = pthread_join(tid_writer[i_thr], &tret); 334 1.1 christos if (err != 0) 335 1.1 christos exit(1); 336 1.1 christos tot_writes += count_writer[i_thr]; 337 1.1 christos } 338 1.1 christos 339 1.1 christos printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads, 340 1.1 christos tot_writes); 341 1.1 christos printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu wdur %6lu " 342 1.1 christos "nr_writers %3u " 343 1.1 christos "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu\n", 344 1.1 christos argv[0], duration, nr_readers, rduration, wduration, 345 1.1 christos nr_writers, wdelay, tot_reads, tot_writes, 346 1.1 christos tot_reads + tot_writes); 347 1.1 christos 348 1.1 christos err = pthread_rwlock_destroy(&lock); 349 1.1 christos if (err != 0) { 350 1.1 christos fprintf(stderr, "pthread_rwlock_destroy: (%d) %s\n", err, strerror(err)); 351 1.1 christos exit(1); 352 1.1 christos } 353 1.1 christos free(tid_reader); 354 1.1 christos free(tid_writer); 355 1.1 christos free(count_reader); 356 1.1 christos free(count_writer); 357 1.1 christos return 0; 358 1.1 christos } 359