1/* Test case for GNOME #651133
2 *
3 * Copyright (C) 2008-2010 Red Hat, Inc.
4 * Copyright (C) 2011 Nokia Corporation
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General
17 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 *
19 * Author: Simon McVittie <simon.mcvittie@collabora.co.uk>
20 */
21
22#include <config.h>
23
24#include <unistd.h>
25#include <string.h>
26
27#include <gio/gio.h>
28
29#include "gdbus-tests.h"
30
31#ifdef HAVE_DBUS1
32# include <dbus/dbus-shared.h>
33#else
34# define DBUS_INTERFACE_DBUS "org.freedesktop.DBus"
35# define DBUS_PATH_DBUS "/org/freedesktop/DBus"
36# define DBUS_SERVICE_DBUS "org.freedesktop.DBus"
37# define DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER 1
38# define DBUS_RELEASE_NAME_REPLY_RELEASED 1
39#endif
40
41#define MY_NAME "com.example.Test.Myself"
42/* This many threads create and destroy GDBusProxy instances, in addition
43 * to the main thread processing their NameOwnerChanged signals.
44 * N_THREADS_MAX is used with "-m slow", N_THREADS otherwise.
45 */
46#define N_THREADS_MAX 10
47#define N_THREADS 2
48/* This many GDBusProxy instances are created by each thread. */
49#define N_REPEATS 100
50/* The main thread requests/releases a name this many times as rapidly as
51 * possible, before performing one "slow" cycle that waits for each method
52 * call result (and therefore, due to D-Bus total ordering, all previous
53 * method calls) to prevent requests from piling up infinitely. The more calls
54 * are made rapidly, the better we reproduce bugs.
55 */
56#define N_RAPID_CYCLES 50
57
58static GMainLoop *loop;
59
60static gpointer
61run_proxy_thread (gpointer data)
62{
63 GDBusConnection *connection = data;
64 int i;
65
66 g_assert (g_main_context_get_thread_default () == NULL);
67
68 for (i = 0; i < N_REPEATS; i++)
69 {
70 GDBusProxy *proxy;
71 GError *error = NULL;
72 GVariant *ret;
73
74 if (g_test_verbose ())
75 g_printerr (format: ".");
76
77 proxy = g_dbus_proxy_new_sync (connection,
78 flags: G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START |
79 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
80 NULL,
81 MY_NAME,
82 object_path: "/com/example/TestObject",
83 interface_name: "com.example.Frob",
84 NULL,
85 error: &error);
86 g_assert_no_error (error);
87 g_assert (proxy != NULL);
88 g_dbus_proxy_set_default_timeout (proxy, G_MAXINT);
89
90 ret = g_dbus_proxy_call_sync (proxy, method_name: "StupidMethod", NULL,
91 flags: G_DBUS_CALL_FLAGS_NO_AUTO_START, timeout_msec: -1,
92 NULL, NULL);
93 /*
94 * we expect this to fail - if we have the name at the moment, we called
95 * an unimplemented method, and if not, there was nothing to call
96 */
97 g_assert (ret == NULL);
98
99 /*
100 * this races with the NameOwnerChanged signal being emitted in an
101 * idle
102 */
103 g_object_unref (object: proxy);
104 }
105
106 g_main_loop_quit (loop);
107 return NULL;
108}
109
110static void release_name (GDBusConnection *connection, gboolean wait);
111
112static void
113request_name_cb (GObject *source,
114 GAsyncResult *res,
115 gpointer user_data)
116{
117 GDBusConnection *connection = G_DBUS_CONNECTION (source);
118 GError *error = NULL;
119 GVariant *var;
120
121 var = g_dbus_connection_call_finish (connection, res, error: &error);
122 g_assert_no_error (error);
123 g_assert_cmpuint (g_variant_get_uint32 (g_variant_get_child_value (var, 0)),
124 ==, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER);
125
126 release_name (connection, TRUE);
127}
128
129static void
130request_name (GDBusConnection *connection,
131 gboolean wait)
132{
133 g_dbus_connection_call (connection,
134 DBUS_SERVICE_DBUS,
135 DBUS_PATH_DBUS,
136 DBUS_INTERFACE_DBUS,
137 method_name: "RequestName",
138 parameters: g_variant_new (format_string: "(su)", MY_NAME, 0),
139 G_VARIANT_TYPE ("(u)"),
140 flags: G_DBUS_CALL_FLAGS_NONE,
141 timeout_msec: -1,
142 NULL,
143 callback: wait ? request_name_cb : NULL,
144 NULL);
145}
146
147static void
148release_name_cb (GObject *source,
149 GAsyncResult *res,
150 gpointer user_data)
151{
152 GDBusConnection *connection = G_DBUS_CONNECTION (source);
153 GError *error = NULL;
154 GVariant *var;
155 int i;
156
157 var = g_dbus_connection_call_finish (connection, res, error: &error);
158 g_assert_no_error (error);
159 g_assert_cmpuint (g_variant_get_uint32 (g_variant_get_child_value (var, 0)),
160 ==, DBUS_RELEASE_NAME_REPLY_RELEASED);
161
162 /* generate some rapid NameOwnerChanged signals to try to trigger crashes */
163 for (i = 0; i < N_RAPID_CYCLES; i++)
164 {
165 request_name (connection, FALSE);
166 release_name (connection, FALSE);
167 }
168
169 /* wait for dbus-daemon to catch up */
170 request_name (connection, TRUE);
171}
172
173static void
174release_name (GDBusConnection *connection,
175 gboolean wait)
176{
177 g_dbus_connection_call (connection,
178 DBUS_SERVICE_DBUS,
179 DBUS_PATH_DBUS,
180 DBUS_INTERFACE_DBUS,
181 method_name: "ReleaseName",
182 parameters: g_variant_new (format_string: "(s)", MY_NAME),
183 G_VARIANT_TYPE ("(u)"),
184 flags: G_DBUS_CALL_FLAGS_NONE,
185 timeout_msec: -1,
186 NULL,
187 callback: wait ? release_name_cb : NULL,
188 NULL);
189}
190
191static void
192test_proxy (void)
193{
194 GDBusConnection *connection;
195 GError *error = NULL;
196 GThread *proxy_threads[N_THREADS_MAX];
197 int i;
198 int n_threads;
199
200 if (g_test_slow ())
201 n_threads = N_THREADS_MAX;
202 else
203 n_threads = N_THREADS;
204
205 session_bus_up ();
206
207 loop = g_main_loop_new (NULL, TRUE);
208
209 connection = g_bus_get_sync (bus_type: G_BUS_TYPE_SESSION,
210 NULL,
211 error: &error);
212 g_assert_no_error (error);
213
214 request_name (connection, TRUE);
215
216 for (i = 0; i < n_threads; i++)
217 {
218 proxy_threads[i] = g_thread_new (name: "run-proxy",
219 func: run_proxy_thread, data: connection);
220 }
221
222 g_main_loop_run (loop);
223
224 for (i = 0; i < n_threads; i++)
225 {
226 g_thread_join (thread: proxy_threads[i]);
227 }
228
229 g_object_unref (object: connection);
230 g_main_loop_unref (loop);
231
232 /* TODO: should call session_bus_down() but that requires waiting
233 * for all the outstanding method calls to complete...
234 */
235 if (g_test_verbose ())
236 g_printerr (format: "\n");
237}
238
239int
240main (int argc,
241 char *argv[])
242{
243 g_test_init (argc: &argc, argv: &argv, NULL);
244
245 g_test_dbus_unset ();
246
247 g_test_add_func (testpath: "/gdbus/proxy/vs-threads", test_func: test_proxy);
248
249 return g_test_run();
250}
251

source code of gtk/subprojects/glib/gio/tests/gdbus-proxy-threads.c