UNPKG

@speckle/objectloader2

Version:

This is an updated objectloader for the Speckle viewer written in typescript

146 lines 5.03 kB
import { DeferredBase } from './deferredBase.js'; export class DefermentManager { options; deferments = new Map(); timer; logger; currentSize = 0; disposed = false; //tracks total deferment requests for each id //this is used to prevent cleaning up deferments that are still being requested totalDefermentRequests = new Map(); constructor(options) { this.options = options; this.resetGlobalTimer(); this.logger = options.logger || (() => { }); } now() { return Date.now(); } isDeferred(id) { return this.deferments.has(id); } get(id) { if (this.disposed) throw new Error('DefermentManager is disposed'); return this.deferments.get(id); } async defer(params) { if (this.disposed) throw new Error('DefermentManager is disposed'); this.trackDefermentRequest(params.id); const now = this.now(); const deferredBase = this.deferments.get(params.id); if (deferredBase) { deferredBase.setAccess(now); return deferredBase.getPromise(); } const notYetFound = new DeferredBase(this.options.ttlms, params.id, now + this.options.ttlms); this.deferments.set(params.id, notYetFound); return notYetFound.getPromise(); } trackDefermentRequest(id) { const request = this.totalDefermentRequests.get(id); if (request) { this.totalDefermentRequests.set(id, request + 1); } else { this.totalDefermentRequests.set(id, 1); } } undefer(item) { if (this.disposed) throw new Error('DefermentManager is disposed'); const now = this.now(); this.currentSize += item.size || 0; //order matters here with found before undefer const deferredBase = this.deferments.get(item.baseId); if (deferredBase) { deferredBase.found(item); deferredBase.setAccess(now); } else { const existing = new DeferredBase(this.options.ttlms, item.baseId, now); existing.found(item); this.deferments.set(item.baseId, existing); } } resetGlobalTimer() { const run = () => { this.cleanDeferments(); this.timer = setTimeout(run, this.options.ttlms); }; this.timer = setTimeout(run, this.options.ttlms); } dispose() { if (this.disposed) return; this.disposed = true; if (this.timer) { clearTimeout(this.timer); this.timer = undefined; } this.clearDeferments(); } clearDeferments() { let waiting = 0; for (const deferredBase of this.deferments.values()) { deferredBase.done(0); if (deferredBase.getItem() === undefined) { waiting++; } } this.currentSize = 0; this.deferments.clear(); this.logger('cleared deferments, left', waiting); } cleanDeferments() { const maxSizeBytes = this.options.maxSizeInMb * 1024 * 1024; if (this.currentSize < maxSizeBytes) { this.logger('deferments size is ok, no need to clean', this.currentSize, maxSizeBytes); return; } const now = this.now(); let cleaned = 0; const start = performance.now(); for (const deferredBase of Array.from(this.deferments.values()) .filter((x) => x.isExpired(now)) .sort((a, b) => this.compareMaybeBasesBySize(a.getItem(), b.getItem()))) { if (deferredBase.done(now)) { //if the deferment is done but has been requested multiple times, //we do not clean it up to allow the requests to resolve const requestCount = this.totalDefermentRequests.get(deferredBase.getId()); if (requestCount && requestCount > 1) { return; } this.currentSize -= deferredBase.getItem()?.size || 0; this.deferments.delete(deferredBase.getId()); cleaned++; if (this.currentSize < maxSizeBytes) { break; } } } this.logger('cleaned deferments: cleaned, left, time', cleaned, this.deferments.size, performance.now() - start); return; } compareMaybeBasesBySize(a, b) { if (a === undefined && b === undefined) return 0; if (a === undefined) return -1; if (b === undefined) return 1; return this.compareMaybe(a.size, b.size); } compareMaybe(a, b) { if (a === undefined && b === undefined) return 0; if (a === undefined) return -1; if (b === undefined) return 1; return a - b; } } //# sourceMappingURL=defermentManager.js.map