2 * Copyright © 2010 Codethink Limited
3 * Copyright © 2012 Canonical Limited
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the licence, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 * Author: Ryan Lortie <desrt@desrt.ca>
23 #include "../engine/dconf-engine.h"
25 /* We interact with GDBus using a worker thread just for dconf.
27 * We want to have a worker thread that's not the main thread for one
28 * main reason: we don't want to have all of our incoming signals and
29 * method call replies being delivered via the default main context
30 * (which may blocked or simply not running at all).
32 * The only question is if we should have our own thread or share the
33 * GDBus worker thread. This file takes the approach that we should
34 * have our own thread. See "dconf-gdbus-filter.c" for an approach that
35 * shares the worker thread with GDBus.
37 * We gain at least one advantage here that we cannot gain any other way
38 * (including sharing a worker thread with GDBus): fast startup.
40 * The first thing that happens when GSettings comes online is a D-Bus
41 * call to establish a watch. We have to bring up the GDBusConnection.
42 * There are two ways to do that: sync and async.
44 * We can't do either of those in GDBus's worker thread (since it
45 * doesn't exist yet). We can't do async in the main thread because the
46 * user may not be running the mainloop (as is the case for the
47 * commandline tool, for example).
49 * That leaves only one option: synchronous initialisation in the main
50 * thread. That's what the "dconf-gdbus-filter" variant of this code
51 * does, and it's slower because of it.
53 * If we have our own worker thread then we can punt synchronous
54 * initialisation of the bus to it and return immediately.
56 * We also gain the advantage that the dconf worker thread and the GDBus
57 * worker thread can both be doing work at the same time. This
58 * advantage is probably quite marginal (and is likely outweighed by the
59 * cost of all the punting around of messages between threads).
65 const gchar
*bus_name
;
66 const gchar
*object_path
;
67 const gchar
*interface_name
;
68 const gchar
*method_name
;
70 const GVariantType
*expected_type
;
71 DConfEngineCallHandle
*handle
;
75 dconf_gdbus_worker_thread (gpointer user_data
)
77 GMainContext
*context
= user_data
;
79 g_main_context_push_thread_default (context
);
82 g_main_context_iteration (context
, TRUE
);
89 dconf_gdbus_get_worker_context (void)
91 static GMainContext
*worker_context
;
93 if (g_once_init_enter (&worker_context
))
95 GMainContext
*context
;
97 /* Work around https://bugzilla.gnome.org/show_bug.cgi?id=674885
99 * This set of types is the same as the set in
100 * glib/gio/gdbusprivate.c:ensure_required_types(). That workaround
101 * is ineffective for us since we're already in the worker thread when
102 * we call g_bus_get_sync() and ensure_required_types() runs. So we do
103 * something similar here before launching the worker thread. Calling
104 * g_bus_get_sync() here would also be possible, but potentially would
105 * cause significant startup latency for every dconf user.
107 g_type_ensure (G_TYPE_TASK
);
108 g_type_ensure (G_TYPE_MEMORY_INPUT_STREAM
);
109 g_type_ensure (G_TYPE_DBUS_CONNECTION_FLAGS
);
110 g_type_ensure (G_TYPE_DBUS_CAPABILITY_FLAGS
);
111 g_type_ensure (G_TYPE_DBUS_AUTH_OBSERVER
);
112 g_type_ensure (G_TYPE_DBUS_CONNECTION
);
113 g_type_ensure (G_TYPE_DBUS_PROXY
);
114 g_type_ensure (G_TYPE_SOCKET_FAMILY
);
115 g_type_ensure (G_TYPE_SOCKET_TYPE
);
116 g_type_ensure (G_TYPE_SOCKET_PROTOCOL
);
117 g_type_ensure (G_TYPE_SOCKET_ADDRESS
);
118 g_type_ensure (G_TYPE_SOCKET
);
120 context
= g_main_context_new ();
121 g_thread_new ("dconf worker", dconf_gdbus_worker_thread
, context
);
122 g_once_init_leave (&worker_context
, context
);
125 return worker_context
;
129 dconf_gdbus_signal_handler (GDBusConnection
*connection
,
130 const gchar
*sender_name
,
131 const gchar
*object_path
,
132 const gchar
*interface_name
,
133 const gchar
*signal_name
,
134 GVariant
*parameters
,
137 GBusType bus_type
= GPOINTER_TO_INT (user_data
);
139 dconf_engine_handle_dbus_signal (bus_type
, sender_name
, object_path
, signal_name
, parameters
);
142 /* The code to create and initialise the GDBusConnection for a
143 * particular bus type is more complicated than it should be.
145 * The complication comes from the fact that we must call
146 * g_dbus_connection_signal_subscribe() from the thread in which the
147 * signal handler will run (which in our case is the worker thread).
148 * g_main_context_push_thread_default() attempts to acquire the context,
149 * preventing us from temporarily pushing the worker's context just for
150 * the sake of setting up the subscription.
152 * We therefore always create the bus connection from the worker thread.
153 * For requests that are already in the worker thread this is a pretty
156 * For requests in other threads (ie: synchronous calls) we have to poke
157 * the worker to instantiate the bus for us (if it doesn't already
158 * exist). We do that by using g_main_context_invoke() to schedule a
159 * dummy request in the worker and then we wait on a GCond until we see
160 * that the bus has been created.
162 * An attempt to get a particular bus can go one of two ways:
164 * - success: we end up with a GDBusConnection.
166 * - failure: we end up with a GError.
168 * One way or another we put the result in dconf_gdbus_get_bus_data[] so
169 * that we only have one pointer value to check. We know what type of
170 * result it is by dconf_gdbus_get_bus_is_error[].
173 static GMutex dconf_gdbus_get_bus_lock
;
174 static GCond dconf_gdbus_get_bus_cond
;
175 static gpointer dconf_gdbus_get_bus_data
[5];
176 static gboolean dconf_gdbus_get_bus_is_error
[5];
178 static GDBusConnection
*
179 dconf_gdbus_get_bus_common (GBusType bus_type
,
180 const GError
**error
)
182 if (dconf_gdbus_get_bus_is_error
[bus_type
])
185 *error
= dconf_gdbus_get_bus_data
[bus_type
];
190 return dconf_gdbus_get_bus_data
[bus_type
];
193 static GDBusConnection
*
194 dconf_gdbus_get_bus_in_worker (GBusType bus_type
,
195 const GError
**error
)
197 g_assert_cmpint (bus_type
, <, G_N_ELEMENTS (dconf_gdbus_get_bus_data
));
199 /* We're in the worker thread and only the worker thread can ever set
200 * this variable so there is no need to take a lock.
202 if (dconf_gdbus_get_bus_data
[bus_type
] == NULL
)
204 GDBusConnection
*connection
;
205 GError
*error
= NULL
;
208 connection
= g_bus_get_sync (bus_type
, NULL
, &error
);
212 g_dbus_connection_signal_subscribe (connection
, NULL
, "ca.desrt.dconf.Writer",
213 NULL
, NULL
, NULL
, G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE
,
214 dconf_gdbus_signal_handler
, GINT_TO_POINTER (bus_type
), NULL
);
215 dconf_gdbus_get_bus_is_error
[bus_type
] = FALSE
;
220 dconf_gdbus_get_bus_is_error
[bus_type
] = TRUE
;
224 g_assert (result
!= NULL
);
226 /* It's possible that another thread was waiting for us to do
227 * this on its behalf. Wake it up.
229 * The other thread cannot actually wake up until we release the
230 * mutex below so we have a guarantee that this CPU will have
231 * flushed all outstanding writes. The other CPU has to acquire
232 * the lock so it cannot have done any out-of-order reads either.
234 g_mutex_lock (&dconf_gdbus_get_bus_lock
);
235 dconf_gdbus_get_bus_data
[bus_type
] = result
;
236 g_cond_broadcast (&dconf_gdbus_get_bus_cond
);
237 g_mutex_unlock (&dconf_gdbus_get_bus_lock
);
240 return dconf_gdbus_get_bus_common (bus_type
, error
);
244 dconf_gdbus_method_call_done (GObject
*source
,
245 GAsyncResult
*result
,
248 GDBusConnection
*connection
= G_DBUS_CONNECTION (source
);
249 DConfEngineCallHandle
*handle
= user_data
;
250 GError
*error
= NULL
;
253 reply
= g_dbus_connection_call_finish (connection
, result
, &error
);
254 dconf_engine_call_handle_reply (handle
, reply
, error
);
255 g_clear_pointer (&reply
, g_variant_unref
);
256 g_clear_error (&error
);
260 dconf_gdbus_method_call (gpointer user_data
)
262 DConfGDBusCall
*call
= user_data
;
263 GDBusConnection
*connection
;
264 const GError
*error
= NULL
;
266 connection
= dconf_gdbus_get_bus_in_worker (call
->bus_type
, &error
);
269 g_dbus_connection_call (connection
, call
->bus_name
, call
->object_path
, call
->interface_name
,
270 call
->method_name
, call
->parameters
, call
->expected_type
, G_DBUS_CALL_FLAGS_NONE
,
271 -1, NULL
, dconf_gdbus_method_call_done
, call
->handle
);
274 dconf_engine_call_handle_reply (call
->handle
, NULL
, error
);
276 g_variant_unref (call
->parameters
);
277 g_slice_free (DConfGDBusCall
, call
);
283 dconf_engine_dbus_call_async_func (GBusType bus_type
,
284 const gchar
*bus_name
,
285 const gchar
*object_path
,
286 const gchar
*interface_name
,
287 const gchar
*method_name
,
288 GVariant
*parameters
,
289 DConfEngineCallHandle
*handle
,
292 DConfGDBusCall
*call
;
295 call
= g_slice_new (DConfGDBusCall
);
296 call
->bus_type
= bus_type
;
297 call
->bus_name
= bus_name
;
298 call
->object_path
= object_path
;
299 call
->interface_name
= interface_name
;
300 call
->method_name
= method_name
;
301 call
->parameters
= g_variant_ref_sink (parameters
);
302 call
->expected_type
= dconf_engine_call_handle_get_expected_type (handle
);
303 call
->handle
= handle
;
305 source
= g_idle_source_new ();
306 g_source_set_callback (source
, dconf_gdbus_method_call
, call
, NULL
);
307 g_source_attach (source
, dconf_gdbus_get_worker_context ());
308 g_source_unref (source
);
313 /* Dummy function to force the bus into existence in the worker. */
315 dconf_gdbus_summon_bus (gpointer user_data
)
317 GBusType bus_type
= GPOINTER_TO_INT (user_data
);
319 dconf_gdbus_get_bus_in_worker (bus_type
, NULL
);
321 return G_SOURCE_REMOVE
;
324 static GDBusConnection
*
325 dconf_gdbus_get_bus_for_sync (GBusType bus_type
,
326 const GError
**error
)
328 g_assert_cmpint (bus_type
, <, G_N_ELEMENTS (dconf_gdbus_get_bus_data
));
330 /* I'm not 100% sure we have to lock as much as we do here, but let's
333 * This codepath is only hit on synchronous calls anyway. You're
334 * probably not doing those if you care a lot about performance.
336 g_mutex_lock (&dconf_gdbus_get_bus_lock
);
337 if (dconf_gdbus_get_bus_data
[bus_type
] == NULL
)
339 g_main_context_invoke (dconf_gdbus_get_worker_context (),
340 dconf_gdbus_summon_bus
,
341 GINT_TO_POINTER (bus_type
));
343 while (dconf_gdbus_get_bus_data
[bus_type
] == NULL
)
344 g_cond_wait (&dconf_gdbus_get_bus_cond
, &dconf_gdbus_get_bus_lock
);
346 g_mutex_unlock (&dconf_gdbus_get_bus_lock
);
348 return dconf_gdbus_get_bus_common (bus_type
, error
);
352 dconf_engine_dbus_call_sync_func (GBusType bus_type
,
353 const gchar
*bus_name
,
354 const gchar
*object_path
,
355 const gchar
*interface_name
,
356 const gchar
*method_name
,
357 GVariant
*parameters
,
358 const GVariantType
*reply_type
,
361 const GError
*inner_error
= NULL
;
362 GDBusConnection
*connection
;
364 connection
= dconf_gdbus_get_bus_for_sync (bus_type
, &inner_error
);
366 if (connection
== NULL
)
368 g_variant_unref (g_variant_ref_sink (parameters
));
371 *error
= g_error_copy (inner_error
);
376 return g_dbus_connection_call_sync (connection
, bus_name
, object_path
, interface_name
, method_name
,
377 parameters
, reply_type
, G_DBUS_CALL_FLAGS_NONE
, -1, NULL
, error
);
382 dconf_engine_dbus_init_for_testing (void)