From 11a98903230c07f2a6302cbc2e21a79e1b5ce930 Mon Sep 17 00:00:00 2001 From: Derek Atkins Date: Fri, 10 Feb 2006 22:43:19 +0000 Subject: [PATCH] Better handling of event removal. This allows us to actually clean up, but also makes sure we don't destroy the handler list out from under us as we're processing events. git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@13196 57a11ea4-9604-0410-9ed3-97b8803252fd --- ChangeLog | 5 +++++ lib/libqof/qof/gnc-event.c | 43 ++++++++++++++++++++++++++++++++------ 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index bdbd41519d..2a87201e89 100644 --- a/ChangeLog +++ b/ChangeLog @@ -11,6 +11,11 @@ detects that we're running on a "broken" system like Ubuntu. This is in response to #330539. + * lib/libqof/qof/gnc-event.c: + Better handling of event removal. This allows us to actually + clean up, but also makes sure we don't destroy the handler + list out from under us as we're processing events. + 2006-02-09 David Hampton * src/bin/gnucash-bin.c: Pass argc by reference instead of value diff --git a/lib/libqof/qof/gnc-event.c b/lib/libqof/qof/gnc-event.c index 26d98d384e..dca429d824 100644 --- a/lib/libqof/qof/gnc-event.c +++ b/lib/libqof/qof/gnc-event.c @@ -38,6 +38,8 @@ typedef struct /* Static Variables ************************************************/ static guint suspend_counter = 0; static gint next_handler_id = 1; +static guint handler_run_level = 0; +static guint pending_deletes = 0; static GList *handlers = NULL; /* This static indicates the debugging module that this .o belongs to. */ @@ -113,14 +115,20 @@ gnc_engine_unregister_event_handler (gint handler_id) of a generated event, such as GNC_EVENT_DESTROY. In that case, we're in the middle of walking the GList and it is wrong to modify the list. So, instead, we just NULL the handler. */ - /* handlers = g_list_remove_link (handlers, node); */ - LEAVE ("(handler_id=%d) handler=%p data=%p", handler_id, hi->handler, hi->user_data); - /* safety */ + LEAVE ("(handler_id=%d) handler=%p data=%p", handler_id, + hi->handler, hi->user_data); + + /* safety -- clear the handler in case we're running events now */ hi->handler = NULL; - /* g_list_free_1 (node); - g_free (hi); */ + if (handler_run_level == 0) { + handlers = g_list_remove_link (handlers, node); + g_list_free_1 (node); + g_free (hi); + } else { + pending_deletes++; + } return; } @@ -177,6 +185,7 @@ gnc_engine_generate_event_internal (QofEntity *entity, return; } + handler_run_level++; for (node = handlers; node; node = next_node) { HandlerInfo *hi = node->data; @@ -184,7 +193,29 @@ gnc_engine_generate_event_internal (QofEntity *entity, next_node = node->next; PINFO ("id=%d hi=%p han=%p", hi->handler_id, hi, hi->handler); if (hi->handler) - hi->handler ((GUID *)&entity->guid, entity->e_type, event_type, hi->user_data); + hi->handler ((GUID *)&entity->guid, entity->e_type, + event_type, hi->user_data); + } + handler_run_level--; + + /* If we're the outtermost event runner and we have pending deletes + * then go delete the handlers now. + */ + if (handler_run_level == 0 && pending_deletes) + { + for (node = handlers; node; node = next_node) + { + HandlerInfo *hi = node->data; + next_node = node->next; + if (hi->handler == NULL) + { + /* remove this node from the list, then free this node */ + handlers = g_list_remove_link (handlers, node); + g_list_free_1 (node); + g_free (hi); + } + } + pending_deletes = 0; } }