I'm writing an async application with async-await. The intention is to use reference counters within each task, and they will stay on only one thread at a time and will never go outside of that task. I believe this is a quite common use case for async programs, but what rust provides isn't ideal:
-
Rc
isn'tSend
at all. Of course we can create a new thread for each task, but that defeats the purpose of the async scheduler. -
Arc
will always use an atomic counter. Worse, usually we'll need to mutate the shared object and end up usingArc<Mutex<T>>
. NeitherArc
norMutex
should be required because no synchronization happens. - Use an arena allocator. But even if the allocator supports deallocation, fragmentation over long running tasks can still be a problem (unless we come up with an allocator as good as
jemalloc
).
The last idea I have is to create a master object that contains an id
.
struct Master {
id: usize,
}
// Master: !Sync
We can create Rc
-like reference counters from the master:
impl Master {
pub fn create<T>(&self, t: T) -> RcWithId<T> {
RcWithId {
object: // Wrap `t` with some single-thread reference count mechanism
id: self.id,
}
}
}
Each time we access the reference counted object, we require the master be available and match the id
.
struct RcWithId<T> {
object: *const T, // Some single-thread reference count mechanism
id: usize,
}
impl<T> RcWithId<T> {
// ...
pub fn access<'a>(&'a self, master: &'a Master) -> &'a T {
if self.id == master.id {
let t = // Get the real object from `self.object`
return t;
}
panic!()
}
}
It should be possible to create a safe abstraction from this. Of course, it will be painful to deal with drop.
My question is, does this idea sound right? Is there any implementation like this (or better) already?