ListNames and ListQueuedOwners updated to work with new kdbus
[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 #include <glib-object.h>
31
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 /* provide backwards compatibility shim when building with a glib <= 2.30.x */
81 #if !GLIB_CHECK_VERSION(2,31,0)
82 #define g_thread_new(name,func,data) g_thread_create(func,data,TRUE,NULL)
83 #endif
84
85 static gpointer
86 ref_thread (gpointer data)
87 {
88   Thread *thread = data;
89   int i;
90
91   for (i = 0; i < N_REFS; i++)
92     {
93       if (thread->lock != NULL)
94         (thread->lock) (thread->mutex);
95
96       if (thread->ref != NULL)
97         {
98           gpointer ret = (thread->ref) (thread->thing);
99
100           g_assert (ret == thread->thing);
101         }
102       else
103         {
104           (thread->ref_void) (thread->thing);
105         }
106
107       if (thread->unlock != NULL)
108         (thread->unlock) (thread->mutex);
109     }
110
111   return NULL;
112 }
113
114 static gpointer
115 cycle_thread (gpointer data)
116 {
117   Thread *thread = data;
118   int i;
119
120   for (i = 0; i < N_REFS; i++)
121     {
122       if (thread->lock != NULL)
123         (thread->lock) (thread->mutex);
124
125       if (thread->ref != NULL)
126         {
127           gpointer ret = (thread->ref) (thread->thing);
128
129           g_assert (ret == thread->thing);
130         }
131       else
132         {
133           (thread->ref_void) (thread->thing);
134         }
135
136       (thread->unref) (thread->thing);
137
138       if (thread->unlock != NULL)
139         (thread->unlock) (thread->mutex);
140     }
141
142   return NULL;
143 }
144
145 static gpointer
146 unref_thread (gpointer data)
147 {
148   Thread *thread = data;
149   int i;
150
151   for (i = 0; i < N_REFS; i++)
152     {
153       if (thread->lock != NULL)
154         (thread->lock) (thread->mutex);
155
156       (thread->unref) (thread->thing);
157
158       if (thread->unlock != NULL)
159         (thread->unlock) (thread->mutex);
160     }
161
162   return NULL;
163 }
164
165 static void
166 last_unref (void *data)
167 {
168   Fixture *f = data;
169
170   g_assert (!f->last_unref);
171   f->last_unref = TRUE;
172 }
173
174 static void
175 wait_for_all_threads (Fixture *f)
176 {
177   int i;
178
179   for (i = 0; i < N_THREADS; i++)
180     g_thread_join (f->threads[i]);
181 }
182
183 static void
184 new_conn_cb (DBusServer *server,
185     DBusConnection *server_connection,
186     void *data)
187 {
188   Fixture *f = data;
189
190   g_assert (f->server_connection == NULL);
191   f->server_connection = dbus_connection_ref (server_connection);
192
193   test_connection_setup (f->loop, f->server_connection);
194 }
195
196 static void
197 setup (Fixture *f,
198     gconstpointer data)
199 {
200   if (!dbus_threads_init_default ())
201     g_error ("OOM");
202
203   f->loop = _dbus_loop_new ();
204   g_assert (f->loop != NULL);
205
206   dbus_error_init (&f->e);
207
208   f->server = dbus_server_listen ("tcp:host=127.0.0.1", &f->e);
209   assert_no_error (&f->e);
210   g_assert (f->server != NULL);
211
212   if (!dbus_connection_allocate_data_slot (&connection_slot))
213     g_error ("OOM");
214
215   if (!dbus_server_allocate_data_slot (&server_slot))
216     g_error ("OOM");
217
218   if (!dbus_message_allocate_data_slot (&message_slot))
219     g_error ("OOM");
220
221   if (!dbus_pending_call_allocate_data_slot (&pending_call_slot))
222     g_error ("OOM");
223 }
224
225 static void
226 setup_connection (Fixture *f,
227     gconstpointer data)
228 {
229   char *address;
230
231   setup (f, data);
232
233   dbus_server_set_new_connection_function (f->server,
234       new_conn_cb, f, NULL);
235
236   if (!test_server_setup (f->loop, f->server))
237     g_error ("failed to set up server");
238
239   address = dbus_server_get_address (f->server);
240   g_assert (address != NULL);
241   f->connection = dbus_connection_open_private (address, &f->e);
242   assert_no_error (&f->e);
243   g_assert (f->connection != NULL);
244   dbus_free (address);
245
246   if (!test_connection_setup (f->loop, f->connection))
247     g_error ("failed to set up connection");
248
249   while (f->server_connection == NULL)
250     _dbus_loop_iterate (f->loop, TRUE);
251
252   test_connection_shutdown (f->loop, f->connection);
253   test_server_shutdown (f->loop, f->server);
254 }
255
256 static void
257 test_connection (Fixture *f,
258     gconstpointer data)
259 {
260   Thread public_api = { f->connection,
261     (RefFunc) dbus_connection_ref,
262     NULL,
263     (VoidFunc) dbus_connection_unref,
264     NULL,
265     NULL,
266     NULL };
267   Thread internal_api = { f->connection,
268     (RefFunc) _dbus_connection_ref_unlocked,
269     NULL,
270     (VoidFunc) _dbus_connection_unref_unlocked,
271     f->connection,
272     (VoidFunc) _dbus_connection_lock,
273     (VoidFunc) _dbus_connection_unlock };
274   int i;
275
276   /* Use a slot as a pseudo-weakref */
277   if (!dbus_connection_set_data (f->connection, connection_slot, f,
278         last_unref))
279     g_error ("OOM");
280
281   for (i = 0; i < N_THREADS; i++)
282     {
283       if ((i % 2) == 0)
284         f->threads[i] = g_thread_new (NULL, ref_thread, &public_api);
285       else
286         f->threads[i] = g_thread_new (NULL, ref_thread, &internal_api);
287
288       g_assert (f->threads[i] != NULL);
289     }
290
291   wait_for_all_threads (f);
292
293   for (i = 0; i < N_THREADS; i++)
294     {
295       if ((i % 2) == 0)
296         f->threads[i] = g_thread_new (NULL, cycle_thread, &public_api);
297       else
298         f->threads[i] = g_thread_new (NULL, cycle_thread, &internal_api);
299
300       g_assert (f->threads[i] != NULL);
301     }
302
303   wait_for_all_threads (f);
304
305   for (i = 0; i < N_THREADS; i++)
306     {
307       if ((i % 2) == 0)
308         f->threads[i] = g_thread_new (NULL, unref_thread, &public_api);
309       else
310         f->threads[i] = g_thread_new (NULL, unref_thread, &internal_api);
311
312       g_assert (f->threads[i] != NULL);
313     }
314
315   wait_for_all_threads (f);
316
317   /* Destroy the connection. This should be the last-unref. */
318   g_assert (!f->last_unref);
319   dbus_connection_close (f->connection);
320   dbus_connection_unref (f->connection);
321   f->connection = NULL;
322   g_assert (f->last_unref);
323 }
324
325 static void
326 server_lock (void *server)
327 {
328   SERVER_LOCK (((DBusServer *) server));
329 }
330
331 static void
332 server_unlock (void *server)
333 {
334   SERVER_UNLOCK (((DBusServer *) server));
335 }
336
337 static void
338 test_server (Fixture *f,
339     gconstpointer data)
340 {
341   Thread public_api = { f->server,
342     (RefFunc) dbus_server_ref,
343     NULL,
344     (VoidFunc) dbus_server_unref,
345     NULL,
346     NULL,
347     NULL };
348   Thread internal_api = { f->server,
349     NULL,
350     (VoidFunc) _dbus_server_ref_unlocked,
351     (VoidFunc) _dbus_server_unref_unlocked,
352     f->server,
353     server_lock,
354     server_unlock };
355   int i;
356
357   if (!dbus_server_set_data (f->server, server_slot, f, last_unref))
358     g_error ("OOM");
359
360   for (i = 0; i < N_THREADS; i++)
361     {
362       if ((i % 2) == 0)
363         f->threads[i] = g_thread_new (NULL, ref_thread, &public_api);
364       else
365         f->threads[i] = g_thread_new (NULL, ref_thread, &internal_api);
366
367       g_assert (f->threads[i] != NULL);
368     }
369
370   wait_for_all_threads (f);
371
372   for (i = 0; i < N_THREADS; i++)
373     {
374       if ((i % 2) == 0)
375         f->threads[i] = g_thread_new (NULL, cycle_thread, &public_api);
376       else
377         f->threads[i] = g_thread_new (NULL, cycle_thread, &internal_api);
378
379       g_assert (f->threads[i] != NULL);
380     }
381
382   wait_for_all_threads (f);
383
384   for (i = 0; i < N_THREADS; i++)
385     {
386       if ((i % 2) == 0)
387         f->threads[i] = g_thread_new (NULL, unref_thread, &public_api);
388       else
389         f->threads[i] = g_thread_new (NULL, unref_thread, &internal_api);
390
391       g_assert (f->threads[i] != NULL);
392     }
393
394   wait_for_all_threads (f);
395
396   /* Destroy the server. This should be the last-unref. */
397   g_assert (!f->last_unref);
398   dbus_server_disconnect (f->server);
399   dbus_server_unref (f->server);
400   f->server = NULL;
401   g_assert (f->last_unref);
402 }
403
404 static void
405 test_message (Fixture *f,
406     gconstpointer data)
407 {
408   DBusMessage *message = dbus_message_new_signal ("/foo", "foo.bar.baz",
409       "Foo");
410   Thread public_api = { message,
411     (RefFunc) dbus_message_ref,
412     NULL,
413     (VoidFunc) dbus_message_unref,
414     NULL,
415     NULL,
416     NULL };
417   int i;
418
419   if (!dbus_message_set_data (message, message_slot, f, last_unref))
420     g_error ("OOM");
421
422   for (i = 0; i < N_THREADS; i++)
423     {
424       f->threads[i] = g_thread_new (NULL, ref_thread, &public_api);
425       g_assert (f->threads[i] != NULL);
426     }
427
428   wait_for_all_threads (f);
429
430   for (i = 0; i < N_THREADS; i++)
431     {
432       f->threads[i] = g_thread_new (NULL, cycle_thread, &public_api);
433       g_assert (f->threads[i] != NULL);
434     }
435
436   wait_for_all_threads (f);
437
438   for (i = 0; i < N_THREADS; i++)
439     {
440       f->threads[i] = g_thread_new (NULL, unref_thread, &public_api);
441       g_assert (f->threads[i] != NULL);
442     }
443
444   wait_for_all_threads (f);
445
446   /* Destroy the server. This should be the last-unref. */
447   g_assert (!f->last_unref);
448   dbus_message_unref (message);
449   g_assert (f->last_unref);
450 }
451
452 static void
453 test_pending_call (Fixture *f,
454     gconstpointer data)
455 {
456   Thread public_api = { NULL,
457     (RefFunc) dbus_pending_call_ref,
458     NULL,
459     (VoidFunc) dbus_pending_call_unref,
460     NULL,
461     NULL,
462     NULL };
463   Thread internal_api = { NULL,
464     (RefFunc) _dbus_pending_call_ref_unlocked,
465     NULL,
466     (VoidFunc) dbus_pending_call_unref,
467     f->connection,
468     (VoidFunc) _dbus_connection_lock,
469     (VoidFunc) _dbus_connection_unlock };
470   /* This one can't be used to ref, only to cycle or unref. */
471   Thread unref_and_unlock_api = { NULL,
472     (RefFunc) _dbus_pending_call_ref_unlocked,
473     NULL,
474     (VoidFunc) _dbus_pending_call_unref_and_unlock,
475     f->connection,
476     (VoidFunc) _dbus_connection_lock,
477     NULL };
478   int i;
479   DBusPendingCall *pending_call;
480
481   _dbus_connection_lock (f->connection);
482   pending_call = _dbus_pending_call_new_unlocked (f->connection,
483       DBUS_TIMEOUT_INFINITE, NULL);
484   g_assert (pending_call != NULL);
485   _dbus_connection_unlock (f->connection);
486
487   public_api.thing = pending_call;
488   internal_api.thing = pending_call;
489   unref_and_unlock_api.thing = pending_call;
490
491   if (!dbus_pending_call_set_data (pending_call, pending_call_slot, f,
492         last_unref))
493     g_error ("OOM");
494
495   for (i = 0; i < N_THREADS; i++)
496     {
497       if ((i % 2) == 0)
498         f->threads[i] = g_thread_new (NULL, ref_thread, &public_api);
499       else
500         f->threads[i] = g_thread_new (NULL, ref_thread, &internal_api);
501
502       g_assert (f->threads[i] != NULL);
503     }
504
505   wait_for_all_threads (f);
506
507   for (i = 0; i < N_THREADS; i++)
508     {
509       switch (i % 3)
510         {
511           case 0:
512             f->threads[i] = g_thread_new (NULL, cycle_thread, &public_api);
513             break;
514           case 1:
515             f->threads[i] = g_thread_new (NULL, cycle_thread, &internal_api);
516             break;
517           default:
518             f->threads[i] = g_thread_new (NULL, cycle_thread,
519                 &unref_and_unlock_api);
520         }
521
522       g_assert (f->threads[i] != NULL);
523     }
524
525   wait_for_all_threads (f);
526
527   for (i = 0; i < N_THREADS; i++)
528     {
529       switch (i % 3)
530         {
531           case 0:
532             f->threads[i] = g_thread_new (NULL, unref_thread, &public_api);
533             break;
534           case 1:
535             f->threads[i] = g_thread_new (NULL, unref_thread, &internal_api);
536             break;
537           default:
538             f->threads[i] = g_thread_new (NULL, unref_thread,
539                 &unref_and_unlock_api);
540         }
541
542       g_assert (f->threads[i] != NULL);
543     }
544
545   wait_for_all_threads (f);
546
547   /* Destroy the pending call. This should be the last-unref. */
548   g_assert (!f->last_unref);
549   dbus_pending_call_unref (pending_call);
550   g_assert (f->last_unref);
551 }
552
553 static void
554 teardown (Fixture *f,
555     gconstpointer data)
556 {
557   if (f->server_connection != NULL)
558     {
559       dbus_connection_close (f->server_connection);
560       dbus_connection_unref (f->server_connection);
561     }
562
563   if (f->connection != NULL)
564     {
565       dbus_connection_close (f->connection);
566       dbus_connection_unref (f->connection);
567     }
568
569   if (f->server != NULL)
570     {
571       dbus_server_disconnect (f->server);
572       dbus_server_unref (f->server);
573     }
574
575   dbus_connection_free_data_slot (&connection_slot);
576   dbus_server_free_data_slot (&server_slot);
577   dbus_message_free_data_slot (&message_slot);
578   dbus_pending_call_free_data_slot (&pending_call_slot);
579
580   _dbus_loop_unref (f->loop);
581   dbus_error_free (&f->e);
582 }
583
584 int
585 main (int argc,
586     char **argv)
587 {
588   /* In GLib >= 2.24, < 2.31 this acts like g_thread_init() but avoids
589    * the deprecation of that function. In GLib >= 2.32 this is not
590    * necessary at all.
591    */
592   g_type_init ();
593
594   g_test_init (&argc, &argv, NULL);
595   g_test_bug_base ("https://bugs.freedesktop.org/show_bug.cgi?id=");
596
597   g_test_add ("/refs/connection", Fixture, NULL, setup_connection,
598       test_connection, teardown);
599   g_test_add ("/refs/message", Fixture, NULL, setup,
600       test_message, teardown);
601   g_test_add ("/refs/pending-call", Fixture, NULL, setup_connection,
602       test_pending_call, teardown);
603   g_test_add ("/refs/server", Fixture, NULL, setup,
604       test_server, teardown);
605
606   return g_test_run ();
607 }