usleep tests: Avoid failure due to known Cygwin 3.5.3 bug.
[gnulib.git] / tests / test-simple-atomic.c
blob4b74b338af1845e2c465c80596e70225cb042f02
1 /* Test of simple atomic operations for multithreading.
2 Copyright (C) 2021-2024 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>, 2021. */
19 #include <config.h>
21 #include "simple-atomic.h"
23 #if USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS
25 /* Whether to help the scheduler through explicit yield().
26 Uncomment this to see if the operating system has a fair scheduler. */
27 #define EXPLICIT_YIELD 1
29 /* Number of simultaneous threads. */
30 #define THREAD_COUNT 4
32 /* Number of operations performed in each thread.
33 With a smaller count, say 100, we often get an "OK" result even with the racy
34 implementation. */
35 #define REPEAT_COUNT 1000
37 #include "glthread/thread.h"
38 #include "glthread/yield.h"
40 #include "macros.h"
42 #if EXPLICIT_YIELD
43 # define yield() gl_thread_yield ()
44 #else
45 # define yield()
46 #endif
48 /* Counters for each thread. */
49 static unsigned int counter[THREAD_COUNT][5];
51 /* A variable of type 'unsigned int'. */
52 static unsigned int int_variable;
54 static void *
55 int_mutator_thread (void *arg)
57 int *pcounter = (int *) arg;
58 int repeat;
60 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
62 if (atomic_compare_and_swap (&int_variable, 0, 10) == 0)
63 pcounter[0]++;
64 yield ();
66 if (atomic_compare_and_swap (&int_variable, 14, 17) == 14)
67 pcounter[1]++;
68 yield ();
70 if (atomic_compare_and_swap (&int_variable, 20, 0) == 20)
71 pcounter[2]++;
72 yield ();
74 if (atomic_compare_and_swap (&int_variable, 10, 14) == 10)
75 pcounter[3]++;
76 yield ();
78 if (atomic_compare_and_swap (&int_variable, 17, 20) == 17)
79 pcounter[4]++;
80 yield ();
83 return NULL;
86 /* A variable of type 'uintptr_t'. */
87 static uintptr_t ptr_variable;
89 static void *
90 ptr_mutator_thread (void *arg)
92 int *pcounter = (int *) arg;
93 int repeat;
95 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
97 if (atomic_compare_and_swap_ptr (&ptr_variable, 0, 10) == 0)
98 pcounter[0]++;
99 yield ();
101 if (atomic_compare_and_swap_ptr (&ptr_variable, 14, 17) == 14)
102 pcounter[1]++;
103 yield ();
105 if (atomic_compare_and_swap_ptr (&ptr_variable, 20, 0) == 20)
106 pcounter[2]++;
107 yield ();
109 if (atomic_compare_and_swap_ptr (&ptr_variable, 10, 14) == 10)
110 pcounter[3]++;
111 yield ();
113 if (atomic_compare_and_swap_ptr (&ptr_variable, 17, 20) == 17)
114 pcounter[4]++;
115 yield ();
118 return NULL;
122 main ()
124 /* Check simple uses of atomic_compare_and_swap. */
126 unsigned int x[3] = { 0xDEADBEEFU, 11, 0xDEADBEEFU };
128 ASSERT (atomic_compare_and_swap (&x[1], 0, 17) == 11);
129 ASSERT (x[1] == 11);
131 ASSERT (atomic_compare_and_swap (&x[1], 4, 11) == 11);
132 ASSERT (x[1] == 11);
134 ASSERT (atomic_compare_and_swap (&x[1], 11, 15) == 11);
135 ASSERT (x[1] == 15);
137 ASSERT (x[0] == 0xDEADBEEFU);
138 ASSERT (x[2] == 0xDEADBEEFU);
141 /* Check simple uses of atomic_compare_and_swap_ptr. */
143 uintptr_t v1 = ~(uintptr_t)0 / 3;
144 uintptr_t v2 = ~(uintptr_t)0 / 5 * 4;
145 uintptr_t v3 = ~(uintptr_t)0 / 7 * 3;
146 uintptr_t x[3] = { 0xDEADBEEFU, v1, 0xDEADBEEFU };
148 ASSERT (atomic_compare_and_swap_ptr (&x[1], 0, v3) == v1);
149 ASSERT (x[1] == v1);
151 ASSERT (atomic_compare_and_swap_ptr (&x[1], 4, v1) == v1);
152 ASSERT (x[1] == v1);
154 ASSERT (atomic_compare_and_swap_ptr (&x[1], v1, v2) == v1);
155 ASSERT (x[1] == v2);
157 ASSERT (x[0] == 0xDEADBEEFU);
158 ASSERT (x[2] == 0xDEADBEEFU);
161 /* Check atomicity of atomic_compare_and_swap. */
163 void * (*funcs[2]) (void *) = { int_mutator_thread, ptr_mutator_thread };
164 int f;
165 for (f = 0; f < 2; f++)
167 void * (*func) (void *) = funcs[f];
168 int i, j;
169 gl_thread_t threads[THREAD_COUNT];
171 /* Initialization. */
172 for (i = 0; i < THREAD_COUNT; i++)
173 for (j = 0; j < 5; j++)
174 counter[i][j] = 0;
176 /* Spawn the threads. */
177 for (i = 0; i < THREAD_COUNT; i++)
178 threads[i] = gl_thread_create (func, &counter[i][0]);
180 /* Wait for the threads to terminate. */
181 for (i = 0; i < THREAD_COUNT; i++)
182 gl_thread_join (threads[i], NULL);
184 /* Sum up the work that the threads have done. */
185 unsigned int sum[5];
186 for (j = 0; j < 5; j++)
188 sum[j] = 0;
189 for (i = 0; i < THREAD_COUNT; i++)
190 sum[j] += counter[i][j];
193 /* If things went atomically, the threads have moved the variable's
194 value through the cycle 0 -> 10 -> 14 -> 17 -> 20 -> 0 ... a large
195 number of times.
196 sum[0] is the number of transitions 0 -> 10.
197 sum[3] is the number of transitions 10 -> 14.
198 sum[1] is the number of transitions 14 -> 17.
199 sum[4] is the number of transitions 17 -> 20.
200 sum[2] is the number of transitions 20 -> 0.
201 Since the cycle started at 0 and ends anywhere (namely, when all
202 threads when through their loop REPEAT_COUNT times), the sequence
203 sum[0], sum[3], sum[1], sum[4], sum[2], sum[0] - 1
204 must be monotonically decreasing.
206 If things did not go atomically, the counters don't exhibit this
207 pattern. */
208 printf ("Counters: %u %u %u %u %u\n",
209 sum[0], sum[3], sum[1], sum[4], sum[2]);
210 ASSERT ((sum[0] == sum[3] || sum[0] == sum[3] + 1)
211 && (sum[3] == sum[1] || sum[3] == sum[1] + 1)
212 && (sum[1] == sum[4] || sum[1] == sum[4] + 1)
213 && (sum[4] == sum[2] || sum[4] == sum[2] + 1)
214 && (sum[2] + 1 == sum[0] || sum[2] == sum[0]));
218 return test_exit_status;
221 #else
223 /* No multithreading available. */
225 #include <stdio.h>
228 main ()
230 fputs ("Skipping test: multithreading not enabled\n", stderr);
231 return 77;
234 #endif