1 /* Test of locking in multithreaded situations.
2 Copyright (C) 2005 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 2, or (at your option)
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, write to the Free Software Foundation,
16 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
18 /* Written by Bruno Haible <bruno@clisp.org>, 2005. */
22 #if USE_POSIX_THREADS || USE_SOLARIS_THREADS || USE_PTH_THREADS || USE_WIN32_THREADS
25 # define TEST_POSIX_THREADS 1
27 #if USE_SOLARIS_THREADS
28 # define TEST_SOLARIS_THREADS 1
31 # define TEST_PTH_THREADS 1
34 # define TEST_WIN32_THREADS 1
37 /* Whether to enable locking.
38 Uncomment this to get a test program without locking, to verify that
40 #define ENABLE_LOCKING 1
42 /* Which tests to perform.
43 Uncomment some of these, to verify that all tests crash if no locking
45 #define DO_TEST_LOCK 1
46 #define DO_TEST_RWLOCK 1
47 #define DO_TEST_RECURSIVE_LOCK 1
48 #define DO_TEST_ONCE 1
50 /* Whether to help the scheduler through explicit yield().
51 Uncomment this to see if the operating system has a fair scheduler. */
52 #define EXPLICIT_YIELD 1
54 /* Whether to print debugging messages. */
55 #define ENABLE_DEBUGGING 0
57 /* Number of simultaneous threads. */
58 #define THREAD_COUNT 10
60 /* Number of operations performed in each thread.
61 This is quite high, because with a smaller count, say 5000, we often get
62 an "OK" result even without ENABLE_LOCKING (on Linux/x86). */
63 #define REPEAT_COUNT 50000
70 # undef USE_POSIX_THREADS
71 # undef USE_SOLARIS_THREADS
72 # undef USE_PTH_THREADS
73 # undef USE_WIN32_THREADS
78 # define dbgprintf printf
80 # define dbgprintf if (0) printf
83 #if TEST_POSIX_THREADS
86 typedef pthread_t gl_thread_t;
87 static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg)
90 if (pthread_create (&thread, NULL, func, arg) != 0)
94 static inline void gl_thread_join (gl_thread_t thread)
97 if (pthread_join (thread, &retval) != 0)
100 static inline void gl_thread_yield (void)
104 static inline void * gl_thread_self (void)
106 return (void *) pthread_self ();
111 typedef pth_t gl_thread_t;
112 static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg)
114 pth_t thread = pth_spawn (NULL, func, arg);
119 static inline void gl_thread_join (gl_thread_t thread)
121 if (!pth_join (thread, NULL))
124 static inline void gl_thread_yield (void)
128 static inline void * gl_thread_self (void)
133 #if TEST_SOLARIS_THREADS
135 typedef thread_t gl_thread_t;
136 static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg)
139 if (thr_create (NULL, 0, func, arg, 0, &thread) != 0)
143 static inline void gl_thread_join (gl_thread_t thread)
146 if (thr_join (thread, NULL, &retval) != 0)
149 static inline void gl_thread_yield (void)
153 static inline void * gl_thread_self (void)
155 return (void *) thr_self ();
158 #if TEST_WIN32_THREADS
159 # include <windows.h>
160 typedef HANDLE gl_thread_t;
161 /* Use a wrapper function, instead of adding WINAPI through a cast. */
162 struct wrapper_args { void * (*func) (void *); void *arg; };
163 static DWORD WINAPI wrapper_func (void *varg)
165 struct wrapper_args *warg = (struct wrapper_args *)varg;
166 void * (*func) (void *) = warg->func;
167 void *arg = warg->arg;
172 static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg)
174 struct wrapper_args *warg =
175 (struct wrapper_args *) malloc (sizeof (struct wrapper_args));
183 CreateThread (NULL, 100000, wrapper_func, warg, 0, &thread_id);
189 static inline void gl_thread_join (gl_thread_t thread)
191 if (WaitForSingleObject (thread, INFINITE) == WAIT_FAILED)
193 if (!CloseHandle (thread))
196 static inline void gl_thread_yield (void)
200 static inline void * gl_thread_self (void)
202 return (void *) GetCurrentThreadId ();
206 # define yield() gl_thread_yield ()
211 #define ACCOUNT_COUNT 4
213 static int account[ACCOUNT_COUNT];
216 random_account (void)
218 return ((unsigned int) rand() >> 3) % ACCOUNT_COUNT;
222 check_accounts (void)
227 for (i = 0; i < ACCOUNT_COUNT; i++)
229 if (sum != ACCOUNT_COUNT * 1000)
233 /* Test normal locks by having several bank accounts and several threads
234 which shuffle around money between the accounts and another thread
235 checking that all the money is still there. */
237 gl_lock_define_initialized(static, my_lock)
240 lock_mutator_thread (void *arg)
244 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
248 dbgprintf ("Mutator %p before lock\n", gl_thread_self ());
249 gl_lock_lock (my_lock);
250 dbgprintf ("Mutator %p after lock\n", gl_thread_self ());
252 i1 = random_account ();
253 i2 = random_account ();
254 value = ((unsigned int) rand() >> 3) % 10;
255 account[i1] += value;
256 account[i2] -= value;
258 dbgprintf ("Mutator %p before unlock\n", gl_thread_self ());
259 gl_lock_unlock (my_lock);
260 dbgprintf ("Mutator %p after unlock\n", gl_thread_self ());
262 dbgprintf ("Mutator %p before check lock\n", gl_thread_self ());
263 gl_lock_lock (my_lock);
265 gl_lock_unlock (my_lock);
266 dbgprintf ("Mutator %p after check unlock\n", gl_thread_self ());
271 dbgprintf ("Mutator %p dying.\n", gl_thread_self ());
275 static volatile int lock_checker_done;
278 lock_checker_thread (void *arg)
280 while (!lock_checker_done)
282 dbgprintf ("Checker %p before check lock\n", gl_thread_self ());
283 gl_lock_lock (my_lock);
285 gl_lock_unlock (my_lock);
286 dbgprintf ("Checker %p after check unlock\n", gl_thread_self ());
291 dbgprintf ("Checker %p dying.\n", gl_thread_self ());
299 gl_thread_t checkerthread;
300 gl_thread_t threads[THREAD_COUNT];
302 /* Initialization. */
303 for (i = 0; i < ACCOUNT_COUNT; i++)
305 lock_checker_done = 0;
307 /* Spawn the threads. */
308 checkerthread = gl_thread_create (lock_checker_thread, NULL);
309 for (i = 0; i < THREAD_COUNT; i++)
310 threads[i] = gl_thread_create (lock_mutator_thread, NULL);
312 /* Wait for the threads to terminate. */
313 for (i = 0; i < THREAD_COUNT; i++)
314 gl_thread_join (threads[i]);
315 lock_checker_done = 1;
316 gl_thread_join (checkerthread);
320 /* Test read-write locks by having several bank accounts and several threads
321 which shuffle around money between the accounts and several other threads
322 that check that all the money is still there. */
324 gl_rwlock_define_initialized(static, my_rwlock)
327 rwlock_mutator_thread (void *arg)
331 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
335 dbgprintf ("Mutator %p before wrlock\n", gl_thread_self ());
336 gl_rwlock_wrlock (my_rwlock);
337 dbgprintf ("Mutator %p after wrlock\n", gl_thread_self ());
339 i1 = random_account ();
340 i2 = random_account ();
341 value = ((unsigned int) rand() >> 3) % 10;
342 account[i1] += value;
343 account[i2] -= value;
345 dbgprintf ("Mutator %p before unlock\n", gl_thread_self ());
346 gl_rwlock_unlock (my_rwlock);
347 dbgprintf ("Mutator %p after unlock\n", gl_thread_self ());
352 dbgprintf ("Mutator %p dying.\n", gl_thread_self ());
356 static volatile int rwlock_checker_done;
359 rwlock_checker_thread (void *arg)
361 while (!rwlock_checker_done)
363 dbgprintf ("Checker %p before check rdlock\n", gl_thread_self ());
364 gl_rwlock_rdlock (my_rwlock);
366 gl_rwlock_unlock (my_rwlock);
367 dbgprintf ("Checker %p after check unlock\n", gl_thread_self ());
372 dbgprintf ("Checker %p dying.\n", gl_thread_self ());
380 gl_thread_t checkerthreads[THREAD_COUNT];
381 gl_thread_t threads[THREAD_COUNT];
383 /* Initialization. */
384 for (i = 0; i < ACCOUNT_COUNT; i++)
386 rwlock_checker_done = 0;
388 /* Spawn the threads. */
389 for (i = 0; i < THREAD_COUNT; i++)
390 checkerthreads[i] = gl_thread_create (rwlock_checker_thread, NULL);
391 for (i = 0; i < THREAD_COUNT; i++)
392 threads[i] = gl_thread_create (rwlock_mutator_thread, NULL);
394 /* Wait for the threads to terminate. */
395 for (i = 0; i < THREAD_COUNT; i++)
396 gl_thread_join (threads[i]);
397 rwlock_checker_done = 1;
398 for (i = 0; i < THREAD_COUNT; i++)
399 gl_thread_join (checkerthreads[i]);
403 /* Test recursive locks by having several bank accounts and several threads
404 which shuffle around money between the accounts (recursively) and another
405 thread checking that all the money is still there. */
407 gl_recursive_lock_define_initialized(static, my_reclock)
414 dbgprintf ("Mutator %p before lock\n", gl_thread_self ());
415 gl_recursive_lock_lock (my_reclock);
416 dbgprintf ("Mutator %p after lock\n", gl_thread_self ());
418 i1 = random_account ();
419 i2 = random_account ();
420 value = ((unsigned int) rand() >> 3) % 10;
421 account[i1] += value;
422 account[i2] -= value;
424 /* Recursive with probability 0.5. */
425 if (((unsigned int) rand() >> 3) % 2)
428 dbgprintf ("Mutator %p before unlock\n", gl_thread_self ());
429 gl_recursive_lock_unlock (my_reclock);
430 dbgprintf ("Mutator %p after unlock\n", gl_thread_self ());
434 reclock_mutator_thread (void *arg)
438 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
442 dbgprintf ("Mutator %p before check lock\n", gl_thread_self ());
443 gl_recursive_lock_lock (my_reclock);
445 gl_recursive_lock_unlock (my_reclock);
446 dbgprintf ("Mutator %p after check unlock\n", gl_thread_self ());
451 dbgprintf ("Mutator %p dying.\n", gl_thread_self ());
455 static volatile int reclock_checker_done;
458 reclock_checker_thread (void *arg)
460 while (!reclock_checker_done)
462 dbgprintf ("Checker %p before check lock\n", gl_thread_self ());
463 gl_recursive_lock_lock (my_reclock);
465 gl_recursive_lock_unlock (my_reclock);
466 dbgprintf ("Checker %p after check unlock\n", gl_thread_self ());
471 dbgprintf ("Checker %p dying.\n", gl_thread_self ());
476 test_recursive_lock (void)
479 gl_thread_t checkerthread;
480 gl_thread_t threads[THREAD_COUNT];
482 /* Initialization. */
483 for (i = 0; i < ACCOUNT_COUNT; i++)
485 reclock_checker_done = 0;
487 /* Spawn the threads. */
488 checkerthread = gl_thread_create (reclock_checker_thread, NULL);
489 for (i = 0; i < THREAD_COUNT; i++)
490 threads[i] = gl_thread_create (reclock_mutator_thread, NULL);
492 /* Wait for the threads to terminate. */
493 for (i = 0; i < THREAD_COUNT; i++)
494 gl_thread_join (threads[i]);
495 reclock_checker_done = 1;
496 gl_thread_join (checkerthread);
500 /* Test once-only execution by having several threads attempt to grab a
501 once-only task simultaneously (triggered by releasing a read-write lock). */
503 gl_once_define(static, fresh_once)
504 static int ready[THREAD_COUNT];
505 static gl_lock_t ready_lock[THREAD_COUNT];
507 static gl_rwlock_t fire_signal[REPEAT_COUNT];
509 static volatile int fire_signal_state;
511 static gl_once_t once_control;
512 static int performed;
513 gl_lock_define_initialized(static, performed_lock)
518 gl_lock_lock (performed_lock);
520 gl_lock_unlock (performed_lock);
524 once_contender_thread (void *arg)
526 int id = (int) (long) arg;
529 for (repeat = 0; repeat <= REPEAT_COUNT; repeat++)
531 /* Tell the main thread that we're ready. */
532 gl_lock_lock (ready_lock[id]);
534 gl_lock_unlock (ready_lock[id]);
536 if (repeat == REPEAT_COUNT)
539 dbgprintf ("Contender %p waiting for signal for round %d\n",
540 gl_thread_self (), repeat);
542 /* Wait for the signal to go. */
543 gl_rwlock_rdlock (fire_signal[repeat]);
544 /* And don't hinder the others (if the scheduler is unfair). */
545 gl_rwlock_unlock (fire_signal[repeat]);
547 /* Wait for the signal to go. */
548 while (fire_signal_state <= repeat)
551 dbgprintf ("Contender %p got the signal for round %d\n",
552 gl_thread_self (), repeat);
554 /* Contend for execution. */
555 gl_once (once_control, once_execute);
565 gl_thread_t threads[THREAD_COUNT];
567 /* Initialize all variables. */
568 for (i = 0; i < THREAD_COUNT; i++)
571 gl_lock_init (ready_lock[i]);
574 for (i = 0; i < REPEAT_COUNT; i++)
575 gl_rwlock_init (fire_signal[i]);
577 fire_signal_state = 0;
580 /* Block all fire_signals. */
581 for (i = REPEAT_COUNT-1; i >= 0; i--)
582 gl_rwlock_wrlock (fire_signal[i]);
584 /* Spawn the threads. */
585 for (i = 0; i < THREAD_COUNT; i++)
586 threads[i] = gl_thread_create (once_contender_thread, (void *) (long) i);
588 for (repeat = 0; repeat <= REPEAT_COUNT; repeat++)
590 /* Wait until every thread is ready. */
591 dbgprintf ("Main thread before synchonizing for round %d\n", repeat);
595 for (i = 0; i < THREAD_COUNT; i++)
597 gl_lock_lock (ready_lock[i]);
598 ready_count += ready[i];
599 gl_lock_unlock (ready_lock[i]);
601 if (ready_count == THREAD_COUNT)
605 dbgprintf ("Main thread after synchonizing for round %d\n", repeat);
609 /* Check that exactly one thread executed the once_execute()
615 if (repeat == REPEAT_COUNT)
618 /* Preparation for the next round: Initialize once_control. */
619 memcpy (&once_control, &fresh_once, sizeof (gl_once_t));
621 /* Preparation for the next round: Reset the performed counter. */
624 /* Preparation for the next round: Reset the ready flags. */
625 for (i = 0; i < THREAD_COUNT; i++)
627 gl_lock_lock (ready_lock[i]);
629 gl_lock_unlock (ready_lock[i]);
632 /* Signal all threads simultaneously. */
633 dbgprintf ("Main thread giving signal for round %d\n", repeat);
635 gl_rwlock_unlock (fire_signal[repeat]);
637 fire_signal_state = repeat + 1;
641 /* Wait for the threads to terminate. */
642 for (i = 0; i < THREAD_COUNT; i++)
643 gl_thread_join (threads[i]);
655 printf ("Starting test_lock ..."); fflush (stdout);
657 printf (" OK\n"); fflush (stdout);
660 printf ("Starting test_rwlock ..."); fflush (stdout);
662 printf (" OK\n"); fflush (stdout);
664 #if DO_TEST_RECURSIVE_LOCK
665 printf ("Starting test_recursive_lock ..."); fflush (stdout);
666 test_recursive_lock ();
667 printf (" OK\n"); fflush (stdout);
670 printf ("Starting test_once ..."); fflush (stdout);
672 printf (" OK\n"); fflush (stdout);
680 /* No multithreading available. */