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