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 /* Whether to enable locking.
22 Uncomment this to get a test program without locking, to verify that
24 #define ENABLE_LOCKING 1
26 /* Which tests to perform.
27 Uncomment some of these, to verify that all tests crash if no locking
29 #define DO_TEST_LOCK 1
30 #define DO_TEST_RECURSIVE_LOCK 1
31 #define DO_TEST_ONCE 1
33 /* Whether to help the scheduler through explicit thrd_yield().
34 Uncomment this to see if the operating system has a fair scheduler. */
35 #define EXPLICIT_YIELD 1
37 /* Whether to use 'volatile' on some variables that communicate information
38 between threads. If set to 0, a semaphore or a lock is used to protect
39 these variables. If set to 1, 'volatile' is used; this is theoretically
40 equivalent but can lead to much slower execution (e.g. 30x slower total
41 run time on a 40-core machine), because 'volatile' does not imply any
42 synchronization/communication between different CPUs. */
43 #define USE_VOLATILE 0
45 #if USE_POSIX_THREADS && HAVE_SEMAPHORE_H
46 /* Whether to use a semaphore to communicate information between threads.
47 If set to 0, a lock is used. If set to 1, a semaphore is used.
48 Uncomment this to reduce the dependencies of this test. */
49 # define USE_SEMAPHORE 1
50 /* Mac OS X provides only named semaphores (sem_open); its facility for
51 unnamed semaphores (sem_init) does not work. */
52 # if defined __APPLE__ && defined __MACH__
53 # define USE_NAMED_SEMAPHORE 1
55 # define USE_UNNAMED_SEMAPHORE 1
59 /* Whether to print debugging messages. */
60 #define ENABLE_DEBUGGING 0
62 /* Number of simultaneous threads. */
63 #define THREAD_COUNT 10
65 /* Number of operations performed in each thread.
66 This is quite high, because with a smaller count, say 5000, we often get
67 an "OK" result even without ENABLE_LOCKING (on Linux/x86). */
68 #define REPEAT_COUNT 50000
76 #include "glthread/lock.h"
81 # include <semaphore.h>
93 # define dbgprintf printf
95 # define dbgprintf if (0) printf
99 # define yield() thrd_yield ()
109 init_atomic_int (struct atomic_int
*ai
)
113 get_atomic_int_value (struct atomic_int
*ai
)
118 set_atomic_int_value (struct atomic_int
*ai
, int new_value
)
120 ai
->value
= new_value
;
123 /* This atomic_int implementation can only support the values 0 and 1.
124 It is initially 0 and can be set to 1 only once. */
125 # if USE_UNNAMED_SEMAPHORE
129 #define atomic_int_semaphore(ai) (&(ai)->semaphore)
131 init_atomic_int (struct atomic_int
*ai
)
133 sem_init (&ai
->semaphore
, 0, 0);
136 # if USE_NAMED_SEMAPHORE
140 #define atomic_int_semaphore(ai) ((ai)->semaphore)
142 init_atomic_int (struct atomic_int
*ai
)
146 for (count
= 0; ; count
++)
149 /* Use getpid() in the name, so that different processes running at the
150 same time will not interfere. Use ai in the name, so that different
151 atomic_int in the same process will not interfere. Use a count in
152 the name, so that even in the (unlikely) case that a semaphore with
153 the specified name already exists, we can try a different name. */
154 sprintf (name
, "test-lock-%lu-%p-%u",
155 (unsigned long) getpid (), ai
, count
);
156 s
= sem_open (name
, O_CREAT
| O_EXCL
, 0600, 0);
160 /* Retry with a different name. */
164 perror ("sem_open failed");
170 /* Try not to leave a semaphore hanging around on the file system
171 eternally, if we can avoid it. */
180 get_atomic_int_value (struct atomic_int
*ai
)
182 if (sem_trywait (atomic_int_semaphore (ai
)) == 0)
184 if (sem_post (atomic_int_semaphore (ai
)))
188 else if (errno
== EAGAIN
)
194 set_atomic_int_value (struct atomic_int
*ai
, int new_value
)
197 /* It's already initialized with 0. */
199 /* To set the value 1: */
200 if (sem_post (atomic_int_semaphore (ai
)))
209 init_atomic_int (struct atomic_int
*ai
)
211 ASSERT (mtx_init (&ai
->lock
, mtx_plain
) == thrd_success
);
214 get_atomic_int_value (struct atomic_int
*ai
)
216 ASSERT (mtx_lock (&ai
->lock
) == thrd_success
);
218 ASSERT (mtx_unlock (&ai
->lock
) == thrd_success
);
222 set_atomic_int_value (struct atomic_int
*ai
, int new_value
)
224 ASSERT (mtx_lock (&ai
->lock
) == thrd_success
);
225 ai
->value
= new_value
;
226 ASSERT (mtx_unlock (&ai
->lock
) == thrd_success
);
230 /* Returns a reference to the current thread as a pointer, for debugging. */
232 /* On IBM z/OS, pthread_t is a struct with an 8-byte '__' field.
233 The first three bytes of this field appear to uniquely identify a
234 pthread_t, though not necessarily representing a pointer. */
235 # define thrd_current_pointer() (*((void **) thrd_current ().__))
237 /* On Solaris, thrd_t is merely an 'unsigned int'. */
238 # define thrd_current_pointer() ((void *) (uintptr_t) thrd_current ())
240 # define thrd_current_pointer() ((void *) thrd_current ())
243 #define ACCOUNT_COUNT 4
245 static int account
[ACCOUNT_COUNT
];
248 random_account (void)
250 return ((unsigned int) rand () >> 3) % ACCOUNT_COUNT
;
254 check_accounts (void)
259 for (i
= 0; i
< ACCOUNT_COUNT
; i
++)
261 if (sum
!= ACCOUNT_COUNT
* 1000)
266 /* ------------------- Test normal (non-recursive) locks ------------------- */
268 /* Test normal locks by having several bank accounts and several threads
269 which shuffle around money between the accounts and another thread
270 checking that all the money is still there. */
272 static mtx_t my_lock
;
275 lock_mutator_thread (void *arg
)
279 for (repeat
= REPEAT_COUNT
; repeat
> 0; repeat
--)
283 dbgprintf ("Mutator %p before lock\n", thrd_current_pointer ());
284 ASSERT (mtx_lock (&my_lock
) == thrd_success
);
285 dbgprintf ("Mutator %p after lock\n", thrd_current_pointer ());
287 i1
= random_account ();
288 i2
= random_account ();
289 value
= ((unsigned int) rand () >> 3) % 10;
290 account
[i1
] += value
;
291 account
[i2
] -= value
;
293 dbgprintf ("Mutator %p before unlock\n", thrd_current_pointer ());
294 ASSERT (mtx_unlock (&my_lock
) == thrd_success
);
295 dbgprintf ("Mutator %p after unlock\n", thrd_current_pointer ());
297 dbgprintf ("Mutator %p before check lock\n", thrd_current_pointer ());
298 ASSERT (mtx_lock (&my_lock
) == thrd_success
);
300 ASSERT (mtx_unlock (&my_lock
) == thrd_success
);
301 dbgprintf ("Mutator %p after check unlock\n", thrd_current_pointer ());
306 dbgprintf ("Mutator %p dying.\n", thrd_current_pointer ());
310 static struct atomic_int lock_checker_done
;
313 lock_checker_thread (void *arg
)
315 while (get_atomic_int_value (&lock_checker_done
) == 0)
317 dbgprintf ("Checker %p before check lock\n", thrd_current_pointer ());
318 ASSERT (mtx_lock (&my_lock
) == thrd_success
);
320 ASSERT (mtx_unlock (&my_lock
) == thrd_success
);
321 dbgprintf ("Checker %p after check unlock\n", thrd_current_pointer ());
326 dbgprintf ("Checker %p dying.\n", thrd_current_pointer ());
331 test_mtx_plain (void)
334 thrd_t checkerthread
;
335 thrd_t threads
[THREAD_COUNT
];
337 /* Initialization. */
338 for (i
= 0; i
< ACCOUNT_COUNT
; i
++)
340 init_atomic_int (&lock_checker_done
);
341 set_atomic_int_value (&lock_checker_done
, 0);
343 /* Spawn the threads. */
344 ASSERT (thrd_create (&checkerthread
, lock_checker_thread
, NULL
)
346 for (i
= 0; i
< THREAD_COUNT
; i
++)
347 ASSERT (thrd_create (&threads
[i
], lock_mutator_thread
, NULL
)
350 /* Wait for the threads to terminate. */
351 for (i
= 0; i
< THREAD_COUNT
; i
++)
352 ASSERT (thrd_join (threads
[i
], NULL
) == thrd_success
);
353 set_atomic_int_value (&lock_checker_done
, 1);
354 ASSERT (thrd_join (checkerthread
, NULL
) == thrd_success
);
359 /* -------------------------- Test recursive locks -------------------------- */
361 /* Test recursive locks by having several bank accounts and several threads
362 which shuffle around money between the accounts (recursively) and another
363 thread checking that all the money is still there. */
365 static mtx_t my_reclock
;
372 dbgprintf ("Mutator %p before lock\n", thrd_current_pointer ());
373 ASSERT (mtx_lock (&my_reclock
) == thrd_success
);
374 dbgprintf ("Mutator %p after lock\n", thrd_current_pointer ());
376 i1
= random_account ();
377 i2
= random_account ();
378 value
= ((unsigned int) rand () >> 3) % 10;
379 account
[i1
] += value
;
380 account
[i2
] -= value
;
382 /* Recursive with probability 0.5. */
383 if (((unsigned int) rand () >> 3) % 2)
386 dbgprintf ("Mutator %p before unlock\n", thrd_current_pointer ());
387 ASSERT (mtx_unlock (&my_reclock
) == thrd_success
);
388 dbgprintf ("Mutator %p after unlock\n", thrd_current_pointer ());
392 reclock_mutator_thread (void *arg
)
396 for (repeat
= REPEAT_COUNT
; repeat
> 0; repeat
--)
400 dbgprintf ("Mutator %p before check lock\n", thrd_current_pointer ());
401 ASSERT (mtx_lock (&my_reclock
) == thrd_success
);
403 ASSERT (mtx_unlock (&my_reclock
) == thrd_success
);
404 dbgprintf ("Mutator %p after check unlock\n", thrd_current_pointer ());
409 dbgprintf ("Mutator %p dying.\n", thrd_current_pointer ());
413 static struct atomic_int reclock_checker_done
;
416 reclock_checker_thread (void *arg
)
418 while (get_atomic_int_value (&reclock_checker_done
) == 0)
420 dbgprintf ("Checker %p before check lock\n", thrd_current_pointer ());
421 ASSERT (mtx_lock (&my_reclock
) == thrd_success
);
423 ASSERT (mtx_unlock (&my_reclock
) == thrd_success
);
424 dbgprintf ("Checker %p after check unlock\n", thrd_current_pointer ());
429 dbgprintf ("Checker %p dying.\n", thrd_current_pointer ());
434 test_mtx_recursive (void)
437 thrd_t checkerthread
;
438 thrd_t threads
[THREAD_COUNT
];
440 /* Initialization. */
441 for (i
= 0; i
< ACCOUNT_COUNT
; i
++)
443 init_atomic_int (&reclock_checker_done
);
444 set_atomic_int_value (&reclock_checker_done
, 0);
446 /* Spawn the threads. */
447 ASSERT (thrd_create (&checkerthread
, reclock_checker_thread
, NULL
)
449 for (i
= 0; i
< THREAD_COUNT
; i
++)
450 ASSERT (thrd_create (&threads
[i
], reclock_mutator_thread
, NULL
)
453 /* Wait for the threads to terminate. */
454 for (i
= 0; i
< THREAD_COUNT
; i
++)
455 ASSERT (thrd_join (threads
[i
], NULL
) == thrd_success
);
456 set_atomic_int_value (&reclock_checker_done
, 1);
457 ASSERT (thrd_join (checkerthread
, NULL
) == thrd_success
);
462 /* ------------------------ Test once-only execution ------------------------ */
464 /* Test once-only execution by having several threads attempt to grab a
465 once-only task simultaneously (triggered by releasing a read-write lock). */
467 static once_flag fresh_once
= ONCE_FLAG_INIT
;
468 static int ready
[THREAD_COUNT
];
469 static mtx_t ready_lock
[THREAD_COUNT
];
471 static gl_rwlock_t fire_signal
[REPEAT_COUNT
];
473 static volatile int fire_signal_state
;
475 static once_flag once_control
;
476 static int performed
;
477 static mtx_t performed_lock
;
482 ASSERT (mtx_lock (&performed_lock
) == thrd_success
);
484 ASSERT (mtx_unlock (&performed_lock
) == thrd_success
);
488 once_contender_thread (void *arg
)
490 int id
= (int) (intptr_t) arg
;
493 for (repeat
= 0; repeat
<= REPEAT_COUNT
; repeat
++)
495 /* Tell the main thread that we're ready. */
496 ASSERT (mtx_lock (&ready_lock
[id
]) == thrd_success
);
498 ASSERT (mtx_unlock (&ready_lock
[id
]) == thrd_success
);
500 if (repeat
== REPEAT_COUNT
)
503 dbgprintf ("Contender %p waiting for signal for round %d\n",
504 thrd_current_pointer (), repeat
);
506 /* Wait for the signal to go. */
507 gl_rwlock_rdlock (fire_signal
[repeat
]);
508 /* And don't hinder the others (if the scheduler is unfair). */
509 gl_rwlock_unlock (fire_signal
[repeat
]);
511 /* Wait for the signal to go. */
512 while (fire_signal_state
<= repeat
)
515 dbgprintf ("Contender %p got the signal for round %d\n",
516 thrd_current_pointer (), repeat
);
518 /* Contend for execution. */
519 call_once (&once_control
, once_execute
);
529 thrd_t threads
[THREAD_COUNT
];
531 /* Initialize all variables. */
532 for (i
= 0; i
< THREAD_COUNT
; i
++)
535 ASSERT (mtx_init (&ready_lock
[i
], mtx_plain
) == thrd_success
);
538 for (i
= 0; i
< REPEAT_COUNT
; i
++)
539 gl_rwlock_init (fire_signal
[i
]);
541 fire_signal_state
= 0;
545 /* Block all fire_signals. */
546 for (i
= REPEAT_COUNT
-1; i
>= 0; i
--)
547 gl_rwlock_wrlock (fire_signal
[i
]);
550 /* Spawn the threads. */
551 for (i
= 0; i
< THREAD_COUNT
; i
++)
552 ASSERT (thrd_create (&threads
[i
],
553 once_contender_thread
, (void *) (intptr_t) i
)
556 for (repeat
= 0; repeat
<= REPEAT_COUNT
; repeat
++)
558 /* Wait until every thread is ready. */
559 dbgprintf ("Main thread before synchronizing for round %d\n", repeat
);
563 for (i
= 0; i
< THREAD_COUNT
; i
++)
565 ASSERT (mtx_lock (&ready_lock
[i
]) == thrd_success
);
566 ready_count
+= ready
[i
];
567 ASSERT (mtx_unlock (&ready_lock
[i
]) == thrd_success
);
569 if (ready_count
== THREAD_COUNT
)
573 dbgprintf ("Main thread after synchronizing for round %d\n", repeat
);
577 /* Check that exactly one thread executed the once_execute()
583 if (repeat
== REPEAT_COUNT
)
586 /* Preparation for the next round: Initialize once_control. */
587 memcpy (&once_control
, &fresh_once
, sizeof (once_flag
));
589 /* Preparation for the next round: Reset the performed counter. */
592 /* Preparation for the next round: Reset the ready flags. */
593 for (i
= 0; i
< THREAD_COUNT
; i
++)
595 ASSERT (mtx_lock (&ready_lock
[i
]) == thrd_success
);
597 ASSERT (mtx_unlock (&ready_lock
[i
]) == thrd_success
);
600 /* Signal all threads simultaneously. */
601 dbgprintf ("Main thread giving signal for round %d\n", repeat
);
603 gl_rwlock_unlock (fire_signal
[repeat
]);
605 fire_signal_state
= repeat
+ 1;
609 /* Wait for the threads to terminate. */
610 for (i
= 0; i
< THREAD_COUNT
; i
++)
611 ASSERT (thrd_join (threads
[i
], NULL
) == thrd_success
);
615 /* -------------------------------------------------------------------------- */
621 /* Declare failure if test takes too long, by using default abort
622 caused by SIGALRM. */
623 int alarm_value
= 600;
624 signal (SIGALRM
, SIG_DFL
);
628 ASSERT (mtx_init (&my_lock
, mtx_plain
) == thrd_success
);
629 ASSERT (mtx_init (&my_reclock
, mtx_plain
| mtx_recursive
) == thrd_success
);
630 ASSERT (mtx_init (&performed_lock
, mtx_plain
) == thrd_success
);
633 printf ("Starting test_mtx_plain ..."); fflush (stdout
);
635 printf (" OK\n"); fflush (stdout
);
637 #if DO_TEST_RECURSIVE_LOCK
638 printf ("Starting test_mtx_recursive ..."); fflush (stdout
);
639 test_mtx_recursive ();
640 printf (" OK\n"); fflush (stdout
);
643 printf ("Starting test_once ..."); fflush (stdout
);
645 printf (" OK\n"); fflush (stdout
);