summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
authorAlexander Larsson <alexl@src.gnome.org>2007-09-13 11:34:59 +0000
committerAlexander Larsson <alexl@src.gnome.org>2007-09-13 11:34:59 +0000
commit491f275396cb079cccaec68fc6791864a4b66ef9 (patch)
tree82936c24b9f648093783f9880573dde49118da75 /common
parent58769adda1207b1f250d1ee09c099acd1c2a3494 (diff)
downloadgvfs-491f275396cb079cccaec68fc6791864a4b66ef9.tar.gz
Work around race condition in dbus
Original git commit by Alexander Larsson <alex@greebo.(none)> at 1171041373 +0100 svn path=/trunk/; revision=312
Diffstat (limited to 'common')
-rw-r--r--common/gdbusutils.c94
1 files changed, 73 insertions, 21 deletions
diff --git a/common/gdbusutils.c b/common/gdbusutils.c
index eff51d26..a1fbee05 100644
--- a/common/gdbusutils.c
+++ b/common/gdbusutils.c
@@ -1162,47 +1162,81 @@ _g_dbus_message_iter_copy (DBusMessageIter *dest,
}
-static dbus_int32_t async_call_slot = -1;
+typedef struct {
+ GAsyncDBusCallback callback;
+ gpointer user_data;
+
+ /* protected by async_call lock: */
+ gboolean ran; /* the pending_call reply handler ran */
+ gboolean idle; /* we queued an idle */
+
+ /* only used for idle */
+ DBusPendingCall *pending;
+} AsyncDBusCallData;
+
+/* Lock to protect the data for working around racecondition
+ between send_with_reply and pending_set_notify */
+G_LOCK_DEFINE_STATIC(async_call);
static void
-async_call_reply (DBusPendingCall *pending,
- void *user_data)
+handle_async_reply (DBusPendingCall *pending,
+ AsyncDBusCallData *data)
{
DBusMessage *reply;
GError *error;
- GAsyncDBusCallback callback;
+
+ g_print ("handle_async_reply\n");
reply = dbus_pending_call_steal_reply (pending);
- callback = dbus_pending_call_get_data (pending, async_call_slot);
- dbus_pending_call_unref (pending);
error = NULL;
if (_g_error_from_message (reply, &error))
{
- callback (NULL, error, user_data);
+ data->callback (NULL, error, data->user_data);
g_error_free (error);
}
else
- callback (reply, NULL, user_data);
-
+ data->callback (reply, NULL, data->user_data);
+
dbus_message_unref (reply);
}
+static void
+async_call_reply (DBusPendingCall *pending,
+ void *_data)
+{
+ AsyncDBusCallData *data = _data;
+
+ G_LOCK (async_call);
+ if (data->idle)
+ return;
+ data->ran = TRUE;
+ G_UNLOCK (async_call);
+
+ handle_async_reply (pending, data);
+}
+
+static gboolean
+idle_async_callback (void *_data)
+{
+ AsyncDBusCallData *data = _data;
+ handle_async_reply (data->pending, data);
+ dbus_pending_call_unref (data->pending);
+ return FALSE;
+}
+
void
_g_dbus_connection_call_async (DBusConnection *connection,
DBusMessage *message,
GAsyncDBusCallback callback,
gpointer user_data)
{
+ AsyncDBusCallData *data;
DBusPendingCall *pending_call;
DBusError derror;
-
- if (async_call_slot == -1)
- {
- if (!dbus_pending_call_allocate_data_slot (&async_call_slot))
- _g_dbus_oom ();
- }
+ g_print ("_g_dbus_connection_call_async\n");
+
if (connection == NULL)
{
dbus_error_init (&derror);
@@ -1232,14 +1266,32 @@ _g_dbus_connection_call_async (DBusConnection *connection,
return;
}
- if (!dbus_pending_call_set_data (pending_call,
- async_call_slot,
- callback, NULL))
- _g_dbus_oom ();
-
+ data = g_new0 (AsyncDBusCallData, 1);
+ data->callback = callback;
+ data->user_data = user_data;
+
+ g_print ("set_notify %p\n", pending_call);
if (!dbus_pending_call_set_notify (pending_call,
async_call_reply,
- user_data, NULL))
+ data, g_free))
_g_dbus_oom ();
+
+
+ /* All this is required to work around a race condition between
+ * send_with_reply and pending_call_set_notify :/
+ */
+ G_LOCK (async_call);
+
+ if (dbus_pending_call_get_completed (pending_call) &&
+ !data->ran)
+ {
+ data->idle = TRUE;
+ data->pending = dbus_pending_call_ref (pending_call);
+ g_idle_add (idle_async_callback, data);
+ }
+
+ G_UNLOCK (async_call);
+
+ dbus_pending_call_unref (pending_call);
}