Revert all changes since a36d4918a6f646e085
[platform/upstream/dbus.git] / test / internals / refs.c
1 /* Regression test for thread-safe reference-counting
2  *
3  * Author: Simon McVittie <simon.mcvittie@collabora.co.uk>
4  * Copyright © 2011 Nokia Corporation
5  *
6  * Permission is hereby granted, free of charge, to any person
7  * obtaining a copy of this software and associated documentation files
8  * (the "Software"), to deal in the Software without restriction,
9  * including without limitation the rights to use, copy, modify, merge,
10  * publish, distribute, sublicense, and/or sell copies of the Software,
11  * and to permit persons to whom the Software is furnished to do so,
12  * subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
21  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24  * SOFTWARE.
25  */
26
27 #include <config.h>
28
29 #include <glib.h>
30
31 #define DBUS_COMPILATION    /* this test uses libdbus-internal */
32 #include <dbus/dbus.h>
33 #include <dbus/dbus-connection-internal.h>
34 #include <dbus/dbus-mainloop.h>
35 #include <dbus/dbus-message-internal.h>
36 #include <dbus/dbus-pending-call-internal.h>
37 #include <dbus/dbus-server-protected.h>
38 #include "test-utils.h"
39
40 static void
41 assert_no_error (const DBusError *e)
42 {
43   if (G_UNLIKELY (dbus_error_is_set (e)))
44     g_error ("expected success but got error: %s: %s", e->name, e->message);
45 }
46
47 #define N_THREADS 200
48 #define N_REFS 10000
49 G_STATIC_ASSERT (((unsigned) N_THREADS * (unsigned) N_REFS) < G_MAXINT32);
50
51 static dbus_int32_t connection_slot = -1;
52 static dbus_int32_t server_slot = -1;
53 static dbus_int32_t message_slot = -1;
54 static dbus_int32_t pending_call_slot = -1;
55
56 typedef struct {
57   DBusError e;
58   DBusLoop *loop;
59   DBusServer *server;
60   DBusConnection *connection;
61   DBusConnection *server_connection;
62   DBusMessage *message;
63   GThread *threads[N_THREADS];
64   gboolean last_unref;
65 } Fixture;
66
67 typedef void *(*RefFunc) (void *);
68 typedef void (*VoidFunc) (void *);
69
70 typedef struct {
71   void *thing;
72   RefFunc ref;
73   VoidFunc ref_void;
74   VoidFunc unref;
75   void *mutex;
76   VoidFunc lock;
77   VoidFunc unlock;
78 } Thread;
79
80 static gpointer
81 ref_thread (gpointer data)
82 {
83   Thread *thread = data;
84   int i;
85
86   for (i = 0; i < N_REFS; i++)
87     {
88       if (thread->lock != NULL)
89         (thread->lock) (thread->mutex);
90
91       if (thread->ref != NULL)
92         {
93           gpointer ret = (thread->ref) (thread->thing);
94
95           g_assert (ret == thread->thing);
96         }
97       else
98         {
99           (thread->ref_void) (thread->thing);
100         }
101
102       if (thread->unlock != NULL)
103         (thread->unlock) (thread->mutex);
104     }
105
106   return NULL;
107 }
108
109 static gpointer
110 cycle_thread (gpointer data)
111 {
112   Thread *thread = data;
113   int i;
114
115   for (i = 0; i < N_REFS; i++)
116     {
117       if (thread->lock != NULL)
118         (thread->lock) (thread->mutex);
119
120       if (thread->ref != NULL)
121         {
122           gpointer ret = (thread->ref) (thread->thing);
123
124           g_assert (ret == thread->thing);
125         }
126       else
127         {
128           (thread->ref_void) (thread->thing);
129         }
130
131       (thread->unref) (thread->thing);
132
133       if (thread->unlock != NULL)
134         (thread->unlock) (thread->mutex);
135     }
136
137   return NULL;
138 }
139
140 static gpointer
141 unref_thread (gpointer data)
142 {
143   Thread *thread = data;
144   int i;
145
146   for (i = 0; i < N_REFS; i++)
147     {
148       if (thread->lock != NULL)
149         (thread->lock) (thread->mutex);
150
151       (thread->unref) (thread->thing);
152
153       if (thread->unlock != NULL)
154         (thread->unlock) (thread->mutex);
155     }
156
157   return NULL;
158 }
159
160 static void
161 last_unref (void *data)
162 {
163   Fixture *f = data;
164
165   g_assert (!f->last_unref);
166   f->last_unref = TRUE;
167 }
168
169 static void
170 wait_for_all_threads (Fixture *f)
171 {
172   int i;
173
174   for (i = 0; i < N_THREADS; i++)
175     g_thread_join (f->threads[i]);
176 }
177
178 static void
179 new_conn_cb (DBusServer *server,
180     DBusConnection *server_connection,
181     void *data)
182 {
183   Fixture *f = data;
184   dbus_bool_t have_mem;
185
186   g_assert (f->server_connection == NULL);
187   f->server_connection = dbus_connection_ref (server_connection);
188
189   test_connection_setup (f->loop, f->server_connection);
190 }
191
192 static void
193 setup (Fixture *f,
194     gconstpointer data)
195 {
196   if (!dbus_threads_init_default ())
197     g_error ("OOM");
198
199   f->loop = _dbus_loop_new ();
200   g_assert (f->loop != NULL);
201
202   dbus_error_init (&f->e);
203
204   f->server = dbus_server_listen ("tcp:host=127.0.0.1", &f->e);
205   assert_no_error (&f->e);
206   g_assert (f->server != NULL);
207
208   if (!dbus_connection_allocate_data_slot (&connection_slot))
209     g_error ("OOM");
210
211   if (!dbus_server_allocate_data_slot (&server_slot))
212     g_error ("OOM");
213
214   if (!dbus_message_allocate_data_slot (&message_slot))
215     g_error ("OOM");
216
217   if (!dbus_pending_call_allocate_data_slot (&pending_call_slot))
218     g_error ("OOM");
219 }
220
221 static void
222 setup_connection (Fixture *f,
223     gconstpointer data)
224 {
225   char *address;
226
227   setup (f, data);
228
229   dbus_server_set_new_connection_function (f->server,
230       new_conn_cb, f, NULL);
231
232   if (!test_server_setup (f->loop, f->server))
233     g_error ("failed to set up server");
234
235   address = dbus_server_get_address (f->server);
236   g_assert (address != NULL);
237   f->connection = dbus_connection_open_private (address, &f->e);
238   assert_no_error (&f->e);
239   g_assert (f->connection != NULL);
240   dbus_free (address);
241
242   if (!test_connection_setup (f->loop, f->connection))
243     g_error ("failed to set up connection");
244
245   while (f->server_connection == NULL)
246     _dbus_loop_iterate (f->loop, TRUE);
247
248   test_connection_shutdown (f->loop, f->connection);
249   test_server_shutdown (f->loop, f->server);
250 }
251
252 static void
253 test_connection (Fixture *f,
254     gconstpointer data)
255 {
256   Thread public_api = { f->connection,
257     (RefFunc) dbus_connection_ref,
258     NULL,
259     (VoidFunc) dbus_connection_unref,
260     NULL,
261     NULL,
262     NULL };
263   Thread internal_api = { f->connection,
264     (RefFunc) _dbus_connection_ref_unlocked,
265     NULL,
266     (VoidFunc) _dbus_connection_unref_unlocked,
267     f->connection,
268     (VoidFunc) _dbus_connection_lock,
269     (VoidFunc) _dbus_connection_unlock };
270   int i;
271
272   /* Use a slot as a pseudo-weakref */
273   if (!dbus_connection_set_data (f->connection, connection_slot, f,
274         last_unref))
275     g_error ("OOM");
276
277   for (i = 0; i < N_THREADS; i++)
278     {
279       if ((i % 2) == 0)
280         f->threads[i] = g_thread_create (ref_thread, &public_api, TRUE, NULL);
281       else
282         f->threads[i] = g_thread_create (ref_thread, &internal_api, TRUE,
283             NULL);
284
285       g_assert (f->threads[i] != NULL);
286     }
287
288   wait_for_all_threads (f);
289
290   for (i = 0; i < N_THREADS; i++)
291     {
292       if ((i % 2) == 0)
293         f->threads[i] = g_thread_create (cycle_thread, &public_api, TRUE,
294             NULL);
295       else
296         f->threads[i] = g_thread_create (cycle_thread, &internal_api, TRUE,
297             NULL);
298
299       g_assert (f->threads[i] != NULL);
300     }
301
302   wait_for_all_threads (f);
303
304   for (i = 0; i < N_THREADS; i++)
305     {
306       if ((i % 2) == 0)
307         f->threads[i] = g_thread_create (unref_thread, &public_api, TRUE,
308             NULL);
309       else
310         f->threads[i] = g_thread_create (unref_thread, &internal_api, TRUE,
311             NULL);
312
313       g_assert (f->threads[i] != NULL);
314     }
315
316   wait_for_all_threads (f);
317
318   /* Destroy the connection. This should be the last-unref. */
319   g_assert (!f->last_unref);
320   dbus_connection_close (f->connection);
321   dbus_connection_unref (f->connection);
322   f->connection = NULL;
323   g_assert (f->last_unref);
324 }
325
326 static void
327 server_lock (void *server)
328 {
329   SERVER_LOCK (((DBusServer *) server));
330 }
331
332 static void
333 server_unlock (void *server)
334 {
335   SERVER_UNLOCK (((DBusServer *) server));
336 }
337
338 static void
339 test_server (Fixture *f,
340     gconstpointer data)
341 {
342   Thread public_api = { f->server,
343     (RefFunc) dbus_server_ref,
344     NULL,
345     (VoidFunc) dbus_server_unref,
346     NULL,
347     NULL,
348     NULL };
349   Thread internal_api = { f->server,
350     NULL,
351     (VoidFunc) _dbus_server_ref_unlocked,
352     (VoidFunc) _dbus_server_unref_unlocked,
353     f->server,
354     server_lock,
355     server_unlock };
356   int i;
357
358   if (!dbus_server_set_data (f->server, server_slot, f, last_unref))
359     g_error ("OOM");
360
361   for (i = 0; i < N_THREADS; i++)
362     {
363       if ((i % 2) == 0)
364         f->threads[i] = g_thread_create (ref_thread, &public_api, TRUE, NULL);
365       else
366         f->threads[i] = g_thread_create (ref_thread, &internal_api, TRUE,
367             NULL);
368
369       g_assert (f->threads[i] != NULL);
370     }
371
372   wait_for_all_threads (f);
373
374   for (i = 0; i < N_THREADS; i++)
375     {
376       if ((i % 2) == 0)
377         f->threads[i] = g_thread_create (cycle_thread, &public_api, TRUE,
378             NULL);
379       else
380         f->threads[i] = g_thread_create (cycle_thread, &internal_api, TRUE,
381             NULL);
382
383       g_assert (f->threads[i] != NULL);
384     }
385
386   wait_for_all_threads (f);
387
388   for (i = 0; i < N_THREADS; i++)
389     {
390       if ((i % 2) == 0)
391         f->threads[i] = g_thread_create (unref_thread, &public_api, TRUE,
392             NULL);
393       else
394         f->threads[i] = g_thread_create (unref_thread, &internal_api, TRUE,
395             NULL);
396
397       g_assert (f->threads[i] != NULL);
398     }
399
400   wait_for_all_threads (f);
401
402   /* Destroy the server. This should be the last-unref. */
403   g_assert (!f->last_unref);
404   dbus_server_disconnect (f->server);
405   dbus_server_unref (f->server);
406   f->server = NULL;
407   g_assert (f->last_unref);
408 }
409
410 static void
411 test_message (Fixture *f,
412     gconstpointer data)
413 {
414   DBusMessage *message = dbus_message_new_signal ("/foo", "foo.bar.baz",
415       "Foo");
416   Thread public_api = { message,
417     (RefFunc) dbus_message_ref,
418     NULL,
419     (VoidFunc) dbus_message_unref,
420     NULL,
421     NULL,
422     NULL };
423   int i;
424
425   if (!dbus_message_set_data (message, message_slot, f, last_unref))
426     g_error ("OOM");
427
428   for (i = 0; i < N_THREADS; i++)
429     {
430       f->threads[i] = g_thread_create (ref_thread, &public_api, TRUE, NULL);
431       g_assert (f->threads[i] != NULL);
432     }
433
434   wait_for_all_threads (f);
435
436   for (i = 0; i < N_THREADS; i++)
437     {
438       f->threads[i] = g_thread_create (cycle_thread, &public_api, TRUE, NULL);
439       g_assert (f->threads[i] != NULL);
440     }
441
442   wait_for_all_threads (f);
443
444   for (i = 0; i < N_THREADS; i++)
445     {
446       f->threads[i] = g_thread_create (unref_thread, &public_api, TRUE, NULL);
447       g_assert (f->threads[i] != NULL);
448     }
449
450   wait_for_all_threads (f);
451
452   /* Destroy the server. This should be the last-unref. */
453   g_assert (!f->last_unref);
454   dbus_message_unref (message);
455   g_assert (f->last_unref);
456 }
457
458 static void
459 test_pending_call (Fixture *f,
460     gconstpointer data)
461 {
462   Thread public_api = { NULL,
463     (RefFunc) dbus_pending_call_ref,
464     NULL,
465     (VoidFunc) dbus_pending_call_unref,
466     NULL,
467     NULL,
468     NULL };
469   Thread internal_api = { NULL,
470     (RefFunc) _dbus_pending_call_ref_unlocked,
471     NULL,
472     (VoidFunc) dbus_pending_call_unref,
473     f->connection,
474     (VoidFunc) _dbus_connection_lock,
475     (VoidFunc) _dbus_connection_unlock };
476   /* This one can't be used to ref, only to cycle or unref. */
477   Thread unref_and_unlock_api = { NULL,
478     (RefFunc) _dbus_pending_call_ref_unlocked,
479     NULL,
480     (VoidFunc) _dbus_pending_call_unref_and_unlock,
481     f->connection,
482     (VoidFunc) _dbus_connection_lock,
483     NULL };
484   int i;
485   DBusPendingCall *pending_call;
486
487   _dbus_connection_lock (f->connection);
488   pending_call = _dbus_pending_call_new_unlocked (f->connection,
489       DBUS_TIMEOUT_INFINITE, NULL);
490   g_assert (pending_call != NULL);
491   _dbus_connection_unlock (f->connection);
492
493   public_api.thing = pending_call;
494   internal_api.thing = pending_call;
495   unref_and_unlock_api.thing = pending_call;
496
497   if (!dbus_pending_call_set_data (pending_call, pending_call_slot, f,
498         last_unref))
499     g_error ("OOM");
500
501   for (i = 0; i < N_THREADS; i++)
502     {
503       if ((i % 2) == 0)
504         f->threads[i] = g_thread_create (ref_thread, &public_api, TRUE, NULL);
505       else
506         f->threads[i] = g_thread_create (ref_thread, &internal_api, TRUE,
507             NULL);
508
509       g_assert (f->threads[i] != NULL);
510     }
511
512   wait_for_all_threads (f);
513
514   for (i = 0; i < N_THREADS; i++)
515     {
516       switch (i % 3)
517         {
518           case 0:
519             f->threads[i] = g_thread_create (cycle_thread, &public_api, TRUE,
520                 NULL);
521             break;
522           case 1:
523             f->threads[i] = g_thread_create (cycle_thread, &internal_api, TRUE,
524                 NULL);
525             break;
526           default:
527             f->threads[i] = g_thread_create (cycle_thread,
528                 &unref_and_unlock_api, TRUE, NULL);
529         }
530
531       g_assert (f->threads[i] != NULL);
532     }
533
534   wait_for_all_threads (f);
535
536   for (i = 0; i < N_THREADS; i++)
537     {
538       switch (i % 3)
539         {
540           case 0:
541             f->threads[i] = g_thread_create (unref_thread, &public_api, TRUE,
542                 NULL);
543             break;
544           case 1:
545             f->threads[i] = g_thread_create (unref_thread, &internal_api, TRUE,
546                 NULL);
547             break;
548           default:
549             f->threads[i] = g_thread_create (unref_thread,
550                 &unref_and_unlock_api, TRUE, NULL);
551         }
552
553       g_assert (f->threads[i] != NULL);
554     }
555
556   wait_for_all_threads (f);
557
558   /* Destroy the pending call. This should be the last-unref. */
559   g_assert (!f->last_unref);
560   dbus_pending_call_unref (pending_call);
561   g_assert (f->last_unref);
562 }
563
564 static void
565 teardown (Fixture *f,
566     gconstpointer data)
567 {
568   if (f->server_connection != NULL)
569     {
570       dbus_connection_close (f->server_connection);
571       dbus_connection_unref (f->server_connection);
572     }
573
574   if (f->connection != NULL)
575     {
576       dbus_connection_close (f->connection);
577       dbus_connection_unref (f->connection);
578     }
579
580   if (f->server != NULL)
581     {
582       dbus_server_disconnect (f->server);
583       dbus_server_unref (f->server);
584     }
585
586   dbus_connection_free_data_slot (&connection_slot);
587   dbus_server_free_data_slot (&server_slot);
588   dbus_message_free_data_slot (&message_slot);
589   dbus_pending_call_free_data_slot (&pending_call_slot);
590
591   _dbus_loop_unref (f->loop);
592   dbus_error_free (&f->e);
593 }
594
595 int
596 main (int argc,
597     char **argv)
598 {
599   g_thread_init (NULL);
600   g_test_init (&argc, &argv, NULL);
601   g_test_bug_base ("https://bugs.freedesktop.org/show_bug.cgi?id=");
602
603   g_test_add ("/refs/connection", Fixture, NULL, setup_connection,
604       test_connection, teardown);
605   g_test_add ("/refs/message", Fixture, NULL, setup,
606       test_message, teardown);
607   g_test_add ("/refs/pending-call", Fixture, NULL, setup_connection,
608       test_pending_call, teardown);
609   g_test_add ("/refs/server", Fixture, NULL, setup,
610       test_server, teardown);
611
612   return g_test_run ();
613 }