attribute: Clarify which file to include.
[gnulib.git] / tests / test-mtx.c
blob54c41870db870ec6e9694d85a658c66c35be07df
1 /* Test of locking in multithreaded situations.
2 Copyright (C) 2005, 2008-2020 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 /* Whether to enable locking.
22 Uncomment this to get a test program without locking, to verify that
23 it crashes. */
24 #define ENABLE_LOCKING 1
26 /* Which tests to perform.
27 Uncomment some of these, to verify that all tests crash if no locking
28 is enabled. */
29 #define DO_TEST_LOCK 1
30 #define DO_TEST_RECURSIVE_LOCK 1
31 #define DO_TEST_ONCE 1
33 /* Whether to help the scheduler through explicit thrd_yield().
34 Uncomment this to see if the operating system has a fair scheduler. */
35 #define EXPLICIT_YIELD 1
37 /* Whether to print debugging messages. */
38 #define ENABLE_DEBUGGING 0
40 /* Number of simultaneous threads. */
41 #define THREAD_COUNT 10
43 /* Number of operations performed in each thread.
44 This is quite high, because with a smaller count, say 5000, we often get
45 an "OK" result even without ENABLE_LOCKING (on Linux/x86). */
46 #define REPEAT_COUNT 50000
48 #include <threads.h>
49 #include <stdint.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
54 #include "glthread/lock.h"
56 #if HAVE_DECL_ALARM
57 # include <signal.h>
58 # include <unistd.h>
59 #endif
61 #include "macros.h"
62 #include "atomic-int-isoc.h"
64 #if ENABLE_DEBUGGING
65 # define dbgprintf printf
66 #else
67 # define dbgprintf if (0) printf
68 #endif
70 #if EXPLICIT_YIELD
71 # define yield() thrd_yield ()
72 #else
73 # define yield()
74 #endif
76 /* Returns a reference to the current thread as a pointer, for debugging. */
77 #if defined __MVS__
78 /* On IBM z/OS, pthread_t is a struct with an 8-byte '__' field.
79 The first three bytes of this field appear to uniquely identify a
80 pthread_t, though not necessarily representing a pointer. */
81 # define thrd_current_pointer() (*((void **) thrd_current ().__))
82 #elif defined __sun
83 /* On Solaris, thrd_t is merely an 'unsigned int'. */
84 # define thrd_current_pointer() ((void *) (uintptr_t) thrd_current ())
85 #else
86 # define thrd_current_pointer() ((void *) thrd_current ())
87 #endif
89 #define ACCOUNT_COUNT 4
91 static int account[ACCOUNT_COUNT];
93 static int
94 random_account (void)
96 return ((unsigned int) rand () >> 3) % ACCOUNT_COUNT;
99 static void
100 check_accounts (void)
102 int i, sum;
104 sum = 0;
105 for (i = 0; i < ACCOUNT_COUNT; i++)
106 sum += account[i];
107 if (sum != ACCOUNT_COUNT * 1000)
108 abort ();
112 /* ------------------- Test normal (non-recursive) locks ------------------- */
114 /* Test normal locks by having several bank accounts and several threads
115 which shuffle around money between the accounts and another thread
116 checking that all the money is still there. */
118 static mtx_t my_lock;
120 static int
121 lock_mutator_thread (void *arg)
123 int repeat;
125 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
127 int i1, i2, value;
129 dbgprintf ("Mutator %p before lock\n", thrd_current_pointer ());
130 ASSERT (mtx_lock (&my_lock) == thrd_success);
131 dbgprintf ("Mutator %p after lock\n", thrd_current_pointer ());
133 i1 = random_account ();
134 i2 = random_account ();
135 value = ((unsigned int) rand () >> 3) % 10;
136 account[i1] += value;
137 account[i2] -= value;
139 dbgprintf ("Mutator %p before unlock\n", thrd_current_pointer ());
140 ASSERT (mtx_unlock (&my_lock) == thrd_success);
141 dbgprintf ("Mutator %p after unlock\n", thrd_current_pointer ());
143 dbgprintf ("Mutator %p before check lock\n", thrd_current_pointer ());
144 ASSERT (mtx_lock (&my_lock) == thrd_success);
145 check_accounts ();
146 ASSERT (mtx_unlock (&my_lock) == thrd_success);
147 dbgprintf ("Mutator %p after check unlock\n", thrd_current_pointer ());
149 yield ();
152 dbgprintf ("Mutator %p dying.\n", thrd_current_pointer ());
153 return 0;
156 static struct atomic_int lock_checker_done;
158 static int
159 lock_checker_thread (void *arg)
161 while (get_atomic_int_value (&lock_checker_done) == 0)
163 dbgprintf ("Checker %p before check lock\n", thrd_current_pointer ());
164 ASSERT (mtx_lock (&my_lock) == thrd_success);
165 check_accounts ();
166 ASSERT (mtx_unlock (&my_lock) == thrd_success);
167 dbgprintf ("Checker %p after check unlock\n", thrd_current_pointer ());
169 yield ();
172 dbgprintf ("Checker %p dying.\n", thrd_current_pointer ());
173 return 0;
176 static void
177 test_mtx_plain (void)
179 int i;
180 thrd_t checkerthread;
181 thrd_t threads[THREAD_COUNT];
183 /* Initialization. */
184 for (i = 0; i < ACCOUNT_COUNT; i++)
185 account[i] = 1000;
186 init_atomic_int (&lock_checker_done);
187 set_atomic_int_value (&lock_checker_done, 0);
189 /* Spawn the threads. */
190 ASSERT (thrd_create (&checkerthread, lock_checker_thread, NULL)
191 == thrd_success);
192 for (i = 0; i < THREAD_COUNT; i++)
193 ASSERT (thrd_create (&threads[i], lock_mutator_thread, NULL)
194 == thrd_success);
196 /* Wait for the threads to terminate. */
197 for (i = 0; i < THREAD_COUNT; i++)
198 ASSERT (thrd_join (threads[i], NULL) == thrd_success);
199 set_atomic_int_value (&lock_checker_done, 1);
200 ASSERT (thrd_join (checkerthread, NULL) == thrd_success);
201 check_accounts ();
205 /* -------------------------- Test recursive locks -------------------------- */
207 /* Test recursive locks by having several bank accounts and several threads
208 which shuffle around money between the accounts (recursively) and another
209 thread checking that all the money is still there. */
211 static mtx_t my_reclock;
213 static void
214 recshuffle (void)
216 int i1, i2, value;
218 dbgprintf ("Mutator %p before lock\n", thrd_current_pointer ());
219 ASSERT (mtx_lock (&my_reclock) == thrd_success);
220 dbgprintf ("Mutator %p after lock\n", thrd_current_pointer ());
222 i1 = random_account ();
223 i2 = random_account ();
224 value = ((unsigned int) rand () >> 3) % 10;
225 account[i1] += value;
226 account[i2] -= value;
228 /* Recursive with probability 0.5. */
229 if (((unsigned int) rand () >> 3) % 2)
230 recshuffle ();
232 dbgprintf ("Mutator %p before unlock\n", thrd_current_pointer ());
233 ASSERT (mtx_unlock (&my_reclock) == thrd_success);
234 dbgprintf ("Mutator %p after unlock\n", thrd_current_pointer ());
237 static int
238 reclock_mutator_thread (void *arg)
240 int repeat;
242 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
244 recshuffle ();
246 dbgprintf ("Mutator %p before check lock\n", thrd_current_pointer ());
247 ASSERT (mtx_lock (&my_reclock) == thrd_success);
248 check_accounts ();
249 ASSERT (mtx_unlock (&my_reclock) == thrd_success);
250 dbgprintf ("Mutator %p after check unlock\n", thrd_current_pointer ());
252 yield ();
255 dbgprintf ("Mutator %p dying.\n", thrd_current_pointer ());
256 return 0;
259 static struct atomic_int reclock_checker_done;
261 static int
262 reclock_checker_thread (void *arg)
264 while (get_atomic_int_value (&reclock_checker_done) == 0)
266 dbgprintf ("Checker %p before check lock\n", thrd_current_pointer ());
267 ASSERT (mtx_lock (&my_reclock) == thrd_success);
268 check_accounts ();
269 ASSERT (mtx_unlock (&my_reclock) == thrd_success);
270 dbgprintf ("Checker %p after check unlock\n", thrd_current_pointer ());
272 yield ();
275 dbgprintf ("Checker %p dying.\n", thrd_current_pointer ());
276 return 0;
279 static void
280 test_mtx_recursive (void)
282 int i;
283 thrd_t checkerthread;
284 thrd_t threads[THREAD_COUNT];
286 /* Initialization. */
287 for (i = 0; i < ACCOUNT_COUNT; i++)
288 account[i] = 1000;
289 init_atomic_int (&reclock_checker_done);
290 set_atomic_int_value (&reclock_checker_done, 0);
292 /* Spawn the threads. */
293 ASSERT (thrd_create (&checkerthread, reclock_checker_thread, NULL)
294 == thrd_success);
295 for (i = 0; i < THREAD_COUNT; i++)
296 ASSERT (thrd_create (&threads[i], reclock_mutator_thread, NULL)
297 == thrd_success);
299 /* Wait for the threads to terminate. */
300 for (i = 0; i < THREAD_COUNT; i++)
301 ASSERT (thrd_join (threads[i], NULL) == thrd_success);
302 set_atomic_int_value (&reclock_checker_done, 1);
303 ASSERT (thrd_join (checkerthread, NULL) == thrd_success);
304 check_accounts ();
308 /* ------------------------ Test once-only execution ------------------------ */
310 /* Test once-only execution by having several threads attempt to grab a
311 once-only task simultaneously (triggered by releasing a read-write lock). */
313 static once_flag fresh_once = ONCE_FLAG_INIT;
314 static int ready[THREAD_COUNT];
315 static mtx_t ready_lock[THREAD_COUNT];
316 #if ENABLE_LOCKING
317 static gl_rwlock_t fire_signal[REPEAT_COUNT];
318 #else
319 static volatile int fire_signal_state;
320 #endif
321 static once_flag once_control;
322 static int performed;
323 static mtx_t performed_lock;
325 static void
326 once_execute (void)
328 ASSERT (mtx_lock (&performed_lock) == thrd_success);
329 performed++;
330 ASSERT (mtx_unlock (&performed_lock) == thrd_success);
333 static int
334 once_contender_thread (void *arg)
336 int id = (int) (intptr_t) arg;
337 int repeat;
339 for (repeat = 0; repeat <= REPEAT_COUNT; repeat++)
341 /* Tell the main thread that we're ready. */
342 ASSERT (mtx_lock (&ready_lock[id]) == thrd_success);
343 ready[id] = 1;
344 ASSERT (mtx_unlock (&ready_lock[id]) == thrd_success);
346 if (repeat == REPEAT_COUNT)
347 break;
349 dbgprintf ("Contender %p waiting for signal for round %d\n",
350 thrd_current_pointer (), repeat);
351 #if ENABLE_LOCKING
352 /* Wait for the signal to go. */
353 gl_rwlock_rdlock (fire_signal[repeat]);
354 /* And don't hinder the others (if the scheduler is unfair). */
355 gl_rwlock_unlock (fire_signal[repeat]);
356 #else
357 /* Wait for the signal to go. */
358 while (fire_signal_state <= repeat)
359 yield ();
360 #endif
361 dbgprintf ("Contender %p got the signal for round %d\n",
362 thrd_current_pointer (), repeat);
364 /* Contend for execution. */
365 call_once (&once_control, once_execute);
368 return 0;
371 static void
372 test_once (void)
374 int i, repeat;
375 thrd_t threads[THREAD_COUNT];
377 /* Initialize all variables. */
378 for (i = 0; i < THREAD_COUNT; i++)
380 ready[i] = 0;
381 ASSERT (mtx_init (&ready_lock[i], mtx_plain) == thrd_success);
383 #if ENABLE_LOCKING
384 for (i = 0; i < REPEAT_COUNT; i++)
385 gl_rwlock_init (fire_signal[i]);
386 #else
387 fire_signal_state = 0;
388 #endif
390 #if ENABLE_LOCKING
391 /* Block all fire_signals. */
392 for (i = REPEAT_COUNT-1; i >= 0; i--)
393 gl_rwlock_wrlock (fire_signal[i]);
394 #endif
396 /* Spawn the threads. */
397 for (i = 0; i < THREAD_COUNT; i++)
398 ASSERT (thrd_create (&threads[i],
399 once_contender_thread, (void *) (intptr_t) i)
400 == thrd_success);
402 for (repeat = 0; repeat <= REPEAT_COUNT; repeat++)
404 /* Wait until every thread is ready. */
405 dbgprintf ("Main thread before synchronizing for round %d\n", repeat);
406 for (;;)
408 int ready_count = 0;
409 for (i = 0; i < THREAD_COUNT; i++)
411 ASSERT (mtx_lock (&ready_lock[i]) == thrd_success);
412 ready_count += ready[i];
413 ASSERT (mtx_unlock (&ready_lock[i]) == thrd_success);
415 if (ready_count == THREAD_COUNT)
416 break;
417 yield ();
419 dbgprintf ("Main thread after synchronizing for round %d\n", repeat);
421 if (repeat > 0)
423 /* Check that exactly one thread executed the once_execute()
424 function. */
425 if (performed != 1)
426 abort ();
429 if (repeat == REPEAT_COUNT)
430 break;
432 /* Preparation for the next round: Initialize once_control. */
433 memcpy (&once_control, &fresh_once, sizeof (once_flag));
435 /* Preparation for the next round: Reset the performed counter. */
436 performed = 0;
438 /* Preparation for the next round: Reset the ready flags. */
439 for (i = 0; i < THREAD_COUNT; i++)
441 ASSERT (mtx_lock (&ready_lock[i]) == thrd_success);
442 ready[i] = 0;
443 ASSERT (mtx_unlock (&ready_lock[i]) == thrd_success);
446 /* Signal all threads simultaneously. */
447 dbgprintf ("Main thread giving signal for round %d\n", repeat);
448 #if ENABLE_LOCKING
449 gl_rwlock_unlock (fire_signal[repeat]);
450 #else
451 fire_signal_state = repeat + 1;
452 #endif
455 /* Wait for the threads to terminate. */
456 for (i = 0; i < THREAD_COUNT; i++)
457 ASSERT (thrd_join (threads[i], NULL) == thrd_success);
461 /* -------------------------------------------------------------------------- */
464 main ()
466 #if HAVE_DECL_ALARM
467 /* Declare failure if test takes too long, by using default abort
468 caused by SIGALRM. */
469 int alarm_value = 600;
470 signal (SIGALRM, SIG_DFL);
471 alarm (alarm_value);
472 #endif
474 ASSERT (mtx_init (&my_lock, mtx_plain) == thrd_success);
475 ASSERT (mtx_init (&my_reclock, mtx_plain | mtx_recursive) == thrd_success);
476 ASSERT (mtx_init (&performed_lock, mtx_plain) == thrd_success);
478 #if DO_TEST_LOCK
479 printf ("Starting test_mtx_plain ..."); fflush (stdout);
480 test_mtx_plain ();
481 printf (" OK\n"); fflush (stdout);
482 #endif
483 #if DO_TEST_RECURSIVE_LOCK
484 printf ("Starting test_mtx_recursive ..."); fflush (stdout);
485 test_mtx_recursive ();
486 printf (" OK\n"); fflush (stdout);
487 #endif
488 #if DO_TEST_ONCE
489 printf ("Starting test_once ..."); fflush (stdout);
490 test_once ();
491 printf (" OK\n"); fflush (stdout);
492 #endif
494 return 0;