Skip to content

Commit 1c6b4a6

Browse files
committed
Put CachedOperation in helpers.
1 parent 28be984 commit 1c6b4a6

File tree

2 files changed

+58
-55
lines changed

2 files changed

+58
-55
lines changed

extensions/ql-vscode/src/definitions.ts

Lines changed: 1 addition & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { EntityValue, getResultSetSchema, LineColumnLocation, UrlValue, ColumnKi
77
import { CodeQLCliServer } from "./cli";
88
import { DatabaseItem, DatabaseManager } from "./databases";
99
import * as helpers from './helpers';
10+
import { CachedOperation } from './helpers';
1011
import * as messages from "./messages";
1112
import { QueryServerClient } from "./queryserver-client";
1213
import { compileAndRunQueryAgainstDatabase, QueryWithResults } from "./run-queries";
@@ -196,58 +197,3 @@ function fileRangeFromURI(uri: UrlValue, db: DatabaseItem): FileRange | undefine
196197
}
197198
}
198199
}
199-
200-
const CACHE_SIZE = 100;
201-
class CachedOperation<U> {
202-
private readonly operation: (t: string) => Promise<U>;
203-
private readonly cached: Map<string, U>;
204-
private readonly lru: string[];
205-
private readonly inProgressCallbacks: Map<string, [(u: U) => void, (reason?: any) => void][]>;
206-
207-
constructor(operation: (t: string) => Promise<U>) {
208-
this.operation = operation;
209-
this.lru = [];
210-
this.inProgressCallbacks = new Map<string, [(u: U) => void, (reason?: any) => void][]>();
211-
this.cached = new Map<string, U>();
212-
}
213-
214-
async get(t: string): Promise<U> {
215-
// Try and retrieve from the cache
216-
const fromCache = this.cached.get(t);
217-
if (fromCache !== undefined) {
218-
// Move to end of lru list
219-
this.lru.push(this.lru.splice(this.lru.findIndex(v => v === t), 1)[0])
220-
return fromCache;
221-
}
222-
// Otherwise check if in progress
223-
const inProgressCallback = this.inProgressCallbacks.get(t);
224-
if (inProgressCallback !== undefined) {
225-
// If so wait for it to resolve
226-
return await new Promise((resolve, reject) => {
227-
inProgressCallback.push([resolve, reject]);
228-
});
229-
}
230-
231-
// Otherwise compute the new value, but leave a callback to allow sharing work
232-
const callbacks: [(u: U) => void, (reason?: any) => void][] = [];
233-
this.inProgressCallbacks.set(t, callbacks);
234-
try {
235-
const result = await this.operation(t);
236-
callbacks.forEach(f => f[0](result));
237-
this.inProgressCallbacks.delete(t);
238-
if (this.lru.length > CACHE_SIZE) {
239-
const toRemove = this.lru.shift()!;
240-
this.cached.delete(toRemove);
241-
}
242-
this.lru.push(t);
243-
this.cached.set(t, result);
244-
return result;
245-
} catch (e) {
246-
// Rethrow error on all callbacks
247-
callbacks.forEach(f => f[1](e));
248-
throw e;
249-
} finally {
250-
this.inProgressCallbacks.delete(t);
251-
}
252-
}
253-
}

extensions/ql-vscode/src/helpers.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,3 +298,60 @@ export async function resolveDatasetFolder(cliServer: CodeQLCliServer, datasetFo
298298
const qlpack = await getQlPackForDbscheme(cliServer, dbscheme);
299299
return { dbscheme, qlpack };
300300
}
301+
302+
/**
303+
* A cached mapping from strings to value of type U.
304+
*/
305+
export class CachedOperation<U> {
306+
private readonly operation: (t: string) => Promise<U>;
307+
private readonly cached: Map<string, U>;
308+
private readonly lru: string[];
309+
private readonly inProgressCallbacks: Map<string, [(u: U) => void, (reason?: any) => void][]>;
310+
311+
constructor(operation: (t: string) => Promise<U>, private cacheSize = 100) {
312+
this.operation = operation;
313+
this.lru = [];
314+
this.inProgressCallbacks = new Map<string, [(u: U) => void, (reason?: any) => void][]>();
315+
this.cached = new Map<string, U>();
316+
}
317+
318+
async get(t: string): Promise<U> {
319+
// Try and retrieve from the cache
320+
const fromCache = this.cached.get(t);
321+
if (fromCache !== undefined) {
322+
// Move to end of lru list
323+
this.lru.push(this.lru.splice(this.lru.findIndex(v => v === t), 1)[0])
324+
return fromCache;
325+
}
326+
// Otherwise check if in progress
327+
const inProgressCallback = this.inProgressCallbacks.get(t);
328+
if (inProgressCallback !== undefined) {
329+
// If so wait for it to resolve
330+
return await new Promise((resolve, reject) => {
331+
inProgressCallback.push([resolve, reject]);
332+
});
333+
}
334+
335+
// Otherwise compute the new value, but leave a callback to allow sharing work
336+
const callbacks: [(u: U) => void, (reason?: any) => void][] = [];
337+
this.inProgressCallbacks.set(t, callbacks);
338+
try {
339+
const result = await this.operation(t);
340+
callbacks.forEach(f => f[0](result));
341+
this.inProgressCallbacks.delete(t);
342+
if (this.lru.length > this.cacheSize) {
343+
const toRemove = this.lru.shift()!;
344+
this.cached.delete(toRemove);
345+
}
346+
this.lru.push(t);
347+
this.cached.set(t, result);
348+
return result;
349+
} catch (e) {
350+
// Rethrow error on all callbacks
351+
callbacks.forEach(f => f[1](e));
352+
throw e;
353+
} finally {
354+
this.inProgressCallbacks.delete(t);
355+
}
356+
}
357+
}

0 commit comments

Comments
 (0)