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
23 /* Whether to enable locking.
24 Uncomment this to get a test program without locking, to verify that
26 #define ENABLE_LOCKING 1
28 /* Which tests to perform.
29 Uncomment some of these, to verify that all tests crash if no locking
31 #define DO_TEST_LOCK 1
32 #define DO_TEST_RECURSIVE_LOCK 1
34 /* Whether to help the scheduler through explicit sched_yield().
35 Uncomment this to see if the operating system has a fair scheduler. */
36 #define EXPLICIT_YIELD 1
38 /* Whether to use 'volatile' on some variables that communicate information
39 between threads. If set to 0, a semaphore or a lock is used to protect
40 these variables. If set to 1, 'volatile' is used; this is theoretically
41 equivalent but can lead to much slower execution (e.g. 30x slower total
42 run time on a 40-core machine), because 'volatile' does not imply any
43 synchronization/communication between different CPUs. */
44 #define USE_VOLATILE 0
46 #if USE_POSIX_THREADS && HAVE_SEMAPHORE_H
47 /* Whether to use a semaphore to communicate information between threads.
48 If set to 0, a lock is used. If set to 1, a semaphore is used.
49 Uncomment this to reduce the dependencies of this test. */
50 # define USE_SEMAPHORE 1
51 /* Mac OS X provides only named semaphores (sem_open); its facility for
52 unnamed semaphores (sem_init) does not work. */
53 # if defined __APPLE__ && defined __MACH__
54 # define USE_NAMED_SEMAPHORE 1
56 # define USE_UNNAMED_SEMAPHORE 1
60 /* Whether to print debugging messages. */
61 #define ENABLE_DEBUGGING 0
63 /* Number of simultaneous threads. */
64 #define THREAD_COUNT 10
66 /* Number of operations performed in each thread.
67 This is quite high, because with a smaller count, say 5000, we often get
68 an "OK" result even without ENABLE_LOCKING (on Linux/x86). */
69 #define REPEAT_COUNT 50000
84 # include <semaphore.h>
96 # define dbgprintf printf
98 # define dbgprintf if (0) printf
102 # define yield() sched_yield ()
112 init_atomic_int (struct atomic_int
*ai
)
116 get_atomic_int_value (struct atomic_int
*ai
)
121 set_atomic_int_value (struct atomic_int
*ai
, int new_value
)
123 ai
->value
= new_value
;
126 /* This atomic_int implementation can only support the values 0 and 1.
127 It is initially 0 and can be set to 1 only once. */
128 # if USE_UNNAMED_SEMAPHORE
132 #define atomic_int_semaphore(ai) (&(ai)->semaphore)
134 init_atomic_int (struct atomic_int
*ai
)
136 sem_init (&ai
->semaphore
, 0, 0);
139 # if USE_NAMED_SEMAPHORE
143 #define atomic_int_semaphore(ai) ((ai)->semaphore)
145 init_atomic_int (struct atomic_int
*ai
)
149 for (count
= 0; ; count
++)
152 /* Use getpid() in the name, so that different processes running at the
153 same time will not interfere. Use ai in the name, so that different
154 atomic_int in the same process will not interfere. Use a count in
155 the name, so that even in the (unlikely) case that a semaphore with
156 the specified name already exists, we can try a different name. */
157 sprintf (name
, "test-lock-%lu-%p-%u",
158 (unsigned long) getpid (), ai
, count
);
159 s
= sem_open (name
, O_CREAT
| O_EXCL
, 0600, 0);
163 /* Retry with a different name. */
167 perror ("sem_open failed");
173 /* Try not to leave a semaphore hanging around on the file system
174 eternally, if we can avoid it. */
183 get_atomic_int_value (struct atomic_int
*ai
)
185 if (sem_trywait (atomic_int_semaphore (ai
)) == 0)
187 if (sem_post (atomic_int_semaphore (ai
)))
191 else if (errno
== EAGAIN
)
197 set_atomic_int_value (struct atomic_int
*ai
, int new_value
)
200 /* It's already initialized with 0. */
202 /* To set the value 1: */
203 if (sem_post (atomic_int_semaphore (ai
)))
208 pthread_mutex_t lock
;
212 init_atomic_int (struct atomic_int
*ai
)
214 pthread_mutexattr_t attr
;
216 ASSERT (pthread_mutexattr_init (&attr
) == 0);
217 ASSERT (pthread_mutexattr_settype (&attr
, PTHREAD_MUTEX_NORMAL
) == 0);
218 ASSERT (pthread_mutex_init (&ai
->lock
, &attr
) == 0);
219 ASSERT (pthread_mutexattr_destroy (&attr
) == 0);
222 get_atomic_int_value (struct atomic_int
*ai
)
224 ASSERT (pthread_mutex_lock (&ai
->lock
) == 0);
226 ASSERT (pthread_mutex_unlock (&ai
->lock
) == 0);
230 set_atomic_int_value (struct atomic_int
*ai
, int new_value
)
232 ASSERT (pthread_mutex_lock (&ai
->lock
) == 0);
233 ai
->value
= new_value
;
234 ASSERT (pthread_mutex_unlock (&ai
->lock
) == 0);
238 /* Returns a reference to the current thread as a pointer, for debugging. */
240 /* On IBM z/OS, pthread_t is a struct with an 8-byte '__' field.
241 The first three bytes of this field appear to uniquely identify a
242 pthread_t, though not necessarily representing a pointer. */
243 # define pthread_self_pointer() (*((void **) pthread_self ().__))
245 # define pthread_self_pointer() ((void *) (uintptr_t) pthread_self ())
248 #define ACCOUNT_COUNT 4
250 static int account
[ACCOUNT_COUNT
];
253 random_account (void)
255 return ((unsigned int) rand () >> 3) % ACCOUNT_COUNT
;
259 check_accounts (void)
264 for (i
= 0; i
< ACCOUNT_COUNT
; i
++)
266 if (sum
!= ACCOUNT_COUNT
* 1000)
271 /* ------------------- Test normal (non-recursive) locks ------------------- */
273 /* Test normal locks by having several bank accounts and several threads
274 which shuffle around money between the accounts and another thread
275 checking that all the money is still there. */
277 static pthread_mutex_t my_lock
;
280 lock_mutator_thread (void *arg
)
284 for (repeat
= REPEAT_COUNT
; repeat
> 0; repeat
--)
288 dbgprintf ("Mutator %p before lock\n", pthread_self_pointer ());
289 ASSERT (pthread_mutex_lock (&my_lock
) == 0);
290 dbgprintf ("Mutator %p after lock\n", pthread_self_pointer ());
292 i1
= random_account ();
293 i2
= random_account ();
294 value
= ((unsigned int) rand () >> 3) % 10;
295 account
[i1
] += value
;
296 account
[i2
] -= value
;
298 dbgprintf ("Mutator %p before unlock\n", pthread_self_pointer ());
299 ASSERT (pthread_mutex_unlock (&my_lock
) == 0);
300 dbgprintf ("Mutator %p after unlock\n", pthread_self_pointer ());
302 dbgprintf ("Mutator %p before check lock\n", pthread_self_pointer ());
303 ASSERT (pthread_mutex_lock (&my_lock
) == 0);
305 ASSERT (pthread_mutex_unlock (&my_lock
) == 0);
306 dbgprintf ("Mutator %p after check unlock\n", pthread_self_pointer ());
311 dbgprintf ("Mutator %p dying.\n", pthread_self_pointer ());
315 static struct atomic_int lock_checker_done
;
318 lock_checker_thread (void *arg
)
320 while (get_atomic_int_value (&lock_checker_done
) == 0)
322 dbgprintf ("Checker %p before check lock\n", pthread_self_pointer ());
323 ASSERT (pthread_mutex_lock (&my_lock
) == 0);
325 ASSERT (pthread_mutex_unlock (&my_lock
) == 0);
326 dbgprintf ("Checker %p after check unlock\n", pthread_self_pointer ());
331 dbgprintf ("Checker %p dying.\n", pthread_self_pointer ());
336 test_pthread_mutex_normal (void)
339 pthread_t checkerthread
;
340 pthread_t threads
[THREAD_COUNT
];
342 /* Initialization. */
343 for (i
= 0; i
< ACCOUNT_COUNT
; i
++)
345 init_atomic_int (&lock_checker_done
);
346 set_atomic_int_value (&lock_checker_done
, 0);
348 /* Spawn the threads. */
349 ASSERT (pthread_create (&checkerthread
, NULL
, lock_checker_thread
, NULL
)
351 for (i
= 0; i
< THREAD_COUNT
; i
++)
352 ASSERT (pthread_create (&threads
[i
], NULL
, lock_mutator_thread
, NULL
) == 0);
354 /* Wait for the threads to terminate. */
355 for (i
= 0; i
< THREAD_COUNT
; i
++)
356 ASSERT (pthread_join (threads
[i
], NULL
) == 0);
357 set_atomic_int_value (&lock_checker_done
, 1);
358 ASSERT (pthread_join (checkerthread
, NULL
) == 0);
363 /* -------------------------- Test recursive locks -------------------------- */
365 /* Test recursive locks by having several bank accounts and several threads
366 which shuffle around money between the accounts (recursively) and another
367 thread checking that all the money is still there. */
369 static pthread_mutex_t my_reclock
;
376 dbgprintf ("Mutator %p before lock\n", pthread_self_pointer ());
377 ASSERT (pthread_mutex_lock (&my_reclock
) == 0);
378 dbgprintf ("Mutator %p after lock\n", pthread_self_pointer ());
380 i1
= random_account ();
381 i2
= random_account ();
382 value
= ((unsigned int) rand () >> 3) % 10;
383 account
[i1
] += value
;
384 account
[i2
] -= value
;
386 /* Recursive with probability 0.5. */
387 if (((unsigned int) rand () >> 3) % 2)
390 dbgprintf ("Mutator %p before unlock\n", pthread_self_pointer ());
391 ASSERT (pthread_mutex_unlock (&my_reclock
) == 0);
392 dbgprintf ("Mutator %p after unlock\n", pthread_self_pointer ());
396 reclock_mutator_thread (void *arg
)
400 for (repeat
= REPEAT_COUNT
; repeat
> 0; repeat
--)
404 dbgprintf ("Mutator %p before check lock\n", pthread_self_pointer ());
405 ASSERT (pthread_mutex_lock (&my_reclock
) == 0);
407 ASSERT (pthread_mutex_unlock (&my_reclock
) == 0);
408 dbgprintf ("Mutator %p after check unlock\n", pthread_self_pointer ());
413 dbgprintf ("Mutator %p dying.\n", pthread_self_pointer ());
417 static struct atomic_int reclock_checker_done
;
420 reclock_checker_thread (void *arg
)
422 while (get_atomic_int_value (&reclock_checker_done
) == 0)
424 dbgprintf ("Checker %p before check lock\n", pthread_self_pointer ());
425 ASSERT (pthread_mutex_lock (&my_reclock
) == 0);
427 ASSERT (pthread_mutex_unlock (&my_reclock
) == 0);
428 dbgprintf ("Checker %p after check unlock\n", pthread_self_pointer ());
433 dbgprintf ("Checker %p dying.\n", pthread_self_pointer ());
438 test_pthread_mutex_recursive (void)
441 pthread_t checkerthread
;
442 pthread_t threads
[THREAD_COUNT
];
444 /* Initialization. */
445 for (i
= 0; i
< ACCOUNT_COUNT
; i
++)
447 init_atomic_int (&reclock_checker_done
);
448 set_atomic_int_value (&reclock_checker_done
, 0);
450 /* Spawn the threads. */
451 ASSERT (pthread_create (&checkerthread
, NULL
, reclock_checker_thread
, NULL
)
453 for (i
= 0; i
< THREAD_COUNT
; i
++)
454 ASSERT (pthread_create (&threads
[i
], NULL
, reclock_mutator_thread
, NULL
)
457 /* Wait for the threads to terminate. */
458 for (i
= 0; i
< THREAD_COUNT
; i
++)
459 ASSERT (pthread_join (threads
[i
], NULL
) == 0);
460 set_atomic_int_value (&reclock_checker_done
, 1);
461 ASSERT (pthread_join (checkerthread
, NULL
) == 0);
466 /* -------------------------------------------------------------------------- */
472 /* Declare failure if test takes too long, by using default abort
473 caused by SIGALRM. */
474 int alarm_value
= 600;
475 signal (SIGALRM
, SIG_DFL
);
480 pthread_mutexattr_t attr
;
482 ASSERT (pthread_mutexattr_init (&attr
) == 0);
483 ASSERT (pthread_mutexattr_settype (&attr
, PTHREAD_MUTEX_NORMAL
) == 0);
484 ASSERT (pthread_mutex_init (&my_lock
, &attr
) == 0);
485 ASSERT (pthread_mutexattr_destroy (&attr
) == 0);
489 pthread_mutexattr_t attr
;
491 ASSERT (pthread_mutexattr_init (&attr
) == 0);
492 ASSERT (pthread_mutexattr_settype (&attr
, PTHREAD_MUTEX_RECURSIVE
) == 0);
493 ASSERT (pthread_mutex_init (&my_reclock
, &attr
) == 0);
494 ASSERT (pthread_mutexattr_destroy (&attr
) == 0);
498 printf ("Starting test_pthread_mutex_normal ..."); fflush (stdout
);
499 test_pthread_mutex_normal ();
500 printf (" OK\n"); fflush (stdout
);
502 #if DO_TEST_RECURSIVE_LOCK
503 printf ("Starting test_pthread_mutex_recursive ..."); fflush (stdout
);
504 test_pthread_mutex_recursive ();
505 printf (" OK\n"); fflush (stdout
);
513 /* No multithreading available. */
520 fputs ("Skipping test: multithreading not enabled\n", stderr
);