mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-11-09 20:43:18 +01:00
Add a test that runs a few threads, which take a bit lock and collectively increment a counter.
177 lines
4.5 KiB
C
177 lines
4.5 KiB
C
#include <glib.h>
|
|
|
|
#include "gtestutils.h"
|
|
|
|
static void
|
|
test_bitlocks (void)
|
|
{
|
|
guint64 start = g_get_monotonic_time ();
|
|
gint lock = 0;
|
|
guint i;
|
|
guint n_iterations;
|
|
|
|
n_iterations = g_test_perf () ? 100000000 : 1;
|
|
|
|
for (i = 0; i < n_iterations; i++)
|
|
{
|
|
g_bit_lock (&lock, 0);
|
|
g_bit_unlock (&lock, 0);
|
|
}
|
|
|
|
{
|
|
gdouble elapsed;
|
|
gdouble rate;
|
|
|
|
elapsed = g_get_monotonic_time () - start;
|
|
elapsed /= 1000000;
|
|
rate = n_iterations / elapsed;
|
|
|
|
g_test_maximized_result (rate, "iterations per second");
|
|
}
|
|
}
|
|
|
|
#define PARALLEL_N_THREADS 5
|
|
#define PARALLEL_LOCKBIT 31
|
|
#define PARALLEL_TOGGLEBIT 30
|
|
#define PARALLEL_SETBIT 29
|
|
#define PARALLEL_LOCKMASK (1 << PARALLEL_LOCKBIT)
|
|
#define PARALLEL_TOGGLEMASK (1 << PARALLEL_TOGGLEBIT)
|
|
#define PARALLEL_SETMASK (1 << PARALLEL_SETBIT)
|
|
#define PARALLEL_MAX_COUNT_SELF 500
|
|
#define PARALLEL_MAX_COUNT_ALL (10 * PARALLEL_MAX_COUNT_SELF)
|
|
static int parallel_thread_ready = 0;
|
|
static int parallel_atomic = 0;
|
|
|
|
static void
|
|
_test_parallel_randomly_toggle (void)
|
|
{
|
|
if (g_random_boolean ())
|
|
g_atomic_int_or (¶llel_atomic, PARALLEL_TOGGLEMASK);
|
|
else
|
|
g_atomic_int_and (¶llel_atomic, ~PARALLEL_TOGGLEMASK);
|
|
}
|
|
|
|
static gpointer
|
|
_test_parallel_run (gpointer thread_arg)
|
|
{
|
|
const int IDX = GPOINTER_TO_INT (thread_arg);
|
|
int count_self = 0;
|
|
|
|
(void) IDX;
|
|
|
|
g_atomic_int_inc (¶llel_thread_ready);
|
|
while (g_atomic_int_get (¶llel_thread_ready) != PARALLEL_N_THREADS)
|
|
g_usleep (10);
|
|
|
|
while (TRUE)
|
|
{
|
|
gint val;
|
|
gint val2;
|
|
gint new_val;
|
|
gint count_all;
|
|
|
|
_test_parallel_randomly_toggle ();
|
|
|
|
/* take a lock. */
|
|
if (g_random_boolean ())
|
|
{
|
|
g_bit_lock (¶llel_atomic, PARALLEL_LOCKBIT);
|
|
val = g_atomic_int_get (¶llel_atomic);
|
|
}
|
|
else
|
|
{
|
|
g_bit_lock_and_get (¶llel_atomic, PARALLEL_LOCKBIT, &val);
|
|
}
|
|
|
|
_test_parallel_randomly_toggle ();
|
|
|
|
/* the toggle bit is random. Clear it. */
|
|
val &= ~PARALLEL_TOGGLEMASK;
|
|
|
|
/* these bits must be set. */
|
|
g_assert_true (val & PARALLEL_LOCKMASK);
|
|
g_assert_true (val & PARALLEL_SETMASK);
|
|
|
|
/* If we fetch again, we must get the same value. Nobody changes the
|
|
* value while we hold the lock, except for the toggle bit. */
|
|
val2 = g_atomic_int_get (¶llel_atomic);
|
|
val2 &= ~PARALLEL_TOGGLEMASK;
|
|
g_assert_cmpint (val, ==, val2);
|
|
|
|
count_all = (val & ~(PARALLEL_LOCKMASK | PARALLEL_SETMASK));
|
|
|
|
if ((g_random_int () % 5) == 0)
|
|
{
|
|
/* regular unlock without any changes. */
|
|
g_bit_unlock (¶llel_atomic, PARALLEL_LOCKBIT);
|
|
continue;
|
|
}
|
|
|
|
/* unlock-and-set with an increased counter. */
|
|
new_val = MIN (count_all + 1, PARALLEL_MAX_COUNT_ALL);
|
|
if (g_random_boolean ())
|
|
new_val |= PARALLEL_SETMASK;
|
|
if (g_random_boolean ())
|
|
new_val |= PARALLEL_TOGGLEMASK;
|
|
|
|
g_bit_unlock_and_set (¶llel_atomic, PARALLEL_LOCKBIT, new_val, ((new_val & PARALLEL_SETMASK) && g_random_boolean ()) ? 0 : PARALLEL_SETMASK);
|
|
|
|
count_self++;
|
|
|
|
if (count_self < PARALLEL_MAX_COUNT_SELF)
|
|
continue;
|
|
if (count_all < PARALLEL_MAX_COUNT_ALL)
|
|
continue;
|
|
|
|
break;
|
|
}
|
|
|
|
/* To indicate success, we return a pointer to ¶llel_atomic. */
|
|
return ¶llel_atomic;
|
|
}
|
|
|
|
static void
|
|
test_parallel (void)
|
|
{
|
|
GThread *threads[PARALLEL_N_THREADS];
|
|
gpointer ptr;
|
|
int i;
|
|
gint val;
|
|
|
|
g_atomic_int_or (¶llel_atomic, PARALLEL_SETMASK);
|
|
|
|
for (i = 0; i < PARALLEL_N_THREADS; i++)
|
|
{
|
|
threads[i] = g_thread_new ("bitlock-parallel", _test_parallel_run, GINT_TO_POINTER (i));
|
|
}
|
|
|
|
for (i = 0; i < PARALLEL_N_THREADS; i++)
|
|
{
|
|
ptr = g_thread_join (threads[i]);
|
|
g_assert_true (ptr == ¶llel_atomic);
|
|
|
|
/* After we join the first thread, we already expect that the resulting
|
|
* atomic's counter is set to PARALLEL_MAX_COUNT_ALL. This stays until
|
|
* the end. */
|
|
val = g_atomic_int_get (¶llel_atomic);
|
|
if (i == PARALLEL_N_THREADS - 1)
|
|
{
|
|
/* at last, the atomic must be unlocked. */
|
|
g_assert_true (!(val & PARALLEL_LOCKMASK));
|
|
}
|
|
val &= ~(PARALLEL_LOCKMASK | PARALLEL_TOGGLEMASK | PARALLEL_SETMASK);
|
|
g_assert_cmpint (val, ==, PARALLEL_MAX_COUNT_ALL);
|
|
}
|
|
}
|
|
|
|
int
|
|
main (int argc, char **argv)
|
|
{
|
|
g_test_init (&argc, &argv, NULL);
|
|
|
|
g_test_add_func ("/bitlock/performance/uncontended", test_bitlocks);
|
|
g_test_add_func ("/bitlock/performance/parallel", test_parallel);
|
|
|
|
return g_test_run ();
|
|
}
|