maint.mk: Update system header list for #include syntax checks.
[gnulib.git] / tests / test-pthread-once2.c
blobf175e79682d85fb0e283b14d29806e03f5411d7c
1 /* Test of once-only execution in multithreaded situations.
2 Copyright (C) 2005, 2008-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>, 2005. */
19 #include <config.h>
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
25 it crashes. */
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 print debugging messages. */
33 #define ENABLE_DEBUGGING 0
35 /* Number of simultaneous threads. */
36 #define THREAD_COUNT 10
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
43 #include <pthread.h>
44 #include <stdint.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
49 #if EXPLICIT_YIELD
50 # include <sched.h>
51 #endif
53 #if HAVE_DECL_ALARM
54 # include <signal.h>
55 # include <unistd.h>
56 #endif
58 #include "macros.h"
60 #if ENABLE_DEBUGGING
61 # define dbgprintf printf
62 #else
63 # define dbgprintf if (0) printf
64 #endif
66 #if EXPLICIT_YIELD
67 # define yield() sched_yield ()
68 #else
69 # define yield()
70 #endif
72 /* Returns a reference to the current thread as a pointer, for debugging. */
73 #if defined __MVS__
74 /* On IBM z/OS, pthread_t is a struct with an 8-byte '__' field.
75 The first three bytes of this field appear to uniquely identify a
76 pthread_t, though not necessarily representing a pointer. */
77 # define pthread_self_pointer() (*((void **) pthread_self ().__))
78 #else
79 # define pthread_self_pointer() ((void *) (uintptr_t) pthread_self ())
80 #endif
83 /* ------------------------ Test once-only execution ------------------------ */
85 /* Test once-only execution by having several threads attempt to grab a
86 once-only task simultaneously (triggered by releasing a read-write lock). */
88 static pthread_once_t fresh_once = PTHREAD_ONCE_INIT;
89 static int ready[THREAD_COUNT];
90 static pthread_mutex_t ready_lock[THREAD_COUNT];
91 #if ENABLE_LOCKING
92 static pthread_rwlock_t fire_signal[REPEAT_COUNT];
93 #else
94 static volatile int fire_signal_state;
95 #endif
96 static pthread_once_t once_control;
97 static int performed;
98 static pthread_mutex_t performed_lock;
100 static void
101 once_execute (void)
103 ASSERT (pthread_mutex_lock (&performed_lock) == 0);
104 performed++;
105 ASSERT (pthread_mutex_unlock (&performed_lock) == 0);
108 static void *
109 once_contender_thread (void *arg)
111 int id = (int) (intptr_t) arg;
112 int repeat;
114 for (repeat = 0; repeat <= REPEAT_COUNT; repeat++)
116 /* Tell the main thread that we're ready. */
117 ASSERT (pthread_mutex_lock (&ready_lock[id]) == 0);
118 ready[id] = 1;
119 ASSERT (pthread_mutex_unlock (&ready_lock[id]) == 0);
121 if (repeat == REPEAT_COUNT)
122 break;
124 dbgprintf ("Contender %p waiting for signal for round %d\n",
125 pthread_self_pointer (), repeat);
126 #if ENABLE_LOCKING
127 /* Wait for the signal to go. */
128 ASSERT (pthread_rwlock_rdlock (&fire_signal[repeat]) == 0);
129 /* And don't hinder the others (if the scheduler is unfair). */
130 ASSERT (pthread_rwlock_unlock (&fire_signal[repeat]) == 0);
131 #else
132 /* Wait for the signal to go. */
133 while (fire_signal_state <= repeat)
134 yield ();
135 #endif
136 dbgprintf ("Contender %p got the signal for round %d\n",
137 pthread_self_pointer (), repeat);
139 /* Contend for execution. */
140 ASSERT (pthread_once (&once_control, once_execute) == 0);
143 return NULL;
146 static void
147 test_once (void)
149 int i, repeat;
150 pthread_t threads[THREAD_COUNT];
152 /* Initialize all variables. */
153 for (i = 0; i < THREAD_COUNT; i++)
155 pthread_mutexattr_t attr;
157 ready[i] = 0;
158 ASSERT (pthread_mutexattr_init (&attr) == 0);
159 ASSERT (pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_NORMAL) == 0);
160 ASSERT (pthread_mutex_init (&ready_lock[i], &attr) == 0);
161 ASSERT (pthread_mutexattr_destroy (&attr) == 0);
163 #if ENABLE_LOCKING
164 for (i = 0; i < REPEAT_COUNT; i++)
165 ASSERT (pthread_rwlock_init (&fire_signal[i], NULL) == 0);
166 #else
167 fire_signal_state = 0;
168 #endif
170 #if ENABLE_LOCKING
171 /* Block all fire_signals. */
172 for (i = REPEAT_COUNT-1; i >= 0; i--)
173 ASSERT (pthread_rwlock_wrlock (&fire_signal[i]) == 0);
174 #endif
176 /* Spawn the threads. */
177 for (i = 0; i < THREAD_COUNT; i++)
178 ASSERT (pthread_create (&threads[i], NULL,
179 once_contender_thread, (void *) (intptr_t) i)
180 == 0);
182 for (repeat = 0; repeat <= REPEAT_COUNT; repeat++)
184 /* Wait until every thread is ready. */
185 dbgprintf ("Main thread before synchronizing for round %d\n", repeat);
186 for (;;)
188 int ready_count = 0;
189 for (i = 0; i < THREAD_COUNT; i++)
191 ASSERT (pthread_mutex_lock (&ready_lock[i]) == 0);
192 ready_count += ready[i];
193 ASSERT (pthread_mutex_unlock (&ready_lock[i]) == 0);
195 if (ready_count == THREAD_COUNT)
196 break;
197 yield ();
199 dbgprintf ("Main thread after synchronizing for round %d\n", repeat);
201 if (repeat > 0)
203 /* Check that exactly one thread executed the once_execute()
204 function. */
205 if (performed != 1)
206 abort ();
209 if (repeat == REPEAT_COUNT)
210 break;
212 /* Preparation for the next round: Initialize once_control. */
213 memcpy (&once_control, &fresh_once, sizeof (pthread_once_t));
215 /* Preparation for the next round: Reset the performed counter. */
216 performed = 0;
218 /* Preparation for the next round: Reset the ready flags. */
219 for (i = 0; i < THREAD_COUNT; i++)
221 ASSERT (pthread_mutex_lock (&ready_lock[i]) == 0);
222 ready[i] = 0;
223 ASSERT (pthread_mutex_unlock (&ready_lock[i]) == 0);
226 /* Signal all threads simultaneously. */
227 dbgprintf ("Main thread giving signal for round %d\n", repeat);
228 #if ENABLE_LOCKING
229 ASSERT (pthread_rwlock_unlock (&fire_signal[repeat]) == 0);
230 #else
231 fire_signal_state = repeat + 1;
232 #endif
235 /* Wait for the threads to terminate. */
236 for (i = 0; i < THREAD_COUNT; i++)
237 ASSERT (pthread_join (threads[i], NULL) == 0);
241 /* -------------------------------------------------------------------------- */
244 main ()
246 #if HAVE_DECL_ALARM
247 /* Declare failure if test takes too long, by using default abort
248 caused by SIGALRM. */
249 int alarm_value = 600;
250 signal (SIGALRM, SIG_DFL);
251 alarm (alarm_value);
252 #endif
255 pthread_mutexattr_t attr;
257 ASSERT (pthread_mutexattr_init (&attr) == 0);
258 ASSERT (pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_NORMAL) == 0);
259 ASSERT (pthread_mutex_init (&performed_lock, &attr) == 0);
260 ASSERT (pthread_mutexattr_destroy (&attr) == 0);
263 printf ("Starting test_once ..."); fflush (stdout);
264 test_once ();
265 printf (" OK\n"); fflush (stdout);
267 return test_exit_status;
270 #else
272 /* No multithreading available. */
274 #include <stdio.h>
277 main ()
279 fputs ("Skipping test: multithreading not enabled\n", stderr);
280 return 77;
283 #endif