I'm working on a versioned data structure and associated manager using tokio
. The manager keeps track of the different versions of the structure, provides a reference to the current version
on request, and needs to take action when a version
is no longer in use. I wanted to use the built-in Arc
type for this, but there's no clear way to extend its functionality.
The solution I came up with is a wrapper data structure around Arc
("AwaitableArc
", which is kind of a misnomer) with manually implemented Clone
and Drop
. When clone
or drop
is called on AwaitableArc
, the normal Arc
method is called, and a notification is sent to an mpsc
channel. Each one has an identifier so that the manager can tell which AwaitableArc
fired.
pub(crate) struct AwaitableArc<T, U: Send + Clone + std::fmt::Debug + 'static> {
id: U,
arc: Arc<T>,
tx: mpsc::Sender<(U, usize)>,
}
impl<T, U: Send + Clone + std::fmt::Debug + 'static> Clone for AwaitableArc<T, U> {
fn clone(&self) -> Self {
let arc = self.arc.clone();
let count = Arc::<T>::strong_count(&arc);
let tx = self.tx.clone();
let id = self.id.clone();
tokio::spawn(async move {
tx.send((id, count)).await.unwrap();
});
Self {
id: self.id.clone(),
arc,
tx: self.tx.clone(),
}
}
}
impl<T, U: Send + Clone + std::fmt::Debug + 'static> Drop for AwaitableArc<T, U> {
fn drop(&mut self) {
let count = Arc::<T>::strong_count(&self.arc) - 1;
drop(&self.arc);
let tx = self.tx.clone();
let id = self.id.clone();
tokio::spawn(async move {
tx.send((id, count)).await.unwrap();
});
drop(&self.id);
drop(&self.tx);
drop(&self)
}
}
impl<T, U: Send + Clone + std::fmt::Debug + 'static> Deref for AwaitableArc<T, U> {
type Target = Arc<T>;
fn deref(&self) -> &Self::Target {
&self.arc
}
}
I have a listener await
ing the mpsc
channel in a loop. It receives (id, strong_count)
and will take action when strong_count
drops to 1 (the manager holds a reference, so dropping to 1 means nobody else is holding a reference and we can delete the structure) This seems like it should work, but I'm not sure if it's good practice. Am I abusing Drop
here?
Is there a simple solution I've missed to respond to changes in Arc::strong_count
?