You can force freeing of an Arc
by decrement_strong_count()
ing in a loop until there are no more owners, but this is a very bad idea. The other owners may access the data afterwards, causing undefined behavior.
Instead, you should make it voluntary. The best way to do that is keeping Weak
s instead of Arc
for the other owners. This way, you are the sole owner, and they can access the data if it has not been dropped by upgrade()
ing the Weak
. Note it will not free the memory of the Arc
itself until all Weak
s are dropped (this is necessary for soundness), but it will drop the contained data.
If you want to know what code is holding the Arc
, this is going to be much harder. Arc
doesn't track its owners, so you have to do it. I don't know a tool that will do that. For example, you can capture a backtrace every time you clone the Arc
and store all backtraces near the data, something like:
use std::ops::Deref;
use std::sync::{Arc, Mutex, MutexGuard};
use backtrace::Backtrace;
use slotmap::basic::IterMut as SlotMapIterMut;
use slotmap::{DefaultKey, SlotMap};
struct CloningAwareArcData<T> {
backtraces: Mutex<SlotMap<DefaultKey, Backtrace>>,
data: T,
}
// This struct doesn't support unsized pointees. It can, although I think it
// will require nightly.
pub struct CloningAwareArc<T> {
data: Arc<CloningAwareArcData<T>>,
backtrace_key: DefaultKey,
}
impl<T> CloningAwareArc<T> {
pub fn new(v: T) -> Self {
let mut backtraces = SlotMap::new();
let backtrace_key = backtraces.insert(Backtrace::new_unresolved());
Self {
data: Arc::new(CloningAwareArcData {
backtraces: Mutex::new(backtraces),
data: v,
}),
backtrace_key,
}
}
/// This function will block cloning & calling it again until the iterator is dropped.
///
/// This is an associated function and not a method in order to not interfere with a method of `T` with the same name.
pub fn active_backtraces(this: &Self) -> ActiveBacktracesIter<'_> {
ActiveBacktracesIter::new(this.backtrace_key, this.data.backtraces.lock().unwrap())
}
}
impl<T> Deref for CloningAwareArc<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.data.data
}
}
impl<T> Clone for CloningAwareArc<T> {
fn clone(&self) -> Self {
let backtrace_key = self
.data
.backtraces
.lock()
.unwrap()
.insert(Backtrace::new_unresolved());
Self {
data: Arc::clone(&self.data),
backtrace_key,
}
}
}
// This struct is safe-referential: we need to store the guard and the
// iterator that refers to the map inside the guard.
// Using parking_lot's `MappedMutexGuard` will not help us here since
// it is only usable if we can map the guard to a reference, but we
// map it to an iterator.
// See https://stackoverflow.com/q/40095383/7884305.
// I tried to write it using ouroboros, but gave up :) Doesn't mean this
// is impossible, but it is pretty hard.
pub struct ActiveBacktracesIter<'a> {
self_key: DefaultKey,
_guard: MutexGuard<'a, SlotMap<DefaultKey, Backtrace>>,
inner: SlotMapIterMut<'static, DefaultKey, Backtrace>,
}
impl<'a> ActiveBacktracesIter<'a> {
fn new(
self_key: DefaultKey,
mut guard: MutexGuard<'a, SlotMap<DefaultKey, Backtrace>>,
) -> Self {
let inner: SlotMapIterMut<'_, DefaultKey, Backtrace> = guard.iter_mut();
// SAFETY: Lifetimes cannot affect layout, and we're holding it only until we drop
// the guard.
let inner: SlotMapIterMut<'static, DefaultKey, Backtrace> =
unsafe { std::mem::transmute(inner) };
Self {
self_key,
_guard: guard,
inner,
}
}
}
impl<'a> Iterator for ActiveBacktracesIter<'a> {
type Item = &'a Backtrace;
fn next(&mut self) -> Option<Self::Item> {
let (key, backtrace) = self.inner.next()?;
if self.self_key != key {
backtrace.resolve();
Some(backtrace)
} else {
// Skip owning `Arc`
self.next()
}
}
// You can implement additional iterator methods and traits, for optimization.
}
However, note that Arc
cloning is supposed to be cheap. Backtrace capture is going to be very expensive, and even if you attach some other data it will make cloning much more expensive than just atomic addition + compare and jump.