diff -u appmenu-gtk-0.3.92/debian/changelog appmenu-gtk-0.3.92/debian/changelog --- appmenu-gtk-0.3.92/debian/changelog +++ appmenu-gtk-0.3.92/debian/changelog @@ -1,3 +1,10 @@ +appmenu-gtk (0.3.92-0ubuntu1.1) precise-proposed; urgency=low + + * Backport r158 and r160 from trunk to fix memory leaks (LP: #787736, #780602) + - RebuildStructs were being leaked in src/bridge.c + + -- Alex Chiang Sat, 24 Nov 2012 11:37:15 -0800 + appmenu-gtk (0.3.92-0ubuntu1) precise; urgency=low * New upstream release. only in patch2: unchanged: --- appmenu-gtk-0.3.92.orig/src/bridge.c +++ appmenu-gtk-0.3.92/src/bridge.c @@ -85,21 +85,13 @@ GDBusProxy *appmenuproxy; gboolean online; -}; - -typedef struct _RecurseContext -{ - AppMenuBridge *bridge; - AppWindowContext *context; - gint count; - gboolean previous; - DbusmenuMenuitem *stack[30]; -} RecurseContext; + GSList *rebuild_list; + guint rebuild_timer_id; +}; G_DEFINE_DYNAMIC_TYPE(AppMenuBridge, app_menu_bridge, UBUNTU_TYPE_MENU_PROXY) -static GHashTable *rebuild_ids = NULL; static GDBusNodeInfo * registrar_node_info = NULL; static GDBusInterfaceInfo * registrar_interface_info = NULL; @@ -213,28 +205,58 @@ g_free (context); } + +/* a widget that was queued for rebuild has been destroyed, + so remove it from our queue */ static void -app_menu_bridge_dispose (GObject *object) +rebuild_list_widget_destroyed (gpointer gbridge, GObject * dead_object) { - AppMenuBridge *bridge = APP_MENU_BRIDGE (object); + AppMenuBridge * bridge = APP_MENU_BRIDGE (gbridge); + AppMenuBridgePrivate * p = bridge->priv; + + p->rebuild_list = g_slist_remove (p->rebuild_list, dead_object); +} - g_list_foreach (bridge->priv->windows, (GFunc)context_dispose, NULL); +static void +rebuild_list_clear (AppMenuBridge * bridge) +{ + GSList * l; + AppMenuBridgePrivate * p = bridge->priv; + + /* unref the widgets and blow away the list */ + for (l=p->rebuild_list; l!=NULL; l=l->next) + g_object_weak_unref (l->data, rebuild_list_widget_destroyed, bridge); + g_slist_free (p->rebuild_list); + p->rebuild_list = NULL; - if (bridge->priv->appmenuproxy) + /* remove the rebuild timer */ + if (p->rebuild_timer_id > 0) { - g_object_unref (bridge->priv->appmenuproxy); - bridge->priv->appmenuproxy = NULL; + g_source_remove (p->rebuild_timer_id); + p->rebuild_timer_id = 0; } } static void +app_menu_bridge_dispose (GObject *object) +{ + AppMenuBridge *bridge = APP_MENU_BRIDGE (object); + AppMenuBridgePrivate * p = bridge->priv; + + rebuild_list_clear (bridge); + g_list_foreach (p->windows, (GFunc)context_dispose, NULL); + g_clear_object (&p->appmenuproxy); +} + +static void app_menu_bridge_finalize (GObject *object) { AppMenuBridge *bridge = APP_MENU_BRIDGE (object); + AppMenuBridgePrivate * p = bridge->priv; - g_list_foreach (bridge->priv->windows, (GFunc)context_free, NULL); - g_list_free (bridge->priv->windows); - bridge->priv->windows = NULL; + g_list_foreach (p->windows, (GFunc)context_free, NULL); + g_list_free (p->windows); + p->windows = NULL; G_OBJECT_CLASS (app_menu_bridge_parent_class)->finalize (object); } @@ -505,63 +527,51 @@ return; } -typedef struct _RebuildData { - AppMenuBridge *bridge; - GtkWidget *widget; -} RebuildData; - static gboolean -do_rebuild (RebuildData *data) +rebuild_timer_func (gpointer gbridge) { - if (data->widget != NULL && gtk_widget_is_toplevel (data->widget)) - { - rebuild_window_items (data->bridge, - data->widget); - } + GSList * l; + AppMenuBridge *bridge = APP_MENU_BRIDGE (gbridge); + AppMenuBridgePrivate * p = bridge->priv; - if (data->widget != NULL) + /* call rebuild_window_items() for each toplevel in our list */ + for (l=p->rebuild_list; l!=NULL; l=l->next) { - g_object_remove_weak_pointer (G_OBJECT (data->widget), (gpointer*)&data->widget); - g_hash_table_remove (rebuild_ids, data->widget); - } + GtkWidget * w = GTK_WIDGET(l->data); - g_free (data); + if (gtk_widget_is_toplevel(w)) + rebuild_window_items (bridge, w); + } - return FALSE; + /* cleanup */ + rebuild_list_clear (bridge); + return G_SOURCE_REMOVE; } +/* widgets tend to get a lot of changes all at once... + + inserting a 100 msec delay here lets us batch up those changes */ static void -rebuild (AppMenuBridge *bridge, - GtkWidget *toplevel) +rebuild (AppMenuBridge * bridge, GtkWidget * toplevel) { - guint id = 0; + AppMenuBridgePrivate * p = bridge->priv; - if (rebuild_ids != NULL) - { - id = GPOINTER_TO_UINT (g_hash_table_lookup (rebuild_ids, toplevel)); + g_return_if_fail (toplevel != NULL); - if (id > 0) - { - g_source_remove (id); - g_hash_table_remove (rebuild_ids, toplevel); - id = 0; - } + /* ensure there's a rebuild timer ticking */ + if (p->rebuild_timer_id == 0) + { + p->rebuild_timer_id = gdk_threads_add_timeout (100, + rebuild_timer_func, + bridge); } - RebuildData *data = g_new0 (RebuildData, 1); - data->bridge = bridge; - data->widget = toplevel; - - id = gdk_threads_add_timeout (100, (GSourceFunc)do_rebuild, data); - - g_object_add_weak_pointer (G_OBJECT (data->widget), (gpointer*)&data->widget); - - if (rebuild_ids == NULL) + /* ensure this widget is in our rebuild list */ + if (g_slist_find (p->rebuild_list, toplevel) == NULL) { - rebuild_ids = g_hash_table_new (g_direct_hash, g_direct_equal); + GObject * o = G_OBJECT(toplevel); + p->rebuild_list = g_slist_prepend (p->rebuild_list, o); + g_object_weak_ref (o, rebuild_list_widget_destroyed, bridge); } - - g_hash_table_insert (rebuild_ids, toplevel, GUINT_TO_POINTER (id)); } static DbusmenuMenuitem * find_menu_bar (GtkWidget * widget); @@ -985,9 +995,4 @@ G_MODULE_EXPORT void menu_proxy_module_unload (UbuntuMenuProxyModule *module) { - if (rebuild_ids) - { - g_hash_table_destroy (rebuild_ids); - rebuild_ids = NULL; - } }