Merge lp:~unity-team/unity/plugging-leaks-with-a-nailgun into lp:unity

Proposed by Mikkel Kamstrup Erlandsen
Status: Merged
Merged at revision: 566
Proposed branch: lp:~unity-team/unity/plugging-leaks-with-a-nailgun
Merge into: lp:unity
Diff against target: 550 lines (+194/-92)
6 files modified
unity-private/places/places-button.vala (+8/-4)
unity-private/places/places-default-renderer-group.vala (+126/-54)
unity-private/places/places-default-renderer.vala (+12/-1)
unity-private/places/places-view.vala (+24/-16)
unity/unity-layered-bin.vala (+17/-12)
unity/unity-stripe-texture.vala (+7/-5)
To merge this branch: bzr merge lp:~unity-team/unity/plugging-leaks-with-a-nailgun
Reviewer Review Type Date Requested Status
Neil J. Patel (community) Approve
Review via email: mp+37231@code.launchpad.net

Description of the change

This branch plugs some leaks we could have in places. These leaks where normally only exposed after several minutes of exercising the places (especially visible on low end graphics hardware like my i945).

While the virt mem can still spike up to dangerous levels causing massive slowdowns it seems that it's eventually garbage collected by the driver and the mem usage goes down to a (somewhat) sane level.

To post a comment you must log in.
Revision history for this message
Neil J. Patel (njpatel) wrote :

Went through and checked all the nooks & crannies of places and they all seem to work fine. It definitely feels faster and mem usage seems reasonable on my system even after a bunch of place switches.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'unity-private/places/places-button.vala'
2--- unity-private/places/places-button.vala 2010-09-17 15:19:02 +0000
3+++ unity-private/places/places-button.vala 2010-10-01 09:55:56 +0000
4@@ -95,11 +95,14 @@
5 if (glow is Ctk.EffectGlow)
6 glow.set_invalidate_effect_cache (true);
7 });
8-
9+
10+ /* Must not point to a lambda or non-static method on 'this'.
11+ * Causes self-refs, hence leaks, otherwise */
12 outline_func = rounded_rect;
13 }
14-
15- public void rounded_rect (Cairo.Context cr, int width, int height)
16+
17+ /* This method *must* be static. Otherwise we'll create a circular ref to self */
18+ private static void rounded_rect (Cairo.Context cr, int width, int height)
19 {
20 var padding = 1;
21 var x = padding;
22@@ -125,7 +128,8 @@
23 cr.curve_to (x, height,
24 x, height,
25 x, height - radius);
26- cr.close_path (); }
27+ cr.close_path ();
28+ }
29
30 private void paint_bg (Cairo.Context cr, int width, int height)
31 requires (this is Button)
32
33=== modified file 'unity-private/places/places-default-renderer-group.vala'
34--- unity-private/places/places-default-renderer-group.vala 2010-09-22 18:20:59 +0000
35+++ unity-private/places/places-default-renderer-group.vala 2010-10-01 09:55:56 +0000
36@@ -59,7 +59,10 @@
37 private Ctk.Image icon;
38 private Ctk.Text text;
39 private Expander expander;
40- private unowned Ctk.IconView renderer;
41+
42+ /* If renderer is null it indicates that we have been disposed,
43+ * and that all changes to the results should be ignored */
44+ private unowned Ctk.IconView? renderer;
45
46 private MoreResultsButton? more_results_button;
47
48@@ -73,8 +76,17 @@
49
50 public signal void activated (string uri, string mimetype);
51
52- private GLib.List<Tile?> cleanup_tiles = new GLib.List<Tile?> ();
53+ private Gee.Queue<Tile> cleanup_tiles = new Gee.LinkedList<Tile> ();
54 private uint cleanup_operation = 0;
55+
56+ private TileFactory tile_factory;
57+
58+ private delegate Tile TileFactory (Dee.ModelIter iter,
59+ string uri,
60+ string icon_hint,
61+ string mimetype,
62+ string display_name,
63+ string comment);
64
65 public DefaultRendererGroup (uint group_id,
66 string group_renderer,
67@@ -86,7 +98,7 @@
68 group_renderer:group_renderer,
69 display_name:display_name,
70 icon_hint:icon_hint,
71- results:results);
72+ results:results);
73 }
74
75 construct
76@@ -98,6 +110,22 @@
77 allow_expand = false;
78 else if (group_renderer == "UnityFolderGroupRenderer")
79 bin_state = ExpandingBinState.EXPANDED;
80+
81+ /* Find the right tile factory for our rendering mode
82+ * IMPORANT: The file_factory must *not* point to a lambda or
83+ * non-static method on 'this'. That leads to self-refs (aka leaks) */
84+ if (group_renderer == "UnityFileInfoRenderer")
85+ {
86+ tile_factory = file_info_tile_factory;
87+ }
88+ else if (group_renderer == "UnityShowcaseRenderer")
89+ {
90+ tile_factory = showcase_tile_factory;
91+ }
92+ else
93+ {
94+ tile_factory = default_tile_factory;
95+ }
96
97 vbox = new Ctk.VBox (SPACING);
98 vbox.spacing = SPACING;
99@@ -240,6 +268,28 @@
100 results.row_removed.connect (on_result_removed);
101
102 }
103+
104+ private override void dispose ()
105+ {
106+ if (renderer != null)
107+ {
108+ /* Drop refs to renderer since we have a cycle:
109+ * self->renderer vs self->vbox->renderer */
110+ renderer = null;
111+ remove_actor (vbox);
112+
113+ /* Disconnect signals, so we don't start doing funky stuff
114+ * after we've been disposed */
115+ results.row_added.disconnect (on_result_added);
116+ results.row_removed.disconnect (on_result_removed);
117+
118+ /* Drain the cleanup queue */
119+ cleanup_tiles.clear ();
120+ }
121+
122+ /* Chain up */
123+ base.dispose ();
124+ }
125
126 private override void allocate (Clutter.ActorBox box,
127 Clutter.AllocationFlags flags)
128@@ -264,68 +314,76 @@
129 /*
130 * Private Methods
131 */
132+
133+ /* This method *must* be static in order to avoid self refs */
134+ private static Tile file_info_tile_factory (Dee.ModelIter iter,
135+ string uri,
136+ string icon_hint,
137+ string mimetype,
138+ string display_name,
139+ string comment)
140+ {
141+ return new FileInfoTile (iter, uri, icon_hint, mimetype, display_name, comment);
142+ }
143+
144+ /* This method *must* be static in order to avoid self refs */
145+ private static Tile showcase_tile_factory (Dee.ModelIter iter,
146+ string uri,
147+ string icon_hint,
148+ string mimetype,
149+ string display_name,
150+ string comment)
151+ {
152+ return new ShowcaseTile (iter, uri, icon_hint, mimetype, display_name, comment);
153+ }
154+
155+ /* This method *must* be static in order to avoid self refs */
156+ private static Tile default_tile_factory (Dee.ModelIter iter,
157+ string uri,
158+ string icon_hint,
159+ string mimetype,
160+ string display_name,
161+ string comment)
162+ {
163+ return new DefaultTile (iter, uri, icon_hint, mimetype, display_name, comment);
164+ }
165+
166 private void on_result_added (Dee.ModelIter iter)
167 {
168+ /* Ignore result set changes if we are disposed */
169+ if (renderer == null)
170+ return;
171+
172 if (!interesting (iter))
173 return;
174
175 if (n_results == OMG_FOOTEL_SUCKS_CANT_HANDLE_MANY_TEXTURES)
176 return;
177-
178- Tile button;
179-
180- if (cleanup_tiles.length () > 0)
181+
182+ /* Try to reuse a tile from the cleanup queue. We are guaranteed
183+ * that it's the right type because all tiles are created from the same
184+ * tile_factory instance */
185+ Tile? button = cleanup_tiles.poll ();
186+
187+ if (button is Tile)
188 {
189- button = cleanup_tiles.nth_data (0);
190 button.update_details (results.get_string (iter, 0),
191 results.get_string (iter, 1),
192 results.get_string (iter, 3),
193 results.get_string (iter, 4),
194 results.get_string (iter, 5));
195 button.iter = iter;
196- cleanup_tiles.remove (button);
197- button.unref (); /* Because Vala holds references when it shouldn't*/;
198- }
199- else if (group_renderer == "UnityFileInfoRenderer")
200- {
201- button = new FileInfoTile (iter,
202- results.get_string (iter, 0),
203- results.get_string (iter, 1),
204- results.get_string (iter, 3),
205- results.get_string (iter, 4),
206- results.get_string (iter, 5));
207-
208- renderer.add_actor (button);
209- button.show ();
210- button.unref (); /* Because Vala holds references when it shouldn't*/;
211- button.activated.connect ((u, m) => { activated (u, m); });
212- }
213- else if (group_renderer == "UnityShowcaseRenderer")
214- {
215- button = new ShowcaseTile (iter,
216- results.get_string (iter, 0),
217- results.get_string (iter, 1),
218- results.get_string (iter, 3),
219- results.get_string (iter, 4),
220- results.get_string (iter, 5));
221- renderer.add_actor (button);
222- button.show ();
223- button.unref (); /* Because Vala holds references when it shouldn't*/;
224-
225- button.activated.connect ((u, m) => { activated (u, m); });
226 }
227 else
228 {
229- button = new DefaultTile (iter,
230- results.get_string (iter, 0),
231- results.get_string (iter, 1),
232- results.get_string (iter, 3),
233- results.get_string (iter, 4),
234- results.get_string (iter, 5));
235+ button = tile_factory (iter,
236+ results.get_string (iter, 0),
237+ results.get_string (iter, 1),
238+ results.get_string (iter, 3),
239+ results.get_string (iter, 4),
240+ results.get_string (iter, 5));
241 renderer.add_actor (button);
242- button.show ();
243- button.unref (); /* Because Vala holds references when it shouldn't*/;
244-
245+ button.show ();
246 button.activated.connect ((u, m) => { activated (u, m); });
247 }
248
249@@ -350,21 +408,27 @@
250
251 private bool cleanup_operation_callback ()
252 {
253- foreach (Tile? tile in cleanup_tiles)
254+ Tile? tile;
255+
256+ /* Drain the cleanup queue */
257+ while ((tile = cleanup_tiles.poll ()) != null)
258 {
259- renderer.remove_actor (tile);
260+ /* If we are disposed renderer will be null */
261+ if (renderer == null)
262+ renderer.remove_actor (tile);
263 }
264
265- cleanup_tiles = null;
266- cleanup_tiles = new GLib.List<Tile?> ();
267-
268 cleanup_operation = 0;
269 return false;
270 }
271
272 private void on_result_removed (Dee.ModelIter iter)
273 {
274- if (!interesting (iter))
275+ /* Ignore result set changes if we are disposed */
276+ if (renderer == null)
277+ return;
278+
279+ if (!interesting (iter))
280 return;
281
282 var children = renderer.get_children ();
283@@ -374,7 +438,7 @@
284
285 if (tile.iter == iter)
286 {
287- cleanup_tiles.append (tile);
288+ cleanup_tiles.offer (tile);
289 if (cleanup_operation == 0)
290 cleanup_operation = Timeout.add (200, cleanup_operation_callback);
291
292@@ -402,6 +466,10 @@
293
294 private void add_to_n_results (int i)
295 {
296+ /* Ignore result set changes if we are disposed */
297+ if (renderer == null)
298+ return;
299+
300 n_results += i;
301
302 if (n_results > renderer.get_n_cols ())
303@@ -430,6 +498,10 @@
304
305 private void on_n_cols_changed ()
306 {
307+ /* Ignore result set changes if we are disposed */
308+ if (renderer == null)
309+ return;
310+
311 var n_cols = renderer.get_n_cols ();
312
313 if (bin_state == ExpandingBinState.UNEXPANDED)
314
315=== modified file 'unity-private/places/places-default-renderer.vala'
316--- unity-private/places/places-default-renderer.vala 2010-09-22 12:04:58 +0000
317+++ unity-private/places/places-default-renderer.vala 2010-10-01 09:55:56 +0000
318@@ -51,8 +51,18 @@
319 Object ();
320 }
321
322- ~DefaultRenderer ()
323+ /* For some reason we must manually drop these in dispose() otherwise
324+ * they end up leaked... And please note that finalize is not good enough
325+ * either - so there is probably a ref cycle somewhere caused by all the
326+ * actor.animate() calls... */
327+ private override void dispose ()
328 {
329+ remove_actor (scroll);
330+ remove_actor (search_empty);
331+ remove_actor (section_empty);
332+
333+ /* Chain up */
334+ base.dispose ();
335 }
336
337 private static double
338@@ -418,6 +428,7 @@
339 group.always_expanded = true;
340 }
341 }
342+
343 }
344 }
345
346
347=== modified file 'unity-private/places/places-view.vala'
348--- unity-private/places/places-view.vala 2010-09-20 22:16:46 +0000
349+++ unity-private/places/places-view.vala 2010-10-01 09:55:56 +0000
350@@ -33,10 +33,10 @@
351 private Ctk.VBox content_box;
352 private LayeredBin layered_bin;
353
354- public PlaceHomeEntry home_entry;
355- public PlaceSearchBar search_bar;
356- private Unity.Place.Renderer renderer;
357- private unowned PlaceEntry? active_entry = null;
358+ public PlaceEntry home_entry;
359+ public PlaceSearchBar search_bar;
360+ private Unity.Place.Renderer? renderer;
361+ private unowned PlaceEntry? active_entry = null;
362
363 private bool is_showing = false;
364
365@@ -81,7 +81,10 @@
366 search_bar.entry.text.captured_event.connect (on_stage_event_captured);
367 search_bar.entry.text.activatable = true;
368 search_bar.entry.text.activate.connect (() => {
369- renderer.activate_default ();
370+ if (renderer is Clutter.Actor)
371+ {
372+ renderer.activate_default ();
373+ }
374 });
375
376 layered_bin = new LayeredBin ();
377@@ -107,6 +110,7 @@
378 active_entry.active = false;
379 }
380 active_entry = null;
381+ remove_current_renderer ();
382 }
383
384 public void on_entry_view_activated (PlaceEntry entry, uint section_id)
385@@ -126,30 +130,34 @@
386 entry.renderer_info_changed.connect (on_entry_renderer_info_changed);
387 }
388
389- private void update_views (PlaceEntry entry, uint section_id=0)
390+ /* Fade out previous results view if we had one, and set
391+ * this.renderer = null */
392+ private void remove_current_renderer ()
393 {
394- search_bar.set_active_entry_view (entry, 0, section_id);
395-
396- /* Create the correct results view */
397 if (renderer is Clutter.Actor)
398 {
399-
400 var anim = renderer.animate (Clutter.AnimationMode.EASE_OUT_QUAD,
401 300,
402 "opacity", 0);
403- anim.completed.connect ((a)=> {
404- (a.get_object () as Clutter.Actor).destroy ();
405- });
406+
407 layered_bin.remove_actor (renderer);
408+ renderer = null;
409 }
410-
411+ }
412+
413+ private void update_views (PlaceEntry entry, uint section_id=0)
414+ {
415+ search_bar.set_active_entry_view (entry, 0, section_id);
416+
417+ remove_current_renderer ();
418+
419+ /* Create the correct results view */
420 renderer = lookup_renderer (entry);
421+
422 layered_bin.add_actor (renderer);
423- renderer.unref (); /* Because lookup_renderer returns it unfloating */
424 renderer.opacity = 0;
425 renderer.animate (Clutter.AnimationMode.EASE_OUT_QUAD, 300,
426 "opacity", 255);
427-
428 renderer.set_models (entry.entry_groups_model,
429 entry.entry_results_model,
430 entry.entry_renderer_hints);
431
432=== modified file 'unity/unity-layered-bin.vala'
433--- unity/unity-layered-bin.vala 2010-09-02 19:19:10 +0000
434+++ unity/unity-layered-bin.vala 2010-10-01 09:55:56 +0000
435@@ -28,9 +28,9 @@
436 */
437 public class LayeredBin : Ctk.Actor, Clutter.Container
438 {
439- private GLib.List<Clutter.Actor?> _children = null;
440+ private Gee.List<Clutter.Actor> _children = new Gee.LinkedList<Clutter.Actor>();
441
442- public unowned GLib.List<Clutter.Actor?> children {
443+ public unowned Gee.List<Clutter.Actor> children {
444 get { return _children; }
445 }
446
447@@ -39,12 +39,17 @@
448 Object ();
449 }
450
451- ~LayeredBin ()
452+ private override void dispose ()
453 {
454 foreach (Clutter.Actor child in _children)
455- child.unparent ();
456+ {
457+ child.unparent ();
458+ }
459
460- _children = null;
461+ _children.clear ();
462+
463+ /* Chain up */
464+ base.dispose ();
465 }
466
467 construct
468@@ -124,7 +129,7 @@
469 private void add (Clutter.Actor actor)
470 {
471 actor.set_parent (this);
472- _children.append (actor);
473+ _children.add (actor);
474
475 queue_relayout ();
476
477@@ -157,30 +162,30 @@
478 private new void raise (Clutter.Actor actor, Clutter.Actor sibling)
479 {
480 if (sibling is Clutter.Actor &&
481- _children.index (sibling) != -1)
482+ _children.index_of (sibling) != -1)
483 {
484 _children.remove (actor);
485- _children.insert (actor, _children.index (sibling) + 1);
486+ _children.insert (_children.index_of (sibling) + 1, actor);
487 }
488 else
489 {
490 _children.remove (actor);
491- _children.append (actor);
492+ _children.add (actor);
493 }
494 }
495
496 private new void lower (Clutter.Actor actor, Clutter.Actor sibling)
497 {
498 if (sibling is Clutter.Actor &&
499- _children.index (sibling) != -1)
500+ _children.index_of (sibling) != -1)
501 {
502 _children.remove (actor);
503- _children.insert (actor, _children.index (sibling) - 1);
504+ _children.insert (_children.index_of (sibling) - 1, actor);
505 }
506 else
507 {
508 _children.remove (actor);
509- _children.prepend (actor);
510+ _children.insert (0, actor);
511 }
512 }
513
514
515=== modified file 'unity/unity-stripe-texture.vala'
516--- unity/unity-stripe-texture.vala 2010-08-26 07:52:44 +0000
517+++ unity/unity-stripe-texture.vala 2010-10-01 09:55:56 +0000
518@@ -35,16 +35,16 @@
519 int width,
520 int height);
521
522- public float radius { get; construct set; }
523-
524 public StripeTextureOutlineFunc outline_paint_func;
525
526 private Cairo.Surface? pattern;
527
528 public StripeTexture (StripeTextureOutlineFunc? func)
529 {
530- Object (radius:10.0f);
531-
532+ Object ();
533+
534+ /* Must not point to a lambda or non-static method on 'this'.
535+ * It causes self-refs if it does */
536 outline_paint_func = rounded_outline;
537 if (func != null)
538 outline_paint_func = func;
539@@ -55,8 +55,10 @@
540 paint_func = paint_bg;
541 }
542
543- public void rounded_outline (Cairo.Context cr, int width, int height)
544+ /* This method must be static in order to avoid self refs */
545+ public static void rounded_outline (Cairo.Context cr, int width, int height)
546 {
547+ float radius = 10;
548 var x = 0;
549 var y = 0;
550 width -= 1;