Hi
I am trying to wrap a C library with Rust, so far I made a successful run, but I tried to be more idiomatic and so destroy the internal C resources upon Drop, but now it is giving me headaches.
The internal resources can be represented as a tree, we have a main resource Father
that is a
factory of Child
objects that in turn are factories of other objects, etc. . We obtain Father
's from a singleton factory of Father
's.
The catchy part is that the FFI requires a reference to the Father
in order to destroy a Child
So far this has been my design
pub struct FatherFactory {
pub(crate) inner: *mut crate::raw::FatherFactory,
}
impl FatherFactory {
pub fn get_instance() -> Option<Self> {
unsafe {
let ff = crate::raw::FatherFactory_get_instance();
if ff.is_null() {
None
} else {
Some(Self { inner: ff })
}
}
}
...
pub fn create_father(
&mut self,
father_id: u32,
) -> Result<Father, ErrorCode> {
unsafe {
let rawfather = crate::raw::FatherFactory_create_father(
self.inner,
father_id,
);
if rawfather.is_null() {
Err(ErrorCode::Err)
} else {
let father = Father::new(FatherInner {
raw: FatherRaw {
parent: self.inner,
me: rawfather,
},
childs: Default::default(),
});
Ok(father)
}
}
}
pub(crate) struct FatherRaw {
pub(crate) me: *mut crate::raw::Father,
pub(crate) parent: *mut crate::raw::FatherFactory,
}
pub(crate) struct FatherInner {
pub(crate) childs: [Option<Arc<RwLock<ChildInner>>>; crate::MAX_CHILDS_PER_FATHER],
pub(crate) raw: FatherRaw,
}
pub struct Father {
pub(crate) inner: Arc<RwLock<FatherInner>>,
}
impl Father {
pub(crate) fn new(inner: FatherInner) -> Self {
Self {
inner: Arc::new(RwLock::new(inner)),
}
}
...
pub fn create_child(
&mut self,
child_name: &str,
) -> Result<Child, ErrorCode> {
unsafe {
let rawchild = crate::raw::Father_create_child(
self.inner.read().unwrap().raw.me,
child_name.as_ptr() as *const _,
);
if rawchild.is_null() {
Err(ErrorCode::Err)
} else {
let maybeidx = self
.inner
.read()
.unwrap()
.childs
.iter()
.position(|x| x.is_none());
if let Some(idx) = maybeidx {
let inner = Arc::new(RwLock::new(ChildInner {
_super: Arc::downgrade(&self.inner),
raw: ChildRaw {
parent: self.inner.read().unwrap().raw.me,
me: rawchild,
},
}));
let child = Child {
inner: Arc::downgrade(&inner),
};
self.inner.write().unwrap().childs[idx] = Some(inner);
Ok(child)
} else {
Err(ErrorCode::NoResources)
}
}
}
}
}
pub(crate) struct ChildRaw {
pub(crate) me: *mut crate::raw::Child,
pub(crate) parent: *mut crate::raw::Father,
}
pub(crate) struct ChildInner {
pub(crate) _super: Weak<RwLock<FatherInner>>,
pub(crate) raw: ChildRaw,
}
pub struct Child {
pub(crate) inner: Weak<RwLock<ChildInner>>,
}
When a drop of the Father
happens I need to drop first the Child
, hence the FatherRaw
structure that allows me to defer the disposal of the father until the 1st field of FatherInner
is dropped. While this code works I dont like how it looks, I dont like having two exactly same *mut crate::raw::Father
(i.e. FatherRaw.me
and ChildRaw.parent
) I'd rather have a reference in the Child
to get the raw info from the Father
.
I already tried using Arc
and Weak
to get this info, but the Father
is destroyed before the drop of the Child
happens, so if I have a Weak
to the Father
it will fail to be upgraded.
I am not sure how to workaround this, ideally without using unsafe code / raw pointers.
Thanks