What kind of semantics do you want, and what guarantees does the long-running process guarantee about shared memory?
If you're OK with having data that's accurate as of the call, but will become out of date in the future, I would recommend having SHMRegion
implement Clone
and using (&*ptr).clone()
.
If you want continually updating memory, that will be a bit trickier. A direct struct, like SHMRegion
, is stored by-value on the stack wherever it exists. If you want to return an SHMRegion
that represents the current data, and you don't want to copy it, I would recommend making a wrapper struct around a pointer to the actual data, and returning that. Something like
struct SHMView {
inner: NonNull<SHMRegion>,
}
// ...
impl SHMRegion {
pub fn new() -> Result<SHMView, Error<String>> {
// ...
let ptr = // ...
SHMView {
inner: NonNull::new(ptr).expect("shmat should not return null pointer")
}
}
Then you'd provide accessor methods on SHMView
which do the appropriate synchronization to prevent race methods with the long-running process and then manually read the fields of the inner SHMRegion
. I'd recommend NonNull
(see its docs), but *mut SHMRegion
or *const SHMRegion
would also be reasonable.
As for being valid, I believe it is. You will want to ensure you're avoiding all race conditions with shared memory writing and reading, but I assume you're handling that in some way.
If the CLI and the long-running process are different binaries, you will need #[repr(C)]
on SHMRegion
to guarantee that it has the same memory layout. You'll probably want that anyways, but maybe you could get away with not having it if they're literally the same program?
And as always for this kind of thing, the nomicon is an excellent resource for ensuring you're doing unsafe things soundly. Data Layout - The Rustonomicon might be useful, as well as Races - The Rustonomicon (this is about in-process data races, but most or all of it applies to what you're doing as well since you're sharing data between processes).
Making this code correct won't be a particularly small endeavor, but it's definitely doable. If you're not locked in, there are some alternatives that might be easier to implement safely (https://www.reddit.com/r/rust/comments/9d7pen/ipc_what_solutions_exist/ is a relatively recent post on this) but that's just if you're looking for a general solution. Shared memory isn't bad for this, you just have to ensure the code is sound.