Bump to m4 1.4.19
[platform/upstream/m4.git] / tests / test-asyncsafe-spin2.c
1 /* Test of spin locks for communication between threads and signal handlers.
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 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 #if !(defined _WIN32 && ! defined __CYGWIN__) && HAVE_PTHREAD_H && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || __clang_major__ >= 3) && !defined __ibmxl__
40
41 /* The GCC built-ins are known to work fine.  */
42 # define REPEAT_COUNT 5000
43 #else
44 /* This is quite high, because with a smaller count, say 50000, we often get
45    an "OK" result even with the racy implementation that we pick on Fedora 13
46    Linux/x86_64 (gcc 4.4).  */
47 # define REPEAT_COUNT 100000
48 #endif
49
50 #include <signal.h>
51 #include <stdint.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55
56 #include "asyncsafe-spin.h"
57 #if !ENABLE_LOCKING
58 # define asyncsafe_spin_init(lock) (void)(lock)
59 # define asyncsafe_spin_lock(lock, mask, saved_mask) \
60     ((void)(lock), (void)(mask), (void)(saved_mask))
61 # define asyncsafe_spin_unlock(lock, saved_mask) \
62     ((void)(lock), (void)(saved_mask))
63 # define asyncsafe_spin_destroy(lock) (void)(lock)
64 #endif
65
66 #include "glthread/lock.h"
67 #include "glthread/thread.h"
68 #include "glthread/yield.h"
69
70 #if HAVE_DECL_ALARM
71 # include <signal.h>
72 # include <unistd.h>
73 #endif
74
75 #include "atomic-int-gnulib.h"
76
77 #if ENABLE_DEBUGGING
78 # define dbgprintf printf
79 #else
80 # define dbgprintf if (0) printf
81 #endif
82
83 #if EXPLICIT_YIELD
84 # define yield() gl_thread_yield ()
85 #else
86 # define yield()
87 #endif
88
89 static sigset_t signals_to_block;
90
91 #define ACCOUNT_COUNT 4
92
93 static int account[ACCOUNT_COUNT];
94
95 static int
96 random_account (void)
97 {
98   return ((unsigned int) rand () >> 3) % ACCOUNT_COUNT;
99 }
100
101 static void
102 check_accounts (void)
103 {
104   int i, sum;
105
106   sum = 0;
107   for (i = 0; i < ACCOUNT_COUNT; i++)
108     sum += account[i];
109   if (sum != ACCOUNT_COUNT * 1000)
110     abort ();
111 }
112
113
114 /* ------------------- Test use like normal locks ------------------- */
115
116 /* Test normal locks by having several bank accounts and several threads
117    which shuffle around money between the accounts and another thread
118    checking that all the money is still there.  */
119
120 static asyncsafe_spinlock_t my_lock;
121
122 static void *
123 lock_mutator_thread (void *arg)
124 {
125   int repeat;
126
127   for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
128     {
129       sigset_t saved_signals;
130       int i1, i2, value;
131
132       dbgprintf ("Mutator %p before lock\n", gl_thread_self_pointer ());
133       asyncsafe_spin_lock (&my_lock, &signals_to_block, &saved_signals);
134       dbgprintf ("Mutator %p after  lock\n", gl_thread_self_pointer ());
135
136       i1 = random_account ();
137       i2 = random_account ();
138       value = ((unsigned int) rand () >> 3) % 10;
139       account[i1] += value;
140       account[i2] -= value;
141
142       dbgprintf ("Mutator %p before unlock\n", gl_thread_self_pointer ());
143       asyncsafe_spin_unlock (&my_lock, &saved_signals);
144       dbgprintf ("Mutator %p after  unlock\n", gl_thread_self_pointer ());
145
146       dbgprintf ("Mutator %p before check lock\n", gl_thread_self_pointer ());
147       asyncsafe_spin_lock (&my_lock, &signals_to_block, &saved_signals);
148       check_accounts ();
149       asyncsafe_spin_unlock (&my_lock, &saved_signals);
150       dbgprintf ("Mutator %p after  check unlock\n", gl_thread_self_pointer ());
151
152       yield ();
153     }
154
155   dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
156   return NULL;
157 }
158
159 static struct atomic_int lock_checker_done;
160
161 static void *
162 lock_checker_thread (void *arg)
163 {
164   while (get_atomic_int_value (&lock_checker_done) == 0)
165     {
166       sigset_t saved_signals;
167
168       dbgprintf ("Checker %p before check lock\n", gl_thread_self_pointer ());
169       asyncsafe_spin_lock (&my_lock, &signals_to_block, &saved_signals);
170       check_accounts ();
171       asyncsafe_spin_unlock (&my_lock, &saved_signals);
172       dbgprintf ("Checker %p after  check unlock\n", gl_thread_self_pointer ());
173
174       yield ();
175     }
176
177   dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
178   return NULL;
179 }
180
181 static void
182 test_asyncsafe_spin (void)
183 {
184   int i;
185   gl_thread_t checkerthread;
186   gl_thread_t threads[THREAD_COUNT];
187
188   /* Initialization.  */
189   for (i = 0; i < ACCOUNT_COUNT; i++)
190     account[i] = 1000;
191   init_atomic_int (&lock_checker_done);
192   set_atomic_int_value (&lock_checker_done, 0);
193
194   /* Spawn the threads.  */
195   checkerthread = gl_thread_create (lock_checker_thread, NULL);
196   for (i = 0; i < THREAD_COUNT; i++)
197     threads[i] = gl_thread_create (lock_mutator_thread, NULL);
198
199   /* Wait for the threads to terminate.  */
200   for (i = 0; i < THREAD_COUNT; i++)
201     gl_thread_join (threads[i], NULL);
202   set_atomic_int_value (&lock_checker_done, 1);
203   gl_thread_join (checkerthread, NULL);
204   check_accounts ();
205 }
206
207
208 /* -------------------------------------------------------------------------- */
209
210 int
211 main ()
212 {
213 #if HAVE_DECL_ALARM
214   /* Declare failure if test takes too long, by using default abort
215      caused by SIGALRM.  */
216   int alarm_value = 600;
217   signal (SIGALRM, SIG_DFL);
218   alarm (alarm_value);
219 #endif
220
221   sigemptyset (&signals_to_block);
222   sigaddset (&signals_to_block, SIGINT);
223
224   asyncsafe_spin_init (&my_lock);
225
226   printf ("Starting test_asyncsafe_spin ..."); fflush (stdout);
227   test_asyncsafe_spin ();
228   printf (" OK\n"); fflush (stdout);
229
230   return 0;
231 }
232
233 #else
234
235 /* No multithreading available.  */
236
237 #include <stdio.h>
238
239 int
240 main ()
241 {
242   fputs ("Skipping test: multithreading not enabled\n", stderr);
243   return 77;
244 }
245
246 #endif