1 /* Test of simple atomic operations for multithreading.
2 Copyright (C) 2021-2025 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. */
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
35 #define REPEAT_COUNT 1000
37 #include "glthread/thread.h"
38 #include "glthread/yield.h"
43 # define yield() gl_thread_yield ()
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
;
55 int_mutator_thread (void *arg
)
57 int *pcounter
= (int *) arg
;
60 for (repeat
= REPEAT_COUNT
; repeat
> 0; repeat
--)
62 if (atomic_compare_and_swap (&int_variable
, 0, 10) == 0)
66 if (atomic_compare_and_swap (&int_variable
, 14, 17) == 14)
70 if (atomic_compare_and_swap (&int_variable
, 20, 0) == 20)
74 if (atomic_compare_and_swap (&int_variable
, 10, 14) == 10)
78 if (atomic_compare_and_swap (&int_variable
, 17, 20) == 17)
86 /* A variable of type 'uintptr_t'. */
87 static uintptr_t ptr_variable
;
90 ptr_mutator_thread (void *arg
)
92 int *pcounter
= (int *) arg
;
95 for (repeat
= REPEAT_COUNT
; repeat
> 0; repeat
--)
97 if (atomic_compare_and_swap_ptr (&ptr_variable
, 0, 10) == 0)
101 if (atomic_compare_and_swap_ptr (&ptr_variable
, 14, 17) == 14)
105 if (atomic_compare_and_swap_ptr (&ptr_variable
, 20, 0) == 20)
109 if (atomic_compare_and_swap_ptr (&ptr_variable
, 10, 14) == 10)
113 if (atomic_compare_and_swap_ptr (&ptr_variable
, 17, 20) == 17)
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);
131 ASSERT (atomic_compare_and_swap (&x
[1], 4, 11) == 11);
134 ASSERT (atomic_compare_and_swap (&x
[1], 11, 15) == 11);
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
);
151 ASSERT (atomic_compare_and_swap_ptr (&x
[1], 4, v1
) == v1
);
154 ASSERT (atomic_compare_and_swap_ptr (&x
[1], v1
, v2
) == v1
);
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
};
165 for (f
= 0; f
< 2; f
++)
167 void * (*func
) (void *) = funcs
[f
];
169 gl_thread_t threads
[THREAD_COUNT
];
171 /* Initialization. */
172 for (i
= 0; i
< THREAD_COUNT
; i
++)
173 for (j
= 0; j
< 5; j
++)
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. */
186 for (j
= 0; j
< 5; j
++)
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
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
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
;
223 /* No multithreading available. */
230 fputs ("Skipping test: multithreading not enabled\n", stderr
);