1.2.0 • Published 11 months ago
ryoiki v1.2.0
Ryoiki
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.
📦 Installation
npm install ryoikior
import { Ryoiki } from 'https://cdn.jsdelivr.net/npm/ryoiki@1/+esm'🚀 Usage
Basic Example
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 unlockKey Concepts
1. Default Lock Range
If the first parameter of
readLockorwriteLockis 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))
2. Lock Waiting Behavior
- 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.
3. Timeout Behavior
- Both
readLockandwriteLocknow support an optionaltimeoutparameter. - 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
timeoutperiod, a timeout error is thrown.
- Defaults to
4. Unlocking
- Always use
finallyto 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
5. Locks, Deadlocks, and Caution
readLockandwriteLockare 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
readLockis waiting for awriteLockto release, and thewriteLockis waiting for areadLockto release.
To prevent deadlocks:
- Ensure that locks are always released as soon as they are no longer needed.
- Use
timeoutto avoid waiting indefinitely.
- A
For a deeper understanding of these concepts, you can refer to these Wikipedia articles:
📖 API
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.
readUnlock(lockId: string): void
- Description: Releases a read lock.
writeUnlock(lockId: string): void
- Description: Releases a write lock.
range(start: number, length: number): [number, number]
- 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]
🌟 Examples
Read and Write Operations with Timeout
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📜 License
MIT License. Feel free to use and contribute! 🙌