Merge lp:~unity-team/unity/plugging-leaks-with-a-nailgun into lp:unity
- plugging-leaks-with-a-nailgun
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Neil J. Patel (community) | Approve | ||
Review via email: mp+37231@code.launchpad.net |
Commit message
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.
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; |
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.