Declare libdconf_service as a dependency
[dconf.git] / gdbus / dconf-gdbus-thread.c
blob8b8f0482a67e19f71795fe16b98df0b00a3cb1ff
1 /*
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>
21 #include "config.h"
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).
62 typedef struct
64 GBusType bus_type;
65 const gchar *bus_name;
66 const gchar *object_path;
67 const gchar *interface_name;
68 const gchar *method_name;
69 GVariant *parameters;
70 const GVariantType *expected_type;
71 DConfEngineCallHandle *handle;
72 } DConfGDBusCall;
74 static gpointer
75 dconf_gdbus_worker_thread (gpointer user_data)
77 GMainContext *context = user_data;
79 g_main_context_push_thread_default (context);
81 for (;;)
82 g_main_context_iteration (context, TRUE);
84 /* srsly, gcc? */
85 return NULL;
88 static GMainContext *
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;
128 static void
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,
135 gpointer user_data)
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
154 * simple affair.
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])
184 if (error)
185 *error = dconf_gdbus_get_bus_data[bus_type];
187 return NULL;
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;
206 gpointer result;
208 connection = g_bus_get_sync (bus_type, NULL, &error);
210 if (connection)
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;
216 result = connection;
218 else
220 dconf_gdbus_get_bus_is_error[bus_type] = TRUE;
221 result = error;
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);
243 static void
244 dconf_gdbus_method_call_done (GObject *source,
245 GAsyncResult *result,
246 gpointer user_data)
248 GDBusConnection *connection = G_DBUS_CONNECTION (source);
249 DConfEngineCallHandle *handle = user_data;
250 GError *error = NULL;
251 GVariant *reply;
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);
259 static gboolean
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);
268 if (connection)
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);
273 else
274 dconf_engine_call_handle_reply (call->handle, NULL, error);
276 g_variant_unref (call->parameters);
277 g_slice_free (DConfGDBusCall, call);
279 return FALSE;
282 gboolean
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,
290 GError **error)
292 DConfGDBusCall *call;
293 GSource *source;
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);
310 return TRUE;
313 /* Dummy function to force the bus into existence in the worker. */
314 static gboolean
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
331 * play it safe.
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);
351 GVariant *
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,
359 GError **error)
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));
370 if (error)
371 *error = g_error_copy (inner_error);
373 return NULL;
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);
380 #ifndef PIC
381 void
382 dconf_engine_dbus_init_for_testing (void)
385 #endif