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