Name locks
This commit is contained in:
parent
7fcd0f0bfa
commit
2fbed09548
6 changed files with 18 additions and 16 deletions
|
|
@ -17,7 +17,7 @@ export class SafeFileSystemOperations implements FileSystemOperations {
|
||||||
private readonly fs: FileSystemOperations,
|
private readonly fs: FileSystemOperations,
|
||||||
private readonly logger: Logger
|
private readonly logger: Logger
|
||||||
) {
|
) {
|
||||||
this.locks = new Locks(logger);
|
this.locks = new Locks(SafeFileSystemOperations.name, logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async listFilesRecursively(
|
public async listFilesRecursively(
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ export class Settings {
|
||||||
>();
|
>();
|
||||||
|
|
||||||
private settings: SyncSettings;
|
private settings: SyncSettings;
|
||||||
private readonly lock: Lock = new Lock();
|
private readonly lock: Lock;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
private readonly logger: Logger,
|
private readonly logger: Logger,
|
||||||
|
|
@ -50,6 +50,8 @@ export class Settings {
|
||||||
...(initialState ?? {})
|
...(initialState ?? {})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.lock = new Lock(Settings.name, this.logger);
|
||||||
|
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
`Loaded settings: ${JSON.stringify(this.settings, null, 2)}`
|
`Loaded settings: ${JSON.stringify(this.settings, null, 2)}`
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ export class CursorTracker {
|
||||||
(cursors: MaybeOutdatedClientCursors[]) => unknown
|
(cursors: MaybeOutdatedClientCursors[]) => unknown
|
||||||
>();
|
>();
|
||||||
|
|
||||||
private readonly updateLock = new Lock();
|
private readonly updateLock = new Lock(CursorTracker.name);
|
||||||
|
|
||||||
private knownRemoteCursors: (ClientCursors & {
|
private knownRemoteCursors: (ClientCursors & {
|
||||||
upToDateness: DocumentUpToDateness;
|
upToDateness: DocumentUpToDateness;
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ describe("withLock", () => {
|
||||||
let locks: Locks<RelativePath>;
|
let locks: Locks<RelativePath>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
locks = new Locks<RelativePath>(logger);
|
locks = new Locks<RelativePath>("locks-test", logger);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should execute function with single key lock", async () => {
|
it("should execute function with single key lock", async () => {
|
||||||
|
|
@ -253,7 +253,7 @@ describe("reset", () => {
|
||||||
let locks: Locks<RelativePath>;
|
let locks: Locks<RelativePath>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
locks = new Locks<RelativePath>(logger);
|
locks = new Locks<RelativePath>("locks-test", logger);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should reject pending waiters with SyncResetError while running operation completes", async () => {
|
it("should reject pending waiters with SyncResetError while running operation completes", async () => {
|
||||||
|
|
@ -265,7 +265,7 @@ describe("reset", () => {
|
||||||
await sleep(1);
|
await sleep(1);
|
||||||
|
|
||||||
const secondPromise = locks.withLock(testPath, async () => "second");
|
const secondPromise = locks.withLock(testPath, async () => "second");
|
||||||
void secondPromise.catch(() => {}); // eslint-disable-line @typescript-eslint/no-empty-function
|
void secondPromise.catch(() => { }); // eslint-disable-line @typescript-eslint/no-empty-function
|
||||||
|
|
||||||
locks.reset();
|
locks.reset();
|
||||||
|
|
||||||
|
|
@ -286,7 +286,7 @@ describe("reset", () => {
|
||||||
await sleep(1);
|
await sleep(1);
|
||||||
|
|
||||||
const secondPromise = locks.withLock(testPath, async () => "second");
|
const secondPromise = locks.withLock(testPath, async () => "second");
|
||||||
void secondPromise.catch(() => {}); // eslint-disable-line @typescript-eslint/no-empty-function
|
void secondPromise.catch(() => { }); // eslint-disable-line @typescript-eslint/no-empty-function
|
||||||
|
|
||||||
locks.reset();
|
locks.reset();
|
||||||
|
|
||||||
|
|
@ -312,7 +312,7 @@ describe("reset", () => {
|
||||||
[testPath, testPath2],
|
[testPath, testPath2],
|
||||||
async () => "multi"
|
async () => "multi"
|
||||||
);
|
);
|
||||||
void multiKeyPromise.catch(() => {}); // eslint-disable-line @typescript-eslint/no-empty-function
|
void multiKeyPromise.catch(() => { }); // eslint-disable-line @typescript-eslint/no-empty-function
|
||||||
|
|
||||||
// Wait for the multi-key operation to acquire testPath and start waiting on testPath2
|
// Wait for the multi-key operation to acquire testPath and start waiting on testPath2
|
||||||
await sleep(10);
|
await sleep(10);
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ export class Locks<T> {
|
||||||
/** Queue of waiters for each key */
|
/** Queue of waiters for each key */
|
||||||
private readonly waiters = new Map<T, WaiterEntry<T>[]>();
|
private readonly waiters = new Map<T, WaiterEntry<T>[]>();
|
||||||
|
|
||||||
public constructor(private readonly logger?: Logger) { }
|
public constructor(private readonly name: string, private readonly logger?: Logger) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes a function while holding exclusive locks on one or more keys.
|
* Executes a function while holding exclusive locks on one or more keys.
|
||||||
|
|
@ -122,7 +122,7 @@ export class Locks<T> {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger?.debug(`Waiting for lock on ${key}`);
|
this.logger?.debug(`Waiting for lock '${this.name}' on '${key}'`);
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// DefaultDict behavior
|
// DefaultDict behavior
|
||||||
|
|
@ -149,18 +149,18 @@ export class Locks<T> {
|
||||||
public unlock(key: T): void {
|
public unlock(key: T): void {
|
||||||
if (!this.locked.has(key)) {
|
if (!this.locked.has(key)) {
|
||||||
this.logger?.debug(
|
this.logger?.debug(
|
||||||
`Attempted to unlock ${key} which is not locked`
|
`Attempted to unlock '${this.name}' on '${key}' which is not locked`
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger?.debug(`Releasing lock on ${key}`);
|
this.logger?.debug(`Releasing lock '${this.name}' on '${key}'`);
|
||||||
|
|
||||||
// Remove first waiter to ensure FIFO order
|
// Remove first waiter to ensure FIFO order
|
||||||
const nextWaiter = this.waiters.get(key)?.shift();
|
const nextWaiter = this.waiters.get(key)?.shift();
|
||||||
|
|
||||||
if (nextWaiter) {
|
if (nextWaiter) {
|
||||||
this.logger?.debug(`Granted lock on ${key}`);
|
this.logger?.debug(`Granted lock '${this.name}' on '${key}'`);
|
||||||
nextWaiter.resolve();
|
nextWaiter.resolve();
|
||||||
} else {
|
} else {
|
||||||
this.locked.delete(key);
|
this.locked.delete(key);
|
||||||
|
|
@ -171,8 +171,8 @@ export class Locks<T> {
|
||||||
export class Lock {
|
export class Lock {
|
||||||
private readonly locks: Locks<boolean>;
|
private readonly locks: Locks<boolean>;
|
||||||
|
|
||||||
public constructor(logger?: Logger) {
|
public constructor(name: string, logger?: Logger) {
|
||||||
this.locks = new Locks(logger);
|
this.locks = new Locks(name, logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async withLock<R>(fn: () => R | Promise<R>): Promise<R> {
|
public async withLock<R>(fn: () => R | Promise<R>): Promise<R> {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ export function slowWebSocketFactory(
|
||||||
private static readonly RECEIVE_KEY = "websocket-receive";
|
private static readonly RECEIVE_KEY = "websocket-receive";
|
||||||
private static readonly SEND_KEY = "websocket-send";
|
private static readonly SEND_KEY = "websocket-send";
|
||||||
|
|
||||||
private readonly locks = new Locks(logger);
|
private readonly locks = new Locks(FlakyWebSocket.name, logger);
|
||||||
|
|
||||||
public set onopen(callback: ((event: Event) => void) | null) {
|
public set onopen(callback: ((event: Event) => void) | null) {
|
||||||
super.onopen = async (event: Event): Promise<void> => {
|
super.onopen = async (event: Event): Promise<void> => {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue