• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/ap/gpl/timemachine/gettext-0.17/gettext-tools/gnulib-tests/
1/* Test of locking in multithreaded situations.
2   Copyright (C) 2005 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 "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>
85typedef pthread_t gl_thread_t;
86static 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}
93static inline void gl_thread_join (gl_thread_t thread)
94{
95  void *retval;
96  if (pthread_join (thread, &retval) != 0)
97    abort ();
98}
99static inline void gl_thread_yield (void)
100{
101  sched_yield ();
102}
103static inline void * gl_thread_self (void)
104{
105  return (void *) pthread_self ();
106}
107#endif
108#if TEST_PTH_THREADS
109# include <pth.h>
110typedef pth_t gl_thread_t;
111static 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}
118static inline void gl_thread_join (gl_thread_t thread)
119{
120  if (!pth_join (thread, NULL))
121    abort ();
122}
123static inline void gl_thread_yield (void)
124{
125  pth_yield (NULL);
126}
127static inline void * gl_thread_self (void)
128{
129  return pth_self ();
130}
131#endif
132#if TEST_SOLARIS_THREADS
133# include <thread.h>
134typedef thread_t gl_thread_t;
135static 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}
142static inline void gl_thread_join (gl_thread_t thread)
143{
144  void *retval;
145  if (thr_join (thread, NULL, &retval) != 0)
146    abort ();
147}
148static inline void gl_thread_yield (void)
149{
150  thr_yield ();
151}
152static inline void * gl_thread_self (void)
153{
154  return (void *) thr_self ();
155}
156#endif
157#if TEST_WIN32_THREADS
158# include <windows.h>
159typedef HANDLE gl_thread_t;
160/* Use a wrapper function, instead of adding WINAPI through a cast.  */
161struct wrapper_args { void * (*func) (void *); void *arg; };
162static 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}
171static 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}
188static 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}
195static inline void gl_thread_yield (void)
196{
197  Sleep (0);
198}
199static 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
212static int account[ACCOUNT_COUNT];
213
214static int
215random_account (void)
216{
217  return ((unsigned int) rand() >> 3) % ACCOUNT_COUNT;
218}
219
220static void
221check_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
236gl_lock_define_initialized(static, my_lock)
237
238static void *
239lock_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
274static volatile int lock_checker_done;
275
276static void *
277lock_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
294void
295test_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
323gl_rwlock_define_initialized(static, my_rwlock)
324
325static void *
326rwlock_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
355static volatile int rwlock_checker_done;
356
357static void *
358rwlock_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
375void
376test_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
406gl_recursive_lock_define_initialized(static, my_reclock)
407
408static void
409recshuffle (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
432static void *
433reclock_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
454static volatile int reclock_checker_done;
455
456static void *
457reclock_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
474void
475test_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
502gl_once_define(static, fresh_once)
503static int ready[THREAD_COUNT];
504static gl_lock_t ready_lock[THREAD_COUNT];
505#if ENABLE_LOCKING
506static gl_rwlock_t fire_signal[REPEAT_COUNT];
507#else
508static volatile int fire_signal_state;
509#endif
510static gl_once_t once_control;
511static int performed;
512gl_lock_define_initialized(static, performed_lock)
513
514static void
515once_execute (void)
516{
517  gl_lock_lock (performed_lock);
518  performed++;
519  gl_lock_unlock (performed_lock);
520}
521
522static void *
523once_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
560void
561test_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
645int
646main ()
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
681int
682main ()
683{
684  return 77;
685}
686
687#endif
688