1 /* Test of locking in multithreaded situations.
2 Copyright (C) 2005, 2008-2020 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/thread.h"
93 #include "glthread/yield.h"
100 #include "atomic-int-gnulib.h"
103 # define dbgprintf printf
105 # define dbgprintf if (0) printf
109 # define yield() gl_thread_yield ()
114 #define ACCOUNT_COUNT 4
116 static int account
[ACCOUNT_COUNT
];
119 random_account (void)
121 return ((unsigned int) rand () >> 3) % ACCOUNT_COUNT
;
125 check_accounts (void)
130 for (i
= 0; i
< ACCOUNT_COUNT
; i
++)
132 if (sum
!= ACCOUNT_COUNT
* 1000)
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
)
146 lock_mutator_thread (void *arg
)
150 for (repeat
= REPEAT_COUNT
; repeat
> 0; repeat
--)
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 int) rand () >> 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
);
171 gl_lock_unlock (my_lock
);
172 dbgprintf ("Mutator %p after check unlock\n", gl_thread_self_pointer ());
177 dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
181 static struct atomic_int lock_checker_done
;
184 lock_checker_thread (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
);
191 gl_lock_unlock (my_lock
);
192 dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ());
197 dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
205 gl_thread_t checkerthread
;
206 gl_thread_t threads
[THREAD_COUNT
];
208 /* Initialization. */
209 for (i
= 0; i
< ACCOUNT_COUNT
; i
++)
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
);
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
)
237 rwlock_mutator_thread (void *arg
)
241 for (repeat
= REPEAT_COUNT
; repeat
> 0; repeat
--)
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 int) rand () >> 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 ());
262 dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
266 static struct atomic_int rwlock_checker_done
;
269 rwlock_checker_thread (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
);
276 gl_rwlock_unlock (my_rwlock
);
277 dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ());
282 dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
290 gl_thread_t checkerthreads
[THREAD_COUNT
];
291 gl_thread_t threads
[THREAD_COUNT
];
293 /* Initialization. */
294 for (i
= 0; i
< ACCOUNT_COUNT
; i
++)
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
);
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
)
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 int) rand () >> 3) % 10;
335 account
[i1
] += value
;
336 account
[i2
] -= value
;
338 /* Recursive with probability 0.5. */
339 if (((unsigned int) rand () >> 3) % 2)
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 ());
348 reclock_mutator_thread (void *arg
)
352 for (repeat
= REPEAT_COUNT
; repeat
> 0; repeat
--)
356 dbgprintf ("Mutator %p before check lock\n", gl_thread_self_pointer ());
357 gl_recursive_lock_lock (my_reclock
);
359 gl_recursive_lock_unlock (my_reclock
);
360 dbgprintf ("Mutator %p after check unlock\n", gl_thread_self_pointer ());
365 dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
369 static struct atomic_int reclock_checker_done
;
372 reclock_checker_thread (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
);
379 gl_recursive_lock_unlock (my_reclock
);
380 dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ());
385 dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
390 test_recursive_lock (void)
393 gl_thread_t checkerthread
;
394 gl_thread_t threads
[THREAD_COUNT
];
396 /* Initialization. */
397 for (i
= 0; i
< ACCOUNT_COUNT
; i
++)
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
);
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
];
425 static gl_rwlock_t fire_signal
[REPEAT_COUNT
];
427 static volatile int fire_signal_state
;
429 static gl_once_t once_control
;
430 static int performed
;
431 gl_lock_define_initialized(static, performed_lock
)
436 gl_lock_lock (performed_lock
);
438 gl_lock_unlock (performed_lock
);
442 once_contender_thread (void *arg
)
444 int id
= (int) (intptr_t) arg
;
447 for (repeat
= 0; repeat
<= REPEAT_COUNT
; repeat
++)
449 /* Tell the main thread that we're ready. */
450 gl_lock_lock (ready_lock
[id
]);
452 gl_lock_unlock (ready_lock
[id
]);
454 if (repeat
== REPEAT_COUNT
)
457 dbgprintf ("Contender %p waiting for signal for round %d\n",
458 gl_thread_self_pointer (), repeat
);
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
]);
465 /* Wait for the signal to go. */
466 while (fire_signal_state
<= repeat
)
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
);
483 gl_thread_t threads
[THREAD_COUNT
];
485 /* Initialize all variables. */
486 for (i
= 0; i
< THREAD_COUNT
; i
++)
489 gl_lock_init (ready_lock
[i
]);
492 for (i
= 0; i
< REPEAT_COUNT
; i
++)
493 gl_rwlock_init (fire_signal
[i
]);
495 fire_signal_state
= 0;
499 /* Block all fire_signals. */
500 for (i
= REPEAT_COUNT
-1; i
>= 0; i
--)
501 gl_rwlock_wrlock (fire_signal
[i
]);
504 /* Spawn the threads. */
505 for (i
= 0; i
< THREAD_COUNT
; 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
);
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
)
526 dbgprintf ("Main thread after synchronizing for round %d\n", repeat
);
530 /* Check that exactly one thread executed the once_execute()
536 if (repeat
== REPEAT_COUNT
)
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. */
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
]);
550 gl_lock_unlock (ready_lock
[i
]);
553 /* Signal all threads simultaneously. */
554 dbgprintf ("Main thread giving signal for round %d\n", repeat
);
556 gl_rwlock_unlock (fire_signal
[repeat
]);
558 fire_signal_state
= repeat
+ 1;
562 /* Wait for the threads to terminate. */
563 for (i
= 0; i
< THREAD_COUNT
; i
++)
564 gl_thread_join (threads
[i
], NULL
);
568 /* -------------------------------------------------------------------------- */
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
);
582 printf ("Starting test_lock ..."); fflush (stdout
);
584 printf (" OK\n"); fflush (stdout
);
587 printf ("Starting test_rwlock ..."); fflush (stdout
);
589 printf (" OK\n"); fflush (stdout
);
591 #if DO_TEST_RECURSIVE_LOCK
592 printf ("Starting test_recursive_lock ..."); fflush (stdout
);
593 test_recursive_lock ();
594 printf (" OK\n"); fflush (stdout
);
597 printf ("Starting test_once ..."); fflush (stdout
);
599 printf (" OK\n"); fflush (stdout
);
607 /* No multithreading available. */
614 fputs ("Skipping test: multithreading not enabled\n", stderr
);