diff options
author | Mike Gorse <mgorse@suse.com> | 2020-09-02 17:11:15 -0500 |
---|---|---|
committer | Mike Gorse <mgorse@suse.com> | 2020-09-02 17:11:15 -0500 |
commit | 4f53ce08cca0306829d46602a52fc7cfe11cf635 (patch) | |
tree | 3314ef34855fd0f10f6e962ef7152c128a324597 /atspi/atspi-event-listener.c | |
parent | 252729eee1c646be904750d6bc462b1cf3e5eb81 (diff) | |
download | at-spi2-core-4f53ce08cca0306829d46602a52fc7cfe11cf635.tar.gz |
Fix handling of event listener removals during an event callback
We should not modify event_listeners while we are iterating through it.
Otherwise, we may crash. If an event listener is removed from within an event
callback, then defer the removal until the callback has ended.
Diffstat (limited to 'atspi/atspi-event-listener.c')
-rw-r--r-- | atspi/atspi-event-listener.c | 31 |
1 files changed, 29 insertions, 2 deletions
diff --git a/atspi/atspi-event-listener.c b/atspi/atspi-event-listener.c index 464177f1..82e39b6d 100644 --- a/atspi/atspi-event-listener.c +++ b/atspi/atspi-event-listener.c @@ -166,6 +166,9 @@ atspi_event_listener_new_simple (AtspiEventListenerSimpleCB callback, } static GList *event_listeners = NULL; +static GList *pending_removals = NULL; +static int in_send = 0; + static gchar * convert_name_from_dbus (const char *name, gboolean path_hack) @@ -788,7 +791,10 @@ atspi_event_listener_deregister_from_callback (AtspiEventListenerCB callback, { DBusMessage *message, *reply; l = g_list_next (l); - event_listeners = g_list_remove (event_listeners, e); + if (in_send) + pending_removals = g_list_append (pending_removals, e); + else + event_listeners = g_list_remove (event_listeners, e); for (i = 0; i < matchrule_array->len; i++) { char *matchrule = g_ptr_array_index (matchrule_array, i); @@ -805,7 +811,8 @@ atspi_event_listener_deregister_from_callback (AtspiEventListenerCB callback, if (reply) dbus_message_unref (reply); - listener_entry_free (e); + if (!in_send) + listener_entry_free (e); } else l = g_list_next (l); @@ -880,6 +887,13 @@ detail_matches_listener (const char *event_detail, const char *listener_detail) : strcmp (listener_detail, event_detail)); } +static void +resolve_pending_removal (gpointer data) +{ + event_listeners = g_list_remove (event_listeners, data); + listener_entry_free (data); +} + void _atspi_send_event (AtspiEvent *e) { @@ -900,6 +914,7 @@ _atspi_send_event (AtspiEvent *e) g_warning ("AT-SPI: Couldn't parse event: %s\n", e->type); return; } + in_send++; for (l = event_listeners; l; l = g_list_next (l)) { EventListenerEntry *entry = l->data; @@ -916,15 +931,27 @@ _atspi_send_event (AtspiEvent *e) } if (!l2) { + for (l2 = pending_removals; l2; l2 = l2->next) + { + if (l2->data == entry) + break; + } + } + if (!l2) + { entry->callback (atspi_event_copy (e), entry->user_data); called_listeners = g_list_prepend (called_listeners, entry); } } } + in_send--; if (detail) g_free (detail); g_free (name); g_free (category); g_list_free (called_listeners); + + g_list_free_full (pending_removals, resolve_pending_removal); + pending_removals = NULL; } DBusHandlerResult |