4

I have an Arc<RwLock<Foo>>.

Is there a way to make something out of this, on which the RwLock's write() does not exist? i.e. Is there some form of RLock which I can make from an RwLock.

Use case: I have a Foo. More than one part of my code needs to mutate the Foo (hence the RwLock), but the majority of my code must only ever have read-only access to the Foo.

Is there a way of achieving this? Or is there a way of doing this without RwLock?

fadedbee
  • 42,671
  • 44
  • 178
  • 308
  • 1
    similar post: https://stackoverflow.com/questions/68908091/how-do-i-send-read-only-data-to-other-threads-without-copying – Ömer Erden Dec 24 '21 at 07:55
  • 2
    just return the `LockGuard` (even the wrapped `Result`) from `read` – Netwave Dec 24 '21 at 08:31
  • 1
    It's hard to answer without a concrete example, but you can try to not use `RwLock` and instead [`Arc::get_mut()`](https://doc.rust-lang.org/std/sync/struct.Arc.html#method.get_mut). – Chayim Friedman Dec 24 '21 at 09:23
  • @ChayimFriedman unfortunately I have multiple clones of the `Arc>`, in different threads. – fadedbee Dec 24 '21 at 14:24
  • @Netwave Would this work if the "read only" reference needs to live for the life of the process? Or would it block the writers? – fadedbee Dec 24 '21 at 14:26
  • 1
    @fadedbee, you need to release the lock at some point, you shouldnt have a reference to the ward (because it would be locked for the entire time, so you would not be able to write it), but you can have as much of those as you want for reading. – Netwave Dec 24 '21 at 16:36

1 Answers1

3

Write your own struct that contains the Arc<RwLock<Foo>>.

#[derive(Clone, Debug)]
pub struct FooReadOnly(Arc<RwLock<Foo>>);

impl FooReadOnly {
    pub fn read(&self) -> LockResult<RwLockReadGuard<'_, Foo>> {
        self.0.read()
    }

}

(A fully fleshed-out version would also contain a wrapper for try_read().)

The general pattern worth noting here is: instead of your data type being visibly inside an Arc<RwLock<...>>, your public type contains the Arc. This allows much more flexibility in what kind of “handles” you can offer than exposing the Arc. This is a fairly common pattern in Rust — if you've ever used a library type that notes you can clone it and get another handle to the same thing, there's a good chance it's doing the same thing inside (if it's not actually a handle to an OS resource like a file descriptor).

If you wanted to fully hide the implementation details, you would also wrap the RwLockReadGuard — all it needs to do is implement Deref<Target = Foo> and forward that to the guard. Such wrappers can also do things like Derefing to some part of the Foo rather than an &Foo exactly.

Kevin Reid
  • 37,492
  • 13
  • 80
  • 108