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. */
24 #if USE_POSIX_THREADS || USE_SOLARIS_THREADS || USE_PTH_THREADS || USE_WIN32_THREADS
27 # define TEST_POSIX_THREADS 1
29 #if USE_SOLARIS_THREADS
30 # define TEST_SOLARIS_THREADS 1
33 # define TEST_PTH_THREADS 1
36 # define TEST_WIN32_THREADS 1
39 /* Whether to enable locking.
40 Uncomment this to get a test program without locking, to verify that
42 #define ENABLE_LOCKING 1
44 /* Which tests to perform.
45 Uncomment some of these, to verify that all tests crash if no locking
47 #define DO_TEST_LOCK 1
48 #define DO_TEST_RWLOCK 1
49 #define DO_TEST_RECURSIVE_LOCK 1
50 #define DO_TEST_ONCE 1
52 /* Whether to help the scheduler through explicit yield().
53 Uncomment this to see if the operating system has a fair scheduler. */
54 #define EXPLICIT_YIELD 1
56 /* Whether to print debugging messages. */
57 #define ENABLE_DEBUGGING 0
59 /* Number of simultaneous threads. */
60 #define THREAD_COUNT 10
62 /* Number of operations performed in each thread.
63 This is quite high, because with a smaller count, say 5000, we often get
64 an "OK" result even without ENABLE_LOCKING (on Linux/x86). */
65 #define REPEAT_COUNT 50000
72 # undef USE_POSIX_THREADS
73 # undef USE_SOLARIS_THREADS
74 # undef USE_PTH_THREADS
75 # undef USE_WIN32_THREADS
80 # define dbgprintf printf
82 # define dbgprintf if (0) printf
85 #if TEST_POSIX_THREADS
88 typedef pthread_t gl_thread_t;
89 static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg)
92 if (pthread_create (&thread, NULL, func, arg) != 0)
96 static inline void gl_thread_join (gl_thread_t thread)
99 if (pthread_join (thread, &retval) != 0)
102 static inline void gl_thread_yield (void)
106 static inline void * gl_thread_self (void)
108 return (void *) pthread_self ();
113 typedef pth_t gl_thread_t;
114 static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg)
116 pth_t thread = pth_spawn (NULL, func, arg);
121 static inline void gl_thread_join (gl_thread_t thread)
123 if (!pth_join (thread, NULL))
126 static inline void gl_thread_yield (void)
130 static inline void * gl_thread_self (void)
135 #if TEST_SOLARIS_THREADS
137 typedef thread_t gl_thread_t;
138 static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg)
141 if (thr_create (NULL, 0, func, arg, 0, &thread) != 0)
145 static inline void gl_thread_join (gl_thread_t thread)
148 if (thr_join (thread, NULL, &retval) != 0)
151 static inline void gl_thread_yield (void)
155 static inline void * gl_thread_self (void)
157 return (void *) thr_self ();
160 #if TEST_WIN32_THREADS
161 # include <windows.h>
162 typedef HANDLE gl_thread_t;
163 /* Use a wrapper function, instead of adding WINAPI through a cast. */
164 struct wrapper_args { void * (*func) (void *); void *arg; };
165 static DWORD WINAPI wrapper_func (void *varg)
167 struct wrapper_args *warg = (struct wrapper_args *)varg;
168 void * (*func) (void *) = warg->func;
169 void *arg = warg->arg;
174 static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg)
176 struct wrapper_args *warg =
177 (struct wrapper_args *) malloc (sizeof (struct wrapper_args));
185 CreateThread (NULL, 100000, wrapper_func, warg, 0, &thread_id);
191 static inline void gl_thread_join (gl_thread_t thread)
193 if (WaitForSingleObject (thread, INFINITE) == WAIT_FAILED)
195 if (!CloseHandle (thread))
198 static inline void gl_thread_yield (void)
202 static inline void * gl_thread_self (void)
204 return (void *) GetCurrentThreadId ();
208 # define yield() gl_thread_yield ()
213 #define ACCOUNT_COUNT 4
215 static int account[ACCOUNT_COUNT];
218 random_account (void)
220 return ((unsigned int) rand() >> 3) % ACCOUNT_COUNT;
224 check_accounts (void)
229 for (i = 0; i < ACCOUNT_COUNT; i++)
231 if (sum != ACCOUNT_COUNT * 1000)
235 /* Test normal locks by having several bank accounts and several threads
236 which shuffle around money between the accounts and another thread
237 checking that all the money is still there. */
239 gl_lock_define_initialized(static, my_lock)
242 lock_mutator_thread (void *arg)
246 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
250 dbgprintf ("Mutator %p before lock\n", gl_thread_self ());
251 gl_lock_lock (my_lock);
252 dbgprintf ("Mutator %p after lock\n", gl_thread_self ());
254 i1 = random_account ();
255 i2 = random_account ();
256 value = ((unsigned int) rand() >> 3) % 10;
257 account[i1] += value;
258 account[i2] -= value;
260 dbgprintf ("Mutator %p before unlock\n", gl_thread_self ());
261 gl_lock_unlock (my_lock);
262 dbgprintf ("Mutator %p after unlock\n", gl_thread_self ());
264 dbgprintf ("Mutator %p before check lock\n", gl_thread_self ());
265 gl_lock_lock (my_lock);
267 gl_lock_unlock (my_lock);
268 dbgprintf ("Mutator %p after check unlock\n", gl_thread_self ());
273 dbgprintf ("Mutator %p dying.\n", gl_thread_self ());
277 static volatile int lock_checker_done;
280 lock_checker_thread (void *arg)
282 while (!lock_checker_done)
284 dbgprintf ("Checker %p before check lock\n", gl_thread_self ());
285 gl_lock_lock (my_lock);
287 gl_lock_unlock (my_lock);
288 dbgprintf ("Checker %p after check unlock\n", gl_thread_self ());
293 dbgprintf ("Checker %p dying.\n", gl_thread_self ());
301 gl_thread_t checkerthread;
302 gl_thread_t threads[THREAD_COUNT];
304 /* Initialization. */
305 for (i = 0; i < ACCOUNT_COUNT; i++)
307 lock_checker_done = 0;
309 /* Spawn the threads. */
310 checkerthread = gl_thread_create (lock_checker_thread, NULL);
311 for (i = 0; i < THREAD_COUNT; i++)
312 threads[i] = gl_thread_create (lock_mutator_thread, NULL);
314 /* Wait for the threads to terminate. */
315 for (i = 0; i < THREAD_COUNT; i++)
316 gl_thread_join (threads[i]);
317 lock_checker_done = 1;
318 gl_thread_join (checkerthread);
322 /* Test read-write locks by having several bank accounts and several threads
323 which shuffle around money between the accounts and several other threads
324 that check that all the money is still there. */
326 gl_rwlock_define_initialized(static, my_rwlock)
329 rwlock_mutator_thread (void *arg)
333 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
337 dbgprintf ("Mutator %p before wrlock\n", gl_thread_self ());
338 gl_rwlock_wrlock (my_rwlock);
339 dbgprintf ("Mutator %p after wrlock\n", gl_thread_self ());
341 i1 = random_account ();
342 i2 = random_account ();
343 value = ((unsigned int) rand() >> 3) % 10;
344 account[i1] += value;
345 account[i2] -= value;
347 dbgprintf ("Mutator %p before unlock\n", gl_thread_self ());
348 gl_rwlock_unlock (my_rwlock);
349 dbgprintf ("Mutator %p after unlock\n", gl_thread_self ());
354 dbgprintf ("Mutator %p dying.\n", gl_thread_self ());
358 static volatile int rwlock_checker_done;
361 rwlock_checker_thread (void *arg)
363 while (!rwlock_checker_done)
365 dbgprintf ("Checker %p before check rdlock\n", gl_thread_self ());
366 gl_rwlock_rdlock (my_rwlock);
368 gl_rwlock_unlock (my_rwlock);
369 dbgprintf ("Checker %p after check unlock\n", gl_thread_self ());
374 dbgprintf ("Checker %p dying.\n", gl_thread_self ());
382 gl_thread_t checkerthreads[THREAD_COUNT];
383 gl_thread_t threads[THREAD_COUNT];
385 /* Initialization. */
386 for (i = 0; i < ACCOUNT_COUNT; i++)
388 rwlock_checker_done = 0;
390 /* Spawn the threads. */
391 for (i = 0; i < THREAD_COUNT; i++)
392 checkerthreads[i] = gl_thread_create (rwlock_checker_thread, NULL);
393 for (i = 0; i < THREAD_COUNT; i++)
394 threads[i] = gl_thread_create (rwlock_mutator_thread, NULL);
396 /* Wait for the threads to terminate. */
397 for (i = 0; i < THREAD_COUNT; i++)
398 gl_thread_join (threads[i]);
399 rwlock_checker_done = 1;
400 for (i = 0; i < THREAD_COUNT; i++)
401 gl_thread_join (checkerthreads[i]);
405 /* Test recursive locks by having several bank accounts and several threads
406 which shuffle around money between the accounts (recursively) and another
407 thread checking that all the money is still there. */
409 gl_recursive_lock_define_initialized(static, my_reclock)
416 dbgprintf ("Mutator %p before lock\n", gl_thread_self ());
417 gl_recursive_lock_lock (my_reclock);
418 dbgprintf ("Mutator %p after lock\n", gl_thread_self ());
420 i1 = random_account ();
421 i2 = random_account ();
422 value = ((unsigned int) rand() >> 3) % 10;
423 account[i1] += value;
424 account[i2] -= value;
426 /* Recursive with probability 0.5. */
427 if (((unsigned int) rand() >> 3) % 2)
430 dbgprintf ("Mutator %p before unlock\n", gl_thread_self ());
431 gl_recursive_lock_unlock (my_reclock);
432 dbgprintf ("Mutator %p after unlock\n", gl_thread_self ());
436 reclock_mutator_thread (void *arg)
440 for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
444 dbgprintf ("Mutator %p before check lock\n", gl_thread_self ());
445 gl_recursive_lock_lock (my_reclock);
447 gl_recursive_lock_unlock (my_reclock);
448 dbgprintf ("Mutator %p after check unlock\n", gl_thread_self ());
453 dbgprintf ("Mutator %p dying.\n", gl_thread_self ());
457 static volatile int reclock_checker_done;
460 reclock_checker_thread (void *arg)
462 while (!reclock_checker_done)
464 dbgprintf ("Checker %p before check lock\n", gl_thread_self ());
465 gl_recursive_lock_lock (my_reclock);
467 gl_recursive_lock_unlock (my_reclock);
468 dbgprintf ("Checker %p after check unlock\n", gl_thread_self ());
473 dbgprintf ("Checker %p dying.\n", gl_thread_self ());
478 test_recursive_lock (void)
481 gl_thread_t checkerthread;
482 gl_thread_t threads[THREAD_COUNT];
484 /* Initialization. */
485 for (i = 0; i < ACCOUNT_COUNT; i++)
487 reclock_checker_done = 0;
489 /* Spawn the threads. */
490 checkerthread = gl_thread_create (reclock_checker_thread, NULL);
491 for (i = 0; i < THREAD_COUNT; i++)
492 threads[i] = gl_thread_create (reclock_mutator_thread, NULL);
494 /* Wait for the threads to terminate. */
495 for (i = 0; i < THREAD_COUNT; i++)
496 gl_thread_join (threads[i]);
497 reclock_checker_done = 1;
498 gl_thread_join (checkerthread);
502 /* Test once-only execution by having several threads attempt to grab a
503 once-only task simultaneously (triggered by releasing a read-write lock). */
505 gl_once_define(static, fresh_once)
506 static int ready[THREAD_COUNT];
507 static gl_lock_t ready_lock[THREAD_COUNT];
509 static gl_rwlock_t fire_signal[REPEAT_COUNT];
511 static volatile int fire_signal_state;
513 static gl_once_t once_control;
514 static int performed;
515 gl_lock_define_initialized(static, performed_lock)
520 gl_lock_lock (performed_lock);
522 gl_lock_unlock (performed_lock);
526 once_contender_thread (void *arg)
528 int id = (int) (long) arg;
531 for (repeat = 0; repeat <= REPEAT_COUNT; repeat++)
533 /* Tell the main thread that we're ready. */
534 gl_lock_lock (ready_lock[id]);
536 gl_lock_unlock (ready_lock[id]);
538 if (repeat == REPEAT_COUNT)
541 dbgprintf ("Contender %p waiting for signal for round %d\n",
542 gl_thread_self (), repeat);
544 /* Wait for the signal to go. */
545 gl_rwlock_rdlock (fire_signal[repeat]);
546 /* And don't hinder the others (if the scheduler is unfair). */
547 gl_rwlock_unlock (fire_signal[repeat]);
549 /* Wait for the signal to go. */
550 while (fire_signal_state <= repeat)
553 dbgprintf ("Contender %p got the signal for round %d\n",
554 gl_thread_self (), repeat);
556 /* Contend for execution. */
557 gl_once (once_control, once_execute);
567 gl_thread_t threads[THREAD_COUNT];
569 /* Initialize all variables. */
570 for (i = 0; i < THREAD_COUNT; i++)
573 gl_lock_init (ready_lock[i]);
576 for (i = 0; i < REPEAT_COUNT; i++)
577 gl_rwlock_init (fire_signal[i]);
579 fire_signal_state = 0;
582 /* Block all fire_signals. */
583 for (i = REPEAT_COUNT-1; i >= 0; i--)
584 gl_rwlock_wrlock (fire_signal[i]);
586 /* Spawn the threads. */
587 for (i = 0; i < THREAD_COUNT; i++)
588 threads[i] = gl_thread_create (once_contender_thread, (void *) (long) i);
590 for (repeat = 0; repeat <= REPEAT_COUNT; repeat++)
592 /* Wait until every thread is ready. */
593 dbgprintf ("Main thread before synchonizing for round %d\n", repeat);
597 for (i = 0; i < THREAD_COUNT; i++)
599 gl_lock_lock (ready_lock[i]);
600 ready_count += ready[i];
601 gl_lock_unlock (ready_lock[i]);
603 if (ready_count == THREAD_COUNT)
607 dbgprintf ("Main thread after synchonizing for round %d\n", repeat);
611 /* Check that exactly one thread executed the once_execute()
617 if (repeat == REPEAT_COUNT)
620 /* Preparation for the next round: Initialize once_control. */
621 memcpy (&once_control, &fresh_once, sizeof (gl_once_t));
623 /* Preparation for the next round: Reset the performed counter. */
626 /* Preparation for the next round: Reset the ready flags. */
627 for (i = 0; i < THREAD_COUNT; i++)
629 gl_lock_lock (ready_lock[i]);
631 gl_lock_unlock (ready_lock[i]);
634 /* Signal all threads simultaneously. */
635 dbgprintf ("Main thread giving signal for round %d\n", repeat);
637 gl_rwlock_unlock (fire_signal[repeat]);
639 fire_signal_state = repeat + 1;
643 /* Wait for the threads to terminate. */
644 for (i = 0; i < THREAD_COUNT; i++)
645 gl_thread_join (threads[i]);
657 printf ("Starting test_lock ..."); fflush (stdout);
659 printf (" OK\n"); fflush (stdout);
662 printf ("Starting test_rwlock ..."); fflush (stdout);
664 printf (" OK\n"); fflush (stdout);
666 #if DO_TEST_RECURSIVE_LOCK
667 printf ("Starting test_recursive_lock ..."); fflush (stdout);
668 test_recursive_lock ();
669 printf (" OK\n"); fflush (stdout);
672 printf ("Starting test_once ..."); fflush (stdout);
674 printf (" OK\n"); fflush (stdout);
682 /* No multithreading available. */