expl: Work around inaccurate implementation on NetBSD.
[gnulib.git] / tests / test-lock.c
blob081cbf72a4d42a4896ab21e7337d8edd10999f32
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. */
19 #include <config.h>
21 #if USE_POSIX_THREADS || USE_SOLARIS_THREADS || USE_PTH_THREADS || USE_WINDOWS_THREADS
23 #if USE_POSIX_THREADS
24 # define TEST_POSIX_THREADS 1
25 #endif
26 #if USE_SOLARIS_THREADS
27 # define TEST_SOLARIS_THREADS 1
28 #endif
29 #if USE_PTH_THREADS
30 # define TEST_PTH_THREADS 1
31 #endif
32 #if USE_WINDOWS_THREADS
33 # define TEST_WINDOWS_THREADS 1
34 #endif
36 /* Whether to enable locking.
37 Uncomment this to get a test program without locking, to verify that
38 it crashes. */
39 #define ENABLE_LOCKING 1
41 /* Which tests to perform.
42 Uncomment some of these, to verify that all tests crash if no locking
43 is enabled. */
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
70 # else
71 # define USE_UNNAMED_SEMAPHORE 1
72 # endif
73 #endif
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
86 #include <stdio.h>
87 #include <stdlib.h>
88 #include <string.h>
90 #if !ENABLE_LOCKING
91 # undef USE_POSIX_THREADS
92 # undef USE_SOLARIS_THREADS
93 # undef USE_PTH_THREADS
94 # undef USE_WINDOWS_THREADS
95 #endif
96 #include "glthread/lock.h"
98 #if !ENABLE_LOCKING
99 # if TEST_POSIX_THREADS
100 # define USE_POSIX_THREADS 1
101 # endif
102 # if TEST_SOLARIS_THREADS
103 # define USE_SOLARIS_THREADS 1
104 # endif
105 # if TEST_PTH_THREADS
106 # define USE_PTH_THREADS 1
107 # endif
108 # if TEST_WINDOWS_THREADS
109 # define USE_WINDOWS_THREADS 1
110 # endif
111 #endif
113 #include "glthread/thread.h"
114 #include "glthread/yield.h"
115 #if USE_SEMAPHORE
116 # include <errno.h>
117 # include <fcntl.h>
118 # include <semaphore.h>
119 # include <unistd.h>
120 #endif
122 #if ENABLE_DEBUGGING
123 # define dbgprintf printf
124 #else
125 # define dbgprintf if (0) printf
126 #endif
128 #if EXPLICIT_YIELD
129 # define yield() gl_thread_yield ()
130 #else
131 # define yield()
132 #endif
134 #if USE_VOLATILE
135 struct atomic_int {
136 volatile int value;
138 static void
139 init_atomic_int (struct atomic_int *ai)
142 static int
143 get_atomic_int_value (struct atomic_int *ai)
145 return ai->value;
147 static void
148 set_atomic_int_value (struct atomic_int *ai, int new_value)
150 ai->value = new_value;
152 #elif USE_SEMAPHORE
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
156 struct atomic_int {
157 sem_t semaphore;
159 #define atomic_int_semaphore(ai) (&(ai)->semaphore)
160 static void
161 init_atomic_int (struct atomic_int *ai)
163 sem_init (&ai->semaphore, 0, 0);
165 # endif
166 # if USE_NAMED_SEMAPHORE
167 struct atomic_int {
168 sem_t *semaphore;
170 #define atomic_int_semaphore(ai) ((ai)->semaphore)
171 static void
172 init_atomic_int (struct atomic_int *ai)
174 sem_t *s;
175 unsigned int count;
176 for (count = 0; ; count++)
178 char name[80];
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);
187 if (s == SEM_FAILED)
189 if (errno == EEXIST)
190 /* Retry with a different name. */
191 continue;
192 else
194 perror ("sem_open failed");
195 abort ();
198 else
200 /* Try not to leave a semaphore hanging around on the file system
201 eternally, if we can avoid it. */
202 sem_unlink (name);
203 break;
206 ai->semaphore = s;
208 # endif
209 static int
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)))
215 abort ();
216 return 1;
218 else if (errno == EAGAIN)
219 return 0;
220 else
221 abort ();
223 static void
224 set_atomic_int_value (struct atomic_int *ai, int new_value)
226 if (new_value == 0)
227 /* It's already initialized with 0. */
228 return;
229 /* To set the value 1: */
230 if (sem_post (atomic_int_semaphore (ai)))
231 abort ();
233 #else
234 struct atomic_int {
235 gl_lock_define (, lock)
236 int value;
238 static void
239 init_atomic_int (struct atomic_int *ai)
241 gl_lock_init (ai->lock);
243 static int
244 get_atomic_int_value (struct atomic_int *ai)
246 gl_lock_lock (ai->lock);
247 int ret = ai->value;
248 gl_lock_unlock (ai->lock);
249 return ret;
251 static void
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);
258 #endif
260 #define ACCOUNT_COUNT 4
262 static int account[ACCOUNT_COUNT];
264 static int
265 random_account (void)
267 return ((unsigned int) rand () >> 3) % ACCOUNT_COUNT;
270 static void
271 check_accounts (void)
273 int i, sum;
275 sum = 0;
276 for (i = 0; i < ACCOUNT_COUNT; i++)
277 sum += account[i];
278 if (sum != ACCOUNT_COUNT * 1000)
279 abort ();
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)
291 static void *
292 lock_mutator_thread (void *arg)
294 int repeat;
296 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
298 int i1, i2, value;
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);
316 check_accounts ();
317 gl_lock_unlock (my_lock);
318 dbgprintf ("Mutator %p after check unlock\n", gl_thread_self_pointer ());
320 yield ();
323 dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
324 return NULL;
327 static struct atomic_int lock_checker_done;
329 static void *
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);
336 check_accounts ();
337 gl_lock_unlock (my_lock);
338 dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ());
340 yield ();
343 dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
344 return NULL;
347 static void
348 test_lock (void)
350 int i;
351 gl_thread_t checkerthread;
352 gl_thread_t threads[THREAD_COUNT];
354 /* Initialization. */
355 for (i = 0; i < ACCOUNT_COUNT; i++)
356 account[i] = 1000;
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);
370 check_accounts ();
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)
382 static void *
383 rwlock_mutator_thread (void *arg)
385 int repeat;
387 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
389 int i1, i2, value;
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 ());
405 yield ();
408 dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
409 return NULL;
412 static struct atomic_int rwlock_checker_done;
414 static void *
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);
421 check_accounts ();
422 gl_rwlock_unlock (my_rwlock);
423 dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ());
425 yield ();
428 dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
429 return NULL;
432 static void
433 test_rwlock (void)
435 int i;
436 gl_thread_t checkerthreads[THREAD_COUNT];
437 gl_thread_t threads[THREAD_COUNT];
439 /* Initialization. */
440 for (i = 0; i < ACCOUNT_COUNT; i++)
441 account[i] = 1000;
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);
457 check_accounts ();
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)
469 static void
470 recshuffle (void)
472 int i1, i2, value;
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)
486 recshuffle ();
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 ());
493 static void *
494 reclock_mutator_thread (void *arg)
496 int repeat;
498 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
500 recshuffle ();
502 dbgprintf ("Mutator %p before check lock\n", gl_thread_self_pointer ());
503 gl_recursive_lock_lock (my_reclock);
504 check_accounts ();
505 gl_recursive_lock_unlock (my_reclock);
506 dbgprintf ("Mutator %p after check unlock\n", gl_thread_self_pointer ());
508 yield ();
511 dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
512 return NULL;
515 static struct atomic_int reclock_checker_done;
517 static void *
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);
524 check_accounts ();
525 gl_recursive_lock_unlock (my_reclock);
526 dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ());
528 yield ();
531 dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
532 return NULL;
535 static void
536 test_recursive_lock (void)
538 int i;
539 gl_thread_t checkerthread;
540 gl_thread_t threads[THREAD_COUNT];
542 /* Initialization. */
543 for (i = 0; i < ACCOUNT_COUNT; i++)
544 account[i] = 1000;
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);
558 check_accounts ();
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];
570 #if ENABLE_LOCKING
571 static gl_rwlock_t fire_signal[REPEAT_COUNT];
572 #else
573 static volatile int fire_signal_state;
574 #endif
575 static gl_once_t once_control;
576 static int performed;
577 gl_lock_define_initialized(static, performed_lock)
579 static void
580 once_execute (void)
582 gl_lock_lock (performed_lock);
583 performed++;
584 gl_lock_unlock (performed_lock);
587 static void *
588 once_contender_thread (void *arg)
590 int id = (int) (long) arg;
591 int repeat;
593 for (repeat = 0; repeat <= REPEAT_COUNT; repeat++)
595 /* Tell the main thread that we're ready. */
596 gl_lock_lock (ready_lock[id]);
597 ready[id] = 1;
598 gl_lock_unlock (ready_lock[id]);
600 if (repeat == REPEAT_COUNT)
601 break;
603 dbgprintf ("Contender %p waiting for signal for round %d\n",
604 gl_thread_self_pointer (), repeat);
605 #if ENABLE_LOCKING
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]);
610 #else
611 /* Wait for the signal to go. */
612 while (fire_signal_state <= repeat)
613 yield ();
614 #endif
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);
622 return NULL;
625 static void
626 test_once (void)
628 int i, repeat;
629 gl_thread_t threads[THREAD_COUNT];
631 /* Initialize all variables. */
632 for (i = 0; i < THREAD_COUNT; i++)
634 ready[i] = 0;
635 gl_lock_init (ready_lock[i]);
637 #if ENABLE_LOCKING
638 for (i = 0; i < REPEAT_COUNT; i++)
639 gl_rwlock_init (fire_signal[i]);
640 #else
641 fire_signal_state = 0;
642 #endif
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);
656 for (;;)
658 int ready_count = 0;
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)
666 break;
667 yield ();
669 dbgprintf ("Main thread after synchronizing for round %d\n", repeat);
671 if (repeat > 0)
673 /* Check that exactly one thread executed the once_execute()
674 function. */
675 if (performed != 1)
676 abort ();
679 if (repeat == REPEAT_COUNT)
680 break;
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. */
686 performed = 0;
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]);
692 ready[i] = 0;
693 gl_lock_unlock (ready_lock[i]);
696 /* Signal all threads simultaneously. */
697 dbgprintf ("Main thread giving signal for round %d\n", repeat);
698 #if ENABLE_LOCKING
699 gl_rwlock_unlock (fire_signal[repeat]);
700 #else
701 fire_signal_state = repeat + 1;
702 #endif
705 /* Wait for the threads to terminate. */
706 for (i = 0; i < THREAD_COUNT; i++)
707 gl_thread_join (threads[i], NULL);
711 /* -------------------------------------------------------------------------- */
714 main ()
716 #if TEST_PTH_THREADS
717 if (!pth_init ())
718 abort ();
719 #endif
721 #if DO_TEST_LOCK
722 printf ("Starting test_lock ..."); fflush (stdout);
723 test_lock ();
724 printf (" OK\n"); fflush (stdout);
725 #endif
726 #if DO_TEST_RWLOCK
727 printf ("Starting test_rwlock ..."); fflush (stdout);
728 test_rwlock ();
729 printf (" OK\n"); fflush (stdout);
730 #endif
731 #if DO_TEST_RECURSIVE_LOCK
732 printf ("Starting test_recursive_lock ..."); fflush (stdout);
733 test_recursive_lock ();
734 printf (" OK\n"); fflush (stdout);
735 #endif
736 #if DO_TEST_ONCE
737 printf ("Starting test_once ..."); fflush (stdout);
738 test_once ();
739 printf (" OK\n"); fflush (stdout);
740 #endif
742 return 0;
745 #else
747 /* No multithreading available. */
749 #include <stdio.h>
752 main ()
754 fputs ("Skipping test: multithreading not enabled\n", stderr);
755 return 77;
758 #endif