Skip to content

feat(rwlock): add an upgradable readers-writer lock - #366

Merged
orthur2 merged 6 commits into
apache:mainfrom
Kaileshwar16:feat/169-upgradable-rwlock
Oct 30, 2026
Merged

orthur2 merged 6 commits into
apache:mainfrom
Kaileshwar16:feat/169-upgradable-rwlock

Conversation

@Kaileshwar16

Copy link
Copy Markdown
Contributor

Closes #169.

Add UpgradableRwLock with ordinary readers, exclusive writers, and one upgradable
reader. Support asynchronous upgrade, guard-preserving try_upgrade(), and downgrade to
an ordinary reader.

Cancelling a pending upgrade releases the consumed guard and its permits. Include rustdoc
examples and deterministic tests for ordering, cancellation, reader limits, cross-thread
visibility, and guard traits.

Design Notes

Reuse the existing RwLock and semaphore implementation. Writers and upgradable readers
acquire a FIFO reservation before requesting data permits, preventing an upgrade from
queuing behind a writer waiting for the upgrader’s read permit.

Ordering is FIFO within each queue, rather than globally: ordinary readers can pass
writers waiting for the reservation. Existing RwLock behavior remains unchanged.

Validation:

  • cargo x lint and cargo x check passed.
  • cargo x test: 625 passed.
  • cargo x miri: 194 passed, 10 existing skips.
  • All 24 feature tests passed 20 repetitions and four Miri seeds.

This is an initial implementation intended to help settle the API design in #169. I'd especially appreciate feedback on the two-queue fairness model, cancellation semantics for a pending upgrade, and whether borrowed guards are sufficient for the first version. I'm happy to adjust the design based on maintainer feedback.

@orthur2
orthur2 self-requested a review October 23, 2026 15:39

@orthur2 orthur2 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking this on. The reservation protocol and the cancellation paths look sound to me.

On the borrowed guards question, I think they’re fine for a first version. The existing owned guards hold an Arc<RwLock>, which we can’t get by projecting an Arc<UpgradableRwLock>, so owned support couldn’t reuse them as they are.

Please also add UpgradableRwLock to the API map in lib.rs and README.

Comment thread CHANGELOG.md Resolved
Comment thread asyncband/src/rwlock/upgradable.rs Resolved
@Kaileshwar16
Kaileshwar16 force-pushed the feat/169-upgradable-rwlock branch from e675886 to 4c117c6 Compare October 24, 2026 09:53
@Kaileshwar16
Kaileshwar16 requested a review from orthur2 October 24, 2026 09:55

@orthur2 orthur2 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, looks good to me now.

@orthur2
orthur2 requested a review from PragmaTwice October 25, 2026 03:18
@PragmaTwice

Copy link
Copy Markdown
Member

Reviewed 4c117c6 and the discussion in #169. The design looks reasonable to me. I'm fine with borrowed guards for the first version, the documented two-queue ordering, and cancellation releasing the consumed guard.

One small test issue: the compile-fail example here is missing:

use asyncband::rwlock::RwLockUpgradableReadGuard;

It currently fails with E0433 because the type is not in scope, so compile_fail passes for the wrong reason. Adding the import produces the expected E0505; removing the later use of borrowed then compiles successfully.

Local validation:

  • cargo x test: 641 passed, including doctests.
  • The 24 new integration tests passed 20 additional runs and Miri seeds 0–3 with strict provenance and symbolic alignment checks.
  • The feature matrix, Clippy, formatting, and rustdoc checks passed.

I also ran benchmarks. These are uncontended release-build results on x86_64 Linux with Rust 1.99.1, pinned to one CPU: medians across 11 rounds, including guard acquisition and release.

Operation RwLock UpgradableRwLock
Ordinary read 25.4 ns 25.3 ns
Direct write 45.1 ns 110.1 ns
8 reads + 1 write, whole cycle 242.2 ns 289.0 ns
Upgradable read N/A 82.8 ns
Upgradable read + upgrade + write N/A 141.2 ns

Ordinary reads stay about the same, while writes have a clear extra cost. I found no consistent regression in the existing RwLock benchmarks against bfa6805. Contended results depended on the workload; these shared-VM measurements do not establish a general performance ranking.

Please fix the doctest and resolve the changelog conflict before merging. Adding benchmarks for the new type would also help track its performance.

@Kaileshwar16

Copy link
Copy Markdown
Contributor Author

@orthur2 i have addressed the maintainer review, can u review the changes

@orthur2

orthur2 commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

Thanks @Kaileshwar16, the latest changes look good to me, and thanks @PragmaTwice for catching the doctest. I'm merging this now. If anything else comes up, feel free to leave a post-merge review and we can follow up in a separate PR.

@orthur2
orthur2 merged commit 6f040df into apache:main Oct 30, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Provide an upgradable readers-writer lock

4 participants