/* * Copyright © 2008 Ryan Lortie * Copyright © 2010 Codethink Limited * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * See the included COPYING file for more information. */ /* LOCKS should be more than the number of contention * counters in gthread.c in order to ensure we exercise * the case where they overlap. */ #define LOCKS 48 #define ITERATIONS 10000 #define THREADS 100 #include #if TEST_EMULATED_FUTEX /* this is defined for the 1bit-mutex-emufutex test. * * we want to test the emulated futex even if futex(2) is available. */ /* side-step some glib build stuff */ #define GLIB_COMPILATION /* rebuild gbitlock.c without futex support, defining our own version of the g_bit_*lock symbols */ #undef g_pointer_bit_lock #undef g_pointer_bit_trylock #undef g_pointer_bit_unlock #define g_bit_lock _emufutex_g_bit_lock #define g_bit_trylock _emufutex_g_bit_trylock #define g_bit_unlock _emufutex_g_bit_unlock #define g_pointer_bit_lock _emufutex_g_pointer_bit_lock #define g_pointer_bit_trylock _emufutex_g_pointer_bit_trylock #define g_pointer_bit_unlock _emufutex_g_pointer_bit_unlock #define G_BIT_LOCK_FORCE_FUTEX_EMULATION #include #endif volatile GThread *owners[LOCKS]; volatile gint locks[LOCKS]; volatile gpointer ptrs[LOCKS]; volatile gint bits[LOCKS]; static void acquire (int nr, gboolean use_pointers) { GThread *self; self = g_thread_self (); g_assert_cmpint (((gsize) ptrs) % 8, ==, 0); if (!(use_pointers ? g_pointer_bit_trylock (&ptrs[nr], bits[nr]) : g_bit_trylock (&locks[nr], bits[nr]))) { if (g_test_verbose ()) g_print ("thread %p going to block on lock %d\n", self, nr); if (use_pointers) g_pointer_bit_lock (&ptrs[nr], bits[nr]); else g_bit_lock (&locks[nr], bits[nr]); } g_assert (owners[nr] == NULL); /* hopefully nobody else is here */ owners[nr] = self; /* let some other threads try to ruin our day */ g_thread_yield (); g_thread_yield (); g_thread_yield (); g_assert (owners[nr] == self); /* hopefully this is still us... */ owners[nr] = NULL; /* make way for the next guy */ if (use_pointers) g_pointer_bit_unlock (&ptrs[nr], bits[nr]); else g_bit_unlock (&locks[nr], bits[nr]); } static gpointer thread_func (gpointer data) { gboolean use_pointers = GPOINTER_TO_INT (data); gint i; GRand *rand; rand = g_rand_new (); for (i = 0; i < ITERATIONS; i++) acquire (g_rand_int_range (rand, 0, LOCKS), use_pointers); g_rand_free (rand); return NULL; } static void testcase (gconstpointer data) { gboolean use_pointers = GPOINTER_TO_INT (data); GThread *threads[THREADS]; int i; g_thread_init (NULL); #ifdef TEST_EMULATED_FUTEX #define SUFFIX "-emufutex" /* ensure that we are using the emulated futex by checking * (at compile-time) for the existance of 'g_futex_address_list' */ g_assert (g_futex_address_list == NULL); #else #define SUFFIX "" #endif for (i = 0; i < LOCKS; i++) bits[i] = g_random_int () % 32; for (i = 0; i < THREADS; i++) threads[i] = g_thread_create (thread_func, GINT_TO_POINTER (use_pointers), TRUE, NULL); for (i = 0; i < THREADS; i++) g_thread_join (threads[i]); for (i = 0; i < LOCKS; i++) { g_assert (owners[i] == NULL); g_assert (locks[i] == 0); } } int main (int argc, char **argv) { g_test_init (&argc, &argv, NULL); g_test_add_data_func ("/glib/1bit-mutex" SUFFIX "/int", (gpointer) 0, testcase); g_test_add_data_func ("/glib/1bit-mutex" SUFFIX "/pointer", (gpointer) 1, testcase); return g_test_run (); }