@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
88 lines • 3.07 kB
JavaScript
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
import { hasAborted, makeAbortError } from "../utils/abort.js";
export class KeyLock {
queues = new Map();
async withLock(key, fn, signal) {
if (hasAborted(signal))
throw makeAbortError(signal);
const entered = this.enter(key, signal);
let queue = entered instanceof Set ? entered : await entered;
try {
// Admission may have transferred immediately before cancellation.
if (hasAborted(signal))
throw makeAbortError(signal);
return await fn({
released: async (body) => {
this.leave(key, queue);
try {
return await body();
}
finally {
const back = this.enter(key);
queue = back instanceof Set ? back : await back;
}
},
});
}
finally {
this.leave(key, queue);
}
}
/** Whether someone holds the key now. */
held(key) {
return this.queues.has(key);
}
discard(key) {
this.queues.delete(key);
}
clear() {
this.queues.clear();
}
/** The key's queue: taken at once when the key is free, else once its
* holder hands it over. */
enter(key, signal) {
const queue = this.queues.get(key);
if (queue === undefined) {
const held = new Set();
this.queues.set(key, held);
return held;
}
return new Promise((resolve, reject) => {
const waiter = {
enter: () => {
signal?.removeEventListener('abort', onAbort);
resolve(queue);
},
};
const onAbort = () => {
queue.delete(waiter);
reject(makeAbortError(signal));
};
queue.add(waiter);
signal?.addEventListener('abort', onAbort, { once: true });
});
}
leave(key, queue) {
const next = queue.values().next().value;
if (next !== undefined) {
queue.delete(next);
next.enter();
}
else if (this.queues.get(key) === queue) {
this.queues.delete(key);
}
}
}
//# sourceMappingURL=lock.js.map