Line data Source code
1 : /* Test of locking in multithreaded situations.
2 : Copyright (C) 2005, 2008-2020 Free Software Foundation, Inc.
3 :
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.
8 :
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.
13 :
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/>. */
16 :
17 : /* Written by Bruno Haible <bruno@clisp.org>, 2005. */
18 :
19 : #include <config.h>
20 :
21 : #if USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS
22 :
23 : #if USE_ISOC_THREADS
24 : # define TEST_ISOC_THREADS 1
25 : #endif
26 : #if USE_POSIX_THREADS
27 : # define TEST_POSIX_THREADS 1
28 : #endif
29 : #if USE_ISOC_AND_POSIX_THREADS
30 : # define TEST_ISOC_AND_POSIX_THREADS 1
31 : #endif
32 : #if USE_WINDOWS_THREADS
33 : # define TEST_WINDOWS_THREADS 1
34 : #endif
35 :
36 : /* Whether to enable locking.
37 : Uncomment this to get a test program without locking, to verify that
38 : it crashes. */
39 : #define ENABLE_LOCKING 1
40 :
41 : /* Which tests to perform.
42 : Uncomment some of these, to verify that all tests crash if no locking
43 : is enabled. */
44 : #define DO_TEST_LOCK 1
45 : #define DO_TEST_RWLOCK 1
46 : #define DO_TEST_RECURSIVE_LOCK 1
47 : #define DO_TEST_ONCE 1
48 :
49 : /* Whether to help the scheduler through explicit yield().
50 : Uncomment this to see if the operating system has a fair scheduler. */
51 : #define EXPLICIT_YIELD 1
52 :
53 : /* Whether to print debugging messages. */
54 : #define ENABLE_DEBUGGING 0
55 :
56 : /* Number of simultaneous threads. */
57 : #define THREAD_COUNT 10
58 :
59 : /* Number of operations performed in each thread.
60 : This is quite high, because with a smaller count, say 5000, we often get
61 : an "OK" result even without ENABLE_LOCKING (on Linux/x86). */
62 : #define REPEAT_COUNT 50000
63 :
64 : #include <stdint.h>
65 : #include <stdio.h>
66 : #include <stdlib.h>
67 : #include <string.h>
68 :
69 : #if !ENABLE_LOCKING
70 : # undef USE_ISOC_THREADS
71 : # undef USE_POSIX_THREADS
72 : # undef USE_ISOC_AND_POSIX_THREADS
73 : # undef USE_WINDOWS_THREADS
74 : #endif
75 : #include "glthread/lock.h"
76 :
77 : #if !ENABLE_LOCKING
78 : # if TEST_ISOC_THREADS
79 : # define USE_ISOC_THREADS 1
80 : # endif
81 : # if TEST_POSIX_THREADS
82 : # define USE_POSIX_THREADS 1
83 : # endif
84 : # if TEST_ISOC_AND_POSIX_THREADS
85 : # define USE_ISOC_AND_POSIX_THREADS 1
86 : # endif
87 : # if TEST_WINDOWS_THREADS
88 : # define USE_WINDOWS_THREADS 1
89 : # endif
90 : #endif
91 :
92 : #include "glthread/thread.h"
93 : #include "glthread/yield.h"
94 :
95 : #if HAVE_DECL_ALARM
96 : # include <signal.h>
97 : # include <unistd.h>
98 : #endif
99 :
100 : #include "atomic-int-gnulib.h"
101 :
102 : #if ENABLE_DEBUGGING
103 : # define dbgprintf printf
104 : #else
105 : # define dbgprintf if (0) printf
106 : #endif
107 :
108 : #if EXPLICIT_YIELD
109 : # define yield() gl_thread_yield ()
110 : #else
111 : # define yield()
112 : #endif
113 :
114 : #define ACCOUNT_COUNT 4
115 :
116 : static int account[ACCOUNT_COUNT];
117 :
118 : static int
119 4002468 : random_account (void)
120 : {
121 4002468 : return ((unsigned int) rand () >> 3) % ACCOUNT_COUNT;
122 : }
123 :
124 : static void
125 1397026 : check_accounts (void)
126 : {
127 : int i, sum;
128 :
129 1397026 : sum = 0;
130 6981277 : for (i = 0; i < ACCOUNT_COUNT; i++)
131 5584251 : sum += account[i];
132 1397026 : if (sum != ACCOUNT_COUNT * 1000)
133 0 : abort ();
134 1397026 : }
135 :
136 :
137 : /* ------------------- Test normal (non-recursive) locks ------------------- */
138 :
139 : /* Test normal locks by having several bank accounts and several threads
140 : which shuffle around money between the accounts and another thread
141 : checking that all the money is still there. */
142 :
143 : gl_lock_define_initialized(static, my_lock)
144 :
145 : static void *
146 10 : lock_mutator_thread (void *arg)
147 : {
148 : int repeat;
149 :
150 495030 : for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
151 : {
152 : int i1, i2, value;
153 :
154 : dbgprintf ("Mutator %p before lock\n", gl_thread_self_pointer ());
155 494481 : gl_lock_lock (my_lock);
156 : dbgprintf ("Mutator %p after lock\n", gl_thread_self_pointer ());
157 :
158 500000 : i1 = random_account ();
159 500000 : i2 = random_account ();
160 500000 : value = ((unsigned int) rand () >> 3) % 10;
161 500000 : account[i1] += value;
162 500000 : account[i2] -= value;
163 :
164 : dbgprintf ("Mutator %p before unlock\n", gl_thread_self_pointer ());
165 500000 : gl_lock_unlock (my_lock);
166 : dbgprintf ("Mutator %p after unlock\n", gl_thread_self_pointer ());
167 :
168 : dbgprintf ("Mutator %p before check lock\n", gl_thread_self_pointer ());
169 498429 : gl_lock_lock (my_lock);
170 500000 : check_accounts ();
171 500000 : gl_lock_unlock (my_lock);
172 : dbgprintf ("Mutator %p after check unlock\n", gl_thread_self_pointer ());
173 :
174 498477 : yield ();
175 : }
176 :
177 : dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
178 549 : return NULL;
179 : }
180 :
181 : static struct atomic_int lock_checker_done;
182 :
183 : static void *
184 1 : lock_checker_thread (void *arg)
185 : {
186 91689 : while (get_atomic_int_value (&lock_checker_done) == 0)
187 : {
188 : dbgprintf ("Checker %p before check lock\n", gl_thread_self_pointer ());
189 91688 : gl_lock_lock (my_lock);
190 91688 : check_accounts ();
191 91688 : gl_lock_unlock (my_lock);
192 : dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ());
193 :
194 91688 : yield ();
195 : }
196 :
197 : dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
198 1 : return NULL;
199 : }
200 :
201 : static void
202 1 : test_lock (void)
203 : {
204 : int i;
205 : gl_thread_t checkerthread;
206 : gl_thread_t threads[THREAD_COUNT];
207 :
208 : /* Initialization. */
209 5 : for (i = 0; i < ACCOUNT_COUNT; i++)
210 4 : account[i] = 1000;
211 1 : init_atomic_int (&lock_checker_done);
212 1 : set_atomic_int_value (&lock_checker_done, 0);
213 :
214 : /* Spawn the threads. */
215 1 : checkerthread = gl_thread_create (lock_checker_thread, NULL);
216 11 : for (i = 0; i < THREAD_COUNT; i++)
217 10 : threads[i] = gl_thread_create (lock_mutator_thread, NULL);
218 :
219 : /* Wait for the threads to terminate. */
220 11 : for (i = 0; i < THREAD_COUNT; i++)
221 10 : gl_thread_join (threads[i], NULL);
222 1 : set_atomic_int_value (&lock_checker_done, 1);
223 1 : gl_thread_join (checkerthread, NULL);
224 1 : check_accounts ();
225 1 : }
226 :
227 :
228 : /* ----------------- Test read-write (non-recursive) locks ----------------- */
229 :
230 : /* Test read-write locks by having several bank accounts and several threads
231 : which shuffle around money between the accounts and several other threads
232 : that check that all the money is still there. */
233 :
234 : gl_rwlock_define_initialized(static, my_rwlock)
235 :
236 : static void *
237 10 : rwlock_mutator_thread (void *arg)
238 : {
239 : int repeat;
240 :
241 495508 : for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
242 : {
243 : int i1, i2, value;
244 :
245 : dbgprintf ("Mutator %p before wrlock\n", gl_thread_self_pointer ());
246 494318 : gl_rwlock_wrlock (my_rwlock);
247 : dbgprintf ("Mutator %p after wrlock\n", gl_thread_self_pointer ());
248 :
249 500000 : i1 = random_account ();
250 500000 : i2 = random_account ();
251 500000 : value = ((unsigned int) rand () >> 3) % 10;
252 500000 : account[i1] += value;
253 500000 : account[i2] -= value;
254 :
255 : dbgprintf ("Mutator %p before unlock\n", gl_thread_self_pointer ());
256 500000 : gl_rwlock_unlock (my_rwlock);
257 : dbgprintf ("Mutator %p after unlock\n", gl_thread_self_pointer ());
258 :
259 498107 : yield ();
260 : }
261 :
262 : dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
263 1190 : return NULL;
264 : }
265 :
266 : static struct atomic_int rwlock_checker_done;
267 :
268 : static void *
269 10 : rwlock_checker_thread (void *arg)
270 : {
271 208396 : while (get_atomic_int_value (&rwlock_checker_done) == 0)
272 : {
273 : dbgprintf ("Checker %p before check rdlock\n", gl_thread_self_pointer ());
274 209493 : gl_rwlock_rdlock (my_rwlock);
275 217431 : check_accounts ();
276 216803 : gl_rwlock_unlock (my_rwlock);
277 : dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ());
278 :
279 217779 : yield ();
280 : }
281 :
282 : dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
283 10 : return NULL;
284 : }
285 :
286 : static void
287 1 : test_rwlock (void)
288 : {
289 : int i;
290 : gl_thread_t checkerthreads[THREAD_COUNT];
291 : gl_thread_t threads[THREAD_COUNT];
292 :
293 : /* Initialization. */
294 5 : for (i = 0; i < ACCOUNT_COUNT; i++)
295 4 : account[i] = 1000;
296 1 : init_atomic_int (&rwlock_checker_done);
297 1 : set_atomic_int_value (&rwlock_checker_done, 0);
298 :
299 : /* Spawn the threads. */
300 11 : for (i = 0; i < THREAD_COUNT; i++)
301 10 : checkerthreads[i] = gl_thread_create (rwlock_checker_thread, NULL);
302 11 : for (i = 0; i < THREAD_COUNT; i++)
303 10 : threads[i] = gl_thread_create (rwlock_mutator_thread, NULL);
304 :
305 : /* Wait for the threads to terminate. */
306 11 : for (i = 0; i < THREAD_COUNT; i++)
307 10 : gl_thread_join (threads[i], NULL);
308 1 : set_atomic_int_value (&rwlock_checker_done, 1);
309 11 : for (i = 0; i < THREAD_COUNT; i++)
310 10 : gl_thread_join (checkerthreads[i], NULL);
311 1 : check_accounts ();
312 1 : }
313 :
314 :
315 : /* -------------------------- Test recursive locks -------------------------- */
316 :
317 : /* Test recursive locks by having several bank accounts and several threads
318 : which shuffle around money between the accounts (recursively) and another
319 : thread checking that all the money is still there. */
320 :
321 : gl_recursive_lock_define_initialized(static, my_reclock)
322 :
323 : static void
324 993012 : recshuffle (void)
325 : {
326 : int i1, i2, value;
327 :
328 : dbgprintf ("Mutator %p before lock\n", gl_thread_self_pointer ());
329 993012 : gl_recursive_lock_lock (my_reclock);
330 : dbgprintf ("Mutator %p after lock\n", gl_thread_self_pointer ());
331 :
332 1001234 : i1 = random_account ();
333 1001234 : i2 = random_account ();
334 1001234 : value = ((unsigned int) rand () >> 3) % 10;
335 1001234 : account[i1] += value;
336 1001234 : account[i2] -= value;
337 :
338 : /* Recursive with probability 0.5. */
339 1001234 : if (((unsigned int) rand () >> 3) % 2)
340 501234 : recshuffle ();
341 :
342 : dbgprintf ("Mutator %p before unlock\n", gl_thread_self_pointer ());
343 1001234 : gl_recursive_lock_unlock (my_reclock);
344 : dbgprintf ("Mutator %p after unlock\n", gl_thread_self_pointer ());
345 997630 : }
346 :
347 : static void *
348 10 : reclock_mutator_thread (void *arg)
349 : {
350 : int repeat;
351 :
352 495348 : for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
353 : {
354 496150 : recshuffle ();
355 :
356 : dbgprintf ("Mutator %p before check lock\n", gl_thread_self_pointer ());
357 496871 : gl_recursive_lock_lock (my_reclock);
358 500000 : check_accounts ();
359 500000 : gl_recursive_lock_unlock (my_reclock);
360 : dbgprintf ("Mutator %p after check unlock\n", gl_thread_self_pointer ());
361 :
362 497482 : yield ();
363 : }
364 :
365 : dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
366 0 : return NULL;
367 : }
368 :
369 : static struct atomic_int reclock_checker_done;
370 :
371 : static void *
372 1 : reclock_checker_thread (void *arg)
373 : {
374 88467 : while (get_atomic_int_value (&reclock_checker_done) == 0)
375 : {
376 : dbgprintf ("Checker %p before check lock\n", gl_thread_self_pointer ());
377 88466 : gl_recursive_lock_lock (my_reclock);
378 88466 : check_accounts ();
379 88466 : gl_recursive_lock_unlock (my_reclock);
380 : dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ());
381 :
382 88466 : yield ();
383 : }
384 :
385 : dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
386 1 : return NULL;
387 : }
388 :
389 : static void
390 1 : test_recursive_lock (void)
391 : {
392 : int i;
393 : gl_thread_t checkerthread;
394 : gl_thread_t threads[THREAD_COUNT];
395 :
396 : /* Initialization. */
397 5 : for (i = 0; i < ACCOUNT_COUNT; i++)
398 4 : account[i] = 1000;
399 1 : init_atomic_int (&reclock_checker_done);
400 1 : set_atomic_int_value (&reclock_checker_done, 0);
401 :
402 : /* Spawn the threads. */
403 1 : checkerthread = gl_thread_create (reclock_checker_thread, NULL);
404 11 : for (i = 0; i < THREAD_COUNT; i++)
405 10 : threads[i] = gl_thread_create (reclock_mutator_thread, NULL);
406 :
407 : /* Wait for the threads to terminate. */
408 11 : for (i = 0; i < THREAD_COUNT; i++)
409 10 : gl_thread_join (threads[i], NULL);
410 1 : set_atomic_int_value (&reclock_checker_done, 1);
411 1 : gl_thread_join (checkerthread, NULL);
412 1 : check_accounts ();
413 1 : }
414 :
415 :
416 : /* ------------------------ Test once-only execution ------------------------ */
417 :
418 : /* Test once-only execution by having several threads attempt to grab a
419 : once-only task simultaneously (triggered by releasing a read-write lock). */
420 :
421 : gl_once_define(static, fresh_once)
422 : static int ready[THREAD_COUNT];
423 : static gl_lock_t ready_lock[THREAD_COUNT];
424 : #if ENABLE_LOCKING
425 : static gl_rwlock_t fire_signal[REPEAT_COUNT];
426 : #else
427 : static volatile int fire_signal_state;
428 : #endif
429 : static gl_once_t once_control;
430 : static int performed;
431 : gl_lock_define_initialized(static, performed_lock)
432 :
433 : static void
434 50000 : once_execute (void)
435 : {
436 50000 : gl_lock_lock (performed_lock);
437 50000 : performed++;
438 50000 : gl_lock_unlock (performed_lock);
439 50000 : }
440 :
441 : static void *
442 10 : once_contender_thread (void *arg)
443 : {
444 10 : int id = (int) (intptr_t) arg;
445 : int repeat;
446 :
447 497250 : for (repeat = 0; repeat <= REPEAT_COUNT; repeat++)
448 : {
449 : /* Tell the main thread that we're ready. */
450 497681 : gl_lock_lock (ready_lock[id]);
451 498530 : ready[id] = 1;
452 498530 : gl_lock_unlock (ready_lock[id]);
453 :
454 498299 : if (repeat == REPEAT_COUNT)
455 10 : break;
456 :
457 : dbgprintf ("Contender %p waiting for signal for round %d\n",
458 : gl_thread_self_pointer (), repeat);
459 : #if ENABLE_LOCKING
460 : /* Wait for the signal to go. */
461 498289 : gl_rwlock_rdlock (fire_signal[repeat]);
462 : /* And don't hinder the others (if the scheduler is unfair). */
463 499643 : gl_rwlock_unlock (fire_signal[repeat]);
464 : #else
465 : /* Wait for the signal to go. */
466 : while (fire_signal_state <= repeat)
467 : yield ();
468 : #endif
469 : dbgprintf ("Contender %p got the signal for round %d\n",
470 : gl_thread_self_pointer (), repeat);
471 :
472 : /* Contend for execution. */
473 499549 : gl_once (once_control, once_execute);
474 : }
475 :
476 0 : return NULL;
477 : }
478 :
479 : static void
480 1 : test_once (void)
481 : {
482 : int i, repeat;
483 : gl_thread_t threads[THREAD_COUNT];
484 :
485 : /* Initialize all variables. */
486 11 : for (i = 0; i < THREAD_COUNT; i++)
487 : {
488 10 : ready[i] = 0;
489 10 : gl_lock_init (ready_lock[i]);
490 : }
491 : #if ENABLE_LOCKING
492 50001 : for (i = 0; i < REPEAT_COUNT; i++)
493 50000 : gl_rwlock_init (fire_signal[i]);
494 : #else
495 : fire_signal_state = 0;
496 : #endif
497 :
498 : #if ENABLE_LOCKING
499 : /* Block all fire_signals. */
500 50001 : for (i = REPEAT_COUNT-1; i >= 0; i--)
501 50000 : gl_rwlock_wrlock (fire_signal[i]);
502 : #endif
503 :
504 : /* Spawn the threads. */
505 11 : for (i = 0; i < THREAD_COUNT; i++)
506 10 : threads[i] =
507 10 : gl_thread_create (once_contender_thread, (void *) (intptr_t) i);
508 :
509 50001 : for (repeat = 0; repeat <= REPEAT_COUNT; repeat++)
510 : {
511 : /* Wait until every thread is ready. */
512 : dbgprintf ("Main thread before synchronizing for round %d\n", repeat);
513 : for (;;)
514 133943 : {
515 183944 : int ready_count = 0;
516 2023384 : for (i = 0; i < THREAD_COUNT; i++)
517 : {
518 1839440 : gl_lock_lock (ready_lock[i]);
519 1839440 : ready_count += ready[i];
520 1839440 : gl_lock_unlock (ready_lock[i]);
521 : }
522 183944 : if (ready_count == THREAD_COUNT)
523 50001 : break;
524 133943 : yield ();
525 : }
526 : dbgprintf ("Main thread after synchronizing for round %d\n", repeat);
527 :
528 50001 : if (repeat > 0)
529 : {
530 : /* Check that exactly one thread executed the once_execute()
531 : function. */
532 50000 : if (performed != 1)
533 0 : abort ();
534 : }
535 :
536 50001 : if (repeat == REPEAT_COUNT)
537 1 : break;
538 :
539 : /* Preparation for the next round: Initialize once_control. */
540 50000 : memcpy (&once_control, &fresh_once, sizeof (gl_once_t));
541 :
542 : /* Preparation for the next round: Reset the performed counter. */
543 50000 : performed = 0;
544 :
545 : /* Preparation for the next round: Reset the ready flags. */
546 550000 : for (i = 0; i < THREAD_COUNT; i++)
547 : {
548 500000 : gl_lock_lock (ready_lock[i]);
549 500000 : ready[i] = 0;
550 500000 : gl_lock_unlock (ready_lock[i]);
551 : }
552 :
553 : /* Signal all threads simultaneously. */
554 : dbgprintf ("Main thread giving signal for round %d\n", repeat);
555 : #if ENABLE_LOCKING
556 50000 : gl_rwlock_unlock (fire_signal[repeat]);
557 : #else
558 : fire_signal_state = repeat + 1;
559 : #endif
560 : }
561 :
562 : /* Wait for the threads to terminate. */
563 11 : for (i = 0; i < THREAD_COUNT; i++)
564 10 : gl_thread_join (threads[i], NULL);
565 1 : }
566 :
567 :
568 : /* -------------------------------------------------------------------------- */
569 :
570 : int
571 1 : main ()
572 : {
573 : #if HAVE_DECL_ALARM
574 : /* Declare failure if test takes too long, by using default abort
575 : caused by SIGALRM. */
576 1 : int alarm_value = 600;
577 1 : signal (SIGALRM, SIG_DFL);
578 1 : alarm (alarm_value);
579 : #endif
580 :
581 : #if DO_TEST_LOCK
582 1 : printf ("Starting test_lock ..."); fflush (stdout);
583 1 : test_lock ();
584 1 : printf (" OK\n"); fflush (stdout);
585 : #endif
586 : #if DO_TEST_RWLOCK
587 1 : printf ("Starting test_rwlock ..."); fflush (stdout);
588 1 : test_rwlock ();
589 1 : printf (" OK\n"); fflush (stdout);
590 : #endif
591 : #if DO_TEST_RECURSIVE_LOCK
592 1 : printf ("Starting test_recursive_lock ..."); fflush (stdout);
593 1 : test_recursive_lock ();
594 1 : printf (" OK\n"); fflush (stdout);
595 : #endif
596 : #if DO_TEST_ONCE
597 1 : printf ("Starting test_once ..."); fflush (stdout);
598 1 : test_once ();
599 1 : printf (" OK\n"); fflush (stdout);
600 : #endif
601 :
602 1 : return 0;
603 : }
604 :
605 : #else
606 :
607 : /* No multithreading available. */
608 :
609 : #include <stdio.h>
610 :
611 : int
612 : main ()
613 : {
614 : fputs ("Skipping test: multithreading not enabled\n", stderr);
615 : return 77;
616 : }
617 :
618 : #endif
|