Branch data Line data Source code
1 : : /* Copyright (C) 2005 Imendio AB
2 : : *
3 : : * SPDX-License-Identifier: LicenseRef-old-glib-tests
4 : : *
5 : : * This software is provided "as is"; redistribution and modification
6 : : * is permitted, provided that the following disclaimer is retained.
7 : : *
8 : : * This software is distributed in the hope that it will be useful,
9 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 : : * In no event shall the authors or contributors be liable for any
12 : : * direct, indirect, incidental, special, exemplary, or consequential
13 : : * damages (including, but not limited to, procurement of substitute
14 : : * goods or services; loss of use, data, or profits; or business
15 : : * interruption) however caused and on any theory of liability, whether
16 : : * in contract, strict liability, or tort (including negligence or
17 : : * otherwise) arising in any way out of the use of this software, even
18 : : * if advised of the possibility of such damage.
19 : : */
20 : : #include <glib-object.h>
21 : :
22 : : #ifdef G_OS_UNIX
23 : : #include <unistd.h>
24 : : #endif
25 : :
26 : : #define TEST_POINTER1 ((gpointer) 47)
27 : : #define TEST_POINTER2 ((gpointer) 49)
28 : : #define TEST_INT1 (-77)
29 : : #define TEST_INT2 (78)
30 : :
31 : : /* --- GTest class --- */
32 : : typedef struct {
33 : : GObject object;
34 : : gint value;
35 : : gpointer test_pointer1;
36 : : gpointer test_pointer2;
37 : : } GTest;
38 : : typedef struct {
39 : : GObjectClass parent_class;
40 : : void (*test_signal1) (GTest * test, gint an_int);
41 : : void (*test_signal2) (GTest * test, gint an_int);
42 : : } GTestClass;
43 : :
44 : : #define G_TYPE_TEST (my_test_get_type ())
45 : : #define MY_TEST(test) (G_TYPE_CHECK_INSTANCE_CAST ((test), G_TYPE_TEST, GTest))
46 : : #define MY_IS_TEST(test) (G_TYPE_CHECK_INSTANCE_TYPE ((test), G_TYPE_TEST))
47 : : #define MY_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_CAST ((tclass), G_TYPE_TEST, GTestClass))
48 : : #define MY_IS_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_TYPE ((tclass), G_TYPE_TEST))
49 : : #define MY_TEST_GET_CLASS(test) (G_TYPE_INSTANCE_GET_CLASS ((test), G_TYPE_TEST, GTestClass))
50 : :
51 : : static GType my_test_get_type (void);
52 : 3 : G_DEFINE_TYPE (GTest, my_test, G_TYPE_OBJECT)
53 : :
54 : : /* Test state */
55 : : typedef struct
56 : : {
57 : : GClosure *closure; /* (unowned) */
58 : : gboolean stopping;
59 : : gboolean seen_signal_handler;
60 : : gboolean seen_cleanup;
61 : : gboolean seen_test_int1;
62 : : gboolean seen_test_int2;
63 : : gboolean seen_thread1;
64 : : gboolean seen_thread2;
65 : : } TestClosureRefcountData;
66 : :
67 : : /* --- functions --- */
68 : : static void
69 : 1 : my_test_init (GTest * test)
70 : : {
71 : 1 : g_test_message ("Init %p", test);
72 : :
73 : 1 : test->value = 0;
74 : 1 : test->test_pointer1 = TEST_POINTER1;
75 : 1 : test->test_pointer2 = TEST_POINTER2;
76 : 1 : }
77 : :
78 : : typedef enum
79 : : {
80 : : PROP_TEST_PROP = 1,
81 : : } MyTestProperty;
82 : :
83 : : typedef enum
84 : : {
85 : : SIGNAL_TEST_SIGNAL1,
86 : : SIGNAL_TEST_SIGNAL2,
87 : : } MyTestSignal;
88 : :
89 : : static guint signals[SIGNAL_TEST_SIGNAL2 + 1] = { 0, };
90 : :
91 : : static void
92 : 0 : my_test_set_property (GObject *object,
93 : : guint prop_id,
94 : : const GValue *value,
95 : : GParamSpec *pspec)
96 : : {
97 : 0 : GTest *test = MY_TEST (object);
98 : 0 : switch (prop_id)
99 : : {
100 : 0 : case PROP_TEST_PROP:
101 : 0 : test->value = g_value_get_int (value);
102 : 0 : break;
103 : 0 : default:
104 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
105 : 0 : break;
106 : : }
107 : 0 : }
108 : :
109 : : static void
110 : 0 : my_test_get_property (GObject *object,
111 : : guint prop_id,
112 : : GValue *value,
113 : : GParamSpec *pspec)
114 : : {
115 : 0 : GTest *test = MY_TEST (object);
116 : 0 : switch (prop_id)
117 : : {
118 : 0 : case PROP_TEST_PROP:
119 : 0 : g_value_set_int (value, test->value);
120 : 0 : break;
121 : 0 : default:
122 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
123 : 0 : break;
124 : : }
125 : 0 : }
126 : :
127 : : static void
128 : 999999 : my_test_test_signal2 (GTest *test,
129 : : gint an_int)
130 : : {
131 : 999999 : }
132 : :
133 : : static void
134 : 999999 : my_test_emit_test_signal1 (GTest *test,
135 : : gint vint)
136 : : {
137 : 999999 : g_signal_emit (G_OBJECT (test), signals[SIGNAL_TEST_SIGNAL1], 0, vint);
138 : 999999 : }
139 : :
140 : : static void
141 : 999999 : my_test_emit_test_signal2 (GTest *test,
142 : : gint vint)
143 : : {
144 : 999999 : g_signal_emit (G_OBJECT (test), signals[SIGNAL_TEST_SIGNAL2], 0, vint);
145 : 999999 : }
146 : :
147 : : static void
148 : 1 : my_test_class_init (GTestClass *klass)
149 : : {
150 : 1 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
151 : :
152 : 1 : gobject_class->set_property = my_test_set_property;
153 : 1 : gobject_class->get_property = my_test_get_property;
154 : :
155 : 1 : signals[SIGNAL_TEST_SIGNAL1] =
156 : 1 : g_signal_new ("test-signal1", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
157 : : G_STRUCT_OFFSET (GTestClass, test_signal1), NULL, NULL,
158 : : g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
159 : 1 : signals[SIGNAL_TEST_SIGNAL2] =
160 : 1 : g_signal_new ("test-signal2", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
161 : : G_STRUCT_OFFSET (GTestClass, test_signal2), NULL, NULL,
162 : : g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
163 : :
164 : 1 : g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TEST_PROP,
165 : : g_param_spec_int ("test-prop", "Test Prop", "Test property",
166 : : 0, 1, 0, G_PARAM_READWRITE));
167 : 1 : klass->test_signal2 = my_test_test_signal2;
168 : 1 : }
169 : :
170 : : static void
171 : 698035 : test_closure (GClosure *closure)
172 : : {
173 : : /* try to produce high contention in closure->ref_count */
174 : 698035 : guint i = 0, n = g_random_int () % 199;
175 : 69793979 : for (i = 0; i < n; i++)
176 : 69095944 : g_closure_ref (closure);
177 : 698035 : g_closure_sink (closure); /* NOP */
178 : 69793979 : for (i = 0; i < n; i++)
179 : 69095944 : g_closure_unref (closure);
180 : 698035 : }
181 : :
182 : : static gpointer
183 : 1 : thread1_main (gpointer user_data)
184 : : {
185 : 1 : TestClosureRefcountData *data = user_data;
186 : 1 : guint i = 0;
187 : :
188 : 338293 : for (i = 1; !g_atomic_int_get (&data->stopping); i++)
189 : : {
190 : 338292 : test_closure (data->closure);
191 : 338292 : if (i % 10000 == 0)
192 : : {
193 : 33 : g_test_message ("Yielding from thread1");
194 : 33 : g_thread_yield (); /* force context switch */
195 : 33 : g_atomic_int_set (&data->seen_thread1, TRUE);
196 : : }
197 : : }
198 : 1 : return NULL;
199 : : }
200 : :
201 : : static gpointer
202 : 1 : thread2_main (gpointer user_data)
203 : : {
204 : 1 : TestClosureRefcountData *data = user_data;
205 : 1 : guint i = 0;
206 : :
207 : 359744 : for (i = 1; !g_atomic_int_get (&data->stopping); i++)
208 : : {
209 : 359743 : test_closure (data->closure);
210 : 359743 : if (i % 10000 == 0)
211 : : {
212 : 35 : g_test_message ("Yielding from thread2");
213 : 35 : g_thread_yield (); /* force context switch */
214 : 35 : g_atomic_int_set (&data->seen_thread2, TRUE);
215 : : }
216 : : }
217 : 1 : return NULL;
218 : : }
219 : :
220 : : static void
221 : 1999998 : test_signal_handler (GTest *test,
222 : : gint vint,
223 : : gpointer user_data)
224 : : {
225 : 1999998 : TestClosureRefcountData *data = user_data;
226 : :
227 : 1999998 : g_assert_true (test->test_pointer1 == TEST_POINTER1);
228 : :
229 : 1999998 : data->seen_signal_handler = TRUE;
230 : 1999998 : data->seen_test_int1 |= vint == TEST_INT1;
231 : 1999998 : data->seen_test_int2 |= vint == TEST_INT2;
232 : 1999998 : }
233 : :
234 : : static void
235 : 1 : destroy_data (gpointer user_data,
236 : : GClosure *closure)
237 : : {
238 : 1 : TestClosureRefcountData *data = user_data;
239 : :
240 : 1 : data->seen_cleanup = TRUE;
241 : 1 : g_assert_true (data->closure == closure);
242 : 1 : g_assert_cmpint (closure->ref_count, ==, 0);
243 : 1 : }
244 : :
245 : : static void
246 : 999999 : test_emissions (GTest *test)
247 : : {
248 : 999999 : my_test_emit_test_signal1 (test, TEST_INT1);
249 : 999999 : my_test_emit_test_signal2 (test, TEST_INT2);
250 : 999999 : }
251 : :
252 : : /* Test that closure refcounting works even when high contested between three
253 : : * threads (the main thread, thread1 and thread2). Both child threads are
254 : : * contesting refs/unrefs, while the main thread periodically emits signals
255 : : * which also do refs/unrefs on closures. */
256 : : static void
257 : 1 : test_closure_refcount (void)
258 : : {
259 : : GThread *thread1, *thread2;
260 : 1 : TestClosureRefcountData test_data = { 0, };
261 : : GClosure *closure;
262 : : GTest *object;
263 : : guint i, n_iterations;
264 : :
265 : 1 : object = g_object_new (G_TYPE_TEST, NULL);
266 : 1 : closure = g_cclosure_new (G_CALLBACK (test_signal_handler), &test_data, destroy_data);
267 : :
268 : 1 : g_signal_connect_closure (object, "test-signal1", closure, FALSE);
269 : 1 : g_signal_connect_closure (object, "test-signal2", closure, FALSE);
270 : :
271 : 1 : test_data.stopping = FALSE;
272 : 1 : test_data.closure = closure;
273 : :
274 : 1 : thread1 = g_thread_new ("thread1", thread1_main, &test_data);
275 : 1 : thread2 = g_thread_new ("thread2", thread2_main, &test_data);
276 : :
277 : : /* The 16-bit compare-and-swap operations currently used for closure
278 : : * refcounts are really slow on some ARM CPUs, notably Cortex-A57.
279 : : * Reduce the number of iterations so that the test completes in a
280 : : * finite time, but don't reduce it so much that the main thread
281 : : * starves the other threads and causes a test failure.
282 : : *
283 : : * https://gitlab.gnome.org/GNOME/glib/issues/1316
284 : : * aka https://bugs.debian.org/880883 */
285 : : #if defined(__aarch64__) || defined(__arm__)
286 : : n_iterations = 100000;
287 : : #else
288 : 1 : n_iterations = 1000000;
289 : : #endif
290 : :
291 : : /* Run the test for a reasonably high number of iterations, and ensure we
292 : : * don’t terminate until at least 10000 iterations have completed in both
293 : : * thread1 and thread2. Even though @n_iterations is high, we can’t guarantee
294 : : * that the scheduler allocates time fairly (or at all!) to thread1 or
295 : : * thread2. */
296 : 1 : for (i = 1;
297 : 1 : i < n_iterations ||
298 : 1000000 : !g_atomic_int_get (&test_data.seen_thread1) ||
299 : 1 : !g_atomic_int_get (&test_data.seen_thread2);
300 : 999999 : i++)
301 : : {
302 : 999999 : test_emissions (object);
303 : 999999 : if (i % 10000 == 0)
304 : : {
305 : 99 : g_test_message ("Yielding from main thread");
306 : 99 : g_thread_yield (); /* force context switch */
307 : : }
308 : : }
309 : :
310 : 1 : g_atomic_int_set (&test_data.stopping, TRUE);
311 : 1 : g_test_message ("Stopping");
312 : :
313 : : /* wait for thread shutdown */
314 : 1 : g_thread_join (thread1);
315 : 1 : g_thread_join (thread2);
316 : :
317 : : /* finalize object, destroy signals, run cleanup code */
318 : 1 : g_object_unref (object);
319 : :
320 : 1 : g_test_message ("Stopped");
321 : :
322 : 1 : g_assert_true (g_atomic_int_get (&test_data.seen_thread1));
323 : 1 : g_assert_true (g_atomic_int_get (&test_data.seen_thread2));
324 : 1 : g_assert_true (test_data.seen_test_int1);
325 : 1 : g_assert_true (test_data.seen_test_int2);
326 : 1 : g_assert_true (test_data.seen_signal_handler);
327 : 1 : g_assert_true (test_data.seen_cleanup);
328 : 1 : }
329 : :
330 : : int
331 : 1 : main (int argc,
332 : : char *argv[])
333 : : {
334 : 1 : g_test_init (&argc, &argv, NULL);
335 : :
336 : 1 : g_test_add_func ("/closure/refcount", test_closure_refcount);
337 : :
338 : 1 : return g_test_run ();
339 : : }
|