fix: add unlink() methods to clear ECMAP

This commit is contained in:
KernelDeimos
2025-09-15 03:19:46 -04:00
parent 509f1add4e
commit 7d7291cde4
2 changed files with 46 additions and 2 deletions

View File

@@ -26,6 +26,21 @@ class ECMAP {
// identifier association caches
this.path_to_uuid = {};
this.uuid_to_path = {};
this.unlinked = false;
}
/**
* unlink() clears all references from this ECMAP to ensure that it will be
* GC'd. This is called by ECMAP.arun() after the callback has resolved.
*/
unlink () {
this.unlink = true;
this.uuid_to_fsNodeContext = null;
this.path_to_fsNodeContext = null;
this.id_to_fsNodeContext = null;
this.path_to_uuid = null;
this.uuid_to_path = null;
}
get logPrefix () {
@@ -38,6 +53,8 @@ class ECMAP {
}
get_fsNodeContext_from_selector (selector) {
if ( this.unlinked ) return null;
this.log('GET', selector.describe());
const retvalue = (() => {
let value;
@@ -69,6 +86,8 @@ class ECMAP {
}
store_fsNodeContext_to_selector (selector, node) {
if ( this.unlinked ) return null;
this.log('STORE', selector.describe());
if ( selector instanceof NodeUIDSelector ) {
this.uuid_to_fsNodeContext[selector.value] = node;
@@ -82,16 +101,22 @@ class ECMAP {
}
store_fsNodeContext (node) {
if ( this.unlinked ) return;
this.store_fsNodeContext_to_selector(node.selector, node);
}
static async arun (cb) {
let context = Context.get();
if ( ! context.get(this.SYMBOL) ) {
const ins = new this();
context = context.sub({
[this.SYMBOL]: new this(),
[this.SYMBOL]: ins,
});
return await context.arun(cb);
const result = await context.arun(cb);
ins.unlink();
context.unlink();
return result;
}
return await cb();
}

View File

@@ -71,10 +71,29 @@ class Context {
static sub (values, opt_name) {
return this.get().sub(values, opt_name);
}
#dead = false;
/**
* Clears this context's values and unlinks from its parent. This context
* will become empty. This is to ensure contexts that aren't used anymore
* get garbage collected. This was added to prevent memory leaks due to
* ECMAP, where currently we're not sure what's holding a reference back
* to the ECMAP (or perhaps its subcontext).
*/
unlink () {
// Settings `values_` to an empty object should clear any references
// that were inside it while avoiding errors if .get() happens to be
// called by a lingering asynchronous function.
this.values_ = {};
this.#dead = true;
}
get (k) {
return this.values_[k];
}
set (k, v) {
if ( this.#dead ) return;
this.values_[k] = v;
}
sub (values, opt_name) {