Ryoiki
is a JavaScript library that provides read/write locks based on specific ranges.
This library helps maintain consistency and stability by controlling concurrent access to data.
npm install ryoiki
or
import { Ryoiki } from 'https://cdn.jsdelivr.net/npm/ryoiki@1/+esm'
import { Ryoiki } from 'ryoiki'
const ryoiki = new Ryoiki()
let lockId: string; // Store lock ID externally
await ryoiki.readLock([0, 10], async (_lockId) => {
lockId = _lockId
console.log('Reading from range [0, 10]')
}).finally(() => ryoiki.readUnlock(lockId)) // Always unlock
-
If the first parameter of
readLock
orwriteLock
is omitted, it defaults to[-Infinity, Infinity]
. -
Example:
let lockId: string await ryoiki.readLock(async (_lockId) => { lockId = _lockId console.log('Read lock applied to the entire range') }).finally(() => ryoiki.readUnlock(lockId))
- Read Lock:
- Can execute immediately if overlapping with other read locks.
- Waits if overlapping with a write lock.
- Write Lock:
- Waits if overlapping with other read or write locks.
- Both
readLock
andwriteLock
now support an optionaltimeout
parameter. - Timeout: The lock request will wait for the specified time in milliseconds before throwing an error if the lock cannot be acquired.
- Defaults to
Infinity
, meaning it will wait indefinitely unless otherwise specified. - If the lock cannot be acquired within the given
timeout
period, a timeout error is thrown.
- Defaults to
-
Always use
finally
to release locks, even if an error occurs in the callback. -
Correct Usage:
let lockId: string await ryoiki.writeLock([0, 10], async (_lockId) => { lockId = _lockId throw new Error('Exception occurred') }).finally(() => ryoiki.writeUnlock(lockId)) // Always unlock
-
readLock
andwriteLock
are used to manage access to data by locking specific ranges.- A read lock allows multiple readers but waits if a write lock exists.
- A write lock prevents both read and write locks in the same range, ensuring exclusive access.
-
Deadlock occurs when two or more processes are unable to proceed because each is waiting for the other to release a lock.
In the context ofRyoiki
, this could happen if:- A
readLock
is waiting for awriteLock
to release, and thewriteLock
is waiting for areadLock
to release.
To prevent deadlocks:
- Ensure that locks are always released as soon as they are no longer needed.
- Use
timeout
to avoid waiting indefinitely.
- A
-
For a deeper understanding of these concepts, you can refer to these Wikipedia articles:
readLock(range?: [number, number], callback: (lockId: string) => Promise<T>, timeout?: number): Promise<T>
- Description: Requests a read lock for the specified range.
- Parameters:
range
: Range to lock as[start, end]
. Defaults to[-Infinity, Infinity]
.callback
: Async function executed after lock is acquired.lockId
: Unique ID for the current lock.
timeout
: Optional timeout in milliseconds. If the lock cannot be acquired within the specified time, the operation will throw a timeout error.- Defaults to
undefined
(wait indefinitely).
- Defaults to
- Returns: The result of the callback function.
writeLock(range?: [number, number], callback: (lockId: string) => Promise<T>, timeout?: number): Promise<T>
- Description: Requests a write lock for the specified range.
- Parameters:
range
: Range to lock as[start, end]
. Defaults to[-Infinity, Infinity]
.callback
: Async function executed after lock is acquired.lockId
: Unique ID for the current lock.
timeout
: Optional timeout in milliseconds. If the lock cannot be acquired within the specified time, the operation will throw a timeout error.- Defaults to
undefined
(wait indefinitely).
- Defaults to
- Returns: The result of the callback function.
- Description: Releases a read lock.
- Description: Releases a write lock.
-
Description: Creates a tuple
[start, start + length]
based on the given start and length. -
Usage Example:
const r = ryoiki.range(0, 10) // [0, 10] const s = ryoiki.range(5, 10) // [5, 15]
const ryoiki = new Ryoiki()
let lockId: string
// Read Lock with timeout
await ryoiki.readLock([0, 10], async (_lockId) => {
lockId = _lockId
console.log('Reading from range [0, 10]')
}, 1000).finally(() => ryoiki.readUnlock(lockId)) // Always unlock
// Write Lock with timeout
await ryoiki.writeLock([5, 15], async (_lockId) => {
lockId = _lockId
console.log('Writing to range [5, 15]')
}, 1000).finally(() => ryoiki.writeUnlock(lockId)) // Always unlock
MIT License. Feel free to use and contribute! π