Merge lp:~charlesk/appmenu-gtk/lp-787736 into lp:appmenu-gtk/12.10

Proposed by Charles Kerr
Status: Merged
Merged at revision: 158
Proposed branch: lp:~charlesk/appmenu-gtk/lp-787736
Merge into: lp:appmenu-gtk/12.10
Diff against target: 218 lines (+90/-79)
1 file modified
src/bridge.c (+90/-79)
To merge this branch: bzr merge lp:~charlesk/appmenu-gtk/lp-787736
Reviewer Review Type Date Requested Status
jenkins (community) continuous-integration Needs Fixing
Lars Karlitski (community) Approve
Indicator Applet Developers Pending
Review via email: mp+103709@code.launchpad.net

Commit message

Fix a memory leak -- RebuildStructs were being leaked. (LP #787736)

Description of the change

Putting this fix in a standalone branch, without a half-dozen surrounding interwoven cleanup commits, to make it easier to cherry pick

To post a comment you must log in.
Revision history for this message
jenkins (martin-mrazik+qa) wrote :

PASSED: Continuous integration, rev:154
http://s-jenkins:8080/job/appmenu-gtk-ci/1/

review: Approve (continuous-integration)
Revision history for this message
jenkins (martin-mrazik+qa) wrote :
review: Approve (continuous-integration)
Revision history for this message
Lars Karlitski (larsu) wrote :

That's much cleaner, I like it.

* rebuild_list_clear is called in both dispose and in finalize

* rebuild_timer_func is the callback for the timer to rebuild widgets. It calls rebuild_list_clear, which removes the timer source with g_source_remove. Is removing sources in their callbacks allowed?

* you could be making life easier for this particular reviewer if you return G_SOURCE_REMOVE instead of FALSE in rebuild_timer_func (he has trouble remembering if TRUE or FALSE remove a source)

review: Needs Information
Revision history for this message
Charles Kerr (charlesk) wrote :

* wrt rebuild_list_clear being called twice, yes. I was continuing the double-clear behavior that's been in place since http://bazaar.launchpad.net/~canonical-dx-team/appmenu-gtk/trunk.12.10/revision/119.3.3 but it's not clear to me if the original behavior was necessary or accidental.

* removing sources in their callbacks works fine.

* Ah, G_SOURCE_REMOVE is a nice mnemonic device. I didn't know about that!

Revision history for this message
jenkins (martin-mrazik+qa) wrote :
review: Approve (continuous-integration)
Revision history for this message
Lars Karlitski (larsu) wrote :

Thanks for the info!

review: Approve
Revision history for this message
jenkins (martin-mrazik+qa) wrote :

FAILED: Autolanding.
No commit message was specified.
http://jenkins.qa.ubuntu.com/job/appmenu-gtk-autolanding/3/

review: Needs Fixing (continuous-integration)
Revision history for this message
Charles Kerr (charlesk) wrote :

(That failure message from jenkins looks like a false negative. Plus, the same code passed twelve hours ago.)

Revision history for this message
jenkins (martin-mrazik+qa) wrote :

FAILED: Autolanding.
No commit message was specified.
http://jenkins.qa.ubuntu.com/job/appmenu-gtk-autolanding/4/

review: Needs Fixing (continuous-integration)
Revision history for this message
Charles Kerr (charlesk) wrote :

:/

Revision history for this message
Allan LeSage (allanlesage) wrote :

Charles here's the link to that dput-failure: go ahead and post a commit message and I'll re-start the job: http://s-jenkins/job/generic-land/346/console

Revision history for this message
Charles Kerr (charlesk) wrote :

Landing this manually after talking over the CI problem with Allan.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/bridge.c'
2--- src/bridge.c 2012-08-22 06:56:22 +0000
3+++ src/bridge.c 2012-08-30 13:08:20 +0000
4@@ -85,21 +85,13 @@
5
6 GDBusProxy *appmenuproxy;
7 gboolean online;
8+
9+ GSList *rebuild_list;
10+ guint rebuild_timer_id;
11 };
12
13-typedef struct _RecurseContext
14-{
15- AppMenuBridge *bridge;
16- AppWindowContext *context;
17-
18- gint count;
19- gboolean previous;
20- DbusmenuMenuitem *stack[30];
21-} RecurseContext;
22-
23 G_DEFINE_DYNAMIC_TYPE(AppMenuBridge, app_menu_bridge, UBUNTU_TYPE_MENU_PROXY)
24
25-static GHashTable *rebuild_ids = NULL;
26 static GDBusNodeInfo * registrar_node_info = NULL;
27 static GDBusInterfaceInfo * registrar_interface_info = NULL;
28
29@@ -213,28 +205,59 @@
30 g_free (context);
31 }
32
33+
34+/* a widget that was queued for rebuild has been destroyed,
35+ so remove it from our queue */
36+static void
37+rebuild_list_widget_destroyed (gpointer gbridge, GObject * dead_object)
38+{
39+ AppMenuBridge * bridge = APP_MENU_BRIDGE (gbridge);
40+ AppMenuBridgePrivate * p = bridge->priv;
41+
42+ p->rebuild_list = g_slist_remove (p->rebuild_list, dead_object);
43+}
44+
45+static void
46+rebuild_list_clear (AppMenuBridge * bridge)
47+{
48+ GSList * l;
49+ AppMenuBridgePrivate * p = bridge->priv;
50+ g_debug (G_STRLOC" clearing bridge %p's rebuild list", bridge);
51+
52+ /* unref the widgets and blow away the list */
53+ for (l=p->rebuild_list; l!=NULL; l=l->next)
54+ g_object_weak_unref (l->data, rebuild_list_widget_destroyed, bridge);
55+ g_slist_free (p->rebuild_list);
56+ p->rebuild_list = NULL;
57+
58+ /* remove the rebuild timer */
59+ if (p->rebuild_timer_id > 0)
60+ {
61+ g_source_remove (p->rebuild_timer_id);
62+ p->rebuild_timer_id = 0;
63+ }
64+}
65+
66 static void
67 app_menu_bridge_dispose (GObject *object)
68 {
69 AppMenuBridge *bridge = APP_MENU_BRIDGE (object);
70-
71- g_list_foreach (bridge->priv->windows, (GFunc)context_dispose, NULL);
72-
73- if (bridge->priv->appmenuproxy)
74- {
75- g_object_unref (bridge->priv->appmenuproxy);
76- bridge->priv->appmenuproxy = NULL;
77- }
78+ AppMenuBridgePrivate * p = bridge->priv;
79+
80+ rebuild_list_clear (bridge);
81+ g_list_foreach (p->windows, (GFunc)context_dispose, NULL);
82+ g_clear_object (&p->appmenuproxy);
83 }
84
85 static void
86 app_menu_bridge_finalize (GObject *object)
87 {
88 AppMenuBridge *bridge = APP_MENU_BRIDGE (object);
89+ AppMenuBridgePrivate * p = bridge->priv;
90
91- g_list_foreach (bridge->priv->windows, (GFunc)context_free, NULL);
92- g_list_free (bridge->priv->windows);
93- bridge->priv->windows = NULL;
94+ g_list_foreach (p->windows, (GFunc)context_free, NULL);
95+ g_list_free (p->windows);
96+ p->windows = NULL;
97
98 G_OBJECT_CLASS (app_menu_bridge_parent_class)->finalize (object);
99 }
100@@ -505,63 +528,56 @@
101 return;
102 }
103
104-typedef struct _RebuildData {
105- AppMenuBridge *bridge;
106- GtkWidget *widget;
107-} RebuildData;
108-
109 static gboolean
110-do_rebuild (RebuildData *data)
111-{
112- if (data->widget != NULL && gtk_widget_is_toplevel (data->widget))
113- {
114- rebuild_window_items (data->bridge,
115- data->widget);
116- }
117-
118- if (data->widget != NULL)
119- {
120- g_object_remove_weak_pointer (G_OBJECT (data->widget), (gpointer*)&data->widget);
121- g_hash_table_remove (rebuild_ids, data->widget);
122- }
123-
124- g_free (data);
125-
126- return FALSE;
127-}
128-
129-static void
130-rebuild (AppMenuBridge *bridge,
131- GtkWidget *toplevel)
132-{
133- guint id = 0;
134-
135- if (rebuild_ids != NULL)
136- {
137- id = GPOINTER_TO_UINT (g_hash_table_lookup (rebuild_ids, toplevel));
138-
139- if (id > 0)
140+rebuild_timer_func (gpointer gbridge)
141+{
142+ GSList * l;
143+ AppMenuBridge *bridge = APP_MENU_BRIDGE (gbridge);
144+ AppMenuBridgePrivate * p = bridge->priv;
145+
146+ /* call rebuild_window_items() for each toplevel in our list */
147+ for (l=p->rebuild_list; l!=NULL; l=l->next)
148+ {
149+ GtkWidget * w = GTK_WIDGET(l->data);
150+
151+ if (gtk_widget_is_toplevel(w))
152 {
153- g_source_remove (id);
154- g_hash_table_remove (rebuild_ids, toplevel);
155- id = 0;
156+ g_debug (G_STRLOC" rebuilding bridge %p's toplevel %p", bridge, w);
157+ rebuild_window_items (bridge, w);
158 }
159 }
160
161- RebuildData *data = g_new0 (RebuildData, 1);
162- data->bridge = bridge;
163- data->widget = toplevel;
164-
165- id = gdk_threads_add_timeout (100, (GSourceFunc)do_rebuild, data);
166-
167- g_object_add_weak_pointer (G_OBJECT (data->widget), (gpointer*)&data->widget);
168-
169- if (rebuild_ids == NULL)
170- {
171- rebuild_ids = g_hash_table_new (g_direct_hash, g_direct_equal);
172- }
173-
174- g_hash_table_insert (rebuild_ids, toplevel, GUINT_TO_POINTER (id));
175+ /* cleanup */
176+ rebuild_list_clear (bridge);
177+ return G_SOURCE_REMOVE;
178+}
179+
180+/* widgets tend to get a lot of changes all at once...
181+ + inserting a 100 msec delay here lets us batch up those changes */
182+static void
183+rebuild (AppMenuBridge * bridge, GtkWidget * toplevel)
184+{
185+ AppMenuBridgePrivate * p = bridge->priv;
186+
187+ g_return_if_fail (toplevel != NULL);
188+
189+ /* ensure there's a rebuild timer ticking */
190+ if (p->rebuild_timer_id == 0)
191+ {
192+ g_debug (G_STRLOC" creating rebuild timer");
193+ p->rebuild_timer_id = gdk_threads_add_timeout (100,
194+ rebuild_timer_func,
195+ bridge);
196+ }
197+
198+ /* ensure this widget is in our rebuild list */
199+ if (g_slist_find (p->rebuild_list, toplevel) == NULL)
200+ {
201+ GObject * o = G_OBJECT(toplevel);
202+ g_debug (G_STRLOC" adding widget %p to bridge %p's queue", o, bridge);
203+ p->rebuild_list = g_slist_prepend (p->rebuild_list, o);
204+ g_object_weak_ref (o, rebuild_list_widget_destroyed, bridge);
205+ }
206 }
207
208 static DbusmenuMenuitem * find_menu_bar (GtkWidget * widget);
209@@ -989,9 +1005,4 @@
210 G_MODULE_EXPORT void
211 menu_proxy_module_unload (UbuntuMenuProxyModule *module)
212 {
213- if (rebuild_ids)
214- {
215- g_hash_table_destroy (rebuild_ids);
216- rebuild_ids = NULL;
217- }
218 }

Subscribers

People subscribed via source and target branches