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