UNPKG

@thermopylae/lib.cache

Version:
94 lines (93 loc) 2.98 kB
import { chrono } from '@thermopylae/lib.utils'; import { Heap } from "../data-structures/heap.js"; import { EXPIRES_AT_SYM } from "../constants.js"; class HeapGarbageCollector { entries; cleanUpInterval; entryExpiredCb; constructor(entryExpiredCb) { this.entries = new Heap((first, second) => { return first[EXPIRES_AT_SYM] - second[EXPIRES_AT_SYM]; }); this.cleanUpInterval = null; this.entryExpiredCb = entryExpiredCb || HeapGarbageCollector.defaultEntryExpiredCallback; } get size() { return this.entries.size; } get idle() { return this.cleanUpInterval == null; } manage(entry) { this.entries.push(entry); this.synchronizeEvictionTimer(); } update(_oldExpiration, entry) { if (!Heap.isPartOfHeap(entry)) { return; } this.entries.heapifyUpdatedNode(entry); this.synchronizeEvictionTimer(); } leave(entry) { if (!Heap.isPartOfHeap(entry)) { return; } this.entries.delete(entry); this.synchronizeEvictionTimer(); } clear() { this.entries.clear(); this.synchronizeEvictionTimer(); } setEntryExpiredCallback(cb) { this.entryExpiredCb = cb; } synchronizeEvictionTimer() { const rootEntry = this.entries.peek(); if (rootEntry === undefined) { if (this.cleanUpInterval != null) { clearTimeout(this.cleanUpInterval.timeoutId); this.cleanUpInterval = null; } return; } if (this.cleanUpInterval == null) { this.cleanUpInterval = { willCleanUpOn: 0, timeoutId: null }; this.scheduleNextGc(rootEntry); return; } if (this.cleanUpInterval.willCleanUpOn !== rootEntry[EXPIRES_AT_SYM]) { clearTimeout(this.cleanUpInterval.timeoutId); this.scheduleNextGc(rootEntry); } } scheduleNextGc(rootEntry) { const runDelay = rootEntry[EXPIRES_AT_SYM] - chrono.unixTime(); this.cleanUpInterval.willCleanUpOn = rootEntry[EXPIRES_AT_SYM]; this.cleanUpInterval.timeoutId = setTimeout(this.evictExpiredEntries, runDelay * 1000); } evictExpiredEntries = () => { let rootEntry; do { rootEntry = this.entries.pop(); this.entryExpiredCb(rootEntry); rootEntry = this.entries.peek(); if (rootEntry === undefined) { this.cleanUpInterval = null; return; } if (rootEntry[EXPIRES_AT_SYM] !== this.cleanUpInterval.willCleanUpOn) { this.scheduleNextGc(rootEntry); return; } } while (true); }; static defaultEntryExpiredCallback() { return undefined; } } export { HeapGarbageCollector };