WIP: Smart create call #184

Draft
schmelczer wants to merge 46 commits from asch/smart-create into main
6 changed files with 18 additions and 16 deletions
Showing only changes of commit 2fbed09548 - Show all commits

View file

@ -17,7 +17,7 @@ export class SafeFileSystemOperations implements FileSystemOperations {
private readonly fs: FileSystemOperations,
private readonly logger: Logger
) {
this.locks = new Locks(logger);
this.locks = new Locks(SafeFileSystemOperations.name, logger);
}
public async listFilesRecursively(

View file

@ -38,7 +38,7 @@ export class Settings {
>();
private settings: SyncSettings;
private readonly lock: Lock = new Lock();
private readonly lock: Lock;
public constructor(
private readonly logger: Logger,
@ -50,6 +50,8 @@ export class Settings {
...(initialState ?? {})
};
this.lock = new Lock(Settings.name, this.logger);
this.logger.debug(
`Loaded settings: ${JSON.stringify(this.settings, null, 2)}`
);

View file

@ -22,7 +22,7 @@ export class CursorTracker {
(cursors: MaybeOutdatedClientCursors[]) => unknown
>();
private readonly updateLock = new Lock();
private readonly updateLock = new Lock(CursorTracker.name);
private knownRemoteCursors: (ClientCursors & {
upToDateness: DocumentUpToDateness;

View file

@ -18,7 +18,7 @@ describe("withLock", () => {
let locks: Locks<RelativePath>;
beforeEach(() => {
locks = new Locks<RelativePath>(logger);
locks = new Locks<RelativePath>("locks-test", logger);
});
it("should execute function with single key lock", async () => {
@ -253,7 +253,7 @@ describe("reset", () => {
let locks: Locks<RelativePath>;
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 () => {
@ -265,7 +265,7 @@ describe("reset", () => {
await sleep(1);
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();
@ -286,7 +286,7 @@ describe("reset", () => {
await sleep(1);
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();
@ -312,7 +312,7 @@ describe("reset", () => {
[testPath, testPath2],
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
await sleep(10);

View file

@ -20,7 +20,7 @@ export class Locks<T> {
/** Queue of waiters for each key */
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.
@ -122,7 +122,7 @@ export class Locks<T> {
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) => {
// DefaultDict behavior
@ -149,18 +149,18 @@ export class Locks<T> {
public unlock(key: T): void {
if (!this.locked.has(key)) {
this.logger?.debug(
`Attempted to unlock ${key} which is not locked`
`Attempted to unlock '${this.name}' on '${key}' which is not locked`
);
return;
}
this.logger?.debug(`Releasing lock on ${key}`);
this.logger?.debug(`Releasing lock '${this.name}' on '${key}'`);
// Remove first waiter to ensure FIFO order
const nextWaiter = this.waiters.get(key)?.shift();
if (nextWaiter) {
this.logger?.debug(`Granted lock on ${key}`);
this.logger?.debug(`Granted lock '${this.name}' on '${key}'`);
nextWaiter.resolve();
} else {
this.locked.delete(key);
@ -171,8 +171,8 @@ export class Locks<T> {
export class Lock {
private readonly locks: Locks<boolean>;
public constructor(logger?: Logger) {
this.locks = new Locks(logger);
public constructor(name: string, logger?: Logger) {
this.locks = new Locks(name, logger);
}
public async withLock<R>(fn: () => R | Promise<R>): Promise<R> {

View file

@ -11,7 +11,7 @@ export function slowWebSocketFactory(
private static readonly RECEIVE_KEY = "websocket-receive";
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) {
super.onopen = async (event: Event): Promise<void> => {