Map_split for RwLockWriteGuard?

RefCell has map_split std::cell::RefMut - Rust

RwLockWriteGuard has... it doesn't even have map: std::sync::RwLockWriteGuard - Rust

Any chance to bring these APIs closer together?

RefCell handles the borrow state itself and as such was able to implement a state in which there are multiple mutable borrows. RwLock on the other hand uses the rwlock implementation of the platform, which doesn't offer such a possibility on any platform as far as I know. While it is probably possible to emulate it, doing so would come at a cost even when this functionality is not used.

Nah, you can rely on the underlying lock to provide all the memory barriers, so the cost is minimal.

On lock: acquire lock, increment atomic count.
On unlock: decrement atomic count, release lock.

This should be sound.

Atomic memory accesses are relatively expensive. Regular uncontended rwlocks already use a single atomic access. Adding a second one would make the unlock operation roughly twice as expensive.

The fact that the barriers are there means you can just use acqrel rather than seqcst so it's as minimal overhead as it gets. And it'd bring the APIs closer together, as they're currently completely uninterchangeable, even tho they're claimed to be equivalent.

I can't speak for map_split but I definitely ran into a scenario recently that would have been massively simplified by having map on RwLock guards.

When you have a chunk of global state that needs to be shared in a RwLock you basically can't expose pieces of that state in any way other than cloning or passing a closure that receives the "piece" of state. It's not a showstopper but I was surprised it wasn't possible at present.

To be fair we do need the ability to Send the guards and also we need guards that take an Arc to the RwLock instead of an &'a, so we can't actually use std's .-. (nor parking_lot's since those take an &'a as well...)

But it still seems like it'd be beneficial to have RwLock's and RefCell's APIs in sync.

Blame pthreads for that one (and probably for C11 and C++11 saying it's UB to unlock on a different thread).

I'd argue that it is actually possible, since this could be implemented using a recursive_rwlock (which I believe is required by C11, so pthread will support it, idk about windows). Whether or not there is a benefit, other than keeping api parity between RwLock and RefCell that outweighs the additional cost of RwLock.

I don't know that a proposal that isn't backable by a legitimate use case would do all that well.

1 Like

Fair. Maybe we shouldn't make posts about API inconsistencies until after we're done working out our requirements and figuring out what works for us. Or maybe we should still point them out anyway, even if they turn out to not affect us after all :wink:

Interesting fact: parking_lot RwWriteGuard actually has a map function. So if you really need it, here it is:

https://docs.rs/lock_api/0.4.2/lock_api/struct.RwLockWriteGuard.html

1 Like

Err, what?

AcqRel isn't as minimal overhead as it gets. On x86, pretty much all read-modify-write (RMW) operations (including Relaxed) have the same cost as SeqCst. On other platforms they have different costs, but it's not cheap (LL/SC loop) and it's certainly not true that AcqRel is as cheap as it gets.

That said, it's not exactly like libstd's options here are minimal cost compared to the platform options, so maybe it's fine.

1 Like

At least the refcounting for map/map_split on write guards can be done without the use of atomics, and thus without a performance cost, as the guards are not Send.