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