1 /* Test of read-write locks in multithreaded situations.
2    Copyright (C) 2005, 2008-2021 Free Software Foundation, Inc.
3 
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.
8 
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.
13 
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/>.  */
16 
17 /* Written by Bruno Haible <bruno@clisp.org>, 2005.  */
18 
19 #include <config.h>
20 
21 #if USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS
22 
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
27 
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
31 
32 /* Whether to print debugging messages.  */
33 #define ENABLE_DEBUGGING 0
34 
35 /* Number of simultaneous threads.  */
36 #define THREAD_COUNT 10
37 
38 /* Number of operations performed in each thread.
39    This is quite high, because with a smaller count, say 5000, we often get
40    an "OK" result even without ENABLE_LOCKING (on Linux/x86).  */
41 #define REPEAT_COUNT 50000
42 
43 #include <pthread.h>
44 #include <stdint.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 
49 #if EXPLICIT_YIELD
50 # include <sched.h>
51 #endif
52 
53 #if HAVE_DECL_ALARM
54 # include <signal.h>
55 # include <unistd.h>
56 #endif
57 
58 #include "macros.h"
59 #include "atomic-int-posix.h"
60 
61 #if ENABLE_DEBUGGING
62 # define dbgprintf printf
63 #else
64 # define dbgprintf if (0) printf
65 #endif
66 
67 #if EXPLICIT_YIELD
68 # define yield() sched_yield ()
69 #else
70 # define yield()
71 #endif
72 
73 /* Returns a reference to the current thread as a pointer, for debugging.  */
74 #if defined __MVS__
75   /* On IBM z/OS, pthread_t is a struct with an 8-byte '__' field.
76      The first three bytes of this field appear to uniquely identify a
77      pthread_t, though not necessarily representing a pointer.  */
78 # define pthread_self_pointer() (*((void **) pthread_self ().__))
79 #else
80 # define pthread_self_pointer() ((void *) (uintptr_t) pthread_self ())
81 #endif
82 
83 #define ACCOUNT_COUNT 4
84 
85 static int account[ACCOUNT_COUNT];
86 
87 static int
random_account(void)88 random_account (void)
89 {
90   return ((unsigned int) rand () >> 3) % ACCOUNT_COUNT;
91 }
92 
93 static void
check_accounts(void)94 check_accounts (void)
95 {
96   int i, sum;
97 
98   sum = 0;
99   for (i = 0; i < ACCOUNT_COUNT; i++)
100     sum += account[i];
101   if (sum != ACCOUNT_COUNT * 1000)
102     abort ();
103 }
104 
105 
106 /* ----------------- Test read-write (non-recursive) locks ----------------- */
107 
108 /* Test read-write locks by having several bank accounts and several threads
109    which shuffle around money between the accounts and several other threads
110    that check that all the money is still there.  */
111 
112 static pthread_rwlock_t my_rwlock = PTHREAD_RWLOCK_INITIALIZER;
113 
114 static void *
rwlock_mutator_thread(void * arg)115 rwlock_mutator_thread (void *arg)
116 {
117   int repeat;
118 
119   for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
120     {
121       int i1, i2, value;
122 
123       dbgprintf ("Mutator %p before wrlock\n", pthread_self_pointer ());
124       ASSERT (pthread_rwlock_wrlock (&my_rwlock) == 0);
125       dbgprintf ("Mutator %p after  wrlock\n", pthread_self_pointer ());
126 
127       i1 = random_account ();
128       i2 = random_account ();
129       value = ((unsigned int) rand () >> 3) % 10;
130       account[i1] += value;
131       account[i2] -= value;
132 
133       dbgprintf ("Mutator %p before unlock\n", pthread_self_pointer ());
134       ASSERT (pthread_rwlock_unlock (&my_rwlock) == 0);
135       dbgprintf ("Mutator %p after  unlock\n", pthread_self_pointer ());
136 
137       yield ();
138     }
139 
140   dbgprintf ("Mutator %p dying.\n", pthread_self_pointer ());
141   return NULL;
142 }
143 
144 static struct atomic_int rwlock_checker_done;
145 
146 static void *
rwlock_checker_thread(void * arg)147 rwlock_checker_thread (void *arg)
148 {
149   while (get_atomic_int_value (&rwlock_checker_done) == 0)
150     {
151       dbgprintf ("Checker %p before check rdlock\n", pthread_self_pointer ());
152       ASSERT (pthread_rwlock_rdlock (&my_rwlock) == 0);
153       check_accounts ();
154       ASSERT (pthread_rwlock_unlock (&my_rwlock) == 0);
155       dbgprintf ("Checker %p after  check unlock\n", pthread_self_pointer ());
156 
157       yield ();
158     }
159 
160   dbgprintf ("Checker %p dying.\n", pthread_self_pointer ());
161   return NULL;
162 }
163 
164 static void
test_rwlock(void)165 test_rwlock (void)
166 {
167   int i;
168   pthread_t checkerthreads[THREAD_COUNT];
169   pthread_t threads[THREAD_COUNT];
170 
171   /* Initialization.  */
172   for (i = 0; i < ACCOUNT_COUNT; i++)
173     account[i] = 1000;
174   init_atomic_int (&rwlock_checker_done);
175   set_atomic_int_value (&rwlock_checker_done, 0);
176 
177   /* Spawn the threads.  */
178   for (i = 0; i < THREAD_COUNT; i++)
179     ASSERT (pthread_create (&checkerthreads[i], NULL,
180                             rwlock_checker_thread, NULL)
181             == 0);
182   for (i = 0; i < THREAD_COUNT; i++)
183     ASSERT (pthread_create (&threads[i], NULL, rwlock_mutator_thread, NULL)
184             == 0);
185 
186   /* Wait for the threads to terminate.  */
187   for (i = 0; i < THREAD_COUNT; i++)
188     ASSERT (pthread_join (threads[i], NULL) == 0);
189   set_atomic_int_value (&rwlock_checker_done, 1);
190   for (i = 0; i < THREAD_COUNT; i++)
191     ASSERT (pthread_join (checkerthreads[i], NULL) == 0);
192   check_accounts ();
193 }
194 
195 
196 /* -------------------------------------------------------------------------- */
197 
198 int
main()199 main ()
200 {
201 #if HAVE_DECL_ALARM
202   /* Declare failure if test takes too long, by using default abort
203      caused by SIGALRM.  */
204   int alarm_value = 600;
205   signal (SIGALRM, SIG_DFL);
206   alarm (alarm_value);
207 #endif
208 
209   printf ("Starting test_rwlock ..."); fflush (stdout);
210   test_rwlock ();
211   printf (" OK\n"); fflush (stdout);
212 
213   return 0;
214 }
215 
216 #else
217 
218 /* No multithreading available.  */
219 
220 #include <stdio.h>
221 
222 int
main()223 main ()
224 {
225   fputs ("Skipping test: multithreading not enabled\n", stderr);
226   return 77;
227 }
228 
229 #endif
230