1 /* Test of locking in multithreaded situations.
2 Copyright (C) 2005, 2008-2018 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_POSIX_THREADS || USE_SOLARIS_THREADS || USE_PTH_THREADS || USE_WINDOWS_THREADS
22
23 #if USE_POSIX_THREADS
24 # define TEST_POSIX_THREADS 1
25 #endif
26 #if USE_SOLARIS_THREADS
27 # define TEST_SOLARIS_THREADS 1
28 #endif
29 #if USE_PTH_THREADS
30 # define TEST_PTH_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 use 'volatile' on some variables that communicate information
54 between threads. If set to 0, a semaphore or a lock is used to protect
55 these variables. If set to 1, 'volatile' is used; this is theoretically
56 equivalent but can lead to much slower execution (e.g. 30x slower total
57 run time on a 40-core machine), because 'volatile' does not imply any
58 synchronization/communication between different CPUs. */
59 #define USE_VOLATILE 0
60
61 #if USE_POSIX_THREADS && HAVE_SEMAPHORE_H
62 /* Whether to use a semaphore to communicate information between threads.
63 If set to 0, a lock is used. If set to 1, a semaphore is used.
64 Uncomment this to reduce the dependencies of this test. */
65 # define USE_SEMAPHORE 1
66 /* Mac OS X provides only named semaphores (sem_open); its facility for
67 unnamed semaphores (sem_init) does not work. */
68 # if defined __APPLE__ && defined __MACH__
69 # define USE_NAMED_SEMAPHORE 1
70 # else
71 # define USE_UNNAMED_SEMAPHORE 1
72 # endif
73 #endif
74
75 /* Whether to print debugging messages. */
76 #define ENABLE_DEBUGGING 0
77
78 /* Number of simultaneous threads. */
79 #define THREAD_COUNT 10
80
81 /* Number of operations performed in each thread.
82 This is quite high, because with a smaller count, say 5000, we often get
83 an "OK" result even without ENABLE_LOCKING (on Linux/x86). */
84 #define REPEAT_COUNT 50000
85
86 #include <stdio.h>
87 #include <stdlib.h>
88 #include <string.h>
89
90 #if !ENABLE_LOCKING
91 # undef USE_POSIX_THREADS
92 # undef USE_SOLARIS_THREADS
93 # undef USE_PTH_THREADS
94 # undef USE_WINDOWS_THREADS
95 #endif
96 #include "glthread/lock.h"
97
98 #if !ENABLE_LOCKING
99 # if TEST_POSIX_THREADS
100 # define USE_POSIX_THREADS 1
101 # endif
102 # if TEST_SOLARIS_THREADS
103 # define USE_SOLARIS_THREADS 1
104 # endif
105 # if TEST_PTH_THREADS
106 # define USE_PTH_THREADS 1
107 # endif
108 # if TEST_WINDOWS_THREADS
109 # define USE_WINDOWS_THREADS 1
110 # endif
111 #endif
112
113 #include "glthread/thread.h"
114 #include "glthread/yield.h"
115 #if USE_SEMAPHORE
116 # include <errno.h>
117 # include <fcntl.h>
118 # include <semaphore.h>
119 # include <unistd.h>
120 #endif
121
122 #if ENABLE_DEBUGGING
123 # define dbgprintf printf
124 #else
125 # define dbgprintf if (0) printf
126 #endif
127
128 #if EXPLICIT_YIELD
129 # define yield() gl_thread_yield ()
130 #else
131 # define yield()
132 #endif
133
134 #if USE_VOLATILE
135 struct atomic_int {
136 volatile int value;
137 };
138 static void
init_atomic_int(struct atomic_int * ai)139 init_atomic_int (struct atomic_int *ai)
140 {
141 }
142 static int
get_atomic_int_value(struct atomic_int * ai)143 get_atomic_int_value (struct atomic_int *ai)
144 {
145 return ai->value;
146 }
147 static void
set_atomic_int_value(struct atomic_int * ai,int new_value)148 set_atomic_int_value (struct atomic_int *ai, int new_value)
149 {
150 ai->value = new_value;
151 }
152 #elif USE_SEMAPHORE
153 /* This atomic_int implementation can only support the values 0 and 1.
154 It is initially 0 and can be set to 1 only once. */
155 # if USE_UNNAMED_SEMAPHORE
156 struct atomic_int {
157 sem_t semaphore;
158 };
159 #define atomic_int_semaphore(ai) (&(ai)->semaphore)
160 static void
init_atomic_int(struct atomic_int * ai)161 init_atomic_int (struct atomic_int *ai)
162 {
163 sem_init (&ai->semaphore, 0, 0);
164 }
165 # endif
166 # if USE_NAMED_SEMAPHORE
167 struct atomic_int {
168 sem_t *semaphore;
169 };
170 #define atomic_int_semaphore(ai) ((ai)->semaphore)
171 static void
init_atomic_int(struct atomic_int * ai)172 init_atomic_int (struct atomic_int *ai)
173 {
174 sem_t *s;
175 unsigned int count;
176 for (count = 0; ; count++)
177 {
178 char name[80];
179 /* Use getpid() in the name, so that different processes running at the
180 same time will not interfere. Use ai in the name, so that different
181 atomic_int in the same process will not interfere. Use a count in
182 the name, so that even in the (unlikely) case that a semaphore with
183 the specified name already exists, we can try a different name. */
184 sprintf (name, "test-lock-%lu-%p-%u",
185 (unsigned long) getpid (), ai, count);
186 s = sem_open (name, O_CREAT | O_EXCL, 0600, 0);
187 if (s == SEM_FAILED)
188 {
189 if (errno == EEXIST)
190 /* Retry with a different name. */
191 continue;
192 else
193 {
194 perror ("sem_open failed");
195 abort ();
196 }
197 }
198 else
199 {
200 /* Try not to leave a semaphore hanging around on the file system
201 eternally, if we can avoid it. */
202 sem_unlink (name);
203 break;
204 }
205 }
206 ai->semaphore = s;
207 }
208 # endif
209 static int
get_atomic_int_value(struct atomic_int * ai)210 get_atomic_int_value (struct atomic_int *ai)
211 {
212 if (sem_trywait (atomic_int_semaphore (ai)) == 0)
213 {
214 if (sem_post (atomic_int_semaphore (ai)))
215 abort ();
216 return 1;
217 }
218 else if (errno == EAGAIN)
219 return 0;
220 else
221 abort ();
222 }
223 static void
set_atomic_int_value(struct atomic_int * ai,int new_value)224 set_atomic_int_value (struct atomic_int *ai, int new_value)
225 {
226 if (new_value == 0)
227 /* It's already initialized with 0. */
228 return;
229 /* To set the value 1: */
230 if (sem_post (atomic_int_semaphore (ai)))
231 abort ();
232 }
233 #else
234 struct atomic_int {
235 gl_lock_define (, lock)
236 int value;
237 };
238 static void
init_atomic_int(struct atomic_int * ai)239 init_atomic_int (struct atomic_int *ai)
240 {
241 gl_lock_init (ai->lock);
242 }
243 static int
get_atomic_int_value(struct atomic_int * ai)244 get_atomic_int_value (struct atomic_int *ai)
245 {
246 gl_lock_lock (ai->lock);
247 int ret = ai->value;
248 gl_lock_unlock (ai->lock);
249 return ret;
250 }
251 static void
set_atomic_int_value(struct atomic_int * ai,int new_value)252 set_atomic_int_value (struct atomic_int *ai, int new_value)
253 {
254 gl_lock_lock (ai->lock);
255 ai->value = new_value;
256 gl_lock_unlock (ai->lock);
257 }
258 #endif
259
260 #define ACCOUNT_COUNT 4
261
262 static int account[ACCOUNT_COUNT];
263
264 static int
random_account(void)265 random_account (void)
266 {
267 return ((unsigned int) rand () >> 3) % ACCOUNT_COUNT;
268 }
269
270 static void
check_accounts(void)271 check_accounts (void)
272 {
273 int i, sum;
274
275 sum = 0;
276 for (i = 0; i < ACCOUNT_COUNT; i++)
277 sum += account[i];
278 if (sum != ACCOUNT_COUNT * 1000)
279 abort ();
280 }
281
282
283 /* ------------------- Test normal (non-recursive) locks ------------------- */
284
285 /* Test normal locks by having several bank accounts and several threads
286 which shuffle around money between the accounts and another thread
287 checking that all the money is still there. */
288
gl_lock_define_initialized(static,my_lock)289 gl_lock_define_initialized(static, my_lock)
290
291 static void *
292 lock_mutator_thread (void *arg)
293 {
294 int repeat;
295
296 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
297 {
298 int i1, i2, value;
299
300 dbgprintf ("Mutator %p before lock\n", gl_thread_self_pointer ());
301 gl_lock_lock (my_lock);
302 dbgprintf ("Mutator %p after lock\n", gl_thread_self_pointer ());
303
304 i1 = random_account ();
305 i2 = random_account ();
306 value = ((unsigned int) rand () >> 3) % 10;
307 account[i1] += value;
308 account[i2] -= value;
309
310 dbgprintf ("Mutator %p before unlock\n", gl_thread_self_pointer ());
311 gl_lock_unlock (my_lock);
312 dbgprintf ("Mutator %p after unlock\n", gl_thread_self_pointer ());
313
314 dbgprintf ("Mutator %p before check lock\n", gl_thread_self_pointer ());
315 gl_lock_lock (my_lock);
316 check_accounts ();
317 gl_lock_unlock (my_lock);
318 dbgprintf ("Mutator %p after check unlock\n", gl_thread_self_pointer ());
319
320 yield ();
321 }
322
323 dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
324 return NULL;
325 }
326
327 static struct atomic_int lock_checker_done;
328
329 static void *
lock_checker_thread(void * arg)330 lock_checker_thread (void *arg)
331 {
332 while (get_atomic_int_value (&lock_checker_done) == 0)
333 {
334 dbgprintf ("Checker %p before check lock\n", gl_thread_self_pointer ());
335 gl_lock_lock (my_lock);
336 check_accounts ();
337 gl_lock_unlock (my_lock);
338 dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ());
339
340 yield ();
341 }
342
343 dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
344 return NULL;
345 }
346
347 static void
test_lock(void)348 test_lock (void)
349 {
350 int i;
351 gl_thread_t checkerthread;
352 gl_thread_t threads[THREAD_COUNT];
353
354 /* Initialization. */
355 for (i = 0; i < ACCOUNT_COUNT; i++)
356 account[i] = 1000;
357 init_atomic_int (&lock_checker_done);
358 set_atomic_int_value (&lock_checker_done, 0);
359
360 /* Spawn the threads. */
361 checkerthread = gl_thread_create (lock_checker_thread, NULL);
362 for (i = 0; i < THREAD_COUNT; i++)
363 threads[i] = gl_thread_create (lock_mutator_thread, NULL);
364
365 /* Wait for the threads to terminate. */
366 for (i = 0; i < THREAD_COUNT; i++)
367 gl_thread_join (threads[i], NULL);
368 set_atomic_int_value (&lock_checker_done, 1);
369 gl_thread_join (checkerthread, NULL);
370 check_accounts ();
371 }
372
373
374 /* ----------------- Test read-write (non-recursive) locks ----------------- */
375
376 /* Test read-write locks by having several bank accounts and several threads
377 which shuffle around money between the accounts and several other threads
378 that check that all the money is still there. */
379
gl_rwlock_define_initialized(static,my_rwlock)380 gl_rwlock_define_initialized(static, my_rwlock)
381
382 static void *
383 rwlock_mutator_thread (void *arg)
384 {
385 int repeat;
386
387 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
388 {
389 int i1, i2, value;
390
391 dbgprintf ("Mutator %p before wrlock\n", gl_thread_self_pointer ());
392 gl_rwlock_wrlock (my_rwlock);
393 dbgprintf ("Mutator %p after wrlock\n", gl_thread_self_pointer ());
394
395 i1 = random_account ();
396 i2 = random_account ();
397 value = ((unsigned int) rand () >> 3) % 10;
398 account[i1] += value;
399 account[i2] -= value;
400
401 dbgprintf ("Mutator %p before unlock\n", gl_thread_self_pointer ());
402 gl_rwlock_unlock (my_rwlock);
403 dbgprintf ("Mutator %p after unlock\n", gl_thread_self_pointer ());
404
405 yield ();
406 }
407
408 dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
409 return NULL;
410 }
411
412 static struct atomic_int rwlock_checker_done;
413
414 static void *
rwlock_checker_thread(void * arg)415 rwlock_checker_thread (void *arg)
416 {
417 while (get_atomic_int_value (&rwlock_checker_done) == 0)
418 {
419 dbgprintf ("Checker %p before check rdlock\n", gl_thread_self_pointer ());
420 gl_rwlock_rdlock (my_rwlock);
421 check_accounts ();
422 gl_rwlock_unlock (my_rwlock);
423 dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ());
424
425 yield ();
426 }
427
428 dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
429 return NULL;
430 }
431
432 static void
test_rwlock(void)433 test_rwlock (void)
434 {
435 int i;
436 gl_thread_t checkerthreads[THREAD_COUNT];
437 gl_thread_t threads[THREAD_COUNT];
438
439 /* Initialization. */
440 for (i = 0; i < ACCOUNT_COUNT; i++)
441 account[i] = 1000;
442 init_atomic_int (&rwlock_checker_done);
443 set_atomic_int_value (&rwlock_checker_done, 0);
444
445 /* Spawn the threads. */
446 for (i = 0; i < THREAD_COUNT; i++)
447 checkerthreads[i] = gl_thread_create (rwlock_checker_thread, NULL);
448 for (i = 0; i < THREAD_COUNT; i++)
449 threads[i] = gl_thread_create (rwlock_mutator_thread, NULL);
450
451 /* Wait for the threads to terminate. */
452 for (i = 0; i < THREAD_COUNT; i++)
453 gl_thread_join (threads[i], NULL);
454 set_atomic_int_value (&rwlock_checker_done, 1);
455 for (i = 0; i < THREAD_COUNT; i++)
456 gl_thread_join (checkerthreads[i], NULL);
457 check_accounts ();
458 }
459
460
461 /* -------------------------- Test recursive locks -------------------------- */
462
463 /* Test recursive locks by having several bank accounts and several threads
464 which shuffle around money between the accounts (recursively) and another
465 thread checking that all the money is still there. */
466
gl_recursive_lock_define_initialized(static,my_reclock)467 gl_recursive_lock_define_initialized(static, my_reclock)
468
469 static void
470 recshuffle (void)
471 {
472 int i1, i2, value;
473
474 dbgprintf ("Mutator %p before lock\n", gl_thread_self_pointer ());
475 gl_recursive_lock_lock (my_reclock);
476 dbgprintf ("Mutator %p after lock\n", gl_thread_self_pointer ());
477
478 i1 = random_account ();
479 i2 = random_account ();
480 value = ((unsigned int) rand () >> 3) % 10;
481 account[i1] += value;
482 account[i2] -= value;
483
484 /* Recursive with probability 0.5. */
485 if (((unsigned int) rand () >> 3) % 2)
486 recshuffle ();
487
488 dbgprintf ("Mutator %p before unlock\n", gl_thread_self_pointer ());
489 gl_recursive_lock_unlock (my_reclock);
490 dbgprintf ("Mutator %p after unlock\n", gl_thread_self_pointer ());
491 }
492
493 static void *
reclock_mutator_thread(void * arg)494 reclock_mutator_thread (void *arg)
495 {
496 int repeat;
497
498 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
499 {
500 recshuffle ();
501
502 dbgprintf ("Mutator %p before check lock\n", gl_thread_self_pointer ());
503 gl_recursive_lock_lock (my_reclock);
504 check_accounts ();
505 gl_recursive_lock_unlock (my_reclock);
506 dbgprintf ("Mutator %p after check unlock\n", gl_thread_self_pointer ());
507
508 yield ();
509 }
510
511 dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
512 return NULL;
513 }
514
515 static struct atomic_int reclock_checker_done;
516
517 static void *
reclock_checker_thread(void * arg)518 reclock_checker_thread (void *arg)
519 {
520 while (get_atomic_int_value (&reclock_checker_done) == 0)
521 {
522 dbgprintf ("Checker %p before check lock\n", gl_thread_self_pointer ());
523 gl_recursive_lock_lock (my_reclock);
524 check_accounts ();
525 gl_recursive_lock_unlock (my_reclock);
526 dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ());
527
528 yield ();
529 }
530
531 dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
532 return NULL;
533 }
534
535 static void
test_recursive_lock(void)536 test_recursive_lock (void)
537 {
538 int i;
539 gl_thread_t checkerthread;
540 gl_thread_t threads[THREAD_COUNT];
541
542 /* Initialization. */
543 for (i = 0; i < ACCOUNT_COUNT; i++)
544 account[i] = 1000;
545 init_atomic_int (&reclock_checker_done);
546 set_atomic_int_value (&reclock_checker_done, 0);
547
548 /* Spawn the threads. */
549 checkerthread = gl_thread_create (reclock_checker_thread, NULL);
550 for (i = 0; i < THREAD_COUNT; i++)
551 threads[i] = gl_thread_create (reclock_mutator_thread, NULL);
552
553 /* Wait for the threads to terminate. */
554 for (i = 0; i < THREAD_COUNT; i++)
555 gl_thread_join (threads[i], NULL);
556 set_atomic_int_value (&reclock_checker_done, 1);
557 gl_thread_join (checkerthread, NULL);
558 check_accounts ();
559 }
560
561
562 /* ------------------------ Test once-only execution ------------------------ */
563
564 /* Test once-only execution by having several threads attempt to grab a
565 once-only task simultaneously (triggered by releasing a read-write lock). */
566
567 gl_once_define(static, fresh_once)
568 static int ready[THREAD_COUNT];
569 static gl_lock_t ready_lock[THREAD_COUNT];
570 #if ENABLE_LOCKING
571 static gl_rwlock_t fire_signal[REPEAT_COUNT];
572 #else
573 static volatile int fire_signal_state;
574 #endif
575 static gl_once_t once_control;
576 static int performed;
gl_lock_define_initialized(static,performed_lock)577 gl_lock_define_initialized(static, performed_lock)
578
579 static void
580 once_execute (void)
581 {
582 gl_lock_lock (performed_lock);
583 performed++;
584 gl_lock_unlock (performed_lock);
585 }
586
587 static void *
once_contender_thread(void * arg)588 once_contender_thread (void *arg)
589 {
590 int id = (int) (long) arg;
591 int repeat;
592
593 for (repeat = 0; repeat <= REPEAT_COUNT; repeat++)
594 {
595 /* Tell the main thread that we're ready. */
596 gl_lock_lock (ready_lock[id]);
597 ready[id] = 1;
598 gl_lock_unlock (ready_lock[id]);
599
600 if (repeat == REPEAT_COUNT)
601 break;
602
603 dbgprintf ("Contender %p waiting for signal for round %d\n",
604 gl_thread_self_pointer (), repeat);
605 #if ENABLE_LOCKING
606 /* Wait for the signal to go. */
607 gl_rwlock_rdlock (fire_signal[repeat]);
608 /* And don't hinder the others (if the scheduler is unfair). */
609 gl_rwlock_unlock (fire_signal[repeat]);
610 #else
611 /* Wait for the signal to go. */
612 while (fire_signal_state <= repeat)
613 yield ();
614 #endif
615 dbgprintf ("Contender %p got the signal for round %d\n",
616 gl_thread_self_pointer (), repeat);
617
618 /* Contend for execution. */
619 gl_once (once_control, once_execute);
620 }
621
622 return NULL;
623 }
624
625 static void
test_once(void)626 test_once (void)
627 {
628 int i, repeat;
629 gl_thread_t threads[THREAD_COUNT];
630
631 /* Initialize all variables. */
632 for (i = 0; i < THREAD_COUNT; i++)
633 {
634 ready[i] = 0;
635 gl_lock_init (ready_lock[i]);
636 }
637 #if ENABLE_LOCKING
638 for (i = 0; i < REPEAT_COUNT; i++)
639 gl_rwlock_init (fire_signal[i]);
640 #else
641 fire_signal_state = 0;
642 #endif
643
644 /* Block all fire_signals. */
645 for (i = REPEAT_COUNT-1; i >= 0; i--)
646 gl_rwlock_wrlock (fire_signal[i]);
647
648 /* Spawn the threads. */
649 for (i = 0; i < THREAD_COUNT; i++)
650 threads[i] = gl_thread_create (once_contender_thread, (void *) (long) i);
651
652 for (repeat = 0; repeat <= REPEAT_COUNT; repeat++)
653 {
654 /* Wait until every thread is ready. */
655 dbgprintf ("Main thread before synchronizing for round %d\n", repeat);
656 for (;;)
657 {
658 int ready_count = 0;
659 for (i = 0; i < THREAD_COUNT; i++)
660 {
661 gl_lock_lock (ready_lock[i]);
662 ready_count += ready[i];
663 gl_lock_unlock (ready_lock[i]);
664 }
665 if (ready_count == THREAD_COUNT)
666 break;
667 yield ();
668 }
669 dbgprintf ("Main thread after synchronizing for round %d\n", repeat);
670
671 if (repeat > 0)
672 {
673 /* Check that exactly one thread executed the once_execute()
674 function. */
675 if (performed != 1)
676 abort ();
677 }
678
679 if (repeat == REPEAT_COUNT)
680 break;
681
682 /* Preparation for the next round: Initialize once_control. */
683 memcpy (&once_control, &fresh_once, sizeof (gl_once_t));
684
685 /* Preparation for the next round: Reset the performed counter. */
686 performed = 0;
687
688 /* Preparation for the next round: Reset the ready flags. */
689 for (i = 0; i < THREAD_COUNT; i++)
690 {
691 gl_lock_lock (ready_lock[i]);
692 ready[i] = 0;
693 gl_lock_unlock (ready_lock[i]);
694 }
695
696 /* Signal all threads simultaneously. */
697 dbgprintf ("Main thread giving signal for round %d\n", repeat);
698 #if ENABLE_LOCKING
699 gl_rwlock_unlock (fire_signal[repeat]);
700 #else
701 fire_signal_state = repeat + 1;
702 #endif
703 }
704
705 /* Wait for the threads to terminate. */
706 for (i = 0; i < THREAD_COUNT; i++)
707 gl_thread_join (threads[i], NULL);
708 }
709
710
711 /* -------------------------------------------------------------------------- */
712
713 int
main()714 main ()
715 {
716 #if TEST_PTH_THREADS
717 if (!pth_init ())
718 abort ();
719 #endif
720
721 #if DO_TEST_LOCK
722 printf ("Starting test_lock ..."); fflush (stdout);
723 test_lock ();
724 printf (" OK\n"); fflush (stdout);
725 #endif
726 #if DO_TEST_RWLOCK
727 printf ("Starting test_rwlock ..."); fflush (stdout);
728 test_rwlock ();
729 printf (" OK\n"); fflush (stdout);
730 #endif
731 #if DO_TEST_RECURSIVE_LOCK
732 printf ("Starting test_recursive_lock ..."); fflush (stdout);
733 test_recursive_lock ();
734 printf (" OK\n"); fflush (stdout);
735 #endif
736 #if DO_TEST_ONCE
737 printf ("Starting test_once ..."); fflush (stdout);
738 test_once ();
739 printf (" OK\n"); fflush (stdout);
740 #endif
741
742 return 0;
743 }
744
745 #else
746
747 /* No multithreading available. */
748
749 #include <stdio.h>
750
751 int
main()752 main ()
753 {
754 fputs ("Skipping test: multithreading not enabled\n", stderr);
755 return 77;
756 }
757
758 #endif
759