Closed Bug 1664463 Opened 4 years ago Closed 4 years ago

FinalizationRegistry callbacks never run

Categories

(Core :: JavaScript: GC, defect)

defect

Tracking

()

RESOLVED DUPLICATE of bug 1542660

People

(Reporter: wingo, Unassigned)

Details

Consider the following JS test file:

let callLater = (()=> {
    if (typeof setTimeout !== 'undefined')
        return f=>setTimeout(f, 0);
    if (typeof enqueueJob !== 'undefined')
        return f=>enqueueJob(f);
    return f=>f();
})();

let maybeGC = (()=> {
    if (typeof gc !== 'undefined')
        return gc;
    return ()=>{};
})();

let nfinalized = 0;
let finalizers = new FinalizationRegistry(f => { print(`finalized ${f}`); nfinalized++ });

class B {};

class A {
    constructor() {
        let b = new B;
        this.b = b;
        finalizers.register(this, b, this);
    }
    installCallback(f) {
        this.b.callback = f;
    }
}

// JS shells have a setTimeout implementation that doesn't wait before
// calling the callback; instead it is just called on the next turn,
// ignoring the timeout argument.  That's fine -- all we want to do is
// to be able to yield and let other tasks run, so that finalizers can
// run.
async function microsleep() {
    await new Promise((resolve, reject) => callLater(resolve));
}

async function allowFinalizersToRun() {
    //maybeGC();
    await microsleep();
}

async function test() {
    for (let j = 0; j < 1e3; j++) {
        print(j);
        for (let i = 0; i < 1e3; i++) {
            let obj = new A(null);
            obj.installCallback(() => console.log("hello"));
        }
        await allowFinalizersToRun();
    }
}

async function main() {
    await test();
    print(`nfinalized: ${nfinalized}`);
}
main()

If you run it from the shell, it shows that no object was finalized. It's also quite slow.

In contrast, V8's d8 does finalize objects, for this test -- some of the objects, if you just run it as-is (passing --harmony-weak-refs on the command line), or all but one of the objects, if you also pass --expose-gc, and you uncomment the maybeGC line.

JSC will also finalize objects. While never invoking finalizers is a legal behavior, I think here it's particularly egregious.

This is a dup of bug 1542660. All variables are marked as "aliased" in generators/async functions, which leads to keeping obj alive through the () => console.log("hello") function.

Hi André, thanks for the info!! That does make sense.

However -- if I split out the allocation and attach-callback business to an inner function, and I call drainJobQueue during the microsleep, then I do indeed get prompt-enough finalization. (The drainJobQueue is necessary it seems because it seems enqueueJob has a higher priority than finalizers. Explicitly calling gc is not necessary.)

Status: NEW → RESOLVED
Closed: 4 years ago
Resolution: --- → DUPLICATE
You need to log in before you can comment on or make changes to this bug.