Home | History | Annotate | Line # | Download | only in tests
      1  1.1  christos /* Test of locking in multithreaded situations.
      2  1.1  christos    Copyright (C) 2005 Free Software Foundation, Inc.
      3  1.1  christos 
      4  1.1  christos    This program is free software; you can redistribute it and/or modify it
      5  1.1  christos    under the terms of the GNU Library General Public License as published
      6  1.1  christos    by the Free Software Foundation; either version 2, or (at your option)
      7  1.1  christos    any later version.
      8  1.1  christos 
      9  1.1  christos    This program is distributed in the hope that it will be useful,
     10  1.1  christos    but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  1.1  christos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12  1.1  christos    Library General Public License for more details.
     13  1.1  christos 
     14  1.1  christos    You should have received a copy of the GNU Library General Public
     15  1.1  christos    License along with this program; if not, write to the Free Software
     16  1.1  christos    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
     17  1.1  christos    USA.  */
     18  1.1  christos 
     19  1.1  christos /* Written by Bruno Haible <bruno (at) clisp.org>, 2005.  */
     20  1.1  christos 
     21  1.1  christos #ifdef HAVE_CONFIG_H
     22  1.1  christos # include <config.h>
     23  1.1  christos #endif
     24  1.1  christos 
     25  1.1  christos #if USE_POSIX_THREADS || USE_SOLARIS_THREADS || USE_PTH_THREADS || USE_WIN32_THREADS
     26  1.1  christos 
     27  1.1  christos #if USE_POSIX_THREADS
     28  1.1  christos # define TEST_POSIX_THREADS 1
     29  1.1  christos #endif
     30  1.1  christos #if USE_SOLARIS_THREADS
     31  1.1  christos # define TEST_SOLARIS_THREADS 1
     32  1.1  christos #endif
     33  1.1  christos #if USE_PTH_THREADS
     34  1.1  christos # define TEST_PTH_THREADS 1
     35  1.1  christos #endif
     36  1.1  christos #if USE_WIN32_THREADS
     37  1.1  christos # define TEST_WIN32_THREADS 1
     38  1.1  christos #endif
     39  1.1  christos 
     40  1.1  christos /* Whether to enable locking.
     41  1.1  christos    Uncomment this to get a test program without locking, to verify that
     42  1.1  christos    it crashes.  */
     43  1.1  christos #define ENABLE_LOCKING 1
     44  1.1  christos 
     45  1.1  christos /* Which tests to perform.
     46  1.1  christos    Uncomment some of these, to verify that all tests crash if no locking
     47  1.1  christos    is enabled.  */
     48  1.1  christos #define DO_TEST_LOCK 1
     49  1.1  christos #define DO_TEST_RWLOCK 1
     50  1.1  christos #define DO_TEST_RECURSIVE_LOCK 1
     51  1.1  christos #define DO_TEST_ONCE 1
     52  1.1  christos 
     53  1.1  christos /* Whether to help the scheduler through explicit yield().
     54  1.1  christos    Uncomment this to see if the operating system has a fair scheduler.  */
     55  1.1  christos #define EXPLICIT_YIELD 1
     56  1.1  christos 
     57  1.1  christos /* Whether to print debugging messages.  */
     58  1.1  christos #define ENABLE_DEBUGGING 0
     59  1.1  christos 
     60  1.1  christos /* Number of simultaneous threads.  */
     61  1.1  christos #define THREAD_COUNT 10
     62  1.1  christos 
     63  1.1  christos /* Number of operations performed in each thread.
     64  1.1  christos    This is quite high, because with a smaller count, say 5000, we often get
     65  1.1  christos    an "OK" result even without ENABLE_LOCKING (on Linux/x86).  */
     66  1.1  christos #define REPEAT_COUNT 50000
     67  1.1  christos 
     68  1.1  christos #include <stdio.h>
     69  1.1  christos #include <stdlib.h>
     70  1.1  christos #include <string.h>
     71  1.1  christos 
     72  1.1  christos #if !ENABLE_LOCKING
     73  1.1  christos # undef USE_POSIX_THREADS
     74  1.1  christos # undef USE_SOLARIS_THREADS
     75  1.1  christos # undef USE_PTH_THREADS
     76  1.1  christos # undef USE_WIN32_THREADS
     77  1.1  christos #endif
     78  1.1  christos #include "lock.h"
     79  1.1  christos 
     80  1.1  christos #if ENABLE_DEBUGGING
     81  1.1  christos # define dbgprintf printf
     82  1.1  christos #else
     83  1.1  christos # define dbgprintf if (0) printf
     84  1.1  christos #endif
     85  1.1  christos 
     86  1.1  christos #if TEST_POSIX_THREADS
     87  1.1  christos # include <pthread.h>
     88  1.1  christos # include <sched.h>
     89  1.1  christos typedef pthread_t gl_thread_t;
     90  1.1  christos static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg)
     91  1.1  christos {
     92  1.1  christos   pthread_t thread;
     93  1.1  christos   if (pthread_create (&thread, NULL, func, arg) != 0)
     94  1.1  christos     abort ();
     95  1.1  christos   return thread;
     96  1.1  christos }
     97  1.1  christos static inline void gl_thread_join (gl_thread_t thread)
     98  1.1  christos {
     99  1.1  christos   void *retval;
    100  1.1  christos   if (pthread_join (thread, &retval) != 0)
    101  1.1  christos     abort ();
    102  1.1  christos }
    103  1.1  christos static inline void gl_thread_yield (void)
    104  1.1  christos {
    105  1.1  christos   sched_yield ();
    106  1.1  christos }
    107  1.1  christos static inline void * gl_thread_self (void)
    108  1.1  christos {
    109  1.1  christos   return (void *) pthread_self ();
    110  1.1  christos }
    111  1.1  christos #endif
    112  1.1  christos #if TEST_PTH_THREADS
    113  1.1  christos # include <pth.h>
    114  1.1  christos typedef pth_t gl_thread_t;
    115  1.1  christos static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg)
    116  1.1  christos {
    117  1.1  christos   pth_t thread = pth_spawn (NULL, func, arg);
    118  1.1  christos   if (thread == NULL)
    119  1.1  christos     abort ();
    120  1.1  christos   return thread;
    121  1.1  christos }
    122  1.1  christos static inline void gl_thread_join (gl_thread_t thread)
    123  1.1  christos {
    124  1.1  christos   if (!pth_join (thread, NULL))
    125  1.1  christos     abort ();
    126  1.1  christos }
    127  1.1  christos static inline void gl_thread_yield (void)
    128  1.1  christos {
    129  1.1  christos   pth_yield (NULL);
    130  1.1  christos }
    131  1.1  christos static inline void * gl_thread_self (void)
    132  1.1  christos {
    133  1.1  christos   return pth_self ();
    134  1.1  christos }
    135  1.1  christos #endif
    136  1.1  christos #if TEST_SOLARIS_THREADS
    137  1.1  christos # include <thread.h>
    138  1.1  christos typedef thread_t gl_thread_t;
    139  1.1  christos static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg)
    140  1.1  christos {
    141  1.1  christos   thread_t thread;
    142  1.1  christos   if (thr_create (NULL, 0, func, arg, 0, &thread) != 0)
    143  1.1  christos     abort ();
    144  1.1  christos   return thread;
    145  1.1  christos }
    146  1.1  christos static inline void gl_thread_join (gl_thread_t thread)
    147  1.1  christos {
    148  1.1  christos   void *retval;
    149  1.1  christos   if (thr_join (thread, NULL, &retval) != 0)
    150  1.1  christos     abort ();
    151  1.1  christos }
    152  1.1  christos static inline void gl_thread_yield (void)
    153  1.1  christos {
    154  1.1  christos   thr_yield ();
    155  1.1  christos }
    156  1.1  christos static inline void * gl_thread_self (void)
    157  1.1  christos {
    158  1.1  christos   return (void *) thr_self ();
    159  1.1  christos }
    160  1.1  christos #endif
    161  1.1  christos #if TEST_WIN32_THREADS
    162  1.1  christos # include <windows.h>
    163  1.1  christos typedef HANDLE gl_thread_t;
    164  1.1  christos /* Use a wrapper function, instead of adding WINAPI through a cast.  */
    165  1.1  christos struct wrapper_args { void * (*func) (void *); void *arg; };
    166  1.1  christos static DWORD WINAPI wrapper_func (void *varg)
    167  1.1  christos {
    168  1.1  christos   struct wrapper_args *warg = (struct wrapper_args *)varg;
    169  1.1  christos   void * (*func) (void *) = warg->func;
    170  1.1  christos   void *arg = warg->arg;
    171  1.1  christos   free (warg);
    172  1.1  christos   func (arg);
    173  1.1  christos   return 0;
    174  1.1  christos }
    175  1.1  christos static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg)
    176  1.1  christos {
    177  1.1  christos   struct wrapper_args *warg =
    178  1.1  christos     (struct wrapper_args *) malloc (sizeof (struct wrapper_args));
    179  1.1  christos   if (warg == NULL)
    180  1.1  christos     abort ();
    181  1.1  christos   warg->func = func;
    182  1.1  christos   warg->arg = arg;
    183  1.1  christos   {
    184  1.1  christos     DWORD thread_id;
    185  1.1  christos     HANDLE thread =
    186  1.1  christos       CreateThread (NULL, 100000, wrapper_func, warg, 0, &thread_id);
    187  1.1  christos     if (thread == NULL)
    188  1.1  christos       abort ();
    189  1.1  christos     return thread;
    190  1.1  christos   }
    191  1.1  christos }
    192  1.1  christos static inline void gl_thread_join (gl_thread_t thread)
    193  1.1  christos {
    194  1.1  christos   if (WaitForSingleObject (thread, INFINITE) == WAIT_FAILED)
    195  1.1  christos     abort ();
    196  1.1  christos   if (!CloseHandle (thread))
    197  1.1  christos     abort ();
    198  1.1  christos }
    199  1.1  christos static inline void gl_thread_yield (void)
    200  1.1  christos {
    201  1.1  christos   Sleep (0);
    202  1.1  christos }
    203  1.1  christos static inline void * gl_thread_self (void)
    204  1.1  christos {
    205  1.1  christos   return (void *) GetCurrentThreadId ();
    206  1.1  christos }
    207  1.1  christos #endif
    208  1.1  christos #if EXPLICIT_YIELD
    209  1.1  christos # define yield() gl_thread_yield ()
    210  1.1  christos #else
    211  1.1  christos # define yield()
    212  1.1  christos #endif
    213  1.1  christos 
    214  1.1  christos #define ACCOUNT_COUNT 4
    215  1.1  christos 
    216  1.1  christos static int account[ACCOUNT_COUNT];
    217  1.1  christos 
    218  1.1  christos static int
    219  1.1  christos random_account (void)
    220  1.1  christos {
    221  1.1  christos   return ((unsigned int) rand() >> 3) % ACCOUNT_COUNT;
    222  1.1  christos }
    223  1.1  christos 
    224  1.1  christos static void
    225  1.1  christos check_accounts (void)
    226  1.1  christos {
    227  1.1  christos   int i, sum;
    228  1.1  christos 
    229  1.1  christos   sum = 0;
    230  1.1  christos   for (i = 0; i < ACCOUNT_COUNT; i++)
    231  1.1  christos     sum += account[i];
    232  1.1  christos   if (sum != ACCOUNT_COUNT * 1000)
    233  1.1  christos     abort ();
    234  1.1  christos }
    235  1.1  christos 
    236  1.1  christos /* Test normal locks by having several bank accounts and several threads
    237  1.1  christos    which shuffle around money between the accounts and another thread
    238  1.1  christos    checking that all the money is still there.  */
    239  1.1  christos 
    240  1.1  christos gl_lock_define_initialized(static, my_lock)
    241  1.1  christos 
    242  1.1  christos static void *
    243  1.1  christos lock_mutator_thread (void *arg)
    244  1.1  christos {
    245  1.1  christos   int repeat;
    246  1.1  christos 
    247  1.1  christos   for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
    248  1.1  christos     {
    249  1.1  christos       int i1, i2, value;
    250  1.1  christos 
    251  1.1  christos       dbgprintf ("Mutator %p before lock\n", gl_thread_self ());
    252  1.1  christos       gl_lock_lock (my_lock);
    253  1.1  christos       dbgprintf ("Mutator %p after  lock\n", gl_thread_self ());
    254  1.1  christos 
    255  1.1  christos       i1 = random_account ();
    256  1.1  christos       i2 = random_account ();
    257  1.1  christos       value = ((unsigned int) rand() >> 3) % 10;
    258  1.1  christos       account[i1] += value;
    259  1.1  christos       account[i2] -= value;
    260  1.1  christos 
    261  1.1  christos       dbgprintf ("Mutator %p before unlock\n", gl_thread_self ());
    262  1.1  christos       gl_lock_unlock (my_lock);
    263  1.1  christos       dbgprintf ("Mutator %p after  unlock\n", gl_thread_self ());
    264  1.1  christos 
    265  1.1  christos       dbgprintf ("Mutator %p before check lock\n", gl_thread_self ());
    266  1.1  christos       gl_lock_lock (my_lock);
    267  1.1  christos       check_accounts ();
    268  1.1  christos       gl_lock_unlock (my_lock);
    269  1.1  christos       dbgprintf ("Mutator %p after  check unlock\n", gl_thread_self ());
    270  1.1  christos 
    271  1.1  christos       yield ();
    272  1.1  christos     }
    273  1.1  christos 
    274  1.1  christos   dbgprintf ("Mutator %p dying.\n", gl_thread_self ());
    275  1.1  christos   return NULL;
    276  1.1  christos }
    277  1.1  christos 
    278  1.1  christos static volatile int lock_checker_done;
    279  1.1  christos 
    280  1.1  christos static void *
    281  1.1  christos lock_checker_thread (void *arg)
    282  1.1  christos {
    283  1.1  christos   while (!lock_checker_done)
    284  1.1  christos     {
    285  1.1  christos       dbgprintf ("Checker %p before check lock\n", gl_thread_self ());
    286  1.1  christos       gl_lock_lock (my_lock);
    287  1.1  christos       check_accounts ();
    288  1.1  christos       gl_lock_unlock (my_lock);
    289  1.1  christos       dbgprintf ("Checker %p after  check unlock\n", gl_thread_self ());
    290  1.1  christos 
    291  1.1  christos       yield ();
    292  1.1  christos     }
    293  1.1  christos 
    294  1.1  christos   dbgprintf ("Checker %p dying.\n", gl_thread_self ());
    295  1.1  christos   return NULL;
    296  1.1  christos }
    297  1.1  christos 
    298  1.1  christos void
    299  1.1  christos test_lock (void)
    300  1.1  christos {
    301  1.1  christos   int i;
    302  1.1  christos   gl_thread_t checkerthread;
    303  1.1  christos   gl_thread_t threads[THREAD_COUNT];
    304  1.1  christos 
    305  1.1  christos   /* Initialization.  */
    306  1.1  christos   for (i = 0; i < ACCOUNT_COUNT; i++)
    307  1.1  christos     account[i] = 1000;
    308  1.1  christos   lock_checker_done = 0;
    309  1.1  christos 
    310  1.1  christos   /* Spawn the threads.  */
    311  1.1  christos   checkerthread = gl_thread_create (lock_checker_thread, NULL);
    312  1.1  christos   for (i = 0; i < THREAD_COUNT; i++)
    313  1.1  christos     threads[i] = gl_thread_create (lock_mutator_thread, NULL);
    314  1.1  christos 
    315  1.1  christos   /* Wait for the threads to terminate.  */
    316  1.1  christos   for (i = 0; i < THREAD_COUNT; i++)
    317  1.1  christos     gl_thread_join (threads[i]);
    318  1.1  christos   lock_checker_done = 1;
    319  1.1  christos   gl_thread_join (checkerthread);
    320  1.1  christos   check_accounts ();
    321  1.1  christos }
    322  1.1  christos 
    323  1.1  christos /* Test read-write locks by having several bank accounts and several threads
    324  1.1  christos    which shuffle around money between the accounts and several other threads
    325  1.1  christos    that check that all the money is still there.  */
    326  1.1  christos 
    327  1.1  christos gl_rwlock_define_initialized(static, my_rwlock)
    328  1.1  christos 
    329  1.1  christos static void *
    330  1.1  christos rwlock_mutator_thread (void *arg)
    331  1.1  christos {
    332  1.1  christos   int repeat;
    333  1.1  christos 
    334  1.1  christos   for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
    335  1.1  christos     {
    336  1.1  christos       int i1, i2, value;
    337  1.1  christos 
    338  1.1  christos       dbgprintf ("Mutator %p before wrlock\n", gl_thread_self ());
    339  1.1  christos       gl_rwlock_wrlock (my_rwlock);
    340  1.1  christos       dbgprintf ("Mutator %p after  wrlock\n", gl_thread_self ());
    341  1.1  christos 
    342  1.1  christos       i1 = random_account ();
    343  1.1  christos       i2 = random_account ();
    344  1.1  christos       value = ((unsigned int) rand() >> 3) % 10;
    345  1.1  christos       account[i1] += value;
    346  1.1  christos       account[i2] -= value;
    347  1.1  christos 
    348  1.1  christos       dbgprintf ("Mutator %p before unlock\n", gl_thread_self ());
    349  1.1  christos       gl_rwlock_unlock (my_rwlock);
    350  1.1  christos       dbgprintf ("Mutator %p after  unlock\n", gl_thread_self ());
    351  1.1  christos 
    352  1.1  christos       yield ();
    353  1.1  christos     }
    354  1.1  christos 
    355  1.1  christos   dbgprintf ("Mutator %p dying.\n", gl_thread_self ());
    356  1.1  christos   return NULL;
    357  1.1  christos }
    358  1.1  christos 
    359  1.1  christos static volatile int rwlock_checker_done;
    360  1.1  christos 
    361  1.1  christos static void *
    362  1.1  christos rwlock_checker_thread (void *arg)
    363  1.1  christos {
    364  1.1  christos   while (!rwlock_checker_done)
    365  1.1  christos     {
    366  1.1  christos       dbgprintf ("Checker %p before check rdlock\n", gl_thread_self ());
    367  1.1  christos       gl_rwlock_rdlock (my_rwlock);
    368  1.1  christos       check_accounts ();
    369  1.1  christos       gl_rwlock_unlock (my_rwlock);
    370  1.1  christos       dbgprintf ("Checker %p after  check unlock\n", gl_thread_self ());
    371  1.1  christos 
    372  1.1  christos       yield ();
    373  1.1  christos     }
    374  1.1  christos 
    375  1.1  christos   dbgprintf ("Checker %p dying.\n", gl_thread_self ());
    376  1.1  christos   return NULL;
    377  1.1  christos }
    378  1.1  christos 
    379  1.1  christos void
    380  1.1  christos test_rwlock (void)
    381  1.1  christos {
    382  1.1  christos   int i;
    383  1.1  christos   gl_thread_t checkerthreads[THREAD_COUNT];
    384  1.1  christos   gl_thread_t threads[THREAD_COUNT];
    385  1.1  christos 
    386  1.1  christos   /* Initialization.  */
    387  1.1  christos   for (i = 0; i < ACCOUNT_COUNT; i++)
    388  1.1  christos     account[i] = 1000;
    389  1.1  christos   rwlock_checker_done = 0;
    390  1.1  christos 
    391  1.1  christos   /* Spawn the threads.  */
    392  1.1  christos   for (i = 0; i < THREAD_COUNT; i++)
    393  1.1  christos     checkerthreads[i] = gl_thread_create (rwlock_checker_thread, NULL);
    394  1.1  christos   for (i = 0; i < THREAD_COUNT; i++)
    395  1.1  christos     threads[i] = gl_thread_create (rwlock_mutator_thread, NULL);
    396  1.1  christos 
    397  1.1  christos   /* Wait for the threads to terminate.  */
    398  1.1  christos   for (i = 0; i < THREAD_COUNT; i++)
    399  1.1  christos     gl_thread_join (threads[i]);
    400  1.1  christos   rwlock_checker_done = 1;
    401  1.1  christos   for (i = 0; i < THREAD_COUNT; i++)
    402  1.1  christos     gl_thread_join (checkerthreads[i]);
    403  1.1  christos   check_accounts ();
    404  1.1  christos }
    405  1.1  christos 
    406  1.1  christos /* Test recursive locks by having several bank accounts and several threads
    407  1.1  christos    which shuffle around money between the accounts (recursively) and another
    408  1.1  christos    thread checking that all the money is still there.  */
    409  1.1  christos 
    410  1.1  christos gl_recursive_lock_define_initialized(static, my_reclock)
    411  1.1  christos 
    412  1.1  christos static void
    413  1.1  christos recshuffle (void)
    414  1.1  christos {
    415  1.1  christos   int i1, i2, value;
    416  1.1  christos 
    417  1.1  christos   dbgprintf ("Mutator %p before lock\n", gl_thread_self ());
    418  1.1  christos   gl_recursive_lock_lock (my_reclock);
    419  1.1  christos   dbgprintf ("Mutator %p after  lock\n", gl_thread_self ());
    420  1.1  christos 
    421  1.1  christos   i1 = random_account ();
    422  1.1  christos   i2 = random_account ();
    423  1.1  christos   value = ((unsigned int) rand() >> 3) % 10;
    424  1.1  christos   account[i1] += value;
    425  1.1  christos   account[i2] -= value;
    426  1.1  christos 
    427  1.1  christos   /* Recursive with probability 0.5.  */
    428  1.1  christos   if (((unsigned int) rand() >> 3) % 2)
    429  1.1  christos     recshuffle ();
    430  1.1  christos 
    431  1.1  christos   dbgprintf ("Mutator %p before unlock\n", gl_thread_self ());
    432  1.1  christos   gl_recursive_lock_unlock (my_reclock);
    433  1.1  christos   dbgprintf ("Mutator %p after  unlock\n", gl_thread_self ());
    434  1.1  christos }
    435  1.1  christos 
    436  1.1  christos static void *
    437  1.1  christos reclock_mutator_thread (void *arg)
    438  1.1  christos {
    439  1.1  christos   int repeat;
    440  1.1  christos 
    441  1.1  christos   for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
    442  1.1  christos     {
    443  1.1  christos       recshuffle ();
    444  1.1  christos 
    445  1.1  christos       dbgprintf ("Mutator %p before check lock\n", gl_thread_self ());
    446  1.1  christos       gl_recursive_lock_lock (my_reclock);
    447  1.1  christos       check_accounts ();
    448  1.1  christos       gl_recursive_lock_unlock (my_reclock);
    449  1.1  christos       dbgprintf ("Mutator %p after  check unlock\n", gl_thread_self ());
    450  1.1  christos 
    451  1.1  christos       yield ();
    452  1.1  christos     }
    453  1.1  christos 
    454  1.1  christos   dbgprintf ("Mutator %p dying.\n", gl_thread_self ());
    455  1.1  christos   return NULL;
    456  1.1  christos }
    457  1.1  christos 
    458  1.1  christos static volatile int reclock_checker_done;
    459  1.1  christos 
    460  1.1  christos static void *
    461  1.1  christos reclock_checker_thread (void *arg)
    462  1.1  christos {
    463  1.1  christos   while (!reclock_checker_done)
    464  1.1  christos     {
    465  1.1  christos       dbgprintf ("Checker %p before check lock\n", gl_thread_self ());
    466  1.1  christos       gl_recursive_lock_lock (my_reclock);
    467  1.1  christos       check_accounts ();
    468  1.1  christos       gl_recursive_lock_unlock (my_reclock);
    469  1.1  christos       dbgprintf ("Checker %p after  check unlock\n", gl_thread_self ());
    470  1.1  christos 
    471  1.1  christos       yield ();
    472  1.1  christos     }
    473  1.1  christos 
    474  1.1  christos   dbgprintf ("Checker %p dying.\n", gl_thread_self ());
    475  1.1  christos   return NULL;
    476  1.1  christos }
    477  1.1  christos 
    478  1.1  christos void
    479  1.1  christos test_recursive_lock (void)
    480  1.1  christos {
    481  1.1  christos   int i;
    482  1.1  christos   gl_thread_t checkerthread;
    483  1.1  christos   gl_thread_t threads[THREAD_COUNT];
    484  1.1  christos 
    485  1.1  christos   /* Initialization.  */
    486  1.1  christos   for (i = 0; i < ACCOUNT_COUNT; i++)
    487  1.1  christos     account[i] = 1000;
    488  1.1  christos   reclock_checker_done = 0;
    489  1.1  christos 
    490  1.1  christos   /* Spawn the threads.  */
    491  1.1  christos   checkerthread = gl_thread_create (reclock_checker_thread, NULL);
    492  1.1  christos   for (i = 0; i < THREAD_COUNT; i++)
    493  1.1  christos     threads[i] = gl_thread_create (reclock_mutator_thread, NULL);
    494  1.1  christos 
    495  1.1  christos   /* Wait for the threads to terminate.  */
    496  1.1  christos   for (i = 0; i < THREAD_COUNT; i++)
    497  1.1  christos     gl_thread_join (threads[i]);
    498  1.1  christos   reclock_checker_done = 1;
    499  1.1  christos   gl_thread_join (checkerthread);
    500  1.1  christos   check_accounts ();
    501  1.1  christos }
    502  1.1  christos 
    503  1.1  christos /* Test once-only execution by having several threads attempt to grab a
    504  1.1  christos    once-only task simultaneously (triggered by releasing a read-write lock).  */
    505  1.1  christos 
    506  1.1  christos gl_once_define(static, fresh_once)
    507  1.1  christos static int ready[THREAD_COUNT];
    508  1.1  christos static gl_lock_t ready_lock[THREAD_COUNT];
    509  1.1  christos #if ENABLE_LOCKING
    510  1.1  christos static gl_rwlock_t fire_signal[REPEAT_COUNT];
    511  1.1  christos #else
    512  1.1  christos static volatile int fire_signal_state;
    513  1.1  christos #endif
    514  1.1  christos static gl_once_t once_control;
    515  1.1  christos static int performed;
    516  1.1  christos gl_lock_define_initialized(static, performed_lock)
    517  1.1  christos 
    518  1.1  christos static void
    519  1.1  christos once_execute (void)
    520  1.1  christos {
    521  1.1  christos   gl_lock_lock (performed_lock);
    522  1.1  christos   performed++;
    523  1.1  christos   gl_lock_unlock (performed_lock);
    524  1.1  christos }
    525  1.1  christos 
    526  1.1  christos static void *
    527  1.1  christos once_contender_thread (void *arg)
    528  1.1  christos {
    529  1.1  christos   int id = (int) (long) arg;
    530  1.1  christos   int repeat;
    531  1.1  christos 
    532  1.1  christos   for (repeat = 0; repeat <= REPEAT_COUNT; repeat++)
    533  1.1  christos     {
    534  1.1  christos       /* Tell the main thread that we're ready.  */
    535  1.1  christos       gl_lock_lock (ready_lock[id]);
    536  1.1  christos       ready[id] = 1;
    537  1.1  christos       gl_lock_unlock (ready_lock[id]);
    538  1.1  christos 
    539  1.1  christos       if (repeat == REPEAT_COUNT)
    540  1.1  christos 	break;
    541  1.1  christos 
    542  1.1  christos       dbgprintf ("Contender %p waiting for signal for round %d\n",
    543  1.1  christos 		 gl_thread_self (), repeat);
    544  1.1  christos #if ENABLE_LOCKING
    545  1.1  christos       /* Wait for the signal to go.  */
    546  1.1  christos       gl_rwlock_rdlock (fire_signal[repeat]);
    547  1.1  christos       /* And don't hinder the others (if the scheduler is unfair).  */
    548  1.1  christos       gl_rwlock_unlock (fire_signal[repeat]);
    549  1.1  christos #else
    550  1.1  christos       /* Wait for the signal to go.  */
    551  1.1  christos       while (fire_signal_state <= repeat)
    552  1.1  christos 	yield ();
    553  1.1  christos #endif
    554  1.1  christos       dbgprintf ("Contender %p got the     signal for round %d\n",
    555  1.1  christos 		 gl_thread_self (), repeat);
    556  1.1  christos 
    557  1.1  christos       /* Contend for execution.  */
    558  1.1  christos       gl_once (once_control, once_execute);
    559  1.1  christos     }
    560  1.1  christos 
    561  1.1  christos   return NULL;
    562  1.1  christos }
    563  1.1  christos 
    564  1.1  christos void
    565  1.1  christos test_once (void)
    566  1.1  christos {
    567  1.1  christos   int i, repeat;
    568  1.1  christos   gl_thread_t threads[THREAD_COUNT];
    569  1.1  christos 
    570  1.1  christos   /* Initialize all variables.  */
    571  1.1  christos   for (i = 0; i < THREAD_COUNT; i++)
    572  1.1  christos     {
    573  1.1  christos       ready[i] = 0;
    574  1.1  christos       gl_lock_init (ready_lock[i]);
    575  1.1  christos     }
    576  1.1  christos #if ENABLE_LOCKING
    577  1.1  christos   for (i = 0; i < REPEAT_COUNT; i++)
    578  1.1  christos     gl_rwlock_init (fire_signal[i]);
    579  1.1  christos #else
    580  1.1  christos   fire_signal_state = 0;
    581  1.1  christos #endif
    582  1.1  christos 
    583  1.1  christos   /* Block all fire_signals.  */
    584  1.1  christos   for (i = REPEAT_COUNT-1; i >= 0; i--)
    585  1.1  christos     gl_rwlock_wrlock (fire_signal[i]);
    586  1.1  christos 
    587  1.1  christos   /* Spawn the threads.  */
    588  1.1  christos   for (i = 0; i < THREAD_COUNT; i++)
    589  1.1  christos     threads[i] = gl_thread_create (once_contender_thread, (void *) (long) i);
    590  1.1  christos 
    591  1.1  christos   for (repeat = 0; repeat <= REPEAT_COUNT; repeat++)
    592  1.1  christos     {
    593  1.1  christos       /* Wait until every thread is ready.  */
    594  1.1  christos       dbgprintf ("Main thread before synchonizing for round %d\n", repeat);
    595  1.1  christos       for (;;)
    596  1.1  christos 	{
    597  1.1  christos 	  int ready_count = 0;
    598  1.1  christos 	  for (i = 0; i < THREAD_COUNT; i++)
    599  1.1  christos 	    {
    600  1.1  christos 	      gl_lock_lock (ready_lock[i]);
    601  1.1  christos 	      ready_count += ready[i];
    602  1.1  christos 	      gl_lock_unlock (ready_lock[i]);
    603  1.1  christos 	    }
    604  1.1  christos 	  if (ready_count == THREAD_COUNT)
    605  1.1  christos 	    break;
    606  1.1  christos 	  yield ();
    607  1.1  christos 	}
    608  1.1  christos       dbgprintf ("Main thread after  synchonizing for round %d\n", repeat);
    609  1.1  christos 
    610  1.1  christos       if (repeat > 0)
    611  1.1  christos 	{
    612  1.1  christos 	  /* Check that exactly one thread executed the once_execute()
    613  1.1  christos 	     function.  */
    614  1.1  christos 	  if (performed != 1)
    615  1.1  christos 	    abort ();
    616  1.1  christos 	}
    617  1.1  christos 
    618  1.1  christos       if (repeat == REPEAT_COUNT)
    619  1.1  christos 	break;
    620  1.1  christos 
    621  1.1  christos       /* Preparation for the next round: Initialize once_control.  */
    622  1.1  christos       memcpy (&once_control, &fresh_once, sizeof (gl_once_t));
    623  1.1  christos 
    624  1.1  christos       /* Preparation for the next round: Reset the performed counter.  */
    625  1.1  christos       performed = 0;
    626  1.1  christos 
    627  1.1  christos       /* Preparation for the next round: Reset the ready flags.  */
    628  1.1  christos       for (i = 0; i < THREAD_COUNT; i++)
    629  1.1  christos 	{
    630  1.1  christos 	  gl_lock_lock (ready_lock[i]);
    631  1.1  christos 	  ready[i] = 0;
    632  1.1  christos 	  gl_lock_unlock (ready_lock[i]);
    633  1.1  christos 	}
    634  1.1  christos 
    635  1.1  christos       /* Signal all threads simultaneously.  */
    636  1.1  christos       dbgprintf ("Main thread giving signal for round %d\n", repeat);
    637  1.1  christos #if ENABLE_LOCKING
    638  1.1  christos       gl_rwlock_unlock (fire_signal[repeat]);
    639  1.1  christos #else
    640  1.1  christos       fire_signal_state = repeat + 1;
    641  1.1  christos #endif
    642  1.1  christos     }
    643  1.1  christos 
    644  1.1  christos   /* Wait for the threads to terminate.  */
    645  1.1  christos   for (i = 0; i < THREAD_COUNT; i++)
    646  1.1  christos     gl_thread_join (threads[i]);
    647  1.1  christos }
    648  1.1  christos 
    649  1.1  christos int
    650  1.1  christos main ()
    651  1.1  christos {
    652  1.1  christos #if TEST_PTH_THREADS
    653  1.1  christos   if (!pth_init ())
    654  1.1  christos     abort ();
    655  1.1  christos #endif
    656  1.1  christos 
    657  1.1  christos #if DO_TEST_LOCK
    658  1.1  christos   printf ("Starting test_lock ..."); fflush (stdout);
    659  1.1  christos   test_lock ();
    660  1.1  christos   printf (" OK\n"); fflush (stdout);
    661  1.1  christos #endif
    662  1.1  christos #if DO_TEST_RWLOCK
    663  1.1  christos   printf ("Starting test_rwlock ..."); fflush (stdout);
    664  1.1  christos   test_rwlock ();
    665  1.1  christos   printf (" OK\n"); fflush (stdout);
    666  1.1  christos #endif
    667  1.1  christos #if DO_TEST_RECURSIVE_LOCK
    668  1.1  christos   printf ("Starting test_recursive_lock ..."); fflush (stdout);
    669  1.1  christos   test_recursive_lock ();
    670  1.1  christos   printf (" OK\n"); fflush (stdout);
    671  1.1  christos #endif
    672  1.1  christos #if DO_TEST_ONCE
    673  1.1  christos   printf ("Starting test_once ..."); fflush (stdout);
    674  1.1  christos   test_once ();
    675  1.1  christos   printf (" OK\n"); fflush (stdout);
    676  1.1  christos #endif
    677  1.1  christos 
    678  1.1  christos   return 0;
    679  1.1  christos }
    680  1.1  christos 
    681  1.1  christos #else
    682  1.1  christos 
    683  1.1  christos /* No multithreading available.  */
    684  1.1  christos 
    685  1.1  christos int
    686  1.1  christos main ()
    687  1.1  christos {
    688  1.1  christos   return 77;
    689  1.1  christos }
    690  1.1  christos 
    691  1.1  christos #endif
    692