openat: Fix theoretically possible issue on GNU/Hurd.
[gnulib.git] / tests / test-pthread-rwlock.c
blob9f15acb9b27cf1c5ffa97fb53561566719256392
1 /* Test of read-write locks 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 /* Whether to help the scheduler through explicit sched_yield().
29 Uncomment this to see if the operating system has a fair scheduler. */
30 #define EXPLICIT_YIELD 1
32 /* Whether to use 'volatile' on some variables that communicate information
33 between threads. If set to 0, a semaphore or a lock is used to protect
34 these variables. If set to 1, 'volatile' is used; this is theoretically
35 equivalent but can lead to much slower execution (e.g. 30x slower total
36 run time on a 40-core machine), because 'volatile' does not imply any
37 synchronization/communication between different CPUs. */
38 #define USE_VOLATILE 0
40 #if USE_POSIX_THREADS && HAVE_SEMAPHORE_H
41 /* Whether to use a semaphore to communicate information between threads.
42 If set to 0, a lock is used. If set to 1, a semaphore is used.
43 Uncomment this to reduce the dependencies of this test. */
44 # define USE_SEMAPHORE 1
45 /* Mac OS X provides only named semaphores (sem_open); its facility for
46 unnamed semaphores (sem_init) does not work. */
47 # if defined __APPLE__ && defined __MACH__
48 # define USE_NAMED_SEMAPHORE 1
49 # else
50 # define USE_UNNAMED_SEMAPHORE 1
51 # endif
52 #endif
54 /* Whether to print debugging messages. */
55 #define ENABLE_DEBUGGING 0
57 /* Number of simultaneous threads. */
58 #define THREAD_COUNT 10
60 /* Number of operations performed in each thread.
61 This is quite high, because with a smaller count, say 5000, we often get
62 an "OK" result even without ENABLE_LOCKING (on Linux/x86). */
63 #define REPEAT_COUNT 50000
65 #include <pthread.h>
66 #include <stdint.h>
67 #include <stdio.h>
68 #include <stdlib.h>
69 #include <string.h>
71 #if EXPLICIT_YIELD
72 # include <sched.h>
73 #endif
75 #if USE_SEMAPHORE
76 # include <errno.h>
77 # include <fcntl.h>
78 # include <semaphore.h>
79 # include <unistd.h>
80 #endif
82 #if HAVE_DECL_ALARM
83 # include <signal.h>
84 # include <unistd.h>
85 #endif
87 #include "macros.h"
89 #if ENABLE_DEBUGGING
90 # define dbgprintf printf
91 #else
92 # define dbgprintf if (0) printf
93 #endif
95 #if EXPLICIT_YIELD
96 # define yield() sched_yield ()
97 #else
98 # define yield()
99 #endif
101 #if USE_VOLATILE
102 struct atomic_int {
103 volatile int value;
105 static void
106 init_atomic_int (struct atomic_int *ai)
109 static int
110 get_atomic_int_value (struct atomic_int *ai)
112 return ai->value;
114 static void
115 set_atomic_int_value (struct atomic_int *ai, int new_value)
117 ai->value = new_value;
119 #elif USE_SEMAPHORE
120 /* This atomic_int implementation can only support the values 0 and 1.
121 It is initially 0 and can be set to 1 only once. */
122 # if USE_UNNAMED_SEMAPHORE
123 struct atomic_int {
124 sem_t semaphore;
126 #define atomic_int_semaphore(ai) (&(ai)->semaphore)
127 static void
128 init_atomic_int (struct atomic_int *ai)
130 sem_init (&ai->semaphore, 0, 0);
132 # endif
133 # if USE_NAMED_SEMAPHORE
134 struct atomic_int {
135 sem_t *semaphore;
137 #define atomic_int_semaphore(ai) ((ai)->semaphore)
138 static void
139 init_atomic_int (struct atomic_int *ai)
141 sem_t *s;
142 unsigned int count;
143 for (count = 0; ; count++)
145 char name[80];
146 /* Use getpid() in the name, so that different processes running at the
147 same time will not interfere. Use ai in the name, so that different
148 atomic_int in the same process will not interfere. Use a count in
149 the name, so that even in the (unlikely) case that a semaphore with
150 the specified name already exists, we can try a different name. */
151 sprintf (name, "test-lock-%lu-%p-%u",
152 (unsigned long) getpid (), ai, count);
153 s = sem_open (name, O_CREAT | O_EXCL, 0600, 0);
154 if (s == SEM_FAILED)
156 if (errno == EEXIST)
157 /* Retry with a different name. */
158 continue;
159 else
161 perror ("sem_open failed");
162 abort ();
165 else
167 /* Try not to leave a semaphore hanging around on the file system
168 eternally, if we can avoid it. */
169 sem_unlink (name);
170 break;
173 ai->semaphore = s;
175 # endif
176 static int
177 get_atomic_int_value (struct atomic_int *ai)
179 if (sem_trywait (atomic_int_semaphore (ai)) == 0)
181 if (sem_post (atomic_int_semaphore (ai)))
182 abort ();
183 return 1;
185 else if (errno == EAGAIN)
186 return 0;
187 else
188 abort ();
190 static void
191 set_atomic_int_value (struct atomic_int *ai, int new_value)
193 if (new_value == 0)
194 /* It's already initialized with 0. */
195 return;
196 /* To set the value 1: */
197 if (sem_post (atomic_int_semaphore (ai)))
198 abort ();
200 #else
201 struct atomic_int {
202 pthread_mutex_t lock;
203 int value;
205 static void
206 init_atomic_int (struct atomic_int *ai)
208 pthread_mutexattr_t attr;
210 ASSERT (pthread_mutexattr_init (&attr) == 0);
211 ASSERT (pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_NORMAL) == 0);
212 ASSERT (pthread_mutex_init (&ai->lock, &attr) == 0);
213 ASSERT (pthread_mutexattr_destroy (&attr) == 0);
215 static int
216 get_atomic_int_value (struct atomic_int *ai)
218 ASSERT (pthread_mutex_lock (&ai->lock) == 0);
219 int ret = ai->value;
220 ASSERT (pthread_mutex_unlock (&ai->lock) == 0);
221 return ret;
223 static void
224 set_atomic_int_value (struct atomic_int *ai, int new_value)
226 ASSERT (pthread_mutex_lock (&ai->lock) == 0);
227 ai->value = new_value;
228 ASSERT (pthread_mutex_unlock (&ai->lock) == 0);
230 #endif
232 /* Returns a reference to the current thread as a pointer, for debugging. */
233 #if defined __MVS__
234 /* On IBM z/OS, pthread_t is a struct with an 8-byte '__' field.
235 The first three bytes of this field appear to uniquely identify a
236 pthread_t, though not necessarily representing a pointer. */
237 # define pthread_self_pointer() (*((void **) pthread_self ().__))
238 #else
239 # define pthread_self_pointer() ((void *) (uintptr_t) pthread_self ())
240 #endif
242 #define ACCOUNT_COUNT 4
244 static int account[ACCOUNT_COUNT];
246 static int
247 random_account (void)
249 return ((unsigned int) rand () >> 3) % ACCOUNT_COUNT;
252 static void
253 check_accounts (void)
255 int i, sum;
257 sum = 0;
258 for (i = 0; i < ACCOUNT_COUNT; i++)
259 sum += account[i];
260 if (sum != ACCOUNT_COUNT * 1000)
261 abort ();
265 /* ----------------- Test read-write (non-recursive) locks ----------------- */
267 /* Test read-write locks by having several bank accounts and several threads
268 which shuffle around money between the accounts and several other threads
269 that check that all the money is still there. */
271 static pthread_rwlock_t my_rwlock = PTHREAD_RWLOCK_INITIALIZER;
273 static void *
274 rwlock_mutator_thread (void *arg)
276 int repeat;
278 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
280 int i1, i2, value;
282 dbgprintf ("Mutator %p before wrlock\n", pthread_self_pointer ());
283 ASSERT (pthread_rwlock_wrlock (&my_rwlock) == 0);
284 dbgprintf ("Mutator %p after wrlock\n", pthread_self_pointer ());
286 i1 = random_account ();
287 i2 = random_account ();
288 value = ((unsigned int) rand () >> 3) % 10;
289 account[i1] += value;
290 account[i2] -= value;
292 dbgprintf ("Mutator %p before unlock\n", pthread_self_pointer ());
293 ASSERT (pthread_rwlock_unlock (&my_rwlock) == 0);
294 dbgprintf ("Mutator %p after unlock\n", pthread_self_pointer ());
296 yield ();
299 dbgprintf ("Mutator %p dying.\n", pthread_self_pointer ());
300 return NULL;
303 static struct atomic_int rwlock_checker_done;
305 static void *
306 rwlock_checker_thread (void *arg)
308 while (get_atomic_int_value (&rwlock_checker_done) == 0)
310 dbgprintf ("Checker %p before check rdlock\n", pthread_self_pointer ());
311 ASSERT (pthread_rwlock_rdlock (&my_rwlock) == 0);
312 check_accounts ();
313 ASSERT (pthread_rwlock_unlock (&my_rwlock) == 0);
314 dbgprintf ("Checker %p after check unlock\n", pthread_self_pointer ());
316 yield ();
319 dbgprintf ("Checker %p dying.\n", pthread_self_pointer ());
320 return NULL;
323 static void
324 test_rwlock (void)
326 int i;
327 pthread_t checkerthreads[THREAD_COUNT];
328 pthread_t threads[THREAD_COUNT];
330 /* Initialization. */
331 for (i = 0; i < ACCOUNT_COUNT; i++)
332 account[i] = 1000;
333 init_atomic_int (&rwlock_checker_done);
334 set_atomic_int_value (&rwlock_checker_done, 0);
336 /* Spawn the threads. */
337 for (i = 0; i < THREAD_COUNT; i++)
338 ASSERT (pthread_create (&checkerthreads[i], NULL,
339 rwlock_checker_thread, NULL)
340 == 0);
341 for (i = 0; i < THREAD_COUNT; i++)
342 ASSERT (pthread_create (&threads[i], NULL, rwlock_mutator_thread, NULL)
343 == 0);
345 /* Wait for the threads to terminate. */
346 for (i = 0; i < THREAD_COUNT; i++)
347 ASSERT (pthread_join (threads[i], NULL) == 0);
348 set_atomic_int_value (&rwlock_checker_done, 1);
349 for (i = 0; i < THREAD_COUNT; i++)
350 ASSERT (pthread_join (checkerthreads[i], NULL) == 0);
351 check_accounts ();
355 /* -------------------------------------------------------------------------- */
358 main ()
360 #if HAVE_DECL_ALARM
361 /* Declare failure if test takes too long, by using default abort
362 caused by SIGALRM. */
363 int alarm_value = 600;
364 signal (SIGALRM, SIG_DFL);
365 alarm (alarm_value);
366 #endif
368 printf ("Starting test_rwlock ..."); fflush (stdout);
369 test_rwlock ();
370 printf (" OK\n"); fflush (stdout);
372 return 0;
375 #else
377 /* No multithreading available. */
379 #include <stdio.h>
382 main ()
384 fputs ("Skipping test: multithreading not enabled\n", stderr);
385 return 77;
388 #endif