1 #include "../engine/dconf-engine.h"
8 gpointer data
; /* either GDBusConnection or GError */
10 guint waiting_for_serial
;
17 DConfEngineCallHandle
*handle
;
20 static ConnectionState connections
[3];
21 static GMutex dconf_gdbus_lock
;
24 connection_state_get_bus_type (ConnectionState
*state
)
26 return state
- connections
;
30 connection_state_ensure_success (ConnectionState
*state
,
36 *error
= g_error_copy (state
->data
);
44 static GDBusConnection
*
45 connection_state_get_connection (ConnectionState
*state
)
47 g_assert (!state
->is_error
);
52 /* This function can be slow (as compared to the one below). */
54 dconf_gdbus_handle_reply (ConnectionState
*state
,
55 GDBusMessage
*message
)
57 DConfEngineCallHandle
*handle
;
61 g_mutex_lock (&dconf_gdbus_lock
);
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
);
73 g_atomic_int_set (&state
->waiting_for_serial
, call
->serial
);
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
);
95 dconf_engine_call_handle_reply (handle
, body
, 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
,
117 ConnectionState
*state
= user_data
;
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... */
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
);
159 static ConnectionState
*
160 dconf_gdbus_get_connection_state (GBusType bus_type
,
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
;
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
);
183 g_dbus_connection_add_filter (connection
, dconf_gdbus_filter_function
, state
, NULL
);
185 state
->is_error
= FALSE
;
190 state
->is_error
= TRUE
;
193 g_once_init_leave (&state
->data
, result
);
196 if (!connection_state_ensure_success (state
, error
))
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
,
212 ConnectionState
*state
;
213 GDBusMessage
*message
;
214 DConfGDBusCall
*call
;
217 state
= dconf_gdbus_get_connection_state (bus_type
, error
);
221 g_variant_unref (g_variant_ref_sink (parameters
));
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
;
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
;
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
);
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
);
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
,
289 ConnectionState
*state
;
291 state
= dconf_gdbus_get_connection_state (bus_type
, error
);
295 g_variant_unref (g_variant_ref_sink (parameters
));
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
);
307 dconf_engine_dbus_init_for_testing (void)