[WeakVec] scan all weak references only before a reallocation would happen

This commit is contained in:
teoxoy 2024-10-18 12:41:04 +02:00 committed by Teodor Tanasoaia
parent 8ba5c82831
commit f669024eeb

View File

@ -2,45 +2,55 @@
use std::sync::Weak; use std::sync::Weak;
/// A container that holds Weak references of T. /// An optimized container for `Weak` references of `T` that minimizes reallocations by
/// /// dropping older elements that no longer have strong references to them.
/// On `push` it scans its contents for weak references with no strong references still alive and drops them.
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct WeakVec<T> { pub(crate) struct WeakVec<T> {
inner: Vec<Option<Weak<T>>>, inner: Vec<Option<Weak<T>>>,
empty_slots: Vec<usize>,
scan_slots_on_next_push: bool,
} }
impl<T> Default for WeakVec<T> { impl<T> Default for WeakVec<T> {
fn default() -> Self { fn default() -> Self {
Self { Self {
inner: Default::default(), inner: Default::default(),
empty_slots: Default::default(),
scan_slots_on_next_push: false,
} }
} }
} }
impl<T> WeakVec<T> { impl<T> WeakVec<T> {
pub(crate) fn new() -> Self { pub(crate) fn new() -> Self {
Self { inner: Vec::new() } Self {
inner: Vec::new(),
empty_slots: Vec::default(),
scan_slots_on_next_push: false,
}
} }
/// Pushes a new element to this collection, dropping older elements that no longer have /// Pushes a new element to this collection.
/// a strong reference to them.
/// ///
/// NOTE: The length and capacity of this collection do not change when old elements are /// If the inner Vec needs to be reallocated, we will first drop older elements that
/// dropped. /// no longer have strong references to them.
pub(crate) fn push(&mut self, value: Weak<T>) { pub(crate) fn push(&mut self, value: Weak<T>) {
let mut to_insert = Some(value); if self.scan_slots_on_next_push {
for slot in &mut self.inner { for (i, value) in self.inner.iter_mut().enumerate() {
if let Some(w) = slot { if let Some(w) = value {
if w.strong_count() == 0 { if w.strong_count() == 0 {
*slot = to_insert.take(); *value = None;
self.empty_slots.push(i);
}
} }
} else {
*slot = to_insert.take();
} }
} }
if let Some(to_insert) = to_insert { if let Some(i) = self.empty_slots.pop() {
self.inner.push(Some(to_insert)); self.inner[i] = Some(value);
self.scan_slots_on_next_push = false;
} else {
self.inner.push(Some(value));
self.scan_slots_on_next_push = self.inner.len() == self.inner.capacity();
} }
} }
} }