Fix crash when calling g_dbus_remove_watch from watch callback

This commit is contained in:
Luiz Augusto von Dentz 2009-05-06 17:15:27 -03:00 committed by Marcel Holtmann
parent d2e73f2d30
commit 5106f7a8cf
1 changed files with 41 additions and 10 deletions

View File

@ -54,6 +54,8 @@ struct name_data {
DBusConnection *connection; DBusConnection *connection;
char *name; char *name;
GSList *callbacks; GSList *callbacks;
GSList *processed;
gboolean lock;
}; };
static struct name_data *name_data_find(DBusConnection *connection, 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); name_listeners = g_slist_append(name_listeners, data);
done: 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; return first;
} }
@ -229,10 +235,9 @@ static gboolean remove_match(DBusConnection *connection, const char *name)
static DBusHandlerResult name_exit_filter(DBusConnection *connection, static DBusHandlerResult name_exit_filter(DBusConnection *connection,
DBusMessage *message, void *user_data) DBusMessage *message, void *user_data)
{ {
GSList *l;
struct name_data *data; struct name_data *data;
struct name_callback *cb;
char *name, *old, *new; char *name, *old, *new;
int keep = 0;
if (!dbus_message_is_signal(message, DBUS_INTERFACE_DBUS, if (!dbus_message_is_signal(message, DBUS_INTERFACE_DBUS,
"NameOwnerChanged")) "NameOwnerChanged"))
@ -253,8 +258,11 @@ static DBusHandlerResult name_exit_filter(DBusConnection *connection,
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
} }
for (l = data->callbacks; l != NULL; l = l->next) { data->lock = TRUE;
struct name_callback *cb = l->data;
while (data->callbacks) {
cb = data->callbacks->data;
if (*new == '\0') { if (*new == '\0') {
if (cb->disc_func) if (cb->disc_func)
cb->disc_func(connection, cb->user_data); cb->disc_func(connection, cb->user_data);
@ -262,11 +270,27 @@ static DBusHandlerResult name_exit_filter(DBusConnection *connection,
if (cb->conn_func) if (cb->conn_func)
cb->conn_func(connection, cb->user_data); 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; return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
name_listeners = g_slist_remove(name_listeners, data); 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) if (cb->id == id)
goto remove; goto remove;
} }
for (lcb = data->processed; lcb; lcb = lcb->next) {
cb = lcb->data;
if (cb->id == id)
goto remove;
}
} }
return FALSE; return FALSE;
remove: remove:
data->callbacks = g_slist_remove(data->callbacks, cb); data->callbacks = g_slist_remove(data->callbacks, cb);
data->processed = g_slist_remove(data->processed, cb);
g_free(cb); g_free(cb);
/* Don't remove the filter if other callbacks exist */ /* Don't remove the filter if other callbacks exist or data is lock
if (data->callbacks) * processing callbacks */
if (data->callbacks || data->lock)
return TRUE; return TRUE;
if (data->name) { if (data->name) {