From b47ab8e27a01822f2ba856e9b3e6d98ebd05b135 Mon Sep 17 00:00:00 2001 From: Devin Ivy Date: Thu, 23 Apr 2026 17:36:14 +0000 Subject: [PATCH] feat(shared): add RwLock.tryReadLock synchronous variant --- src/shared/rwlock.ts | 13 +++++++++++++ test/shared/rwlock.test.ts | 33 +++++++++++++++++++++++++++++++++ 2 file(s) changed, 46 insertion(s)(+), 0 deletion(s)(-) diff --git a/src/shared/rwlock.ts b/src/shared/rwlock.ts --- a/src/shared/rwlock.ts +++ b/src/shared/rwlock.ts @@ -63,6 +63,19 @@ } } + /** + * Attempts to acquire a read lock without waiting. + * Makes a single atomic attempt; returns `null` on contention even from other readers. + * @returns A disposable {@link ReadGuard} if acquired, otherwise `null`. + */ + tryReadLock(): ReadGuard | null { + const state = Atomics.load(this.view, 0); + if (state >= UNLOCKED && Atomics.compareExchange(this.view, 0, state, state + 1) === state) { + return new ReadGuard(this); + } + return null; + } + /** Releases a read lock. Wakes a waiting writer if this was the last reader. */ readUnlock(): void { const prev = Atomics.sub(this.view, 0, 1); diff --git a/test/shared/rwlock.test.ts b/test/shared/rwlock.test.ts --- a/test/shared/rwlock.test.ts +++ b/test/shared/rwlock.test.ts @@ -118,4 +118,37 @@ const results = await Promise.all([reader1Done, reader2Done]); assert.deepEqual(results.sort(), ['r1', 'r2']); }); + + it('tryReadLock returns a guard when unlocked', () => { + const rw = rwlock(); + const guard = rw.tryReadLock(); + assert.ok(guard); + assert.equal(typeof guard[Symbol.dispose], 'function'); + guard[Symbol.dispose](); + // Should be able to read-lock again after dispose + const guard2 = rw.tryReadLock(); + assert.ok(guard2); + rw.readUnlock(); + }); + + it('multiple concurrent tryReadLock calls all succeed', () => { + const rw = rwlock(); + const g1 = rw.tryReadLock(); + const g2 = rw.tryReadLock(); + const g3 = rw.tryReadLock(); + assert.ok(g1); + assert.ok(g2); + assert.ok(g3); + rw.readUnlock(); + rw.readUnlock(); + rw.readUnlock(); + }); + + it('tryReadLock returns null when write-locked', async () => { + const rw = rwlock(); + await rw.writeLock(); + const guard = rw.tryReadLock(); + assert.equal(guard, null); + rw.writeUnlock(); + }); }); -- tangled.sh