From 56d6f19365e06a71990f706c485456b7b9a44908 Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 31 May 2023 11:57:26 +0100 Subject: [PATCH] Move CachedOperation to a new file --- extensions/ql-vscode/src/helpers.ts | 71 ------------------- .../contextual/template-provider.ts | 2 +- .../ql-vscode/src/pure/cached-operation.ts | 70 ++++++++++++++++++ 3 files changed, 71 insertions(+), 72 deletions(-) create mode 100644 extensions/ql-vscode/src/pure/cached-operation.ts diff --git a/extensions/ql-vscode/src/helpers.ts b/extensions/ql-vscode/src/helpers.ts index d4e8a9a8c..444338cc4 100644 --- a/extensions/ql-vscode/src/helpers.ts +++ b/extensions/ql-vscode/src/helpers.ts @@ -584,77 +584,6 @@ export async function getPrimaryDbscheme( return dbscheme; } -/** - * A cached mapping from strings to value of type U. - */ -export class CachedOperation { - private readonly operation: (t: string, ...args: any[]) => Promise; - private readonly cached: Map; - private readonly lru: string[]; - private readonly inProgressCallbacks: Map< - string, - Array<[(u: U) => void, (reason?: any) => void]> - >; - - constructor( - operation: (t: string, ...args: any[]) => Promise, - private cacheSize = 100, - ) { - this.operation = operation; - this.lru = []; - this.inProgressCallbacks = new Map< - string, - Array<[(u: U) => void, (reason?: any) => void]> - >(); - this.cached = new Map(); - } - - async get(t: string, ...args: any[]): Promise { - // Try and retrieve from the cache - const fromCache = this.cached.get(t); - if (fromCache !== undefined) { - // Move to end of lru list - this.lru.push( - this.lru.splice( - this.lru.findIndex((v) => v === t), - 1, - )[0], - ); - return fromCache; - } - // Otherwise check if in progress - const inProgressCallback = this.inProgressCallbacks.get(t); - if (inProgressCallback !== undefined) { - // If so wait for it to resolve - return await new Promise((resolve, reject) => { - inProgressCallback.push([resolve, reject]); - }); - } - - // Otherwise compute the new value, but leave a callback to allow sharing work - const callbacks: Array<[(u: U) => void, (reason?: any) => void]> = []; - this.inProgressCallbacks.set(t, callbacks); - try { - const result = await this.operation(t, ...args); - callbacks.forEach((f) => f[0](result)); - this.inProgressCallbacks.delete(t); - if (this.lru.length > this.cacheSize) { - const toRemove = this.lru.shift()!; - this.cached.delete(toRemove); - } - this.lru.push(t); - this.cached.set(t, result); - return result; - } catch (e) { - // Rethrow error on all callbacks - callbacks.forEach((f) => f[1](e)); - throw e; - } finally { - this.inProgressCallbacks.delete(t); - } - } -} - /** * The following functions al heuristically determine metadata about databases. */ diff --git a/extensions/ql-vscode/src/language-support/contextual/template-provider.ts b/extensions/ql-vscode/src/language-support/contextual/template-provider.ts index 26ba9d72f..8c34c29ab 100644 --- a/extensions/ql-vscode/src/language-support/contextual/template-provider.ts +++ b/extensions/ql-vscode/src/language-support/contextual/template-provider.ts @@ -17,7 +17,7 @@ import { } from "../../common/vscode/archive-filesystem-provider"; import { CodeQLCliServer } from "../../codeql-cli/cli"; import { DatabaseManager } from "../../databases/local-databases"; -import { CachedOperation } from "../../helpers"; +import { CachedOperation } from "../../pure/cached-operation"; import { ProgressCallback, withProgress } from "../../common/vscode/progress"; import { KeyType } from "./key-type"; import { diff --git a/extensions/ql-vscode/src/pure/cached-operation.ts b/extensions/ql-vscode/src/pure/cached-operation.ts new file mode 100644 index 000000000..c51970de1 --- /dev/null +++ b/extensions/ql-vscode/src/pure/cached-operation.ts @@ -0,0 +1,70 @@ +/** + * A cached mapping from strings to value of type U. + */ +export class CachedOperation { + private readonly operation: (t: string, ...args: any[]) => Promise; + private readonly cached: Map; + private readonly lru: string[]; + private readonly inProgressCallbacks: Map< + string, + Array<[(u: U) => void, (reason?: any) => void]> + >; + + constructor( + operation: (t: string, ...args: any[]) => Promise, + private cacheSize = 100, + ) { + this.operation = operation; + this.lru = []; + this.inProgressCallbacks = new Map< + string, + Array<[(u: U) => void, (reason?: any) => void]> + >(); + this.cached = new Map(); + } + + async get(t: string, ...args: any[]): Promise { + // Try and retrieve from the cache + const fromCache = this.cached.get(t); + if (fromCache !== undefined) { + // Move to end of lru list + this.lru.push( + this.lru.splice( + this.lru.findIndex((v) => v === t), + 1, + )[0], + ); + return fromCache; + } + // Otherwise check if in progress + const inProgressCallback = this.inProgressCallbacks.get(t); + if (inProgressCallback !== undefined) { + // If so wait for it to resolve + return await new Promise((resolve, reject) => { + inProgressCallback.push([resolve, reject]); + }); + } + + // Otherwise compute the new value, but leave a callback to allow sharing work + const callbacks: Array<[(u: U) => void, (reason?: any) => void]> = []; + this.inProgressCallbacks.set(t, callbacks); + try { + const result = await this.operation(t, ...args); + callbacks.forEach((f) => f[0](result)); + this.inProgressCallbacks.delete(t); + if (this.lru.length > this.cacheSize) { + const toRemove = this.lru.shift()!; + this.cached.delete(toRemove); + } + this.lru.push(t); + this.cached.set(t, result); + return result; + } catch (e) { + // Rethrow error on all callbacks + callbacks.forEach((f) => f[1](e)); + throw e; + } finally { + this.inProgressCallbacks.delete(t); + } + } +}