#how to translate c++ function `delete_nodes` which calls `free` on a std vector of *Nodes

39 messages · Page 1 of 1 (latest)

tribal cove
#

hi folks! i am porting some c++ where we have something like std::vector<*Node>

To make this better, I am migrating towards Vec<Arc<RwLock<Option<Node>>>>

I am nearly there. However, now I need to consider the c++ function delete_nodes and delete_node where we traverse the vector, finalize each node and free their memory

So... what can be done here in rust land?

At the moment, I am assuming the c++ frees the nodes in the proper places (in other words that no memory accesses are attempted after the node is properly freed.

I dont have enough of the system translated yet to test this assumption at runtime.

I am thinking that perhaps all I need to do is simply take the option from the interior of this Arc.
If anybody else tries to use it, they will see the fact that it has become none and have to reroute their logic or panic.

Is this an acceptable solution?

kind basalt
#

when Arc's reference counter hits 0 (meaning no one has pointer to data) the data it holds get deallocated

crude stag
tribal cove
#

in the c++ it is just a raw pointer -- i have found that sometimes this pointer can be null

#

there are checks for example that it is not null

#

so when i translated these sections, i used an option to express these semantics

#

hopefully, as this translation becomes more advanced, the option itself can even be removed

#

that would be best

#

but, for now, i want to keep the semantics as close as possible to the original c++

#

these pointers are stored in a vec and accessed from multiple threads

#

it looks like most of the time they are just read

crude stag
#

if that's the case I'd say it's more like Vec<Option<Arc<RwLock<Node>>>>

tribal cove
#

i went back and forth a bit as to whether the option should be on the outer or inner layer

#

there are tradeoffs

crude stag
#

it depends whether the pointer itself is used concurrently, or the Nodes

tribal cove
#

interesting

#

full disclosure, i am actually doing something like this: ```rust
pub struct Amo<T>(Arc<RwLock<Option<T>>>);

pub type AmoReadGuard<'a, T> = MappedRwLockReadGuard<'a, T>;
pub type AmoWriteGuard<'a, T> = MappedRwLockWriteGuard<'a, T>;

pub type AmoOuterReadGuard<'a, T> = RwLockReadGuard<'a, Option<T>>;
pub type AmoOuterWriteGuard<'a, T> = RwLockWriteGuard<'a, Option<T>>;

impl<T> Clone for Amo<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}

impl<T> From<T> for Amo<T> {
fn from(x: T) -> Self {
Self(Arc::new(RwLock::new(Some(x))))
}
}

impl<T> From<&Self> for Amo<T> {
fn from(x: &Self) -> Self {
x.clone()
}
}

impl<T> From<Option<T>> for Amo<T> {
fn from(x: Option<T>) -> Self {
Self(Arc::new(RwLock::new(x)))
}
}

impl<T> Amo<T> {

delegate!{
    to self.0.read() {
        pub fn is_some(&self) -> bool;
        pub fn is_none(&self) -> bool;
    }

    to self.0.write() {
        pub fn take(&self) -> Option<T>;
    }
}

//fn inner(&self) -> Arc<RwLock<Option<T>>> { self.0.clone() }

pub fn load(&self, inner: Option<T>) {
    todo!();
}

pub fn none() -> Self {
    Self(Arc::new(RwLock::new(None)))
}

pub fn get<'a>(&'a self) -> AmoReadGuard<'a, T> {
    let mut guard = self.0.read();
    RwLockReadGuard::map(guard, |g| g.as_ref().unwrap())
}

pub fn get_mut<'a>(&'a self) -> AmoWriteGuard<'a, T> {
    let mut guard = self.0.write();
    RwLockWriteGuard::map(guard, |g| g.as_mut().unwrap())
}

pub fn getopt<'a>(&'a self) -> AmoOuterReadGuard<'a, T> {
    self.0.read()
}

pub fn getopt_mut<'a>(&'a self) -> AmoOuterWriteGuard<'a, T> {
    self.0.write()
}

}

crude stag
#

what you have now would be more like std::vector<**Node>

tribal cove
#

this interface is in flux -- i am not sure i want the getopt variants of get

#

actually i can probably delegate read and write to the inner item -- still not sure i want to expose this

#

get and get_mut are working pretty well

crude stag
#

I don't really know if I can tell you something more without a full dive into the semantics of C++ code

#

I'd say just go with what you have for now

tribal cove
#

thanks

crude stag
#

you say you're going to change that anyway 🙂

#

you'll probably be able to refine what you really want with further iterations

tribal cove
#

basically what is happening is that the Amo construct I have outlined above is working pretty well at the moment, but there is a funciton in the c++ where the original author wanted to explicitly call free on the Nodes. what I am currently doing is just calling take on the Amo, but suppose I may need to refine this approach as the codebase grows and I gain a higher sense of exactly what is going on where, when, and why

#

exactly

#

thanks for the help

#

i suppose the worst case is that these Amo Nodes are never truly deallocated and just become a bunch of null options

#

idk.. i dont that is the way it will actually go

#

anyway, I'll just keep it in mind and write a note in the source code

#

thanks for the help!

#

I am having this other problem though because this morning I switched Mutex to RwLock in Amo

#

Amo stands for "Arc Mutex Option" by the way

#

maybe it could be Alo for "Arc Lock Option", but I kind of like Amo now

#

I want to write a wait_until_wake function taking a Condvar as well as a RwLockReadGuard and a timeout

#

and then eventually call condvar.wait_until(lock,timeout_time)

#

it works if lock is a MutexGuard, but not a RwLockReadGuard