@speckle/objectloader2
Version:
This is an updated objectloader for the Speckle viewer written in typescript
146 lines • 5.03 kB
JavaScript
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