c32snrtombs: Add tests.
[gnulib.git] / tests / test-mtx.c
blobcbd6fd45a3c03d553ea651f44bcf5d25e452054a
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. */
19 #include <config.h>
21 /* Whether to enable locking.
22 Uncomment this to get a test program without locking, to verify that
23 it crashes. */
24 #define ENABLE_LOCKING 1
26 /* Which tests to perform.
27 Uncomment some of these, to verify that all tests crash if no locking
28 is enabled. */
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
54 # else
55 # define USE_UNNAMED_SEMAPHORE 1
56 # endif
57 #endif
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
70 #include <threads.h>
71 #include <stdint.h>
72 #include <stdio.h>
73 #include <stdlib.h>
74 #include <string.h>
76 #include "glthread/lock.h"
78 #if USE_SEMAPHORE
79 # include <errno.h>
80 # include <fcntl.h>
81 # include <semaphore.h>
82 # include <unistd.h>
83 #endif
85 #if HAVE_DECL_ALARM
86 # include <signal.h>
87 # include <unistd.h>
88 #endif
90 #include "macros.h"
92 #if ENABLE_DEBUGGING
93 # define dbgprintf printf
94 #else
95 # define dbgprintf if (0) printf
96 #endif
98 #if EXPLICIT_YIELD
99 # define yield() thrd_yield ()
100 #else
101 # define yield()
102 #endif
104 #if USE_VOLATILE
105 struct atomic_int {
106 volatile int value;
108 static void
109 init_atomic_int (struct atomic_int *ai)
112 static int
113 get_atomic_int_value (struct atomic_int *ai)
115 return ai->value;
117 static void
118 set_atomic_int_value (struct atomic_int *ai, int new_value)
120 ai->value = new_value;
122 #elif USE_SEMAPHORE
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
126 struct atomic_int {
127 sem_t semaphore;
129 #define atomic_int_semaphore(ai) (&(ai)->semaphore)
130 static void
131 init_atomic_int (struct atomic_int *ai)
133 sem_init (&ai->semaphore, 0, 0);
135 # endif
136 # if USE_NAMED_SEMAPHORE
137 struct atomic_int {
138 sem_t *semaphore;
140 #define atomic_int_semaphore(ai) ((ai)->semaphore)
141 static void
142 init_atomic_int (struct atomic_int *ai)
144 sem_t *s;
145 unsigned int count;
146 for (count = 0; ; count++)
148 char name[80];
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);
157 if (s == SEM_FAILED)
159 if (errno == EEXIST)
160 /* Retry with a different name. */
161 continue;
162 else
164 perror ("sem_open failed");
165 abort ();
168 else
170 /* Try not to leave a semaphore hanging around on the file system
171 eternally, if we can avoid it. */
172 sem_unlink (name);
173 break;
176 ai->semaphore = s;
178 # endif
179 static int
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)))
185 abort ();
186 return 1;
188 else if (errno == EAGAIN)
189 return 0;
190 else
191 abort ();
193 static void
194 set_atomic_int_value (struct atomic_int *ai, int new_value)
196 if (new_value == 0)
197 /* It's already initialized with 0. */
198 return;
199 /* To set the value 1: */
200 if (sem_post (atomic_int_semaphore (ai)))
201 abort ();
203 #else
204 struct atomic_int {
205 mtx_t lock;
206 int value;
208 static void
209 init_atomic_int (struct atomic_int *ai)
211 ASSERT (mtx_init (&ai->lock, mtx_plain) == thrd_success);
213 static int
214 get_atomic_int_value (struct atomic_int *ai)
216 ASSERT (mtx_lock (&ai->lock) == thrd_success);
217 int ret = ai->value;
218 ASSERT (mtx_unlock (&ai->lock) == thrd_success);
219 return ret;
221 static void
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);
228 #endif
230 /* Returns a reference to the current thread as a pointer, for debugging. */
231 #if defined __MVS__
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 ().__))
236 #elif defined __sun
237 /* On Solaris, thrd_t is merely an 'unsigned int'. */
238 # define thrd_current_pointer() ((void *) (uintptr_t) thrd_current ())
239 #else
240 # define thrd_current_pointer() ((void *) thrd_current ())
241 #endif
243 #define ACCOUNT_COUNT 4
245 static int account[ACCOUNT_COUNT];
247 static int
248 random_account (void)
250 return ((unsigned int) rand () >> 3) % ACCOUNT_COUNT;
253 static void
254 check_accounts (void)
256 int i, sum;
258 sum = 0;
259 for (i = 0; i < ACCOUNT_COUNT; i++)
260 sum += account[i];
261 if (sum != ACCOUNT_COUNT * 1000)
262 abort ();
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;
274 static int
275 lock_mutator_thread (void *arg)
277 int repeat;
279 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
281 int i1, i2, value;
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);
299 check_accounts ();
300 ASSERT (mtx_unlock (&my_lock) == thrd_success);
301 dbgprintf ("Mutator %p after check unlock\n", thrd_current_pointer ());
303 yield ();
306 dbgprintf ("Mutator %p dying.\n", thrd_current_pointer ());
307 return 0;
310 static struct atomic_int lock_checker_done;
312 static int
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);
319 check_accounts ();
320 ASSERT (mtx_unlock (&my_lock) == thrd_success);
321 dbgprintf ("Checker %p after check unlock\n", thrd_current_pointer ());
323 yield ();
326 dbgprintf ("Checker %p dying.\n", thrd_current_pointer ());
327 return 0;
330 static void
331 test_mtx_plain (void)
333 int i;
334 thrd_t checkerthread;
335 thrd_t threads[THREAD_COUNT];
337 /* Initialization. */
338 for (i = 0; i < ACCOUNT_COUNT; i++)
339 account[i] = 1000;
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)
345 == thrd_success);
346 for (i = 0; i < THREAD_COUNT; i++)
347 ASSERT (thrd_create (&threads[i], lock_mutator_thread, NULL)
348 == thrd_success);
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);
355 check_accounts ();
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;
367 static void
368 recshuffle (void)
370 int i1, i2, value;
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)
384 recshuffle ();
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 ());
391 static int
392 reclock_mutator_thread (void *arg)
394 int repeat;
396 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
398 recshuffle ();
400 dbgprintf ("Mutator %p before check lock\n", thrd_current_pointer ());
401 ASSERT (mtx_lock (&my_reclock) == thrd_success);
402 check_accounts ();
403 ASSERT (mtx_unlock (&my_reclock) == thrd_success);
404 dbgprintf ("Mutator %p after check unlock\n", thrd_current_pointer ());
406 yield ();
409 dbgprintf ("Mutator %p dying.\n", thrd_current_pointer ());
410 return 0;
413 static struct atomic_int reclock_checker_done;
415 static int
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);
422 check_accounts ();
423 ASSERT (mtx_unlock (&my_reclock) == thrd_success);
424 dbgprintf ("Checker %p after check unlock\n", thrd_current_pointer ());
426 yield ();
429 dbgprintf ("Checker %p dying.\n", thrd_current_pointer ());
430 return 0;
433 static void
434 test_mtx_recursive (void)
436 int i;
437 thrd_t checkerthread;
438 thrd_t threads[THREAD_COUNT];
440 /* Initialization. */
441 for (i = 0; i < ACCOUNT_COUNT; i++)
442 account[i] = 1000;
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)
448 == thrd_success);
449 for (i = 0; i < THREAD_COUNT; i++)
450 ASSERT (thrd_create (&threads[i], reclock_mutator_thread, NULL)
451 == thrd_success);
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);
458 check_accounts ();
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];
470 #if ENABLE_LOCKING
471 static gl_rwlock_t fire_signal[REPEAT_COUNT];
472 #else
473 static volatile int fire_signal_state;
474 #endif
475 static once_flag once_control;
476 static int performed;
477 static mtx_t performed_lock;
479 static void
480 once_execute (void)
482 ASSERT (mtx_lock (&performed_lock) == thrd_success);
483 performed++;
484 ASSERT (mtx_unlock (&performed_lock) == thrd_success);
487 static int
488 once_contender_thread (void *arg)
490 int id = (int) (intptr_t) arg;
491 int repeat;
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);
497 ready[id] = 1;
498 ASSERT (mtx_unlock (&ready_lock[id]) == thrd_success);
500 if (repeat == REPEAT_COUNT)
501 break;
503 dbgprintf ("Contender %p waiting for signal for round %d\n",
504 thrd_current_pointer (), repeat);
505 #if ENABLE_LOCKING
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]);
510 #else
511 /* Wait for the signal to go. */
512 while (fire_signal_state <= repeat)
513 yield ();
514 #endif
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);
522 return 0;
525 static void
526 test_once (void)
528 int i, repeat;
529 thrd_t threads[THREAD_COUNT];
531 /* Initialize all variables. */
532 for (i = 0; i < THREAD_COUNT; i++)
534 ready[i] = 0;
535 ASSERT (mtx_init (&ready_lock[i], mtx_plain) == thrd_success);
537 #if ENABLE_LOCKING
538 for (i = 0; i < REPEAT_COUNT; i++)
539 gl_rwlock_init (fire_signal[i]);
540 #else
541 fire_signal_state = 0;
542 #endif
544 #if ENABLE_LOCKING
545 /* Block all fire_signals. */
546 for (i = REPEAT_COUNT-1; i >= 0; i--)
547 gl_rwlock_wrlock (fire_signal[i]);
548 #endif
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)
554 == thrd_success);
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);
560 for (;;)
562 int ready_count = 0;
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)
570 break;
571 yield ();
573 dbgprintf ("Main thread after synchronizing for round %d\n", repeat);
575 if (repeat > 0)
577 /* Check that exactly one thread executed the once_execute()
578 function. */
579 if (performed != 1)
580 abort ();
583 if (repeat == REPEAT_COUNT)
584 break;
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. */
590 performed = 0;
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);
596 ready[i] = 0;
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);
602 #if ENABLE_LOCKING
603 gl_rwlock_unlock (fire_signal[repeat]);
604 #else
605 fire_signal_state = repeat + 1;
606 #endif
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 /* -------------------------------------------------------------------------- */
618 main ()
620 #if HAVE_DECL_ALARM
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);
625 alarm (alarm_value);
626 #endif
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);
632 #if DO_TEST_LOCK
633 printf ("Starting test_mtx_plain ..."); fflush (stdout);
634 test_mtx_plain ();
635 printf (" OK\n"); fflush (stdout);
636 #endif
637 #if DO_TEST_RECURSIVE_LOCK
638 printf ("Starting test_mtx_recursive ..."); fflush (stdout);
639 test_mtx_recursive ();
640 printf (" OK\n"); fflush (stdout);
641 #endif
642 #if DO_TEST_ONCE
643 printf ("Starting test_once ..."); fflush (stdout);
644 test_once ();
645 printf (" OK\n"); fflush (stdout);
646 #endif
648 return 0;