build: enable running autoreconf on tarballs
[dconf.git] / gdbus / dconf-gdbus-filter.c
blob79b2dd717a316ab0ae90df2073d527551bece212
1 #include "../engine/dconf-engine.h"
6 typedef struct
8 gpointer data; /* either GDBusConnection or GError */
9 guint is_error;
10 guint waiting_for_serial;
11 GQueue queue;
12 } ConnectionState;
14 typedef struct
16 guint32 serial;
17 DConfEngineCallHandle *handle;
18 } DConfGDBusCall;
20 static ConnectionState connections[3];
21 static GMutex dconf_gdbus_lock;
23 static GBusType
24 connection_state_get_bus_type (ConnectionState *state)
26 return state - connections;
29 static gboolean
30 connection_state_ensure_success (ConnectionState *state,
31 GError **error)
33 if (state->is_error)
35 if (error)
36 *error = g_error_copy (state->data);
38 return FALSE;
41 return TRUE;
44 static GDBusConnection *
45 connection_state_get_connection (ConnectionState *state)
47 g_assert (!state->is_error);
49 return state->data;
52 /* This function can be slow (as compared to the one below). */
53 static void
54 dconf_gdbus_handle_reply (ConnectionState *state,
55 GDBusMessage *message)
57 DConfEngineCallHandle *handle;
58 GError *error = NULL;
59 GVariant *body;
61 g_mutex_lock (&dconf_gdbus_lock);
63 DConfGDBusCall *call;
65 call = g_queue_pop_head (&state->queue);
66 g_assert_cmpuint (g_dbus_message_get_reply_serial (message), ==, call->serial);
67 handle = call->handle;
69 g_slice_free (DConfGDBusCall, call);
71 call = g_queue_peek_head (&state->queue);
72 if (call)
73 g_atomic_int_set (&state->waiting_for_serial, call->serial);
74 else
75 g_atomic_int_set (&state->waiting_for_serial, -1);
77 g_mutex_unlock (&dconf_gdbus_lock);
79 body = g_dbus_message_get_body (message);
81 if (g_dbus_message_get_message_type (message) == G_DBUS_MESSAGE_TYPE_ERROR)
83 const GVariantType *first_child_type;
84 const gchar *error_message = NULL;
86 first_child_type = g_variant_type_first (g_variant_get_type (body));
88 if (g_variant_type_equal (first_child_type, G_VARIANT_TYPE_STRING))
89 g_variant_get_child (body, 0, "&s", &error_message);
91 error = g_dbus_error_new_for_dbus_error (g_dbus_message_get_error_name (message), error_message);
92 body = NULL;
95 dconf_engine_call_handle_reply (handle, body, error);
97 if (error)
98 g_error_free (error);
101 /* We optimise for this function being super-efficient since it gets run
102 * on every single D-Bus message in or out.
104 * We want to bail out as quickly as possible in the case that this
105 * message does not interest us. That means we should not hold locks or
106 * anything like that.
108 * In the case that this message _does_ interest us (which should be
109 * rare) we can take a lot more time.
111 static GDBusMessage *
112 dconf_gdbus_filter_function (GDBusConnection *connection,
113 GDBusMessage *message,
114 gboolean incoming,
115 gpointer user_data)
117 ConnectionState *state = user_data;
119 if (incoming)
121 switch (g_dbus_message_get_message_type (message))
123 case G_DBUS_MESSAGE_TYPE_SIGNAL:
125 const gchar *interface;
127 interface = g_dbus_message_get_interface (message);
128 if (interface && g_str_equal (interface, "ca.desrt.dconf.Writer"))
129 dconf_engine_handle_dbus_signal (connection_state_get_bus_type (state),
130 g_dbus_message_get_sender (message),
131 g_dbus_message_get_path (message),
132 g_dbus_message_get_member (message),
133 g_dbus_message_get_body (message));
135 /* Others could theoretically be interested in this... */
137 break;
139 case G_DBUS_MESSAGE_TYPE_METHOD_RETURN:
140 case G_DBUS_MESSAGE_TYPE_ERROR:
141 if G_UNLIKELY (g_dbus_message_get_reply_serial (message) == g_atomic_int_get (&state->waiting_for_serial))
143 /* This is definitely for us. */
144 dconf_gdbus_handle_reply (state, message);
146 /* Nobody else should be interested in it. */
147 g_clear_object (&message);
149 break;
151 default:
152 break;
156 return message;
159 static ConnectionState *
160 dconf_gdbus_get_connection_state (GBusType bus_type,
161 GError **error)
163 ConnectionState *state;
165 g_assert (bus_type < G_N_ELEMENTS (connections));
167 state = &connections[bus_type];
169 if (g_once_init_enter (&state->data))
171 GDBusConnection *connection;
172 GError *error = NULL;
173 gpointer result;
175 /* This will only block the first time...
177 * Optimising this away is probably not worth the effort.
179 connection = g_bus_get_sync (bus_type, NULL, &error);
181 if (connection)
183 g_dbus_connection_add_filter (connection, dconf_gdbus_filter_function, state, NULL);
184 result = connection;
185 state->is_error = FALSE;
187 else
189 result = error;
190 state->is_error = TRUE;
193 g_once_init_leave (&state->data, result);
196 if (!connection_state_ensure_success (state, error))
197 return FALSE;
199 return state;
202 gboolean
203 dconf_engine_dbus_call_async_func (GBusType bus_type,
204 const gchar *bus_name,
205 const gchar *object_path,
206 const gchar *interface_name,
207 const gchar *method_name,
208 GVariant *parameters,
209 DConfEngineCallHandle *handle,
210 GError **error)
212 ConnectionState *state;
213 GDBusMessage *message;
214 DConfGDBusCall *call;
215 gboolean success;
217 state = dconf_gdbus_get_connection_state (bus_type, error);
219 if (state == NULL)
221 g_variant_unref (g_variant_ref_sink (parameters));
222 return FALSE;
225 message = g_dbus_message_new_method_call (bus_name, object_path, interface_name, method_name);
226 g_dbus_message_set_body (message, parameters);
228 g_mutex_lock (&dconf_gdbus_lock);
230 volatile guint *serial_ptr;
231 guint my_serial;
233 /* We need to set the serial in call->serial. Sometimes we also
234 * need to set it in state->waiting_for_serial (in the case that no
235 * other items are queued yet).
237 * g_dbus_connection_send_message() only has one out_serial parameter
238 * so we can only set one of them atomically. If needed, we elect
239 * to set the waiting_for_serial because that is the one that is
240 * accessed from the filter function without holding the lock.
242 * The serial number in the call structure is only accessed after the
243 * lock is acquired which allows us to take our time setting it (for
244 * as long as we're still holding the lock).
246 * In the case that waiting_for_serial should not be set we just use
247 * a local variable and use that to fill call->serial.
249 * Also: the queue itself isn't accessed until after the lock is
250 * taken, so we can delay adding the call to the queue until we know
251 * that the sending of the message was successful.
254 if (g_queue_is_empty (&state->queue))
255 serial_ptr = &state->waiting_for_serial;
256 else
257 serial_ptr = &my_serial;
259 success = g_dbus_connection_send_message (connection_state_get_connection (state), message,
260 G_DBUS_SEND_MESSAGE_FLAGS_NONE, serial_ptr, error);
262 if (success)
264 call = g_slice_new (DConfGDBusCall);
266 call->handle = handle;
267 call->serial = *serial_ptr;
269 g_queue_push_tail (&state->queue, call);
272 g_mutex_unlock (&dconf_gdbus_lock);
274 g_object_unref (message);
276 return success;
279 GVariant *
280 dconf_engine_dbus_call_sync_func (GBusType bus_type,
281 const gchar *bus_name,
282 const gchar *object_path,
283 const gchar *interface_name,
284 const gchar *method_name,
285 GVariant *parameters,
286 const GVariantType *reply_type,
287 GError **error)
289 ConnectionState *state;
291 state = dconf_gdbus_get_connection_state (bus_type, error);
293 if (state == NULL)
295 g_variant_unref (g_variant_ref_sink (parameters));
297 return NULL;
300 return g_dbus_connection_call_sync (connection_state_get_connection (state),
301 bus_name, object_path, interface_name, method_name, parameters, reply_type,
302 G_DBUS_CALL_FLAGS_NONE, -1, NULL, error);
305 #ifndef PIC
306 void
307 dconf_engine_dbus_init_for_testing (void)
310 #endif