From 429a4f133056d5632f7c01c9ad5e7bf00f2b1303 Mon Sep 17 00:00:00 2001 From: iris-ai <4+iris-ai@noreply.localhost> Date: Mon, 21 Sep 2026 00:16:44 -0400 Subject: [PATCH] Drop the refcount a widget handle can never raise StrongWidget has no Clone on purpose, so the RefCounter in it never counted anything: the count stayed at zero, RefCounter::drop always answered true, and StrongWidget::refs had no caller -- while every widget made paid a heap allocation for an Arc and every one created or dropped paid two atomic read-modify-writes. The handle is the id, the sender and the type now, and Drop sends. It is 32 bytes rather than 40, and 40 rather than 48 for a dyn one. RefCounter stays for TextureHandle, which does clone -- several widgets showing one picture share its slot -- and is trimmed to what that needs: quiet_clone and refs had no callers at all, and new was Default spelt out. Bryan asked for this in #19 rather than in the review of the code written before the gate, where the fourteenth sweep had left it. --- core/src/primitive/texture.rs | 2 +- core/src/util/refcount.rs | 21 +++++---------------- core/src/widget/handle.rs | 18 +++++------------- 3 files changed, 11 insertions(+), 30 deletions(-) diff --git a/core/src/primitive/texture.rs b/core/src/primitive/texture.rs index 4671c29..e9d9e6b 100644 --- a/core/src/primitive/texture.rs +++ b/core/src/primitive/texture.rs @@ -66,7 +66,7 @@ impl Textures { TextureHandle { slot: self.push(image), size, - counter: RefCounter::new(), + counter: RefCounter::default(), send: self.send.clone(), } } diff --git a/core/src/util/refcount.rs b/core/src/util/refcount.rs index 77c1660..a72af94 100644 --- a/core/src/util/refcount.rs +++ b/core/src/util/refcount.rs @@ -3,29 +3,18 @@ use std::sync::{ atomic::{AtomicU32, Ordering}, }; -#[derive(Debug)] +/// How many handles to one thing there are, less one: a fresh counter is a +/// single handle, which is what the derived default gives. Only +/// [`crate::TextureHandle`] has any, a texture slot being shared by every +/// widget drawing that picture. +#[derive(Debug, Default)] pub struct RefCounter(Arc); impl RefCounter { - pub fn new() -> Self { - Self(Arc::new(0.into())) - } - pub fn refs(&self) -> u32 { - self.0.load(Ordering::Acquire) - } pub fn drop(&mut self) -> bool { let refs = self.0.fetch_sub(1, Ordering::Release); refs == 0 } - pub fn quiet_clone(&self) -> Self { - Self(self.0.clone()) - } -} - -impl Default for RefCounter { - fn default() -> Self { - Self::new() - } } impl Clone for RefCounter { diff --git a/core/src/widget/handle.rs b/core/src/widget/handle.rs index 7b52275..612b864 100644 --- a/core/src/widget/handle.rs +++ b/core/src/widget/handle.rs @@ -1,9 +1,6 @@ use std::{marker::Unsize, ops::CoerceUnsized, sync::mpsc::Sender}; -use crate::{ - UiRsc, Widget, - util::{RefCounter, SlotId}, -}; +use crate::{UiRsc, Widget, util::SlotId}; pub type WidgetId = SlotId; @@ -11,10 +8,12 @@ pub type WidgetId = SlotId; /// This is a strong handle that does not impl Clone, and when it is dropped, /// a signal is sent to the owning UI to clean up the resources. /// +/// There is nothing to count: not being `Clone` is what makes one handle the +/// only one, so the drop is the last one and the widget is one container's. +/// /// TODO: ergonomic clones when they get put in rust-analyzer & don't cause ICEs? pub struct StrongWidget { pub(super) id: WidgetId, - counter: RefCounter, send: Sender, ty: *const W, } @@ -37,7 +36,6 @@ impl StrongWidget { pub(crate) fn new(id: WidgetId, send: Sender) -> Self { Self { id, - counter: RefCounter::new(), send, ty: null_ptr(), } @@ -47,10 +45,6 @@ impl StrongWidget { self.id } - pub fn refs(&self) -> u32 { - self.counter.refs() - } - pub fn weak(&self) -> WeakWidget { let Self { ty, id, .. } = *self; WeakWidget { ty, id } @@ -74,9 +68,7 @@ impl WeakWidget { impl Drop for StrongWidget { fn drop(&mut self) { - if self.counter.drop() { - let _ = self.send.send(self.id); - } + let _ = self.send.send(self.id); } }