openat: Fix theoretically possible issue on GNU/Hurd.
[gnulib.git] / tests / test-pthread-mutex.c
blob55eeb88c9be6a3ae76d2105aa9dedf6f8cb65e38
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 #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
25 it crashes. */
26 #define ENABLE_LOCKING 1
28 /* Which tests to perform.
29 Uncomment some of these, to verify that all tests crash if no locking
30 is enabled. */
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
55 # else
56 # define USE_UNNAMED_SEMAPHORE 1
57 # endif
58 #endif
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
71 #include <pthread.h>
72 #include <stdint.h>
73 #include <stdio.h>
74 #include <stdlib.h>
75 #include <string.h>
77 #if EXPLICIT_YIELD
78 # include <sched.h>
79 #endif
81 #if USE_SEMAPHORE
82 # include <errno.h>
83 # include <fcntl.h>
84 # include <semaphore.h>
85 # include <unistd.h>
86 #endif
88 #if HAVE_DECL_ALARM
89 # include <signal.h>
90 # include <unistd.h>
91 #endif
93 #include "macros.h"
95 #if ENABLE_DEBUGGING
96 # define dbgprintf printf
97 #else
98 # define dbgprintf if (0) printf
99 #endif
101 #if EXPLICIT_YIELD
102 # define yield() sched_yield ()
103 #else
104 # define yield()
105 #endif
107 #if USE_VOLATILE
108 struct atomic_int {
109 volatile int value;
111 static void
112 init_atomic_int (struct atomic_int *ai)
115 static int
116 get_atomic_int_value (struct atomic_int *ai)
118 return ai->value;
120 static void
121 set_atomic_int_value (struct atomic_int *ai, int new_value)
123 ai->value = new_value;
125 #elif USE_SEMAPHORE
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
129 struct atomic_int {
130 sem_t semaphore;
132 #define atomic_int_semaphore(ai) (&(ai)->semaphore)
133 static void
134 init_atomic_int (struct atomic_int *ai)
136 sem_init (&ai->semaphore, 0, 0);
138 # endif
139 # if USE_NAMED_SEMAPHORE
140 struct atomic_int {
141 sem_t *semaphore;
143 #define atomic_int_semaphore(ai) ((ai)->semaphore)
144 static void
145 init_atomic_int (struct atomic_int *ai)
147 sem_t *s;
148 unsigned int count;
149 for (count = 0; ; count++)
151 char name[80];
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);
160 if (s == SEM_FAILED)
162 if (errno == EEXIST)
163 /* Retry with a different name. */
164 continue;
165 else
167 perror ("sem_open failed");
168 abort ();
171 else
173 /* Try not to leave a semaphore hanging around on the file system
174 eternally, if we can avoid it. */
175 sem_unlink (name);
176 break;
179 ai->semaphore = s;
181 # endif
182 static int
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)))
188 abort ();
189 return 1;
191 else if (errno == EAGAIN)
192 return 0;
193 else
194 abort ();
196 static void
197 set_atomic_int_value (struct atomic_int *ai, int new_value)
199 if (new_value == 0)
200 /* It's already initialized with 0. */
201 return;
202 /* To set the value 1: */
203 if (sem_post (atomic_int_semaphore (ai)))
204 abort ();
206 #else
207 struct atomic_int {
208 pthread_mutex_t lock;
209 int value;
211 static void
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);
221 static int
222 get_atomic_int_value (struct atomic_int *ai)
224 ASSERT (pthread_mutex_lock (&ai->lock) == 0);
225 int ret = ai->value;
226 ASSERT (pthread_mutex_unlock (&ai->lock) == 0);
227 return ret;
229 static void
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);
236 #endif
238 /* Returns a reference to the current thread as a pointer, for debugging. */
239 #if defined __MVS__
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 ().__))
244 #else
245 # define pthread_self_pointer() ((void *) (uintptr_t) pthread_self ())
246 #endif
248 #define ACCOUNT_COUNT 4
250 static int account[ACCOUNT_COUNT];
252 static int
253 random_account (void)
255 return ((unsigned int) rand () >> 3) % ACCOUNT_COUNT;
258 static void
259 check_accounts (void)
261 int i, sum;
263 sum = 0;
264 for (i = 0; i < ACCOUNT_COUNT; i++)
265 sum += account[i];
266 if (sum != ACCOUNT_COUNT * 1000)
267 abort ();
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;
279 static void *
280 lock_mutator_thread (void *arg)
282 int repeat;
284 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
286 int i1, i2, value;
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);
304 check_accounts ();
305 ASSERT (pthread_mutex_unlock (&my_lock) == 0);
306 dbgprintf ("Mutator %p after check unlock\n", pthread_self_pointer ());
308 yield ();
311 dbgprintf ("Mutator %p dying.\n", pthread_self_pointer ());
312 return NULL;
315 static struct atomic_int lock_checker_done;
317 static void *
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);
324 check_accounts ();
325 ASSERT (pthread_mutex_unlock (&my_lock) == 0);
326 dbgprintf ("Checker %p after check unlock\n", pthread_self_pointer ());
328 yield ();
331 dbgprintf ("Checker %p dying.\n", pthread_self_pointer ());
332 return NULL;
335 static void
336 test_pthread_mutex_normal (void)
338 int i;
339 pthread_t checkerthread;
340 pthread_t threads[THREAD_COUNT];
342 /* Initialization. */
343 for (i = 0; i < ACCOUNT_COUNT; i++)
344 account[i] = 1000;
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)
350 == 0);
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);
359 check_accounts ();
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;
371 static void
372 recshuffle (void)
374 int i1, i2, value;
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)
388 recshuffle ();
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 ());
395 static void *
396 reclock_mutator_thread (void *arg)
398 int repeat;
400 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
402 recshuffle ();
404 dbgprintf ("Mutator %p before check lock\n", pthread_self_pointer ());
405 ASSERT (pthread_mutex_lock (&my_reclock) == 0);
406 check_accounts ();
407 ASSERT (pthread_mutex_unlock (&my_reclock) == 0);
408 dbgprintf ("Mutator %p after check unlock\n", pthread_self_pointer ());
410 yield ();
413 dbgprintf ("Mutator %p dying.\n", pthread_self_pointer ());
414 return NULL;
417 static struct atomic_int reclock_checker_done;
419 static void *
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);
426 check_accounts ();
427 ASSERT (pthread_mutex_unlock (&my_reclock) == 0);
428 dbgprintf ("Checker %p after check unlock\n", pthread_self_pointer ());
430 yield ();
433 dbgprintf ("Checker %p dying.\n", pthread_self_pointer ());
434 return NULL;
437 static void
438 test_pthread_mutex_recursive (void)
440 int i;
441 pthread_t checkerthread;
442 pthread_t threads[THREAD_COUNT];
444 /* Initialization. */
445 for (i = 0; i < ACCOUNT_COUNT; i++)
446 account[i] = 1000;
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)
452 == 0);
453 for (i = 0; i < THREAD_COUNT; i++)
454 ASSERT (pthread_create (&threads[i], NULL, reclock_mutator_thread, NULL)
455 == 0);
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);
462 check_accounts ();
466 /* -------------------------------------------------------------------------- */
469 main ()
471 #if HAVE_DECL_ALARM
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);
476 alarm (alarm_value);
477 #endif
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);
497 #if DO_TEST_LOCK
498 printf ("Starting test_pthread_mutex_normal ..."); fflush (stdout);
499 test_pthread_mutex_normal ();
500 printf (" OK\n"); fflush (stdout);
501 #endif
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);
506 #endif
508 return 0;
511 #else
513 /* No multithreading available. */
515 #include <stdio.h>
518 main ()
520 fputs ("Skipping test: multithreading not enabled\n", stderr);
521 return 77;
524 #endif