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