blob: b7b06d7349bd64530e404b890b8a9a92dbd56186 [file] [log] [blame]
/*
* 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.1 of the License, or (at your option) any later version.
*
* See the included COPYING file for more information.
*/
#include "config.h"
/* 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 <glib.h>
#if TEST_EMULATED_FUTEX
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmissing-prototypes"
/* 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 <glib/gbitlock.c>
#pragma GCC diagnostic pop
#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) % sizeof(gint), ==, 0);
if (!(use_pointers ?
g_pointer_bit_trylock (&ptrs[nr], bits[nr])
: g_bit_trylock (&locks[nr], bits[nr])))
{
if (g_test_verbose ())
g_printerr ("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;
#ifdef TEST_EMULATED_FUTEX
#define SUFFIX "-emufutex"
/* ensure that we are using the emulated futex by checking
* (at compile-time) for the existence 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_new ("foo", thread_func,
GINT_TO_POINTER (use_pointers));
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 ();
}