vault-link/frontend/sync-client/src/utils/locks.ts
2025-06-01 09:51:33 +01:00

60 lines
1.3 KiB
TypeScript

import type { Logger } from "../tracing/logger";
// Manages locks on T to prevent concurrent modifications
// allowing the client's FileOperations implementation to be simpler.
// Locks are granted in a first-in-first-out order.
export class Locks<T> {
private readonly locked = new Set<T>();
private readonly waiters = new Map<T, (() => unknown)[]>();
public constructor(private readonly logger: Logger) {}
public tryLock(key: T): boolean {
if (this.locked.has(key)) {
return false;
}
this.locked.add(key);
return true;
}
public async waitForLock(key: T): Promise<void> {
if (this.tryLock(key)) {
return Promise.resolve();
}
this.logger.debug(`Waiting for lock on ${key}`);
return new Promise((resolve) => {
let waiting = this.waiters.get(key);
if (!waiting) {
waiting = [];
this.waiters.set(key, waiting);
}
waiting.push(resolve);
});
}
public unlock(key: T): void {
if (!this.locked.has(key)) {
throw new Error(`Document ${key} is not locked, cannot unlock`);
}
// Remove the first element to ensure FIFO unblocking order
const nextWaiting = this.waiters.get(key)?.shift();
if (nextWaiting) {
this.logger.debug(`Granted lock on ${key}`);
nextWaiting();
} else {
this.locked.delete(key);
}
}
public reset(): void {
this.locked.clear();
this.waiters.clear();
}
}