From dac2c5a39fadfabfb3d3e04d3c997eac2fbba185 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Thu, 8 Oct 2026 16:30:51 -0400 Subject: [PATCH] Retired frees only images no one else holds An image shared with something still drawing waits for a later paint. --- src/primitives/retired.rs | 7 +++++-- src/primitives/tests.rs | 13 +++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/primitives/retired.rs b/src/primitives/retired.rs index 2e5badb..a5346b2 100644 --- a/src/primitives/retired.rs +++ b/src/primitives/retired.rs @@ -14,9 +14,12 @@ impl Retired { self.0.push(image); } - /// Frees every retired image; call while painting. + /// Frees images no one else holds; call while painting. pub fn release(&mut self, window: &mut Window) { - for image in self.0.drain(..) { + // A shared image may still be drawn; it waits. + let (free, held): (Vec<_>, Vec<_>) = self.0.drain(..).partition(|image| Arc::strong_count(image) == 1); + self.0 = held; + for image in free { if let Err(error) = window.drop_image(image) { log::error!("images: a retired image stayed: {error}"); } diff --git a/src/primitives/tests.rs b/src/primitives/tests.rs index cfa70f9..3bf58aa 100644 --- a/src/primitives/tests.rs +++ b/src/primitives/tests.rs @@ -261,4 +261,17 @@ fn a_retired_image_is_freed_at_the_next_paint(cx: &mut TestAppContext) { }); cx.run_until_parked(); assert!(view.read_with(cx, |painted, _| painted.retired.is_empty()), "the old image was freed"); + // One still held elsewhere waits until it is let go. + let pixels = image::RgbaImage::from_pixel(2, 2, image::Rgba([4, 5, 6, 255])); + let shared = std::sync::Arc::new(gpui::RenderImage::new([image::Frame::new(pixels)])); + view.update(cx, |painted, cx| { + painted.retired.push(shared.clone()); + cx.notify(); + }); + cx.run_until_parked(); + assert!(!view.read_with(cx, |painted, _| painted.retired.is_empty()), "a held image waits"); + drop(shared); + view.update(cx, |_, cx| cx.notify()); + cx.run_until_parked(); + assert!(view.read_with(cx, |painted, _| painted.retired.is_empty()), "then it is freed"); }