-
Notifications
You must be signed in to change notification settings - Fork 33
Dealing with shared ownership (Rc and Arc) #37
Description
A central idea behind this crate is that most resources in Rust have one well-defined owner. heap_size_of_children
can trace recursively and only consider owned resources. For &T
references it can return zero since they usually either point to non-heap memory, or to heap memory owned by something else that will be accounted for there.
This breaks down for types like Rc<T>
and Arc<T>
that introduce "shared ownership". They do have heap memory that we want to count, but in a fully generic context there is no clear single point where it should be counted.
(In a more specific context there count be a "main" Rc
reference for a given resource. For example, following only the "first child" and "next sibling" references in a Kuchiki tree allows traversing the whole tree without duplication.)
In the current version (90dd7e0) this crate has:
impl<T: HeapSizeOf> HeapSizeOf for Arc<T> {
fn heap_size_of_children(&self) -> usize {
(**self).heap_size_of_children()
}
}
This impl is returns an incorrect number for two reasons:
-
It’s too low because it only counts the size of resources owned by
T
, not the size ofT
itself and the two reference counts thatArc
holds internally. This part is easy to fix. -
It’s too high because there could be many
Arc
references to the sameT
value, and this impl duplicates by that many times the count of memory owned byT
.One idea that I’ve heard/read (I don’t remember from who, sorry) was to divide by the strong reference count. If all strong references are traced correctly these fractions should add up to the correct value. But the
usize
return type would have to be changed tof32
orf64
to get remotely accurate results, which is weird at best. AndArc::strong_count
andRc::strong_count
are still#[unstable]
: Tracking issue for Arc/Rc extras rust-lang/rust#28356
The current version of this crate has another impl:
impl<T> HeapSizeOf for Vec<Rc<T>> {
fn heap_size_of_children(&self) -> usize {
// The fate of measuring Rc<T> is still undecided, but we still want to measure
// the space used for storing them.
unsafe {
heap_size_of(self.as_ptr() as *const c_void)
}
}
}
My understanding of it as that, in practice, when we can’t have a correct impl of HeapSizeOf
, Servo sometimes implement it (incorrectly) returning zero because an underestimated answer is better(?) than no answer.
This Vec<Rc<T>>
follows this idea, just making the error smaller: don’t count Rc
’s but count the Vec
containing them.
Still, I don’t think there’s a reason to treat Rc<T>
and Arc<T>
differently.