1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
|
#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 ();
}
|