Merge 'LimboRwLock write and read lock fixes' from Pere Diaz Bou

* `write` was returning `true` even though it shouldn't because it
should return `false` in case it was already acquired.
* `read` theoritically can increase `nread` after another thread calls
`unlock` between first lock load and increase of `nread`. So it looks
something like this:
                  1. THREAD  1: read()
                  2. THREAD 2: get lock = `SHARED_LOCK`
                  3. THREAD 1: unlock -> lock is now `NO_LOCK`
                  4. THREAD 2: increase nread, return true.
    This is obviously wrong because `nreads` will be ` > 0 ` but `lock`
is `NO_LOCK`

Closes #1676
This commit is contained in:
Pere Diaz Bou
2025-06-09 16:15:46 +02:00

View File

@@ -106,8 +106,30 @@ impl LimboRwLock {
ok
}
SHARED_LOCK => {
// There is this race condition where we could've unlocked after loading lock ==
// SHARED_LOCK.
self.nreads.fetch_add(1, Ordering::SeqCst);
true
let lock_after_load = self.lock.load(Ordering::SeqCst);
if lock_after_load != lock {
// try to lock it again
let res = self.lock.compare_exchange(
lock_after_load,
SHARED_LOCK,
Ordering::SeqCst,
Ordering::SeqCst,
);
let ok = res.is_ok();
if ok {
// we were able to acquire it back
true
} else {
// we couldn't acquire it back, reduce number again
self.nreads.fetch_sub(1, Ordering::SeqCst);
false
}
} else {
true
}
}
WRITE_LOCK => false,
_ => unreachable!(),
@@ -133,7 +155,7 @@ impl LimboRwLock {
// no op
false
}
WRITE_LOCK => true,
WRITE_LOCK => false,
_ => unreachable!(),
};
tracing::trace!("write_lock({})", ok);