diff options
author | Luiz Augusto von Dentz <luiz.dentz@openbossa.org> | 2009-05-06 17:15:27 -0300 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2009-05-06 13:54:51 -0700 |
commit | 5ba06ab2d7cccb0743fb077e8e2714d5ca404533 (patch) | |
tree | cc7eb1ff9462ac624c58e817e7d445d5d99292cd /gdbus | |
parent | b374f6d47e4953aa20795ac6b5ba2709dab382cf (diff) | |
download | connman-5ba06ab2d7cccb0743fb077e8e2714d5ca404533.tar.gz connman-5ba06ab2d7cccb0743fb077e8e2714d5ca404533.tar.bz2 connman-5ba06ab2d7cccb0743fb077e8e2714d5ca404533.zip |
Fix crash when calling g_dbus_remove_watch from watch callback
Diffstat (limited to 'gdbus')
-rw-r--r-- | gdbus/watch.c | 51 |
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) { |