RwLock is UnwindSafe
despite not poisoning on read()
, can cause broken invariants
#89832
Labels
A-concurrency
Area: Concurrency
C-bug
Category: This is a bug.
T-libs-api
Relevant to the library API team, which will review and decide on the PR/issue.
RwLock
only poisons on panic withwrite()
, it explicitly does not poison on panic withread()
. This makes sense for most types, but for types with interior mutabilityread()
can expose that interior mutability and allow broken invariants to be witnessed without usingAssertUnwindSafe
or spawning threads.If I understand this correctly, due to the lack of read poisoning, it really should only be
UnwindSafe where T: UnwindSafe
(and I believeRefUnwindSafe where T: RefUnwindSafe
, though I'm not certain as I'm still struggling to properly conceptualize this.).On a similar note,
RwLockReadGuard
isUnwindSafe
andRefUnwindSafe
without any conditions. So isRwLockWriteGuard
, but that's the one that does poisoning so it's fine there.RwLockReadGuard
should at least requireT: RefUnwindSafe
for it to beUnwindSafe
andRefUnwindSafe
. I believe that ifRwLock
is adjusted thenRwLockReadGuard
will pick it up automatically due to the auto trait rules, though in this caseRwLockWriteGuard
will need the manual implementations as it does poisoning.I'm not sure if there's any quick go-to for testing unwind safety, but I wrote a simple type that maintains a logical invariant with interior mutability. Without
RwLock
I get the expected compiler error trying to pass a reference to it across acatch_unwind
barrier. Wrapping it inRwLock
gets rid of the compiler error without introducing poisoning, which then allows for observing a broken invariant. Playground linkIn this code I'm passing the
RwLockReadGuard
across thecatch_unwind
barrier, but passing the&RwLock
across and calling.read().unwrap()
on each access produces the same results (meaning this can't just be fixed by changingRwLockReadGuard
).Meta
Playground rust version: Stable channel, 1.55.0
Additional context
I discovered this when I was trying to figure out how to make a type with interior mutability safe to pass to something that requires
RefUnwindSafe
. I thought "it has interior mutability, maybe I should just use aRwLock
and only take reads on it", at which point I discovered that this doesn't add poisoning despite beingRefUnwindSafe
. In retrospect, reading can't add poisoning (given that there can be concurrent reads, and you can't poison an extant guard) so I really do need a mutex, but the fact thatRwLock
makes the compiler happy here is a problem.The text was updated successfully, but these errors were encountered: