summaryrefslogtreecommitdiffstats
path: root/gdbus/watch.c
diff options
context:
space:
mode:
authorLuiz Augusto von Dentz <luiz.dentz@openbossa.org>2009-05-06 17:15:27 -0300
committerMarcel Holtmann <marcel@holtmann.org>2009-05-06 13:55:09 -0700
commit5106f7a8cfba0dd89f57f8f9f5fd9261887873ab (patch)
tree000c04e523a4de09942e0ecedaa1391623614443 /gdbus/watch.c
parentd2e73f2d305c02c99789644f41d7d6aab5689f52 (diff)
downloadofono-5106f7a8cfba0dd89f57f8f9f5fd9261887873ab.tar.bz2
Fix crash when calling g_dbus_remove_watch from watch callback
Diffstat (limited to 'gdbus/watch.c')
-rw-r--r--gdbus/watch.c51
1 files changed, 41 insertions, 10 deletions
diff --git a/gdbus/watch.c b/gdbus/watch.c
index 7d7853fc..c7a4e691 100644
--- a/gdbus/watch.c
+++ b/gdbus/watch.c
@@ -54,6 +54,8 @@ struct name_data {
DBusConnection *connection;
char *name;
GSList *callbacks;
+ GSList *processed;
+ gboolean lock;
};
static struct name_data *name_data_find(DBusConnection *connection,
@@ -146,7 +148,11 @@ static int name_data_add(DBusConnection *connection, const char *name,
name_listeners = g_slist_append(name_listeners, data);
done:
- data->callbacks = g_slist_append(data->callbacks, cb);
+ if (data->lock)
+ data->processed = g_slist_append(data->processed, cb);
+ else
+ data->callbacks = g_slist_append(data->callbacks, cb);
+
return first;
}
@@ -229,10 +235,9 @@ static gboolean remove_match(DBusConnection *connection, const char *name)
static DBusHandlerResult name_exit_filter(DBusConnection *connection,
DBusMessage *message, void *user_data)
{
- GSList *l;
struct name_data *data;
+ struct name_callback *cb;
char *name, *old, *new;
- int keep = 0;
if (!dbus_message_is_signal(message, DBUS_INTERFACE_DBUS,
"NameOwnerChanged"))
@@ -253,8 +258,11 @@ static DBusHandlerResult name_exit_filter(DBusConnection *connection,
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
- for (l = data->callbacks; l != NULL; l = l->next) {
- struct name_callback *cb = l->data;
+ data->lock = TRUE;
+
+ while (data->callbacks) {
+ cb = data->callbacks->data;
+
if (*new == '\0') {
if (cb->disc_func)
cb->disc_func(connection, cb->user_data);
@@ -262,11 +270,27 @@ static DBusHandlerResult name_exit_filter(DBusConnection *connection,
if (cb->conn_func)
cb->conn_func(connection, cb->user_data);
}
- if (cb->conn_func && cb->disc_func)
- keep = 1;
+
+ /* Check if the watch was removed/freed by the callback
+ * function */
+ if (!g_slist_find(data->callbacks, cb))
+ continue;
+
+ data->callbacks = g_slist_remove(data->callbacks, cb);
+
+ if (!cb->conn_func || !cb->disc_func) {
+ g_free(cb);
+ continue;
+ }
+
+ data->processed = g_slist_append(data->processed, cb);
}
- if (keep)
+ data->callbacks = data->processed;
+ data->processed = NULL;
+ data->lock = FALSE;
+
+ if (data->callbacks)
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
name_listeners = g_slist_remove(name_listeners, data);
@@ -349,16 +373,23 @@ gboolean g_dbus_remove_watch(DBusConnection *connection, guint id)
if (cb->id == id)
goto remove;
}
+ for (lcb = data->processed; lcb; lcb = lcb->next) {
+ cb = lcb->data;
+ if (cb->id == id)
+ goto remove;
+ }
}
return FALSE;
remove:
data->callbacks = g_slist_remove(data->callbacks, cb);
+ data->processed = g_slist_remove(data->processed, cb);
g_free(cb);
- /* Don't remove the filter if other callbacks exist */
- if (data->callbacks)
+ /* Don't remove the filter if other callbacks exist or data is lock
+ * processing callbacks */
+ if (data->callbacks || data->lock)
return TRUE;
if (data->name) {