GLib: implement GMutex natively on Linux
[platform/upstream/glib.git] / glib / tests / 1bit-mutex.c
1 /*
2  * Copyright © 2008 Ryan Lortie
3  * Copyright © 2010 Codethink Limited
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * 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
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_print ("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
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
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 existance 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
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 }