Fix last ChangeLog entry.
[gnulib.git] / tests / test-lock.c
blob35142031cbc9d2e0b89a7191dd2a3a5018425811
1 /* Test of locking in multithreaded situations.
2 Copyright (C) 2005, 2008-2025 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 /* Written by Bruno Haible <bruno@clisp.org>, 2005. */
19 #include <config.h>
21 #if USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS
23 #if USE_ISOC_THREADS
24 # define TEST_ISOC_THREADS 1
25 #endif
26 #if USE_POSIX_THREADS
27 # define TEST_POSIX_THREADS 1
28 #endif
29 #if USE_ISOC_AND_POSIX_THREADS
30 # define TEST_ISOC_AND_POSIX_THREADS 1
31 #endif
32 #if USE_WINDOWS_THREADS
33 # define TEST_WINDOWS_THREADS 1
34 #endif
36 /* Whether to enable locking.
37 Uncomment this to get a test program without locking, to verify that
38 it crashes. */
39 #define ENABLE_LOCKING 1
41 /* Which tests to perform.
42 Uncomment some of these, to verify that all tests crash if no locking
43 is enabled. */
44 #define DO_TEST_LOCK 1
45 #define DO_TEST_RWLOCK 1
46 #define DO_TEST_RECURSIVE_LOCK 1
47 #define DO_TEST_ONCE 1
49 /* Whether to help the scheduler through explicit yield().
50 Uncomment this to see if the operating system has a fair scheduler. */
51 #define EXPLICIT_YIELD 1
53 /* Whether to print debugging messages. */
54 #define ENABLE_DEBUGGING 0
56 /* Number of simultaneous threads. */
57 #define THREAD_COUNT 10
59 /* Number of operations performed in each thread.
60 This is quite high, because with a smaller count, say 5000, we often get
61 an "OK" result even without ENABLE_LOCKING (on Linux/x86). */
62 #define REPEAT_COUNT 50000
64 #include <stdint.h>
65 #include <stdio.h>
66 #include <stdlib.h>
67 #include <string.h>
69 #if !ENABLE_LOCKING
70 # undef USE_ISOC_THREADS
71 # undef USE_POSIX_THREADS
72 # undef USE_ISOC_AND_POSIX_THREADS
73 # undef USE_WINDOWS_THREADS
74 #endif
75 #include "glthread/lock.h"
77 #if !ENABLE_LOCKING
78 # if TEST_ISOC_THREADS
79 # define USE_ISOC_THREADS 1
80 # endif
81 # if TEST_POSIX_THREADS
82 # define USE_POSIX_THREADS 1
83 # endif
84 # if TEST_ISOC_AND_POSIX_THREADS
85 # define USE_ISOC_AND_POSIX_THREADS 1
86 # endif
87 # if TEST_WINDOWS_THREADS
88 # define USE_WINDOWS_THREADS 1
89 # endif
90 #endif
92 #include "glthread/once.h"
93 #include "glthread/thread.h"
94 #include "glthread/yield.h"
96 #if HAVE_DECL_ALARM
97 # include <signal.h>
98 # include <unistd.h>
99 #endif
101 #include "atomic-int-gnulib.h"
103 #if ENABLE_DEBUGGING
104 # define dbgprintf printf
105 #else
106 # define dbgprintf if (0) printf
107 #endif
109 #if EXPLICIT_YIELD
110 # define yield() gl_thread_yield ()
111 #else
112 # define yield()
113 #endif
115 #define ACCOUNT_COUNT 4
117 static int account[ACCOUNT_COUNT];
119 static int
120 random_account (void)
122 return ((unsigned long) random () >> 3) % ACCOUNT_COUNT;
125 static void
126 check_accounts (void)
128 int i, sum;
130 sum = 0;
131 for (i = 0; i < ACCOUNT_COUNT; i++)
132 sum += account[i];
133 if (sum != ACCOUNT_COUNT * 1000)
134 abort ();
138 /* ------------------- Test normal (non-recursive) locks ------------------- */
140 /* Test normal locks by having several bank accounts and several threads
141 which shuffle around money between the accounts and another thread
142 checking that all the money is still there. */
144 gl_lock_define_initialized(static, my_lock)
146 static void *
147 lock_mutator_thread (_GL_UNUSED void *arg)
149 int repeat;
151 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
153 int i1, i2, value;
155 dbgprintf ("Mutator %p before lock\n", gl_thread_self_pointer ());
156 gl_lock_lock (my_lock);
157 dbgprintf ("Mutator %p after lock\n", gl_thread_self_pointer ());
159 i1 = random_account ();
160 i2 = random_account ();
161 value = ((unsigned long) random () >> 3) % 10;
162 account[i1] += value;
163 account[i2] -= value;
165 dbgprintf ("Mutator %p before unlock\n", gl_thread_self_pointer ());
166 gl_lock_unlock (my_lock);
167 dbgprintf ("Mutator %p after unlock\n", gl_thread_self_pointer ());
169 dbgprintf ("Mutator %p before check lock\n", gl_thread_self_pointer ());
170 gl_lock_lock (my_lock);
171 check_accounts ();
172 gl_lock_unlock (my_lock);
173 dbgprintf ("Mutator %p after check unlock\n", gl_thread_self_pointer ());
175 yield ();
178 dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
179 return NULL;
182 static struct atomic_int lock_checker_done;
184 static void *
185 lock_checker_thread (_GL_UNUSED void *arg)
187 while (get_atomic_int_value (&lock_checker_done) == 0)
189 dbgprintf ("Checker %p before check lock\n", gl_thread_self_pointer ());
190 gl_lock_lock (my_lock);
191 check_accounts ();
192 gl_lock_unlock (my_lock);
193 dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ());
195 yield ();
198 dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
199 return NULL;
202 static void
203 test_lock (void)
205 int i;
206 gl_thread_t checkerthread;
207 gl_thread_t threads[THREAD_COUNT];
209 /* Initialization. */
210 for (i = 0; i < ACCOUNT_COUNT; i++)
211 account[i] = 1000;
212 init_atomic_int (&lock_checker_done);
213 set_atomic_int_value (&lock_checker_done, 0);
215 /* Spawn the threads. */
216 checkerthread = gl_thread_create (lock_checker_thread, NULL);
217 for (i = 0; i < THREAD_COUNT; i++)
218 threads[i] = gl_thread_create (lock_mutator_thread, NULL);
220 /* Wait for the threads to terminate. */
221 for (i = 0; i < THREAD_COUNT; i++)
222 gl_thread_join (threads[i], NULL);
223 set_atomic_int_value (&lock_checker_done, 1);
224 gl_thread_join (checkerthread, NULL);
225 check_accounts ();
229 /* ----------------- Test read-write (non-recursive) locks ----------------- */
231 /* Test read-write locks by having several bank accounts and several threads
232 which shuffle around money between the accounts and several other threads
233 that check that all the money is still there. */
235 gl_rwlock_define_initialized(static, my_rwlock)
237 static void *
238 rwlock_mutator_thread (_GL_UNUSED void *arg)
240 int repeat;
242 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
244 int i1, i2, value;
246 dbgprintf ("Mutator %p before wrlock\n", gl_thread_self_pointer ());
247 gl_rwlock_wrlock (my_rwlock);
248 dbgprintf ("Mutator %p after wrlock\n", gl_thread_self_pointer ());
250 i1 = random_account ();
251 i2 = random_account ();
252 value = ((unsigned long) random () >> 3) % 10;
253 account[i1] += value;
254 account[i2] -= value;
256 dbgprintf ("Mutator %p before unlock\n", gl_thread_self_pointer ());
257 gl_rwlock_unlock (my_rwlock);
258 dbgprintf ("Mutator %p after unlock\n", gl_thread_self_pointer ());
260 yield ();
263 dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
264 return NULL;
267 static struct atomic_int rwlock_checker_done;
269 static void *
270 rwlock_checker_thread (_GL_UNUSED void *arg)
272 while (get_atomic_int_value (&rwlock_checker_done) == 0)
274 dbgprintf ("Checker %p before check rdlock\n", gl_thread_self_pointer ());
275 gl_rwlock_rdlock (my_rwlock);
276 check_accounts ();
277 gl_rwlock_unlock (my_rwlock);
278 dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ());
280 yield ();
283 dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
284 return NULL;
287 static void
288 test_rwlock (void)
290 int i;
291 gl_thread_t checkerthreads[THREAD_COUNT];
292 gl_thread_t threads[THREAD_COUNT];
294 /* Initialization. */
295 for (i = 0; i < ACCOUNT_COUNT; i++)
296 account[i] = 1000;
297 init_atomic_int (&rwlock_checker_done);
298 set_atomic_int_value (&rwlock_checker_done, 0);
300 /* Spawn the threads. */
301 for (i = 0; i < THREAD_COUNT; i++)
302 checkerthreads[i] = gl_thread_create (rwlock_checker_thread, NULL);
303 for (i = 0; i < THREAD_COUNT; i++)
304 threads[i] = gl_thread_create (rwlock_mutator_thread, NULL);
306 /* Wait for the threads to terminate. */
307 for (i = 0; i < THREAD_COUNT; i++)
308 gl_thread_join (threads[i], NULL);
309 set_atomic_int_value (&rwlock_checker_done, 1);
310 for (i = 0; i < THREAD_COUNT; i++)
311 gl_thread_join (checkerthreads[i], NULL);
312 check_accounts ();
316 /* -------------------------- Test recursive locks -------------------------- */
318 /* Test recursive locks by having several bank accounts and several threads
319 which shuffle around money between the accounts (recursively) and another
320 thread checking that all the money is still there. */
322 gl_recursive_lock_define_initialized(static, my_reclock)
324 static void
325 recshuffle (void)
327 int i1, i2, value;
329 dbgprintf ("Mutator %p before lock\n", gl_thread_self_pointer ());
330 gl_recursive_lock_lock (my_reclock);
331 dbgprintf ("Mutator %p after lock\n", gl_thread_self_pointer ());
333 i1 = random_account ();
334 i2 = random_account ();
335 value = ((unsigned long) random () >> 3) % 10;
336 account[i1] += value;
337 account[i2] -= value;
339 /* Recursive with probability 0.5. */
340 if (((unsigned long) random () >> 3) % 2)
341 recshuffle ();
343 dbgprintf ("Mutator %p before unlock\n", gl_thread_self_pointer ());
344 gl_recursive_lock_unlock (my_reclock);
345 dbgprintf ("Mutator %p after unlock\n", gl_thread_self_pointer ());
348 static void *
349 reclock_mutator_thread (_GL_UNUSED void *arg)
351 int repeat;
353 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
355 recshuffle ();
357 dbgprintf ("Mutator %p before check lock\n", gl_thread_self_pointer ());
358 gl_recursive_lock_lock (my_reclock);
359 check_accounts ();
360 gl_recursive_lock_unlock (my_reclock);
361 dbgprintf ("Mutator %p after check unlock\n", gl_thread_self_pointer ());
363 yield ();
366 dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
367 return NULL;
370 static struct atomic_int reclock_checker_done;
372 static void *
373 reclock_checker_thread (_GL_UNUSED void *arg)
375 while (get_atomic_int_value (&reclock_checker_done) == 0)
377 dbgprintf ("Checker %p before check lock\n", gl_thread_self_pointer ());
378 gl_recursive_lock_lock (my_reclock);
379 check_accounts ();
380 gl_recursive_lock_unlock (my_reclock);
381 dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ());
383 yield ();
386 dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
387 return NULL;
390 static void
391 test_recursive_lock (void)
393 int i;
394 gl_thread_t checkerthread;
395 gl_thread_t threads[THREAD_COUNT];
397 /* Initialization. */
398 for (i = 0; i < ACCOUNT_COUNT; i++)
399 account[i] = 1000;
400 init_atomic_int (&reclock_checker_done);
401 set_atomic_int_value (&reclock_checker_done, 0);
403 /* Spawn the threads. */
404 checkerthread = gl_thread_create (reclock_checker_thread, NULL);
405 for (i = 0; i < THREAD_COUNT; i++)
406 threads[i] = gl_thread_create (reclock_mutator_thread, NULL);
408 /* Wait for the threads to terminate. */
409 for (i = 0; i < THREAD_COUNT; i++)
410 gl_thread_join (threads[i], NULL);
411 set_atomic_int_value (&reclock_checker_done, 1);
412 gl_thread_join (checkerthread, NULL);
413 check_accounts ();
417 /* ------------------------ Test once-only execution ------------------------ */
419 /* Test once-only execution by having several threads attempt to grab a
420 once-only task simultaneously (triggered by releasing a read-write lock). */
422 gl_once_define(static, fresh_once)
423 static int ready[THREAD_COUNT];
424 static gl_lock_t ready_lock[THREAD_COUNT];
425 #if ENABLE_LOCKING
426 static gl_rwlock_t fire_signal[REPEAT_COUNT];
427 #else
428 static volatile int fire_signal_state;
429 #endif
430 static gl_once_t once_control;
431 static int performed;
432 gl_lock_define_initialized(static, performed_lock)
434 static void
435 once_execute (void)
437 gl_lock_lock (performed_lock);
438 performed++;
439 gl_lock_unlock (performed_lock);
442 static void *
443 once_contender_thread (void *arg)
445 int id = (int) (intptr_t) arg;
446 int repeat;
448 for (repeat = 0; repeat <= REPEAT_COUNT; repeat++)
450 /* Tell the main thread that we're ready. */
451 gl_lock_lock (ready_lock[id]);
452 ready[id] = 1;
453 gl_lock_unlock (ready_lock[id]);
455 if (repeat == REPEAT_COUNT)
456 break;
458 dbgprintf ("Contender %p waiting for signal for round %d\n",
459 gl_thread_self_pointer (), repeat);
460 #if ENABLE_LOCKING
461 /* Wait for the signal to go. */
462 gl_rwlock_rdlock (fire_signal[repeat]);
463 /* And don't hinder the others (if the scheduler is unfair). */
464 gl_rwlock_unlock (fire_signal[repeat]);
465 #else
466 /* Wait for the signal to go. */
467 while (fire_signal_state <= repeat)
468 yield ();
469 #endif
470 dbgprintf ("Contender %p got the signal for round %d\n",
471 gl_thread_self_pointer (), repeat);
473 /* Contend for execution. */
474 gl_once (once_control, once_execute);
477 return NULL;
480 static void
481 test_once (void)
483 int i, repeat;
484 gl_thread_t threads[THREAD_COUNT];
486 /* Initialize all variables. */
487 for (i = 0; i < THREAD_COUNT; i++)
489 ready[i] = 0;
490 gl_lock_init (ready_lock[i]);
492 #if ENABLE_LOCKING
493 for (i = 0; i < REPEAT_COUNT; i++)
494 gl_rwlock_init (fire_signal[i]);
495 #else
496 fire_signal_state = 0;
497 #endif
499 #if ENABLE_LOCKING
500 /* Block all fire_signals. */
501 for (i = REPEAT_COUNT-1; i >= 0; i--)
502 gl_rwlock_wrlock (fire_signal[i]);
503 #endif
505 /* Spawn the threads. */
506 for (i = 0; i < THREAD_COUNT; i++)
507 threads[i] =
508 gl_thread_create (once_contender_thread, (void *) (intptr_t) i);
510 for (repeat = 0; repeat <= REPEAT_COUNT; repeat++)
512 /* Wait until every thread is ready. */
513 dbgprintf ("Main thread before synchronizing for round %d\n", repeat);
514 for (;;)
516 int ready_count = 0;
517 for (i = 0; i < THREAD_COUNT; i++)
519 gl_lock_lock (ready_lock[i]);
520 ready_count += ready[i];
521 gl_lock_unlock (ready_lock[i]);
523 if (ready_count == THREAD_COUNT)
524 break;
525 yield ();
527 dbgprintf ("Main thread after synchronizing for round %d\n", repeat);
529 if (repeat > 0)
531 /* Check that exactly one thread executed the once_execute()
532 function. */
533 if (performed != 1)
534 abort ();
537 if (repeat == REPEAT_COUNT)
538 break;
540 /* Preparation for the next round: Initialize once_control. */
541 memcpy (&once_control, &fresh_once, sizeof (gl_once_t));
543 /* Preparation for the next round: Reset the performed counter. */
544 performed = 0;
546 /* Preparation for the next round: Reset the ready flags. */
547 for (i = 0; i < THREAD_COUNT; i++)
549 gl_lock_lock (ready_lock[i]);
550 ready[i] = 0;
551 gl_lock_unlock (ready_lock[i]);
554 /* Signal all threads simultaneously. */
555 dbgprintf ("Main thread giving signal for round %d\n", repeat);
556 #if ENABLE_LOCKING
557 gl_rwlock_unlock (fire_signal[repeat]);
558 #else
559 fire_signal_state = repeat + 1;
560 #endif
563 /* Wait for the threads to terminate. */
564 for (i = 0; i < THREAD_COUNT; i++)
565 gl_thread_join (threads[i], NULL);
569 /* -------------------------------------------------------------------------- */
572 main ()
574 #if HAVE_DECL_ALARM
575 /* Declare failure if test takes too long, by using default abort
576 caused by SIGALRM. */
577 int alarm_value = 600;
578 signal (SIGALRM, SIG_DFL);
579 alarm (alarm_value);
580 #endif
582 #if DO_TEST_LOCK
583 printf ("Starting test_lock ..."); fflush (stdout);
584 test_lock ();
585 printf (" OK\n"); fflush (stdout);
586 #endif
587 #if DO_TEST_RWLOCK
588 printf ("Starting test_rwlock ..."); fflush (stdout);
589 test_rwlock ();
590 printf (" OK\n"); fflush (stdout);
591 #endif
592 #if DO_TEST_RECURSIVE_LOCK
593 printf ("Starting test_recursive_lock ..."); fflush (stdout);
594 test_recursive_lock ();
595 printf (" OK\n"); fflush (stdout);
596 #endif
597 #if DO_TEST_ONCE
598 printf ("Starting test_once ..."); fflush (stdout);
599 test_once ();
600 printf (" OK\n"); fflush (stdout);
601 #endif
603 return 0;
606 #else
608 /* No multithreading available. */
610 #include <stdio.h>
613 main ()
615 fputs ("Skipping test: multithreading not enabled\n", stderr);
616 return 77;
619 #endif