Files
rspade_system/app/RSpade/Core/Js/ReadWriteLock.js
root f6fac6c4bc Fix bin/publish: copy docs.dist from project root
Fix bin/publish: use correct .env path for rspade_system
Fix bin/publish script: prevent grep exit code 1 from terminating script

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-21 02:08:33 +00:00

123 lines
3.7 KiB
JavaScript
Executable File

/**
* ReadWriteLock implementation for RSpade framework
* Provides exclusive (write) and shared (read) locking mechanisms for asynchronous operations
*/
class ReadWriteLock {
static #locks = new Map();
/**
* Get or create a lock object for a given name
* @private
*/
static #get_lock(name) {
let s = this.#locks.get(name);
if (!s) {
s = { readers: 0, writer_active: false, reader_q: [], writer_q: [] };
this.#locks.set(name, s);
}
return s;
}
/**
* Schedule the next operation for a lock
* @private
*/
static #schedule(name) {
const s = this.#get_lock(name);
if (s.writer_active || s.readers > 0) return;
// run one writer if queued
if (s.writer_q.length > 0) {
const { cb, resolve, reject } = s.writer_q.shift();
s.writer_active = true;
Promise.resolve()
.then(cb)
.then(resolve, reject)
.finally(() => {
s.writer_active = false;
this.#schedule(name);
});
return;
}
// otherwise run all queued readers in parallel
if (s.reader_q.length > 0) {
const batch = s.reader_q.splice(0);
s.readers += batch.length;
for (const { cb, resolve, reject } of batch) {
Promise.resolve()
.then(cb)
.then(resolve, reject)
.finally(() => {
s.readers -= 1;
if (s.readers === 0) this.#schedule(name);
});
}
}
}
/**
* Acquire an exclusive mutex lock by name.
* Only one writer runs at a time; blocks readers until finished.
* @param {string} name
* @param {() => any|Promise<any>} cb
* @returns {Promise<any>}
*/
static acquire(name, cb) {
return new Promise((resolve, reject) => {
const s = this.#get_lock(name);
s.writer_q.push({ cb, resolve, reject });
this.#schedule(name);
});
}
/**
* Acquire a shared read lock by name.
* Multiple readers can run in parallel; blocks when writer is active.
* @param {string} name
* @param {() => any|Promise<any>} cb
* @returns {Promise<any>}
*/
static acquire_read(name, cb) {
return new Promise((resolve, reject) => {
const s = this.#get_lock(name);
if (s.writer_active || s.writer_q.length > 0) {
s.reader_q.push({ cb, resolve, reject });
return this.#schedule(name);
}
s.readers += 1;
Promise.resolve()
.then(cb)
.then(resolve, reject)
.finally(() => {
s.readers -= 1;
if (s.readers === 0) this.#schedule(name);
});
});
}
/**
* Force-unlock a mutex (use with caution).
* Completely removes the lock state, potentially breaking waiting operations.
* @param {string} name
*/
static force_unlock(name) {
this.#locks.delete(name);
}
/**
* Get information about pending operations on a mutex.
* @param {string} name
* @returns {{readers: number, writer_active: boolean, reader_q: number, writer_q: number}}
*/
static pending(name) {
const s = this.#locks.get(name);
if (!s) return { readers: 0, writer_active: false, reader_q: 0, writer_q: 0 };
return {
readers: s.readers,
writer_active: s.writer_active,
reader_q: s.reader_q.length,
writer_q: s.writer_q.length
};
}
}