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. */
21 #if USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS
24 # define TEST_ISOC_THREADS 1
27 # define TEST_POSIX_THREADS 1
29 #if USE_ISOC_AND_POSIX_THREADS
30 # define TEST_ISOC_AND_POSIX_THREADS 1
32 #if USE_WINDOWS_THREADS
33 # define TEST_WINDOWS_THREADS 1
36 /* Whether to enable locking.
37 Uncomment this to get a test program without locking, to verify that
39 #define ENABLE_LOCKING 1
41 /* Which tests to perform.
42 Uncomment some of these, to verify that all tests crash if no locking
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
70 # undef USE_ISOC_THREADS
71 # undef USE_POSIX_THREADS
72 # undef USE_ISOC_AND_POSIX_THREADS
73 # undef USE_WINDOWS_THREADS
75 #include "glthread/lock.h"
78 # if TEST_ISOC_THREADS
79 # define USE_ISOC_THREADS 1
81 # if TEST_POSIX_THREADS
82 # define USE_POSIX_THREADS 1
84 # if TEST_ISOC_AND_POSIX_THREADS
85 # define USE_ISOC_AND_POSIX_THREADS 1
87 # if TEST_WINDOWS_THREADS
88 # define USE_WINDOWS_THREADS 1
92 #include "glthread/once.h"
93 #include "glthread/thread.h"
94 #include "glthread/yield.h"
101 #include "atomic-int-gnulib.h"
104 # define dbgprintf printf
106 # define dbgprintf if (0) printf
110 # define yield() gl_thread_yield ()
115 #define ACCOUNT_COUNT 4
117 static int account
[ACCOUNT_COUNT
];
120 random_account (void)
122 return ((unsigned long) random () >> 3) % ACCOUNT_COUNT
;
126 check_accounts (void)
131 for (i
= 0; i
< ACCOUNT_COUNT
; i
++)
133 if (sum
!= ACCOUNT_COUNT
* 1000)
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
)
147 lock_mutator_thread (_GL_UNUSED
void *arg
)
151 for (repeat
= REPEAT_COUNT
; repeat
> 0; repeat
--)
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
);
172 gl_lock_unlock (my_lock
);
173 dbgprintf ("Mutator %p after check unlock\n", gl_thread_self_pointer ());
178 dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
182 static struct atomic_int lock_checker_done
;
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
);
192 gl_lock_unlock (my_lock
);
193 dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ());
198 dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
206 gl_thread_t checkerthread
;
207 gl_thread_t threads
[THREAD_COUNT
];
209 /* Initialization. */
210 for (i
= 0; i
< ACCOUNT_COUNT
; i
++)
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
);
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
)
238 rwlock_mutator_thread (_GL_UNUSED
void *arg
)
242 for (repeat
= REPEAT_COUNT
; repeat
> 0; repeat
--)
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 ());
263 dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
267 static struct atomic_int rwlock_checker_done
;
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
);
277 gl_rwlock_unlock (my_rwlock
);
278 dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ());
283 dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
291 gl_thread_t checkerthreads
[THREAD_COUNT
];
292 gl_thread_t threads
[THREAD_COUNT
];
294 /* Initialization. */
295 for (i
= 0; i
< ACCOUNT_COUNT
; i
++)
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
);
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
)
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)
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 ());
349 reclock_mutator_thread (_GL_UNUSED
void *arg
)
353 for (repeat
= REPEAT_COUNT
; repeat
> 0; repeat
--)
357 dbgprintf ("Mutator %p before check lock\n", gl_thread_self_pointer ());
358 gl_recursive_lock_lock (my_reclock
);
360 gl_recursive_lock_unlock (my_reclock
);
361 dbgprintf ("Mutator %p after check unlock\n", gl_thread_self_pointer ());
366 dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
370 static struct atomic_int reclock_checker_done
;
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
);
380 gl_recursive_lock_unlock (my_reclock
);
381 dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ());
386 dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
391 test_recursive_lock (void)
394 gl_thread_t checkerthread
;
395 gl_thread_t threads
[THREAD_COUNT
];
397 /* Initialization. */
398 for (i
= 0; i
< ACCOUNT_COUNT
; i
++)
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
);
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
];
426 static gl_rwlock_t fire_signal
[REPEAT_COUNT
];
428 static volatile int fire_signal_state
;
430 static gl_once_t once_control
;
431 static int performed
;
432 gl_lock_define_initialized(static, performed_lock
)
437 gl_lock_lock (performed_lock
);
439 gl_lock_unlock (performed_lock
);
443 once_contender_thread (void *arg
)
445 int id
= (int) (intptr_t) arg
;
448 for (repeat
= 0; repeat
<= REPEAT_COUNT
; repeat
++)
450 /* Tell the main thread that we're ready. */
451 gl_lock_lock (ready_lock
[id
]);
453 gl_lock_unlock (ready_lock
[id
]);
455 if (repeat
== REPEAT_COUNT
)
458 dbgprintf ("Contender %p waiting for signal for round %d\n",
459 gl_thread_self_pointer (), repeat
);
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
]);
466 /* Wait for the signal to go. */
467 while (fire_signal_state
<= repeat
)
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
);
484 gl_thread_t threads
[THREAD_COUNT
];
486 /* Initialize all variables. */
487 for (i
= 0; i
< THREAD_COUNT
; i
++)
490 gl_lock_init (ready_lock
[i
]);
493 for (i
= 0; i
< REPEAT_COUNT
; i
++)
494 gl_rwlock_init (fire_signal
[i
]);
496 fire_signal_state
= 0;
500 /* Block all fire_signals. */
501 for (i
= REPEAT_COUNT
-1; i
>= 0; i
--)
502 gl_rwlock_wrlock (fire_signal
[i
]);
505 /* Spawn the threads. */
506 for (i
= 0; i
< THREAD_COUNT
; 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
);
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
)
527 dbgprintf ("Main thread after synchronizing for round %d\n", repeat
);
531 /* Check that exactly one thread executed the once_execute()
537 if (repeat
== REPEAT_COUNT
)
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. */
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
]);
551 gl_lock_unlock (ready_lock
[i
]);
554 /* Signal all threads simultaneously. */
555 dbgprintf ("Main thread giving signal for round %d\n", repeat
);
557 gl_rwlock_unlock (fire_signal
[repeat
]);
559 fire_signal_state
= repeat
+ 1;
563 /* Wait for the threads to terminate. */
564 for (i
= 0; i
< THREAD_COUNT
; i
++)
565 gl_thread_join (threads
[i
], NULL
);
569 /* -------------------------------------------------------------------------- */
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
);
583 printf ("Starting test_lock ..."); fflush (stdout
);
585 printf (" OK\n"); fflush (stdout
);
588 printf ("Starting test_rwlock ..."); fflush (stdout
);
590 printf (" OK\n"); fflush (stdout
);
592 #if DO_TEST_RECURSIVE_LOCK
593 printf ("Starting test_recursive_lock ..."); fflush (stdout
);
594 test_recursive_lock ();
595 printf (" OK\n"); fflush (stdout
);
598 printf ("Starting test_once ..."); fflush (stdout
);
600 printf (" OK\n"); fflush (stdout
);
608 /* No multithreading available. */
615 fputs ("Skipping test: multithreading not enabled\n", stderr
);