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 |
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 (address: &locks[nr], lock_bit: bits[nr]))) |
77 | { |
78 | if (g_test_verbose ()) |
79 | g_printerr (format: "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 (address: &locks[nr], lock_bit: 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 (address: &locks[nr], lock_bit: 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 (nr: g_rand_int_range (rand_: rand, begin: 0, LOCKS), use_pointers); |
115 | |
116 | g_rand_free (rand_: 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 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 (name: "foo" , func: thread_func, |
144 | GINT_TO_POINTER (use_pointers)); |
145 | |
146 | for (i = 0; i < THREADS; i++) |
147 | g_thread_join (thread: 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: &argc, argv: &argv, NULL); |
160 | |
161 | g_test_add_data_func (testpath: "/glib/1bit-mutex" SUFFIX "/int" , test_data: (gpointer) 0, test_func: testcase); |
162 | g_test_add_data_func (testpath: "/glib/1bit-mutex" SUFFIX "/pointer" , test_data: (gpointer) 1, test_func: testcase); |
163 | |
164 | return g_test_run (); |
165 | } |
166 | |