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