Markdown pictures: an asset leaves the cache with its last holder
CI / site (push) Successful in 30s
CI / platforms (windows-latest) (push) Successful in 4m42s
CI / crate (push) Failing after 27m9s
CI / platforms (ubuntu-24.04) (push) Canceled after 0s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 5.5 committed 2026-10-12 00:11:26 -04:00
1 parent ab9a14c692
commit 8b9820be74
2 files changed
+86 -14

No files matched your search

+41 -8
View File
@@ -1,26 +1,59 @@
use std::sync::Arc;
use std::{
any::TypeId,
collections::HashMap,
hash::{DefaultHasher, Hash, Hasher},
sync::Arc,
};
use gpui::{App, Asset, ElementId, RenderImage, Window};
use gpui::{App, Asset, ElementId, Global, RenderImage, Window};
/// An element's asset; freed on change or when it leaves.
/// Holders of each asset, keyed as gpui's cache keys it.
#[derive(Default)]
struct Holders(HashMap<(TypeId, u64), usize>);
impl Global for Holders {}
fn key<A: Asset>(source: &A::Source) -> (TypeId, u64) {
let mut hasher = DefaultHasher::new();
source.hash(&mut hasher);
(TypeId::of::<A>(), hasher.finish())
}
/// An element's asset; the last holder to leave frees it.
pub(crate) struct Held<A: Asset> {
source: Option<A::Source>,
image: Option<Arc<RenderImage>>,
}
impl<A: Asset> Held<A> {
fn take(&mut self, source: &A::Source, cx: &mut App) {
let holders = &mut cx.default_global::<Holders>().0;
*holders.entry(key::<A>(source)).or_default() += 1;
self.source = Some(source.clone());
}
fn free(&mut self, cx: &mut App) {
if let Some(source) = self.source.take() {
cx.remove_asset::<A>(&source);
let Some(source) = self.source.take() else {
return;
};
let image = self.image.take();
let holders = &mut cx.default_global::<Holders>().0;
let key = key::<A>(&source);
let count = holders.get_mut(&key).expect("a held asset is counted");
*count -= 1;
if *count > 0 {
return;
}
if let Some(image) = self.image.take() {
holders.remove(&key);
cx.remove_asset::<A>(&source);
if let Some(image) = image {
// The window may be drawing; free after it.
cx.defer(move |cx| cx.drop_image(image, None));
}
}
}
/// `source` as `A` for element `id`; the previous one freed.
/// `source` as `A` for element `id`; the previous one let go.
pub(crate) fn hold<A: Asset>(
id: impl Into<ElementId>,
source: &A::Source,
@@ -43,7 +76,7 @@ where
held.update(cx, |held, cx| {
if held.source.as_ref() != Some(source) {
held.free(cx);
held.source = Some(source.clone());
held.take(source, cx);
}
if let Some(loaded) = &loaded {
held.image = image(loaded);