1 /*
2  * Copyright © 2008 Ryan Lortie
3  * Copyright © 2010 Codethink Limited
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * See the included COPYING file for more information.
11  */
12 
13 #include "config.h"
14 
15 /* LOCKS should be more than the number of contention
16  * counters in gthread.c in order to ensure we exercise
17  * the case where they overlap.
18  */
19 #define LOCKS      48
20 #define ITERATIONS 10000
21 #define THREADS    100
22 
23 #include <glib.h>
24 
25 #if TEST_EMULATED_FUTEX
26 
27 #pragma GCC diagnostic push
28 #pragma GCC diagnostic ignored "-Wmissing-prototypes"
29 
30   /* this is defined for the 1bit-mutex-emufutex test.
31    *
32    * we want to test the emulated futex even if futex(2) is available.
33    */
34 
35   /* side-step some glib build stuff */
36   #define GLIB_COMPILATION
37 
38   /* rebuild gbitlock.c without futex support,
39      defining our own version of the g_bit_*lock symbols
40    */
41   #undef g_pointer_bit_lock
42   #undef g_pointer_bit_trylock
43   #undef g_pointer_bit_unlock
44 
45   #define g_bit_lock            _emufutex_g_bit_lock
46   #define g_bit_trylock         _emufutex_g_bit_trylock
47   #define g_bit_unlock          _emufutex_g_bit_unlock
48   #define g_pointer_bit_lock    _emufutex_g_pointer_bit_lock
49   #define g_pointer_bit_trylock _emufutex_g_pointer_bit_trylock
50   #define g_pointer_bit_unlock  _emufutex_g_pointer_bit_unlock
51 
52   #define G_BIT_LOCK_FORCE_FUTEX_EMULATION
53 
54   #include <glib/gbitlock.c>
55 
56 #pragma GCC diagnostic pop
57 #endif
58 
59 volatile GThread *owners[LOCKS];
60 volatile gint     locks[LOCKS];
61 volatile gpointer ptrs[LOCKS];
62 volatile gint     bits[LOCKS];
63 
64 static void
acquire(int nr,gboolean use_pointers)65 acquire (int      nr,
66          gboolean use_pointers)
67 {
68   GThread *self;
69 
70   self = g_thread_self ();
71 
72   g_assert_cmpint (((gsize) ptrs) % sizeof(gint), ==, 0);
73 
74   if (!(use_pointers ?
75           g_pointer_bit_trylock (&ptrs[nr], bits[nr])
76         : g_bit_trylock (&locks[nr], bits[nr])))
77     {
78       if (g_test_verbose ())
79         g_printerr ("thread %p going to block on lock %d\n", self, nr);
80 
81       if (use_pointers)
82         g_pointer_bit_lock (&ptrs[nr], bits[nr]);
83       else
84         g_bit_lock (&locks[nr], bits[nr]);
85     }
86 
87   g_assert (owners[nr] == NULL);   /* hopefully nobody else is here */
88   owners[nr] = self;
89 
90   /* let some other threads try to ruin our day */
91   g_thread_yield ();
92   g_thread_yield ();
93   g_thread_yield ();
94 
95   g_assert (owners[nr] == self);   /* hopefully this is still us... */
96   owners[nr] = NULL;               /* make way for the next guy */
97 
98   if (use_pointers)
99     g_pointer_bit_unlock (&ptrs[nr], bits[nr]);
100   else
101     g_bit_unlock (&locks[nr], bits[nr]);
102 }
103 
104 static gpointer
thread_func(gpointer data)105 thread_func (gpointer data)
106 {
107   gboolean use_pointers = GPOINTER_TO_INT (data);
108   gint i;
109   GRand *rand;
110 
111   rand = g_rand_new ();
112 
113   for (i = 0; i < ITERATIONS; i++)
114     acquire (g_rand_int_range (rand, 0, LOCKS), use_pointers);
115 
116   g_rand_free (rand);
117 
118   return NULL;
119 }
120 
121 static void
testcase(gconstpointer data)122 testcase (gconstpointer data)
123 {
124   gboolean use_pointers = GPOINTER_TO_INT (data);
125   GThread *threads[THREADS];
126   int i;
127 
128 #ifdef TEST_EMULATED_FUTEX
129   #define SUFFIX "-emufutex"
130 
131   /* ensure that we are using the emulated futex by checking
132    * (at compile-time) for the existence of 'g_futex_address_list'
133    */
134   g_assert (g_futex_address_list == NULL);
135 #else
136   #define SUFFIX ""
137 #endif
138 
139   for (i = 0; i < LOCKS; i++)
140     bits[i] = g_random_int () % 32;
141 
142   for (i = 0; i < THREADS; i++)
143     threads[i] = g_thread_new ("foo", thread_func,
144                                GINT_TO_POINTER (use_pointers));
145 
146   for (i = 0; i < THREADS; i++)
147     g_thread_join (threads[i]);
148 
149   for (i = 0; i < LOCKS; i++)
150     {
151       g_assert (owners[i] == NULL);
152       g_assert (locks[i] == 0);
153     }
154 }
155 
156 int
main(int argc,char ** argv)157 main (int argc, char **argv)
158 {
159   g_test_init (&argc, &argv, NULL);
160 
161   g_test_add_data_func ("/glib/1bit-mutex" SUFFIX "/int", (gpointer) 0, testcase);
162   g_test_add_data_func ("/glib/1bit-mutex" SUFFIX "/pointer", (gpointer) 1, testcase);
163 
164   return g_test_run ();
165 }
166