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. */
21 /* Whether to enable locking.
22 Uncomment this to get a test program without locking, to verify that
24 #define ENABLE_LOCKING 1
26 /* Which tests to perform.
27 Uncomment some of these, to verify that all tests crash if no locking
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
54 #include "glthread/lock.h"
62 #include "atomic-int-isoc.h"
65 # define dbgprintf printf
67 # define dbgprintf if (0) printf
71 # define yield() thrd_yield ()
76 /* Returns a reference to the current thread as a pointer, for debugging. */
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 ().__))
83 /* On Solaris, thrd_t is merely an 'unsigned int'. */
84 # define thrd_current_pointer() ((void *) (uintptr_t) thrd_current ())
86 # define thrd_current_pointer() ((void *) thrd_current ())
89 #define ACCOUNT_COUNT 4
91 static int account
[ACCOUNT_COUNT
];
96 return ((unsigned int) rand () >> 3) % ACCOUNT_COUNT
;
100 check_accounts (void)
105 for (i
= 0; i
< ACCOUNT_COUNT
; i
++)
107 if (sum
!= ACCOUNT_COUNT
* 1000)
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
;
121 lock_mutator_thread (void *arg
)
125 for (repeat
= REPEAT_COUNT
; repeat
> 0; repeat
--)
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
);
146 ASSERT (mtx_unlock (&my_lock
) == thrd_success
);
147 dbgprintf ("Mutator %p after check unlock\n", thrd_current_pointer ());
152 dbgprintf ("Mutator %p dying.\n", thrd_current_pointer ());
156 static struct atomic_int lock_checker_done
;
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
);
166 ASSERT (mtx_unlock (&my_lock
) == thrd_success
);
167 dbgprintf ("Checker %p after check unlock\n", thrd_current_pointer ());
172 dbgprintf ("Checker %p dying.\n", thrd_current_pointer ());
177 test_mtx_plain (void)
180 thrd_t checkerthread
;
181 thrd_t threads
[THREAD_COUNT
];
183 /* Initialization. */
184 for (i
= 0; i
< ACCOUNT_COUNT
; i
++)
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
)
192 for (i
= 0; i
< THREAD_COUNT
; i
++)
193 ASSERT (thrd_create (&threads
[i
], lock_mutator_thread
, NULL
)
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
);
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
;
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)
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 ());
238 reclock_mutator_thread (void *arg
)
242 for (repeat
= REPEAT_COUNT
; repeat
> 0; repeat
--)
246 dbgprintf ("Mutator %p before check lock\n", thrd_current_pointer ());
247 ASSERT (mtx_lock (&my_reclock
) == thrd_success
);
249 ASSERT (mtx_unlock (&my_reclock
) == thrd_success
);
250 dbgprintf ("Mutator %p after check unlock\n", thrd_current_pointer ());
255 dbgprintf ("Mutator %p dying.\n", thrd_current_pointer ());
259 static struct atomic_int reclock_checker_done
;
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
);
269 ASSERT (mtx_unlock (&my_reclock
) == thrd_success
);
270 dbgprintf ("Checker %p after check unlock\n", thrd_current_pointer ());
275 dbgprintf ("Checker %p dying.\n", thrd_current_pointer ());
280 test_mtx_recursive (void)
283 thrd_t checkerthread
;
284 thrd_t threads
[THREAD_COUNT
];
286 /* Initialization. */
287 for (i
= 0; i
< ACCOUNT_COUNT
; i
++)
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
)
295 for (i
= 0; i
< THREAD_COUNT
; i
++)
296 ASSERT (thrd_create (&threads
[i
], reclock_mutator_thread
, NULL
)
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
);
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
];
317 static gl_rwlock_t fire_signal
[REPEAT_COUNT
];
319 static volatile int fire_signal_state
;
321 static once_flag once_control
;
322 static int performed
;
323 static mtx_t performed_lock
;
328 ASSERT (mtx_lock (&performed_lock
) == thrd_success
);
330 ASSERT (mtx_unlock (&performed_lock
) == thrd_success
);
334 once_contender_thread (void *arg
)
336 int id
= (int) (intptr_t) arg
;
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
);
344 ASSERT (mtx_unlock (&ready_lock
[id
]) == thrd_success
);
346 if (repeat
== REPEAT_COUNT
)
349 dbgprintf ("Contender %p waiting for signal for round %d\n",
350 thrd_current_pointer (), repeat
);
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
]);
357 /* Wait for the signal to go. */
358 while (fire_signal_state
<= repeat
)
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
);
375 thrd_t threads
[THREAD_COUNT
];
377 /* Initialize all variables. */
378 for (i
= 0; i
< THREAD_COUNT
; i
++)
381 ASSERT (mtx_init (&ready_lock
[i
], mtx_plain
) == thrd_success
);
384 for (i
= 0; i
< REPEAT_COUNT
; i
++)
385 gl_rwlock_init (fire_signal
[i
]);
387 fire_signal_state
= 0;
391 /* Block all fire_signals. */
392 for (i
= REPEAT_COUNT
-1; i
>= 0; i
--)
393 gl_rwlock_wrlock (fire_signal
[i
]);
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
)
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
);
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
)
419 dbgprintf ("Main thread after synchronizing for round %d\n", repeat
);
423 /* Check that exactly one thread executed the once_execute()
429 if (repeat
== REPEAT_COUNT
)
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. */
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
);
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
);
449 gl_rwlock_unlock (fire_signal
[repeat
]);
451 fire_signal_state
= repeat
+ 1;
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 /* -------------------------------------------------------------------------- */
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
);
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
);
479 printf ("Starting test_mtx_plain ..."); fflush (stdout
);
481 printf (" OK\n"); fflush (stdout
);
483 #if DO_TEST_RECURSIVE_LOCK
484 printf ("Starting test_mtx_recursive ..."); fflush (stdout
);
485 test_mtx_recursive ();
486 printf (" OK\n"); fflush (stdout
);
489 printf ("Starting test_once ..."); fflush (stdout
);
491 printf (" OK\n"); fflush (stdout
);