1 /* Test of locking in multithreaded situations.
2 Copyright (C) 2005, 2008-2019 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_POSIX_THREADS || USE_SOLARIS_THREADS || USE_PTH_THREADS || USE_WINDOWS_THREADS
24 # define TEST_POSIX_THREADS 1
26 #if USE_SOLARIS_THREADS
27 # define TEST_SOLARIS_THREADS 1
30 # define TEST_PTH_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 use 'volatile' on some variables that communicate information
54 between threads. If set to 0, a semaphore or a lock is used to protect
55 these variables. If set to 1, 'volatile' is used; this is theoretically
56 equivalent but can lead to much slower execution (e.g. 30x slower total
57 run time on a 40-core machine), because 'volatile' does not imply any
58 synchronization/communication between different CPUs. */
59 #define USE_VOLATILE 0
61 #if USE_POSIX_THREADS && HAVE_SEMAPHORE_H
62 /* Whether to use a semaphore to communicate information between threads.
63 If set to 0, a lock is used. If set to 1, a semaphore is used.
64 Uncomment this to reduce the dependencies of this test. */
65 # define USE_SEMAPHORE 1
66 /* Mac OS X provides only named semaphores (sem_open); its facility for
67 unnamed semaphores (sem_init) does not work. */
68 # if defined __APPLE__ && defined __MACH__
69 # define USE_NAMED_SEMAPHORE 1
71 # define USE_UNNAMED_SEMAPHORE 1
75 /* Whether to print debugging messages. */
76 #define ENABLE_DEBUGGING 0
78 /* Number of simultaneous threads. */
79 #define THREAD_COUNT 10
81 /* Number of operations performed in each thread.
82 This is quite high, because with a smaller count, say 5000, we often get
83 an "OK" result even without ENABLE_LOCKING (on Linux/x86). */
84 #define REPEAT_COUNT 50000
91 # undef USE_POSIX_THREADS
92 # undef USE_SOLARIS_THREADS
93 # undef USE_PTH_THREADS
94 # undef USE_WINDOWS_THREADS
96 #include "glthread/lock.h"
99 # if TEST_POSIX_THREADS
100 # define USE_POSIX_THREADS 1
102 # if TEST_SOLARIS_THREADS
103 # define USE_SOLARIS_THREADS 1
105 # if TEST_PTH_THREADS
106 # define USE_PTH_THREADS 1
108 # if TEST_WINDOWS_THREADS
109 # define USE_WINDOWS_THREADS 1
113 #include "glthread/thread.h"
114 #include "glthread/yield.h"
118 # include <semaphore.h>
123 # define dbgprintf printf
125 # define dbgprintf if (0) printf
129 # define yield() gl_thread_yield ()
139 init_atomic_int (struct atomic_int
*ai
)
143 get_atomic_int_value (struct atomic_int
*ai
)
148 set_atomic_int_value (struct atomic_int
*ai
, int new_value
)
150 ai
->value
= new_value
;
153 /* This atomic_int implementation can only support the values 0 and 1.
154 It is initially 0 and can be set to 1 only once. */
155 # if USE_UNNAMED_SEMAPHORE
159 #define atomic_int_semaphore(ai) (&(ai)->semaphore)
161 init_atomic_int (struct atomic_int
*ai
)
163 sem_init (&ai
->semaphore
, 0, 0);
166 # if USE_NAMED_SEMAPHORE
170 #define atomic_int_semaphore(ai) ((ai)->semaphore)
172 init_atomic_int (struct atomic_int
*ai
)
176 for (count
= 0; ; count
++)
179 /* Use getpid() in the name, so that different processes running at the
180 same time will not interfere. Use ai in the name, so that different
181 atomic_int in the same process will not interfere. Use a count in
182 the name, so that even in the (unlikely) case that a semaphore with
183 the specified name already exists, we can try a different name. */
184 sprintf (name
, "test-lock-%lu-%p-%u",
185 (unsigned long) getpid (), ai
, count
);
186 s
= sem_open (name
, O_CREAT
| O_EXCL
, 0600, 0);
190 /* Retry with a different name. */
194 perror ("sem_open failed");
200 /* Try not to leave a semaphore hanging around on the file system
201 eternally, if we can avoid it. */
210 get_atomic_int_value (struct atomic_int
*ai
)
212 if (sem_trywait (atomic_int_semaphore (ai
)) == 0)
214 if (sem_post (atomic_int_semaphore (ai
)))
218 else if (errno
== EAGAIN
)
224 set_atomic_int_value (struct atomic_int
*ai
, int new_value
)
227 /* It's already initialized with 0. */
229 /* To set the value 1: */
230 if (sem_post (atomic_int_semaphore (ai
)))
235 gl_lock_define (, lock
)
239 init_atomic_int (struct atomic_int
*ai
)
241 gl_lock_init (ai
->lock
);
244 get_atomic_int_value (struct atomic_int
*ai
)
246 gl_lock_lock (ai
->lock
);
248 gl_lock_unlock (ai
->lock
);
252 set_atomic_int_value (struct atomic_int
*ai
, int new_value
)
254 gl_lock_lock (ai
->lock
);
255 ai
->value
= new_value
;
256 gl_lock_unlock (ai
->lock
);
260 #define ACCOUNT_COUNT 4
262 static int account
[ACCOUNT_COUNT
];
265 random_account (void)
267 return ((unsigned int) rand () >> 3) % ACCOUNT_COUNT
;
271 check_accounts (void)
276 for (i
= 0; i
< ACCOUNT_COUNT
; i
++)
278 if (sum
!= ACCOUNT_COUNT
* 1000)
283 /* ------------------- Test normal (non-recursive) locks ------------------- */
285 /* Test normal locks by having several bank accounts and several threads
286 which shuffle around money between the accounts and another thread
287 checking that all the money is still there. */
289 gl_lock_define_initialized(static, my_lock
)
292 lock_mutator_thread (void *arg
)
296 for (repeat
= REPEAT_COUNT
; repeat
> 0; repeat
--)
300 dbgprintf ("Mutator %p before lock\n", gl_thread_self_pointer ());
301 gl_lock_lock (my_lock
);
302 dbgprintf ("Mutator %p after lock\n", gl_thread_self_pointer ());
304 i1
= random_account ();
305 i2
= random_account ();
306 value
= ((unsigned int) rand () >> 3) % 10;
307 account
[i1
] += value
;
308 account
[i2
] -= value
;
310 dbgprintf ("Mutator %p before unlock\n", gl_thread_self_pointer ());
311 gl_lock_unlock (my_lock
);
312 dbgprintf ("Mutator %p after unlock\n", gl_thread_self_pointer ());
314 dbgprintf ("Mutator %p before check lock\n", gl_thread_self_pointer ());
315 gl_lock_lock (my_lock
);
317 gl_lock_unlock (my_lock
);
318 dbgprintf ("Mutator %p after check unlock\n", gl_thread_self_pointer ());
323 dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
327 static struct atomic_int lock_checker_done
;
330 lock_checker_thread (void *arg
)
332 while (get_atomic_int_value (&lock_checker_done
) == 0)
334 dbgprintf ("Checker %p before check lock\n", gl_thread_self_pointer ());
335 gl_lock_lock (my_lock
);
337 gl_lock_unlock (my_lock
);
338 dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ());
343 dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
351 gl_thread_t checkerthread
;
352 gl_thread_t threads
[THREAD_COUNT
];
354 /* Initialization. */
355 for (i
= 0; i
< ACCOUNT_COUNT
; i
++)
357 init_atomic_int (&lock_checker_done
);
358 set_atomic_int_value (&lock_checker_done
, 0);
360 /* Spawn the threads. */
361 checkerthread
= gl_thread_create (lock_checker_thread
, NULL
);
362 for (i
= 0; i
< THREAD_COUNT
; i
++)
363 threads
[i
] = gl_thread_create (lock_mutator_thread
, NULL
);
365 /* Wait for the threads to terminate. */
366 for (i
= 0; i
< THREAD_COUNT
; i
++)
367 gl_thread_join (threads
[i
], NULL
);
368 set_atomic_int_value (&lock_checker_done
, 1);
369 gl_thread_join (checkerthread
, NULL
);
374 /* ----------------- Test read-write (non-recursive) locks ----------------- */
376 /* Test read-write locks by having several bank accounts and several threads
377 which shuffle around money between the accounts and several other threads
378 that check that all the money is still there. */
380 gl_rwlock_define_initialized(static, my_rwlock
)
383 rwlock_mutator_thread (void *arg
)
387 for (repeat
= REPEAT_COUNT
; repeat
> 0; repeat
--)
391 dbgprintf ("Mutator %p before wrlock\n", gl_thread_self_pointer ());
392 gl_rwlock_wrlock (my_rwlock
);
393 dbgprintf ("Mutator %p after wrlock\n", gl_thread_self_pointer ());
395 i1
= random_account ();
396 i2
= random_account ();
397 value
= ((unsigned int) rand () >> 3) % 10;
398 account
[i1
] += value
;
399 account
[i2
] -= value
;
401 dbgprintf ("Mutator %p before unlock\n", gl_thread_self_pointer ());
402 gl_rwlock_unlock (my_rwlock
);
403 dbgprintf ("Mutator %p after unlock\n", gl_thread_self_pointer ());
408 dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
412 static struct atomic_int rwlock_checker_done
;
415 rwlock_checker_thread (void *arg
)
417 while (get_atomic_int_value (&rwlock_checker_done
) == 0)
419 dbgprintf ("Checker %p before check rdlock\n", gl_thread_self_pointer ());
420 gl_rwlock_rdlock (my_rwlock
);
422 gl_rwlock_unlock (my_rwlock
);
423 dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ());
428 dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
436 gl_thread_t checkerthreads
[THREAD_COUNT
];
437 gl_thread_t threads
[THREAD_COUNT
];
439 /* Initialization. */
440 for (i
= 0; i
< ACCOUNT_COUNT
; i
++)
442 init_atomic_int (&rwlock_checker_done
);
443 set_atomic_int_value (&rwlock_checker_done
, 0);
445 /* Spawn the threads. */
446 for (i
= 0; i
< THREAD_COUNT
; i
++)
447 checkerthreads
[i
] = gl_thread_create (rwlock_checker_thread
, NULL
);
448 for (i
= 0; i
< THREAD_COUNT
; i
++)
449 threads
[i
] = gl_thread_create (rwlock_mutator_thread
, NULL
);
451 /* Wait for the threads to terminate. */
452 for (i
= 0; i
< THREAD_COUNT
; i
++)
453 gl_thread_join (threads
[i
], NULL
);
454 set_atomic_int_value (&rwlock_checker_done
, 1);
455 for (i
= 0; i
< THREAD_COUNT
; i
++)
456 gl_thread_join (checkerthreads
[i
], NULL
);
461 /* -------------------------- Test recursive locks -------------------------- */
463 /* Test recursive locks by having several bank accounts and several threads
464 which shuffle around money between the accounts (recursively) and another
465 thread checking that all the money is still there. */
467 gl_recursive_lock_define_initialized(static, my_reclock
)
474 dbgprintf ("Mutator %p before lock\n", gl_thread_self_pointer ());
475 gl_recursive_lock_lock (my_reclock
);
476 dbgprintf ("Mutator %p after lock\n", gl_thread_self_pointer ());
478 i1
= random_account ();
479 i2
= random_account ();
480 value
= ((unsigned int) rand () >> 3) % 10;
481 account
[i1
] += value
;
482 account
[i2
] -= value
;
484 /* Recursive with probability 0.5. */
485 if (((unsigned int) rand () >> 3) % 2)
488 dbgprintf ("Mutator %p before unlock\n", gl_thread_self_pointer ());
489 gl_recursive_lock_unlock (my_reclock
);
490 dbgprintf ("Mutator %p after unlock\n", gl_thread_self_pointer ());
494 reclock_mutator_thread (void *arg
)
498 for (repeat
= REPEAT_COUNT
; repeat
> 0; repeat
--)
502 dbgprintf ("Mutator %p before check lock\n", gl_thread_self_pointer ());
503 gl_recursive_lock_lock (my_reclock
);
505 gl_recursive_lock_unlock (my_reclock
);
506 dbgprintf ("Mutator %p after check unlock\n", gl_thread_self_pointer ());
511 dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
515 static struct atomic_int reclock_checker_done
;
518 reclock_checker_thread (void *arg
)
520 while (get_atomic_int_value (&reclock_checker_done
) == 0)
522 dbgprintf ("Checker %p before check lock\n", gl_thread_self_pointer ());
523 gl_recursive_lock_lock (my_reclock
);
525 gl_recursive_lock_unlock (my_reclock
);
526 dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ());
531 dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
536 test_recursive_lock (void)
539 gl_thread_t checkerthread
;
540 gl_thread_t threads
[THREAD_COUNT
];
542 /* Initialization. */
543 for (i
= 0; i
< ACCOUNT_COUNT
; i
++)
545 init_atomic_int (&reclock_checker_done
);
546 set_atomic_int_value (&reclock_checker_done
, 0);
548 /* Spawn the threads. */
549 checkerthread
= gl_thread_create (reclock_checker_thread
, NULL
);
550 for (i
= 0; i
< THREAD_COUNT
; i
++)
551 threads
[i
] = gl_thread_create (reclock_mutator_thread
, NULL
);
553 /* Wait for the threads to terminate. */
554 for (i
= 0; i
< THREAD_COUNT
; i
++)
555 gl_thread_join (threads
[i
], NULL
);
556 set_atomic_int_value (&reclock_checker_done
, 1);
557 gl_thread_join (checkerthread
, NULL
);
562 /* ------------------------ Test once-only execution ------------------------ */
564 /* Test once-only execution by having several threads attempt to grab a
565 once-only task simultaneously (triggered by releasing a read-write lock). */
567 gl_once_define(static, fresh_once
)
568 static int ready
[THREAD_COUNT
];
569 static gl_lock_t ready_lock
[THREAD_COUNT
];
571 static gl_rwlock_t fire_signal
[REPEAT_COUNT
];
573 static volatile int fire_signal_state
;
575 static gl_once_t once_control
;
576 static int performed
;
577 gl_lock_define_initialized(static, performed_lock
)
582 gl_lock_lock (performed_lock
);
584 gl_lock_unlock (performed_lock
);
588 once_contender_thread (void *arg
)
590 int id
= (int) (long) arg
;
593 for (repeat
= 0; repeat
<= REPEAT_COUNT
; repeat
++)
595 /* Tell the main thread that we're ready. */
596 gl_lock_lock (ready_lock
[id
]);
598 gl_lock_unlock (ready_lock
[id
]);
600 if (repeat
== REPEAT_COUNT
)
603 dbgprintf ("Contender %p waiting for signal for round %d\n",
604 gl_thread_self_pointer (), repeat
);
606 /* Wait for the signal to go. */
607 gl_rwlock_rdlock (fire_signal
[repeat
]);
608 /* And don't hinder the others (if the scheduler is unfair). */
609 gl_rwlock_unlock (fire_signal
[repeat
]);
611 /* Wait for the signal to go. */
612 while (fire_signal_state
<= repeat
)
615 dbgprintf ("Contender %p got the signal for round %d\n",
616 gl_thread_self_pointer (), repeat
);
618 /* Contend for execution. */
619 gl_once (once_control
, once_execute
);
629 gl_thread_t threads
[THREAD_COUNT
];
631 /* Initialize all variables. */
632 for (i
= 0; i
< THREAD_COUNT
; i
++)
635 gl_lock_init (ready_lock
[i
]);
638 for (i
= 0; i
< REPEAT_COUNT
; i
++)
639 gl_rwlock_init (fire_signal
[i
]);
641 fire_signal_state
= 0;
644 /* Block all fire_signals. */
645 for (i
= REPEAT_COUNT
-1; i
>= 0; i
--)
646 gl_rwlock_wrlock (fire_signal
[i
]);
648 /* Spawn the threads. */
649 for (i
= 0; i
< THREAD_COUNT
; i
++)
650 threads
[i
] = gl_thread_create (once_contender_thread
, (void *) (long) i
);
652 for (repeat
= 0; repeat
<= REPEAT_COUNT
; repeat
++)
654 /* Wait until every thread is ready. */
655 dbgprintf ("Main thread before synchronizing for round %d\n", repeat
);
659 for (i
= 0; i
< THREAD_COUNT
; i
++)
661 gl_lock_lock (ready_lock
[i
]);
662 ready_count
+= ready
[i
];
663 gl_lock_unlock (ready_lock
[i
]);
665 if (ready_count
== THREAD_COUNT
)
669 dbgprintf ("Main thread after synchronizing for round %d\n", repeat
);
673 /* Check that exactly one thread executed the once_execute()
679 if (repeat
== REPEAT_COUNT
)
682 /* Preparation for the next round: Initialize once_control. */
683 memcpy (&once_control
, &fresh_once
, sizeof (gl_once_t
));
685 /* Preparation for the next round: Reset the performed counter. */
688 /* Preparation for the next round: Reset the ready flags. */
689 for (i
= 0; i
< THREAD_COUNT
; i
++)
691 gl_lock_lock (ready_lock
[i
]);
693 gl_lock_unlock (ready_lock
[i
]);
696 /* Signal all threads simultaneously. */
697 dbgprintf ("Main thread giving signal for round %d\n", repeat
);
699 gl_rwlock_unlock (fire_signal
[repeat
]);
701 fire_signal_state
= repeat
+ 1;
705 /* Wait for the threads to terminate. */
706 for (i
= 0; i
< THREAD_COUNT
; i
++)
707 gl_thread_join (threads
[i
], NULL
);
711 /* -------------------------------------------------------------------------- */
722 printf ("Starting test_lock ..."); fflush (stdout
);
724 printf (" OK\n"); fflush (stdout
);
727 printf ("Starting test_rwlock ..."); fflush (stdout
);
729 printf (" OK\n"); fflush (stdout
);
731 #if DO_TEST_RECURSIVE_LOCK
732 printf ("Starting test_recursive_lock ..."); fflush (stdout
);
733 test_recursive_lock ();
734 printf (" OK\n"); fflush (stdout
);
737 printf ("Starting test_once ..."); fflush (stdout
);
739 printf (" OK\n"); fflush (stdout
);
747 /* No multithreading available. */
754 fputs ("Skipping test: multithreading not enabled\n", stderr
);