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