1 /* Test of locking in multithreaded situations.
2 Copyright (C) 2005, 2008-2011 Free Software Foundation, Inc.
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.
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.
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/>. */
17 /* Written by Bruno Haible <bruno@clisp.org>, 2005. */
21 #if USE_POSIX_THREADS || USE_SOLARIS_THREADS || USE_PTH_THREADS || USE_WIN32_THREADS
24 # define TEST_POSIX_THREADS 1
26 #if USE_SOLARIS_THREADS
27 # define TEST_SOLARIS_THREADS 1
30 # define TEST_PTH_THREADS 1
33 # define TEST_WIN32_THREADS 1
36 /* Whether to enable locking.
37 Uncomment this to get a test program without locking, to verify that
39 #define ENABLE_LOCKING 1
41 /* Which tests to perform.
42 Uncomment some of these, to verify that all tests crash if no locking
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
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
53 /* Whether to print debugging messages. */
54 #define ENABLE_DEBUGGING 0
56 /* Number of simultaneous threads. */
57 #define THREAD_COUNT 10
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
69 # undef USE_POSIX_THREADS
70 # undef USE_SOLARIS_THREADS
71 # undef USE_PTH_THREADS
72 # undef USE_WIN32_THREADS
74 #include "glthread/lock.h"
77 # if TEST_POSIX_THREADS
78 # define USE_POSIX_THREADS 1
80 # if TEST_SOLARIS_THREADS
81 # define USE_SOLARIS_THREADS 1
84 # define USE_PTH_THREADS 1
86 # if TEST_WIN32_THREADS
87 # define USE_WIN32_THREADS 1
91 #include "glthread/thread.h"
92 #include "glthread/yield.h"
95 # define dbgprintf printf
97 # define dbgprintf if (0) printf
101 # define yield() gl_thread_yield ()
106 #define ACCOUNT_COUNT 4
108 static int account[ACCOUNT_COUNT];
111 random_account (void)
113 return ((unsigned int) rand () >> 3) % ACCOUNT_COUNT;
117 check_accounts (void)
122 for (i = 0; i < ACCOUNT_COUNT; i++)
124 if (sum != ACCOUNT_COUNT * 1000)
129 /* ------------------- Test normal (non-recursive) locks ------------------- */
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. */
135 gl_lock_define_initialized(static, my_lock)
138 lock_mutator_thread (void *arg)
142 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
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 ());
150 i1 = random_account ();
151 i2 = random_account ();
152 value = ((unsigned int) rand () >> 3) % 10;
153 account[i1] += value;
154 account[i2] -= value;
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 ());
160 dbgprintf ("Mutator %p before check lock\n", gl_thread_self_pointer ());
161 gl_lock_lock (my_lock);
163 gl_lock_unlock (my_lock);
164 dbgprintf ("Mutator %p after check unlock\n", gl_thread_self_pointer ());
169 dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
173 static volatile int lock_checker_done;
176 lock_checker_thread (void *arg)
178 while (!lock_checker_done)
180 dbgprintf ("Checker %p before check lock\n", gl_thread_self_pointer ());
181 gl_lock_lock (my_lock);
183 gl_lock_unlock (my_lock);
184 dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ());
189 dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
197 gl_thread_t checkerthread;
198 gl_thread_t threads[THREAD_COUNT];
200 /* Initialization. */
201 for (i = 0; i < ACCOUNT_COUNT; i++)
203 lock_checker_done = 0;
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);
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);
219 /* ----------------- Test read-write (non-recursive) locks ----------------- */
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. */
225 gl_rwlock_define_initialized(static, my_rwlock)
228 rwlock_mutator_thread (void *arg)
232 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
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 ());
240 i1 = random_account ();
241 i2 = random_account ();
242 value = ((unsigned int) rand () >> 3) % 10;
243 account[i1] += value;
244 account[i2] -= value;
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 ());
253 dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
257 static volatile int rwlock_checker_done;
260 rwlock_checker_thread (void *arg)
262 while (!rwlock_checker_done)
264 dbgprintf ("Checker %p before check rdlock\n", gl_thread_self_pointer ());
265 gl_rwlock_rdlock (my_rwlock);
267 gl_rwlock_unlock (my_rwlock);
268 dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ());
273 dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
281 gl_thread_t checkerthreads[THREAD_COUNT];
282 gl_thread_t threads[THREAD_COUNT];
284 /* Initialization. */
285 for (i = 0; i < ACCOUNT_COUNT; i++)
287 rwlock_checker_done = 0;
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);
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);
305 /* -------------------------- Test recursive locks -------------------------- */
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. */
311 gl_recursive_lock_define_initialized(static, my_reclock)
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 ());
322 i1 = random_account ();
323 i2 = random_account ();
324 value = ((unsigned int) rand () >> 3) % 10;
325 account[i1] += value;
326 account[i2] -= value;
328 /* Recursive with probability 0.5. */
329 if (((unsigned int) rand () >> 3) % 2)
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 ());
338 reclock_mutator_thread (void *arg)
342 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
346 dbgprintf ("Mutator %p before check lock\n", gl_thread_self_pointer ());
347 gl_recursive_lock_lock (my_reclock);
349 gl_recursive_lock_unlock (my_reclock);
350 dbgprintf ("Mutator %p after check unlock\n", gl_thread_self_pointer ());
355 dbgprintf ("Mutator %p dying.\n", gl_thread_self_pointer ());
359 static volatile int reclock_checker_done;
362 reclock_checker_thread (void *arg)
364 while (!reclock_checker_done)
366 dbgprintf ("Checker %p before check lock\n", gl_thread_self_pointer ());
367 gl_recursive_lock_lock (my_reclock);
369 gl_recursive_lock_unlock (my_reclock);
370 dbgprintf ("Checker %p after check unlock\n", gl_thread_self_pointer ());
375 dbgprintf ("Checker %p dying.\n", gl_thread_self_pointer ());
380 test_recursive_lock (void)
383 gl_thread_t checkerthread;
384 gl_thread_t threads[THREAD_COUNT];
386 /* Initialization. */
387 for (i = 0; i < ACCOUNT_COUNT; i++)
389 reclock_checker_done = 0;
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);
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);
405 /* ------------------------ Test once-only execution ------------------------ */
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). */
410 gl_once_define(static, fresh_once)
411 static int ready[THREAD_COUNT];
412 static gl_lock_t ready_lock[THREAD_COUNT];
414 static gl_rwlock_t fire_signal[REPEAT_COUNT];
416 static volatile int fire_signal_state;
418 static gl_once_t once_control;
419 static int performed;
420 gl_lock_define_initialized(static, performed_lock)
425 gl_lock_lock (performed_lock);
427 gl_lock_unlock (performed_lock);
431 once_contender_thread (void *arg)
433 int id = (int) (long) arg;
436 for (repeat = 0; repeat <= REPEAT_COUNT; repeat++)
438 /* Tell the main thread that we're ready. */
439 gl_lock_lock (ready_lock[id]);
441 gl_lock_unlock (ready_lock[id]);
443 if (repeat == REPEAT_COUNT)
446 dbgprintf ("Contender %p waiting for signal for round %d\n",
447 gl_thread_self_pointer (), repeat);
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]);
454 /* Wait for the signal to go. */
455 while (fire_signal_state <= repeat)
458 dbgprintf ("Contender %p got the signal for round %d\n",
459 gl_thread_self_pointer (), repeat);
461 /* Contend for execution. */
462 gl_once (once_control, once_execute);
472 gl_thread_t threads[THREAD_COUNT];
474 /* Initialize all variables. */
475 for (i = 0; i < THREAD_COUNT; i++)
478 gl_lock_init (ready_lock[i]);
481 for (i = 0; i < REPEAT_COUNT; i++)
482 gl_rwlock_init (fire_signal[i]);
484 fire_signal_state = 0;
487 /* Block all fire_signals. */
488 for (i = REPEAT_COUNT-1; i >= 0; i--)
489 gl_rwlock_wrlock (fire_signal[i]);
491 /* Spawn the threads. */
492 for (i = 0; i < THREAD_COUNT; i++)
493 threads[i] = gl_thread_create (once_contender_thread, (void *) (long) i);
495 for (repeat = 0; repeat <= REPEAT_COUNT; repeat++)
497 /* Wait until every thread is ready. */
498 dbgprintf ("Main thread before synchonizing for round %d\n", repeat);
502 for (i = 0; i < THREAD_COUNT; i++)
504 gl_lock_lock (ready_lock[i]);
505 ready_count += ready[i];
506 gl_lock_unlock (ready_lock[i]);
508 if (ready_count == THREAD_COUNT)
512 dbgprintf ("Main thread after synchonizing for round %d\n", repeat);
516 /* Check that exactly one thread executed the once_execute()
522 if (repeat == REPEAT_COUNT)
525 /* Preparation for the next round: Initialize once_control. */
526 memcpy (&once_control, &fresh_once, sizeof (gl_once_t));
528 /* Preparation for the next round: Reset the performed counter. */
531 /* Preparation for the next round: Reset the ready flags. */
532 for (i = 0; i < THREAD_COUNT; i++)
534 gl_lock_lock (ready_lock[i]);
536 gl_lock_unlock (ready_lock[i]);
539 /* Signal all threads simultaneously. */
540 dbgprintf ("Main thread giving signal for round %d\n", repeat);
542 gl_rwlock_unlock (fire_signal[repeat]);
544 fire_signal_state = repeat + 1;
548 /* Wait for the threads to terminate. */
549 for (i = 0; i < THREAD_COUNT; i++)
550 gl_thread_join (threads[i], NULL);
554 /* -------------------------------------------------------------------------- */
565 printf ("Starting test_lock ..."); fflush (stdout);
567 printf (" OK\n"); fflush (stdout);
570 printf ("Starting test_rwlock ..."); fflush (stdout);
572 printf (" OK\n"); fflush (stdout);
574 #if DO_TEST_RECURSIVE_LOCK
575 printf ("Starting test_recursive_lock ..."); fflush (stdout);
576 test_recursive_lock ();
577 printf (" OK\n"); fflush (stdout);
580 printf ("Starting test_once ..."); fflush (stdout);
582 printf (" OK\n"); fflush (stdout);
590 /* No multithreading available. */
597 fputs ("Skipping test: multithreading not enabled\n", stderr);