usleep tests: Avoid failure due to known Cygwin 3.5.3 bug.
[gnulib.git] / tests / test-lock.c
blobb5a5240f30656c356ac5835c136dc6aff5ff0515
1 /* Test of locking in multithreaded situations.
2 Copyright (C) 2005, 2008-2024 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/thread.h"
93 #include "glthread/yield.h"
95 #if HAVE_DECL_ALARM
96 # include <signal.h>
97 # include <unistd.h>
98 #endif
100 #include "atomic-int-gnulib.h"
102 #if ENABLE_DEBUGGING
103 # define dbgprintf printf
104 #else
105 # define dbgprintf if (0) printf
106 #endif
108 #if EXPLICIT_YIELD
109 # define yield() gl_thread_yield ()
110 #else
111 # define yield()
112 #endif
114 #define ACCOUNT_COUNT 4
116 static int account[ACCOUNT_COUNT];
118 static int
119 random_account (void)
121 return ((unsigned long) random () >> 3) % ACCOUNT_COUNT;
124 static void
125 check_accounts (void)
127 int i, sum;
129 sum = 0;
130 for (i = 0; i < ACCOUNT_COUNT; i++)
131 sum += account[i];
132 if (sum != ACCOUNT_COUNT * 1000)
133 abort ();
137 /* ------------------- Test normal (non-recursive) locks ------------------- */
139 /* Test normal locks by having several bank accounts and several threads
140 which shuffle around money between the accounts and another thread
141 checking that all the money is still there. */
143 gl_lock_define_initialized(static, my_lock)
145 static void *
146 lock_mutator_thread (_GL_UNUSED void *arg)
148 int repeat;
150 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
152 int i1, i2, value;
154 dbgprintf ("Mutator %p before lock\n", gl_thread_self_pointer ());
155 gl_lock_lock (my_lock);
156 dbgprintf ("Mutator %p after lock\n", gl_thread_self_pointer ());
158 i1 = random_account ();
159 i2 = random_account ();
160 value = ((unsigned long) random () >> 3) % 10;
161 account[i1] += value;
162 account[i2] -= value;
164 dbgprintf ("Mutator %p before unlock\n", gl_thread_self_pointer ());
165 gl_lock_unlock (my_lock);
166 dbgprintf ("Mutator %p after unlock\n", gl_thread_self_pointer ());
168 dbgprintf ("Mutator %p before check lock\n", gl_thread_self_pointer ());
169 gl_lock_lock (my_lock);
170 check_accounts ();
171 gl_lock_unlock (my_lock);
172 dbgprintf ("Mutator %p after check unlock\n", gl_thread_self_pointer ());
174 yield ();
177 dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
178 return NULL;
181 static struct atomic_int lock_checker_done;
183 static void *
184 lock_checker_thread (_GL_UNUSED void *arg)
186 while (get_atomic_int_value (&lock_checker_done) == 0)
188 dbgprintf ("Checker %p before check lock\n", gl_thread_self_pointer ());
189 gl_lock_lock (my_lock);
190 check_accounts ();
191 gl_lock_unlock (my_lock);
192 dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ());
194 yield ();
197 dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
198 return NULL;
201 static void
202 test_lock (void)
204 int i;
205 gl_thread_t checkerthread;
206 gl_thread_t threads[THREAD_COUNT];
208 /* Initialization. */
209 for (i = 0; i < ACCOUNT_COUNT; i++)
210 account[i] = 1000;
211 init_atomic_int (&lock_checker_done);
212 set_atomic_int_value (&lock_checker_done, 0);
214 /* Spawn the threads. */
215 checkerthread = gl_thread_create (lock_checker_thread, NULL);
216 for (i = 0; i < THREAD_COUNT; i++)
217 threads[i] = gl_thread_create (lock_mutator_thread, NULL);
219 /* Wait for the threads to terminate. */
220 for (i = 0; i < THREAD_COUNT; i++)
221 gl_thread_join (threads[i], NULL);
222 set_atomic_int_value (&lock_checker_done, 1);
223 gl_thread_join (checkerthread, NULL);
224 check_accounts ();
228 /* ----------------- Test read-write (non-recursive) locks ----------------- */
230 /* Test read-write locks by having several bank accounts and several threads
231 which shuffle around money between the accounts and several other threads
232 that check that all the money is still there. */
234 gl_rwlock_define_initialized(static, my_rwlock)
236 static void *
237 rwlock_mutator_thread (_GL_UNUSED void *arg)
239 int repeat;
241 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
243 int i1, i2, value;
245 dbgprintf ("Mutator %p before wrlock\n", gl_thread_self_pointer ());
246 gl_rwlock_wrlock (my_rwlock);
247 dbgprintf ("Mutator %p after wrlock\n", gl_thread_self_pointer ());
249 i1 = random_account ();
250 i2 = random_account ();
251 value = ((unsigned long) random () >> 3) % 10;
252 account[i1] += value;
253 account[i2] -= value;
255 dbgprintf ("Mutator %p before unlock\n", gl_thread_self_pointer ());
256 gl_rwlock_unlock (my_rwlock);
257 dbgprintf ("Mutator %p after unlock\n", gl_thread_self_pointer ());
259 yield ();
262 dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
263 return NULL;
266 static struct atomic_int rwlock_checker_done;
268 static void *
269 rwlock_checker_thread (_GL_UNUSED void *arg)
271 while (get_atomic_int_value (&rwlock_checker_done) == 0)
273 dbgprintf ("Checker %p before check rdlock\n", gl_thread_self_pointer ());
274 gl_rwlock_rdlock (my_rwlock);
275 check_accounts ();
276 gl_rwlock_unlock (my_rwlock);
277 dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ());
279 yield ();
282 dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
283 return NULL;
286 static void
287 test_rwlock (void)
289 int i;
290 gl_thread_t checkerthreads[THREAD_COUNT];
291 gl_thread_t threads[THREAD_COUNT];
293 /* Initialization. */
294 for (i = 0; i < ACCOUNT_COUNT; i++)
295 account[i] = 1000;
296 init_atomic_int (&rwlock_checker_done);
297 set_atomic_int_value (&rwlock_checker_done, 0);
299 /* Spawn the threads. */
300 for (i = 0; i < THREAD_COUNT; i++)
301 checkerthreads[i] = gl_thread_create (rwlock_checker_thread, NULL);
302 for (i = 0; i < THREAD_COUNT; i++)
303 threads[i] = gl_thread_create (rwlock_mutator_thread, NULL);
305 /* Wait for the threads to terminate. */
306 for (i = 0; i < THREAD_COUNT; i++)
307 gl_thread_join (threads[i], NULL);
308 set_atomic_int_value (&rwlock_checker_done, 1);
309 for (i = 0; i < THREAD_COUNT; i++)
310 gl_thread_join (checkerthreads[i], NULL);
311 check_accounts ();
315 /* -------------------------- Test recursive locks -------------------------- */
317 /* Test recursive locks by having several bank accounts and several threads
318 which shuffle around money between the accounts (recursively) and another
319 thread checking that all the money is still there. */
321 gl_recursive_lock_define_initialized(static, my_reclock)
323 static void
324 recshuffle (void)
326 int i1, i2, value;
328 dbgprintf ("Mutator %p before lock\n", gl_thread_self_pointer ());
329 gl_recursive_lock_lock (my_reclock);
330 dbgprintf ("Mutator %p after lock\n", gl_thread_self_pointer ());
332 i1 = random_account ();
333 i2 = random_account ();
334 value = ((unsigned long) random () >> 3) % 10;
335 account[i1] += value;
336 account[i2] -= value;
338 /* Recursive with probability 0.5. */
339 if (((unsigned long) random () >> 3) % 2)
340 recshuffle ();
342 dbgprintf ("Mutator %p before unlock\n", gl_thread_self_pointer ());
343 gl_recursive_lock_unlock (my_reclock);
344 dbgprintf ("Mutator %p after unlock\n", gl_thread_self_pointer ());
347 static void *
348 reclock_mutator_thread (_GL_UNUSED void *arg)
350 int repeat;
352 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
354 recshuffle ();
356 dbgprintf ("Mutator %p before check lock\n", gl_thread_self_pointer ());
357 gl_recursive_lock_lock (my_reclock);
358 check_accounts ();
359 gl_recursive_lock_unlock (my_reclock);
360 dbgprintf ("Mutator %p after check unlock\n", gl_thread_self_pointer ());
362 yield ();
365 dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
366 return NULL;
369 static struct atomic_int reclock_checker_done;
371 static void *
372 reclock_checker_thread (_GL_UNUSED void *arg)
374 while (get_atomic_int_value (&reclock_checker_done) == 0)
376 dbgprintf ("Checker %p before check lock\n", gl_thread_self_pointer ());
377 gl_recursive_lock_lock (my_reclock);
378 check_accounts ();
379 gl_recursive_lock_unlock (my_reclock);
380 dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ());
382 yield ();
385 dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
386 return NULL;
389 static void
390 test_recursive_lock (void)
392 int i;
393 gl_thread_t checkerthread;
394 gl_thread_t threads[THREAD_COUNT];
396 /* Initialization. */
397 for (i = 0; i < ACCOUNT_COUNT; i++)
398 account[i] = 1000;
399 init_atomic_int (&reclock_checker_done);
400 set_atomic_int_value (&reclock_checker_done, 0);
402 /* Spawn the threads. */
403 checkerthread = gl_thread_create (reclock_checker_thread, NULL);
404 for (i = 0; i < THREAD_COUNT; i++)
405 threads[i] = gl_thread_create (reclock_mutator_thread, NULL);
407 /* Wait for the threads to terminate. */
408 for (i = 0; i < THREAD_COUNT; i++)
409 gl_thread_join (threads[i], NULL);
410 set_atomic_int_value (&reclock_checker_done, 1);
411 gl_thread_join (checkerthread, NULL);
412 check_accounts ();
416 /* ------------------------ Test once-only execution ------------------------ */
418 /* Test once-only execution by having several threads attempt to grab a
419 once-only task simultaneously (triggered by releasing a read-write lock). */
421 gl_once_define(static, fresh_once)
422 static int ready[THREAD_COUNT];
423 static gl_lock_t ready_lock[THREAD_COUNT];
424 #if ENABLE_LOCKING
425 static gl_rwlock_t fire_signal[REPEAT_COUNT];
426 #else
427 static volatile int fire_signal_state;
428 #endif
429 static gl_once_t once_control;
430 static int performed;
431 gl_lock_define_initialized(static, performed_lock)
433 static void
434 once_execute (void)
436 gl_lock_lock (performed_lock);
437 performed++;
438 gl_lock_unlock (performed_lock);
441 static void *
442 once_contender_thread (void *arg)
444 int id = (int) (intptr_t) arg;
445 int repeat;
447 for (repeat = 0; repeat <= REPEAT_COUNT; repeat++)
449 /* Tell the main thread that we're ready. */
450 gl_lock_lock (ready_lock[id]);
451 ready[id] = 1;
452 gl_lock_unlock (ready_lock[id]);
454 if (repeat == REPEAT_COUNT)
455 break;
457 dbgprintf ("Contender %p waiting for signal for round %d\n",
458 gl_thread_self_pointer (), repeat);
459 #if ENABLE_LOCKING
460 /* Wait for the signal to go. */
461 gl_rwlock_rdlock (fire_signal[repeat]);
462 /* And don't hinder the others (if the scheduler is unfair). */
463 gl_rwlock_unlock (fire_signal[repeat]);
464 #else
465 /* Wait for the signal to go. */
466 while (fire_signal_state <= repeat)
467 yield ();
468 #endif
469 dbgprintf ("Contender %p got the signal for round %d\n",
470 gl_thread_self_pointer (), repeat);
472 /* Contend for execution. */
473 gl_once (once_control, once_execute);
476 return NULL;
479 static void
480 test_once (void)
482 int i, repeat;
483 gl_thread_t threads[THREAD_COUNT];
485 /* Initialize all variables. */
486 for (i = 0; i < THREAD_COUNT; i++)
488 ready[i] = 0;
489 gl_lock_init (ready_lock[i]);
491 #if ENABLE_LOCKING
492 for (i = 0; i < REPEAT_COUNT; i++)
493 gl_rwlock_init (fire_signal[i]);
494 #else
495 fire_signal_state = 0;
496 #endif
498 #if ENABLE_LOCKING
499 /* Block all fire_signals. */
500 for (i = REPEAT_COUNT-1; i >= 0; i--)
501 gl_rwlock_wrlock (fire_signal[i]);
502 #endif
504 /* Spawn the threads. */
505 for (i = 0; i < THREAD_COUNT; i++)
506 threads[i] =
507 gl_thread_create (once_contender_thread, (void *) (intptr_t) i);
509 for (repeat = 0; repeat <= REPEAT_COUNT; repeat++)
511 /* Wait until every thread is ready. */
512 dbgprintf ("Main thread before synchronizing for round %d\n", repeat);
513 for (;;)
515 int ready_count = 0;
516 for (i = 0; i < THREAD_COUNT; i++)
518 gl_lock_lock (ready_lock[i]);
519 ready_count += ready[i];
520 gl_lock_unlock (ready_lock[i]);
522 if (ready_count == THREAD_COUNT)
523 break;
524 yield ();
526 dbgprintf ("Main thread after synchronizing for round %d\n", repeat);
528 if (repeat > 0)
530 /* Check that exactly one thread executed the once_execute()
531 function. */
532 if (performed != 1)
533 abort ();
536 if (repeat == REPEAT_COUNT)
537 break;
539 /* Preparation for the next round: Initialize once_control. */
540 memcpy (&once_control, &fresh_once, sizeof (gl_once_t));
542 /* Preparation for the next round: Reset the performed counter. */
543 performed = 0;
545 /* Preparation for the next round: Reset the ready flags. */
546 for (i = 0; i < THREAD_COUNT; i++)
548 gl_lock_lock (ready_lock[i]);
549 ready[i] = 0;
550 gl_lock_unlock (ready_lock[i]);
553 /* Signal all threads simultaneously. */
554 dbgprintf ("Main thread giving signal for round %d\n", repeat);
555 #if ENABLE_LOCKING
556 gl_rwlock_unlock (fire_signal[repeat]);
557 #else
558 fire_signal_state = repeat + 1;
559 #endif
562 /* Wait for the threads to terminate. */
563 for (i = 0; i < THREAD_COUNT; i++)
564 gl_thread_join (threads[i], NULL);
568 /* -------------------------------------------------------------------------- */
571 main ()
573 #if HAVE_DECL_ALARM
574 /* Declare failure if test takes too long, by using default abort
575 caused by SIGALRM. */
576 int alarm_value = 600;
577 signal (SIGALRM, SIG_DFL);
578 alarm (alarm_value);
579 #endif
581 #if DO_TEST_LOCK
582 printf ("Starting test_lock ..."); fflush (stdout);
583 test_lock ();
584 printf (" OK\n"); fflush (stdout);
585 #endif
586 #if DO_TEST_RWLOCK
587 printf ("Starting test_rwlock ..."); fflush (stdout);
588 test_rwlock ();
589 printf (" OK\n"); fflush (stdout);
590 #endif
591 #if DO_TEST_RECURSIVE_LOCK
592 printf ("Starting test_recursive_lock ..."); fflush (stdout);
593 test_recursive_lock ();
594 printf (" OK\n"); fflush (stdout);
595 #endif
596 #if DO_TEST_ONCE
597 printf ("Starting test_once ..."); fflush (stdout);
598 test_once ();
599 printf (" OK\n"); fflush (stdout);
600 #endif
602 return 0;
605 #else
607 /* No multithreading available. */
609 #include <stdio.h>
612 main ()
614 fputs ("Skipping test: multithreading not enabled\n", stderr);
615 return 77;
618 #endif