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. */
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
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
50 # define USE_UNNAMED_SEMAPHORE 1
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
78 # include <semaphore.h>
90 # define dbgprintf printf
92 # define dbgprintf if (0) printf
96 # define yield() sched_yield ()
106 init_atomic_int (struct atomic_int
*ai
)
110 get_atomic_int_value (struct atomic_int
*ai
)
115 set_atomic_int_value (struct atomic_int
*ai
, int new_value
)
117 ai
->value
= new_value
;
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
126 #define atomic_int_semaphore(ai) (&(ai)->semaphore)
128 init_atomic_int (struct atomic_int
*ai
)
130 sem_init (&ai
->semaphore
, 0, 0);
133 # if USE_NAMED_SEMAPHORE
137 #define atomic_int_semaphore(ai) ((ai)->semaphore)
139 init_atomic_int (struct atomic_int
*ai
)
143 for (count
= 0; ; count
++)
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);
157 /* Retry with a different name. */
161 perror ("sem_open failed");
167 /* Try not to leave a semaphore hanging around on the file system
168 eternally, if we can avoid it. */
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
)))
185 else if (errno
== EAGAIN
)
191 set_atomic_int_value (struct atomic_int
*ai
, int new_value
)
194 /* It's already initialized with 0. */
196 /* To set the value 1: */
197 if (sem_post (atomic_int_semaphore (ai
)))
202 pthread_mutex_t lock
;
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);
216 get_atomic_int_value (struct atomic_int
*ai
)
218 ASSERT (pthread_mutex_lock (&ai
->lock
) == 0);
220 ASSERT (pthread_mutex_unlock (&ai
->lock
) == 0);
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);
232 /* Returns a reference to the current thread as a pointer, for debugging. */
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 ().__))
239 # define pthread_self_pointer() ((void *) (uintptr_t) pthread_self ())
242 #define ACCOUNT_COUNT 4
244 static int account
[ACCOUNT_COUNT
];
247 random_account (void)
249 return ((unsigned int) rand () >> 3) % ACCOUNT_COUNT
;
253 check_accounts (void)
258 for (i
= 0; i
< ACCOUNT_COUNT
; i
++)
260 if (sum
!= ACCOUNT_COUNT
* 1000)
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
;
274 rwlock_mutator_thread (void *arg
)
278 for (repeat
= REPEAT_COUNT
; repeat
> 0; repeat
--)
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 ());
299 dbgprintf ("Mutator %p dying.\n", pthread_self_pointer ());
303 static struct atomic_int rwlock_checker_done
;
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);
313 ASSERT (pthread_rwlock_unlock (&my_rwlock
) == 0);
314 dbgprintf ("Checker %p after check unlock\n", pthread_self_pointer ());
319 dbgprintf ("Checker %p dying.\n", pthread_self_pointer ());
327 pthread_t checkerthreads
[THREAD_COUNT
];
328 pthread_t threads
[THREAD_COUNT
];
330 /* Initialization. */
331 for (i
= 0; i
< ACCOUNT_COUNT
; i
++)
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
)
341 for (i
= 0; i
< THREAD_COUNT
; i
++)
342 ASSERT (pthread_create (&threads
[i
], NULL
, rwlock_mutator_thread
, NULL
)
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);
355 /* -------------------------------------------------------------------------- */
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
);
368 printf ("Starting test_rwlock ..."); fflush (stdout
);
370 printf (" OK\n"); fflush (stdout
);
377 /* No multithreading available. */
384 fputs ("Skipping test: multithreading not enabled\n", stderr
);