Thanks, that makes more sense now. I feel like I'm back in junior school ![]()
It appears for sure I can't use Arc<T> or Mutex<T>.
What I need is a kind of Mutex that never exposes &mut T, but only &T. More pondering required!
Thanks, that makes more sense now. I feel like I'm back in junior school ![]()
It appears for sure I can't use Arc<T> or Mutex<T>.
What I need is a kind of Mutex that never exposes &mut T, but only &T. More pondering required!
You can write your own types that wrap std::sync::Mutex and std::sync::MutexGuard and impose this restriction, then add unsafe impl Send unsafe impl Sync to allow it to be used from other threads.
struct LibThingReadOnly<'l> {
mutex: std::sync::Mutex<&'l LibThing>,
}
struct LibThingReadOnlyGuard<'l, 'm> {
guard: std::sync::MutexGuard<'m, &'l LibThing>,
}
impl<'l> LibThingReadOnly<'l> {
fn lock<'m>(&'m self) -> LibThingReadOnlyGuard<'l, 'm> {...}
}
impl LibThingReadOnlyGuard<'_> {
// expose the read-only operations of the C library here
}
// SAFETY: `LibThingReadOnly` does not own or exclusively borrow the
// `LibThing`, so it cannot drop the `LibThing` on the wrong thread or
// transfer ownership to anything else.
unsafe impl Send for LibThingReadOnly<'_> {}
// SAFETY: `LibThingReadOnly` ensures that only the operations which may
// be called from another thread are callable through this.
unsafe impl Sync for LibThingReadOnly<'_> {}
Beautiful, thanks ![]()
I have corrected an error in this example code — I wrote the Send and Sync impls on LibThingReadOnlyGuard when they should be on LibThingReadOnly. (It might still be correct to implement Send for LibThingReadOnlyGuard, but definitely not Sync since that is what it is meant to prevent.)
Really healpful , thanks