Revert "[heap][test] Fix weakrefs tests for conservative stack scanning"

This reverts commit 20a954f4bc.

Reason for revert: Alas, GC stress failures:
https://ci.chromium.org/ui/p/v8/builders/ci/V8%20Linux64%20GC%20Stress%20-%20custom%20snapshot/45646/overview

Original change's description:
> [heap][test] Fix weakrefs tests for conservative stack scanning
>
> 31 out of the 36 JS tests in test/mjsunit/harmony/weakrefs/ rely on
> precise GC with the following general pattern: they allocate some
> objects, clear all references to them, invoke a GC, then perform
> some test that assumes that the GC has reclaimed the objects.
> When conservative stack scanning is used, this may fail.
>
> This CL fixes the tests, ensuring that a precise GC will be invoked
> when necessary, without scanning the stack. To achieve this, the GC
> has to be invoked in asynchronous execution mode, which ensures that
> it will be invoked from the event loop without a stack. In some
> cases, this change requires a non-trivial change in the tests.
>
> In 5 tests, part of the test's objective was to verify that a weak
> reference is not cleared before the end of the turn. In those, it
> was not possible to invoke GC asynchronously, as this would
> immediately start a new turn. These tests still use synchronous GC
> and they have been modified, if necessary, to allow for CSS (i.e.,
> to not test that all possible garbage is reclaimed after a
> sequential GC). Because of CSS, these tests may not always test
> everything that they were intended to.
>
> Tests with trivial fix:
>
> - cleanup-from-different-realm
> - cleanup
> - cleanup-proxy-from-different-realm
> - cleanupsome-2
> - cleanupsome-after-unregister
> - cleanupsome
> - finalizationregistry-keeps-holdings-alive
> - multiple-dirty-finalization-groups
> - stress-finalizationregistry-dirty-enqueue
> - undefined-holdings
> - unregister-after-cleanup
> - unregister-before-cleanup
> - unregister-called-twice
> - unregister-inside-cleanup2
> - unregister-inside-cleanup3
> - unregister-inside-cleanup
> - unregister-many
> - unregister-when-cleanup-already-scheduled
> - weak-cell-basics
>
> Tests with non-trivial fixes; same logic but very restructured:
>
> - cleanup-is-not-a-microtask:
> - cleanup-on-detached-realm
> - finalizationregistry-scheduled-for-cleanup-multiple-times
> - finalizationregistry-independent-lifetime
> - finalizationregistry-independent-lifetime-multiple
> - reentrant-gc-from-cleanup
> - symbol-in-finalizationregistry
>   (was 2nd part of former symbol-as-weakref-target-gc)
> - weak-unregistertoken
>
> Tests with non-trivial fixes; same logic, restructured, using
> synchronous GC:
>
> - finalizationregistry-and-weakref
> - symbol-as-weakref-target-gc
>   (was 1st part of former symbol-as-weakref-target-gc)
> - two-weakrefs
> - weakref-creation-keeps-alive
> - weakref-deref-keeps-alive
>
> Bug: v8:13257
> Bug: v8:13662
> Change-Id: I53586bd16cdb98fa976e1fa798ef498bdf286238
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4191774
> Reviewed-by: Marja Hölttä <marja@chromium.org>
> Reviewed-by: Shu-yu Guo <syg@chromium.org>
> Commit-Queue: Nikolaos Papaspyrou <nikolaos@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#85477}

Bug: v8:13257
Bug: v8:13662
Change-Id: Icc7a907928ccac058f8acdf320c21b2df04c1b78
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4192256
Auto-Submit: Shu-yu Guo <syg@chromium.org>
Commit-Queue: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Cr-Commit-Position: refs/heads/main@{#85479}
This commit is contained in:
Shu-yu Guo 2023-01-25 18:52:38 +00:00 committed by V8 LUCI CQ
parent 930b17be77
commit 95b79bf04b
32 changed files with 826 additions and 1094 deletions

View File

@ -4,36 +4,31 @@
// Flags: --expose-gc --noincremental-marking
(async function () {
let r = Realm.create();
let r = Realm.create();
let cleanup = Realm.eval(r, "var stored_global; function cleanup() { stored_global = globalThis; } cleanup");
let realm_global_this = Realm.eval(r, "globalThis");
let cleanup = Realm.eval(r, "var stored_global; function cleanup() { stored_global = globalThis; } cleanup");
let realm_global_this = Realm.eval(r, "globalThis");
let fg = new FinalizationRegistry(cleanup);
let fg = new FinalizationRegistry(cleanup);
// Create an object and a register it in the FinalizationRegistry. The object needs
// to be inside a closure so that we can reliably kill them!
let weak_cell;
// Create an object and register it in the FinalizationRegistry. The object needs
// to be inside a closure so that we can reliably kill them!
(function () {
let object = {};
fg.register(object, {});
// Object goes out of scope.
})();
// We need to invoke GC asynchronously and wait for it to finish, so that
// it doesn't need to scan the stack. Otherwise, the objects may not be
// reclaimed because of conservative stack scanning and the test may not
// work as intended.
await gc({ type: 'major', execution: 'async' });
// Assert that the cleanup function was called in its Realm.
let timeout_func = function () {
let stored_global = Realm.eval(r, "stored_global;");
assertNotEquals(stored_global, globalThis);
assertEquals(stored_global, realm_global_this);
}
setTimeout(timeout_func, 0);
(function() {
let object = {};
fg.register(object, {});
// object goes out of scope.
})();
gc();
// Assert that the cleanup function was called in its Realm.
let timeout_func = function() {
let stored_global = Realm.eval(r, "stored_global;");
assertNotEquals(stored_global, globalThis);
assertEquals(stored_global, realm_global_this);
}
setTimeout(timeout_func, 0);

View File

@ -2,55 +2,60 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --expose-gc --noincremental-marking
// Flags: --expose-gc --noincremental-marking --allow-natives-syntax
// This test asserts that the cleanup function call, scheduled by GC, is
// not a microtask but a normal task.
// This test asserts that the cleanup function call, scheduled by GC, is a
// microtask and not a normal task.
(async function () {
// Inside a microtask, cause GC (which should schedule the cleanup as
// microtask). lso schedule another microtask. Assert that the cleanup
// function ran before the other microtask.
let microtaskInvoked = false;
const microtask = () => {
assertFalse(cleanedUp);
assertFalse(microtaskInvoked);
microtaskInvoked = true;
};
let cleanedUp = false;
let cleanedUp = false;
const cleanup = (holdings) => {
assertFalse(cleanedUp);
assertTrue(microtaskInvoked);
cleanedUp = true;
};
function scheduleMicrotask(func) {
Promise.resolve().then(func);
}
const fg = new FinalizationRegistry(cleanup);
let log = [];
(function() {
// Use a closure here to avoid other references to object which might keep
// it alive (e.g., stack frames pointing to it).
const object = {};
fg.register(object, {});
})();
let cleanup = (holdings) => {
cleanedUp = true;
}
// The GC will schedule the cleanup as a regular task.
// We need to invoke GC asynchronously and wait for it to finish, so that
// it doesn't need to scan the stack. Otherwise, the objects may not be
// reclaimed because of conservative stack scanning and the test may not
// work as intended.
await gc({ type: 'major', execution: 'async' });
assertFalse(cleanedUp);
// Schedule the microtask.
Promise.resolve().then(microtask);
// Nothing else hasn't been called yet, as we're still in synchronous
// execution.
assertFalse(microtaskInvoked);
assertFalse(cleanedUp);
// The microtask and the cleanup callbacks will verify that these two are
// invoked in the right order: microtask -> cleanup.
setTimeout(() => { assertTrue(cleanedUp); }, 0);
let fg = new FinalizationRegistry(cleanup);
let o = null;
(function() {
// Use a closure here to avoid other references to o which might keep it alive
// (e.g., stack frames pointing to it).
o = {};
fg.register(o, {});
})();
let microtask = function() {
log.push("first_microtask");
// cause GC during a microtask
o = null;
gc();
}
assertFalse(cleanedUp);
// enqueue microtask that triggers GC
Promise.resolve().then(microtask);
// but cleanup callback hasn't been called yet, as we're still in
// synchronous execution
assertFalse(cleanedUp);
// flush the microtask queue to run the microtask that triggers GC
%PerformMicrotaskCheckpoint();
// still no cleanup callback, because it runs after as a separate task
assertFalse(cleanedUp);
setTimeout(() => {
assertTrue(cleanedUp);
}, 0);

View File

@ -4,66 +4,35 @@
// Flags: --expose-gc --noincremental-marking
(async function () {
const r = Realm.create();
const FG = Realm.eval(r, "FinalizationRegistry");
Realm.detachGlobal(r);
const cleanup_not_run = function (holdings) {
assertUnreachable();
}
let fg_not_run = new FG(cleanup_not_run);
(function () {
const object = {};
fg_not_run.register(object, "first");
// Object becomes unreachable.
})();
let cleanedUp = false;
let fg_run;
// Schedule a GC, which will schedule fg_not_run for cleanup.
// Here and below, we need to invoke GC asynchronously and wait for it to
// finish, so that it doesn't need to scan the stack. Otherwise, the objects
// may not be reclaimed because of conservative stack scanning and the test
// may not work as intended.
let task_1_gc = (async function () {
await gc({ type: 'major', execution: 'async' });
// Disposing the realm cancels the already scheduled fg_not_run's finalizer.
Realm.dispose(r);
const cleanup = function (holdings) {
assertEquals(holdings, "second");
assertFalse(cleanedUp);
cleanedUp = true;
}
fg_run = new FG(cleanup);
// FGs that are alive after disposal can still schedule tasks.
(function () {
const object = {};
fg_run.register(object, "second");
// Object becomes unreachable.
})();
})();
// Schedule a second GC for execution after that, which will now schedule
// fg_run for cleanup.
let task_2_gc = (async function () {
await gc({ type: 'major', execution: 'async' });
// Check that the cleanup task has had the chance to run yet.
assertFalse(cleanedUp);
})();
// Wait for the two GCs to be executed.
await task_1_gc;
await task_2_gc;
// Give the cleanup task a chance to run and check it worked correctly.
setTimeout(function () { assertTrue(cleanedUp); }, 0);
let cleanedUp = false;
let r = Realm.create();
let FG = Realm.eval(r, "FinalizationRegistry");
Realm.detachGlobal(r);
let fg_not_run = new FG(() => {
assertUnreachable();
});
(() => {
fg_not_run.register({});
})();
gc();
// Disposing the realm cancels the already scheduled fg_not_run's finalizer.
Realm.dispose(r);
let fg = new FG(()=> {
cleanedUp = true;
});
// FGs that are alive after disposal can still schedule tasks.
(() => {
let object = {};
fg.register(object, {});
// object becomes unreachable.
})();
gc();
setTimeout(function() { assertTrue(cleanedUp); }, 0);

View File

@ -4,39 +4,31 @@
// Flags: --expose-gc --noincremental-marking
(async function () {
let r = Realm.create();
let r = Realm.create();
let cleanup = Realm.eval(r, "var stored_global; let cleanup = new Proxy(function() { stored_global = globalThis;}, {}); cleanup");
let realm_global_this = Realm.eval(r, "globalThis");
let cleanup = Realm.eval(r, "var stored_global; let cleanup = new Proxy(function() { stored_global = globalThis;}, {}); cleanup");
let realm_global_this = Realm.eval(r, "globalThis");
let fg = new FinalizationRegistry(cleanup);
let fg = new FinalizationRegistry(cleanup);
// Create an object and register it in the FinalizationRegistry. The object needs
// to be inside a closure so that we can reliably kill them!
let weak_cell;
// Create an object and register it in the FinalizationRegistry. The object needs
// to be inside a closure so that we can reliably kill them!
let weak_cell;
(function () {
let object = {};
fg.register(object, "holdings");
// object goes out of scope.
})();
// We need to invoke GC asynchronously and wait for it to finish, so that
// it doesn't need to scan the stack. Otherwise, the objects may not be
// reclaimed because of conservative stack scanning and the test may not
// work as intended.
await gc({ type: 'major', execution: 'async' });
// Assert that the cleanup function was called in its Realm.
let timeout_func = function () {
let stored_global = Realm.eval(r, "stored_global;");
assertNotEquals(stored_global, globalThis);
assertEquals(stored_global, realm_global_this);
}
setTimeout(timeout_func, 0);
(function() {
let object = {};
fg.register(object, "holdings");
// object goes out of scope.
})();
gc();
// Assert that the cleanup function was called in its Realm.
let timeout_func = function() {
let stored_global = Realm.eval(r, "stored_global;");
assertNotEquals(stored_global, globalThis);
assertEquals(stored_global, realm_global_this);
}
setTimeout(timeout_func, 0);

View File

@ -4,56 +4,48 @@
// Flags: --expose-gc --noincremental-marking
(async function () {
let cleanup_called = 0;
let holdings_list = [];
let cleanup = function(holdings) {
holdings_list.push(holdings);
cleanup_called++;
}
let cleanup_called = 0;
let holdings_list = [];
let cleanup = function (holdings) {
holdings_list.push(holdings);
cleanup_called++;
}
let fg = new FinalizationRegistry(cleanup);
let o1 = {};
let o2 = {};
// Ignition holds references to objects in temporary registers. These will be
// released when the function exits. So only access o inside a function to
// prevent any references to objects in temporary registers when a gc is
(function () {
fg.register(o1, 1);
fg.register(o2, 2);
})();
// Here and below, we need to invoke GC asynchronously and wait for it to
// finish, so that it doesn't need to scan the stack. Otherwise, the objects
// may not be reclaimed because of conservative stack scanning and the test
// may not work as intended.
await gc({ type: 'major', execution: 'async' });
assertEquals(cleanup_called, 0);
// Drop the last references to o1 and o2.
(function () {
o1 = null;
o2 = null;
})();
// GC will reclaim the target objects; the cleanup function will be called the
// next time we enter the event loop.
await gc({ type: 'major', execution: 'async' });
assertEquals(cleanup_called, 0);
let timeout_func = function () {
assertEquals(cleanup_called, 2);
assertEquals(holdings_list.length, 2);
if (holdings_list[0] == 1) {
assertEquals(holdings_list[1], 2);
} else {
assertEquals(holdings_list[0], 2);
assertEquals(holdings_list[1], 1);
}
}
setTimeout(timeout_func, 0);
let fg = new FinalizationRegistry(cleanup);
let o1 = {};
let o2 = {};
// Ignition holds references to objects in temporary registers. These will be
// released when the function exits. So only access o inside a function to
// prevent any references to objects in temporary registers when a gc is
(function() {
fg.register(o1, 1);
fg.register(o2, 2);
})();
gc();
assertEquals(cleanup_called, 0);
// Drop the last references to o1 and o2.
(function() {
o1 = null;
o2 = null;
})();
// GC will reclaim the target objects; the cleanup function will be called the
// next time we enter the event loop.
gc();
assertEquals(cleanup_called, 0);
let timeout_func = function() {
assertEquals(cleanup_called, 2);
assertEquals(holdings_list.length, 2);
if (holdings_list[0] == 1) {
assertEquals(holdings_list[1], 2);
} else {
assertEquals(holdings_list[0], 2);
assertEquals(holdings_list[1], 1);
}
}
setTimeout(timeout_func, 0);

View File

@ -4,34 +4,26 @@
// Flags: --harmony-weak-refs-with-cleanup-some --expose-gc --noincremental-marking --allow-natives-syntax
(async function () {
let cleanup_count = 0;
let cleanup_holdings = [];
let cleanup = function(holdings) {
cleanup_holdings.push(holdings);
++cleanup_count;
}
let cleanup_count = 0;
let cleanup_holdings = [];
let cleanup = function (holdings) {
cleanup_holdings.push(holdings);
++cleanup_count;
}
let fg = new FinalizationRegistry(cleanup);
(function () {
let o = {};
fg.register(o, "holdings");
assertEquals(0, cleanup_count);
})();
// GC will detect o as dead.
// We need to invoke GC asynchronously and wait for it to finish, so that
// it doesn't need to scan the stack. Otherwise, the objects may not be
// reclaimed because of conservative stack scanning and the test may not
// work as intended.
await gc({ type: 'major', execution: 'async' });
// passing no callback, should trigger cleanup function
fg.cleanupSome();
assertEquals(1, cleanup_count);
assertEquals(1, cleanup_holdings.length);
assertEquals("holdings", cleanup_holdings[0]);
let fg = new FinalizationRegistry(cleanup);
(function() {
let o = {};
fg.register(o, "holdings");
assertEquals(0, cleanup_count);
})();
// GC will detect o as dead.
gc();
// passing no callback, should trigger cleanup function
fg.cleanupSome();
assertEquals(1, cleanup_count);
assertEquals(1, cleanup_holdings.length);
assertEquals("holdings", cleanup_holdings[0]);

View File

@ -4,39 +4,31 @@
// Flags: --harmony-weak-refs-with-cleanup-some --expose-gc --noincremental-marking
(async function () {
let cleanup_count = 0;
let cleanup_holdings = [];
let cleanup = function(holdings) {
cleanup_holdings.push(holdings);
++cleanup_count;
}
let cleanup_count = 0;
let cleanup_holdings = [];
let cleanup = function (holdings) {
cleanup_holdings.push(holdings);
++cleanup_count;
}
let fg = new FinalizationRegistry(cleanup);
let key = { "k": "this is the key" };
(function () {
let o = {};
weak_cell = fg.register(o, "holdings", key);
// cleanupSome won't do anything since there are no reclaimed targets.
fg.cleanupSome();
assertEquals(0, cleanup_count);
return o;
})();
// GC will detect the WeakCell as dirty.
// We need to invoke GC asynchronously and wait for it to finish, so that
// it doesn't need to scan the stack. Otherwise, the objects may not be
// reclaimed because of conservative stack scanning and the test may not
// work as intended.
await gc({ type: 'major', execution: 'async' });
// Unregister the tracked object just before calling cleanupSome.
fg.unregister(key);
let fg = new FinalizationRegistry(cleanup);
let key = {"k": "this is the key"};
(function() {
let o = {};
weak_cell = fg.register(o, "holdings", key);
// cleanupSome won't do anything since there are no reclaimed targets.
fg.cleanupSome();
assertEquals(0, cleanup_count);
return o;
})();
// GC will detect the WeakCell as dirty.
gc();
// Unregister the tracked object just before calling cleanupSome.
fg.unregister(key);
fg.cleanupSome();
assertEquals(0, cleanup_count);

View File

@ -4,39 +4,31 @@
// Flags: --harmony-weak-refs-with-cleanup-some --expose-gc --noincremental-marking --allow-natives-syntax
(async function () {
let cleanup_count = 0;
let cleanup_holdings = [];
let cleanup = function(holdings) {
%AbortJS("shouldn't be called");
}
let cleanup_count = 0;
let cleanup_holdings = [];
let cleanup = function (holdings) {
%AbortJS("shouldn't be called");
}
let cleanup2 = function(holdings) {
cleanup_holdings.push(holdings);
++cleanup_count;
}
let cleanup2 = function (holdings) {
cleanup_holdings.push(holdings);
++cleanup_count;
}
let fg = new FinalizationRegistry(cleanup);
(function () {
let o = {};
fg.register(o, "holdings");
// cleanupSome won't do anything since there are no reclaimed targets.
fg.cleanupSome(cleanup2);
assertEquals(0, cleanup_count);
})();
// GC will detect o as dead.
// We need to invoke GC asynchronously and wait for it to finish, so that
// it doesn't need to scan the stack. Otherwise, the objects may not be
// reclaimed because of conservative stack scanning and the test may not
// work as intended.
await gc({ type: 'major', execution: 'async' });
let fg = new FinalizationRegistry(cleanup);
(function() {
let o = {};
fg.register(o, "holdings");
// cleanupSome won't do anything since there are no reclaimed targets.
fg.cleanupSome(cleanup2);
assertEquals(1, cleanup_count);
assertEquals(1, cleanup_holdings.length);
assertEquals("holdings", cleanup_holdings[0]);
assertEquals(0, cleanup_count);
})();
// GC will detect o as dead.
gc();
fg.cleanupSome(cleanup2);
assertEquals(1, cleanup_count);
assertEquals(1, cleanup_holdings.length);
assertEquals("holdings", cleanup_holdings[0]);

View File

@ -4,46 +4,39 @@
// Flags: --expose-gc --noincremental-marking
(async function () {
let cleanup_called = false;
let cleanup = function(holdings) {
assertFalse(cleanup_called);
let holdings_list = [];
holdings_list.push(holdings);
assertEquals(1, holdings_list.length);
assertEquals("holdings", holdings_list[0]);
cleanup_called = true;
}
let cleanup_called = false;
const cleanup = function(holdings) {
assertFalse(cleanup_called);
assertEquals("holdings", holdings);
cleanup_called = true;
}
let fg = new FinalizationRegistry(cleanup);
let weak_ref;
(function() {
let o = {};
weak_ref = new WeakRef(o);
fg.register(o, "holdings");
})();
const fg = new FinalizationRegistry(cleanup);
let weak_ref;
(function() {
const o = {};
weak_ref = new WeakRef(o);
fg.register(o, "holdings");
})();
// Since the WeakRef was created during this turn, it is not cleared by GC. The
// pointer inside the FinalizationRegistry is not cleared either, since the WeakRef
// keeps the target object alive.
gc();
(function() {
assertNotEquals(undefined, weak_ref.deref());
})();
// Since the WeakRef was created during this turn, it is not cleared by GC. The
// pointer inside the FinalizationRegistry is not cleared either, since the WeakRef
// keeps the target object alive.
// Here we invoke GC synchronously and, with conservative stack scanning, there is
// a chance that the object is not reclaimed now. In any case, the WeakRef should
// not be cleared.
// Trigger gc in next task
setTimeout(() => {
gc();
assertNotEquals(undefined, weak_ref.deref());
assertFalse(cleanup_called);
// Trigger GC in next task. Now the WeakRef is cleared but the cleanup has
// not been called yet.
// We need to invoke GC asynchronously and wait for it to finish, so that
// it doesn't need to scan the stack. Otherwise, the objects may not be
// reclaimed because of conservative stack scanning and the test may not
// work as intended.
await gc({ type: 'major', execution: 'async' });
assertEquals(undefined, weak_ref.deref());
assertFalse(cleanup_called);
// Check that the cleanup callback was called in a follow up task.
setTimeout(() => { assertTrue(cleanup_called); }, 0);
})();
// Check that cleanup callback was called in a follow up task
setTimeout(() => {
assertTrue(cleanup_called);
assertEquals(undefined, weak_ref.deref());
}, 0);
}, 0);

View File

@ -4,49 +4,29 @@
// Flags: --expose-gc --noincremental-marking --no-concurrent-inlining
(async function () {
let cleanup_called = false;
function cleanup(holdings) {
cleanup_called = true;
};
let cleanup_called_2 = false;
function cleanup2(holdings) {
cleanup_called_2 = true;
};
const fg = new FinalizationRegistry(cleanup);
let task_1_gc = (async function () {
const fg2 = new FinalizationRegistry(cleanup2);
(function () {
fg.register({}, "holdings1");
fg2.register({}, "holdings2");
})();
// Schedule fg and fg2 for cleanup.
await gc({ type: 'major', execution: 'async' });
assertFalse(cleanup_called);
assertFalse(cleanup_called_2);
})();
// Schedule a task to collect fg2, but fg is still alive.
let task_2_gc = (async function () {
await gc({ type: 'major', execution: 'async' });
assertFalse(cleanup_called);
assertFalse(cleanup_called_2);
let cleanup_called = false;
function cleanup(holdings) {
cleanup_called = true;
};
let cleanup_called_2 = false;
function cleanup2(holdings) {
cleanup_called_2 = true;
};
let fg = new FinalizationRegistry(cleanup);
(function() {
let fg2 = new FinalizationRegistry(cleanup2);
(function() {
fg.register({}, {});
fg2.register({}, {});
})();
// Wait for the two GC tasks to be executed.
await task_1_gc;
await task_2_gc;
// Check that only the cleanup for fg will be called.
setTimeout(function() {
assertTrue(cleanup_called);
assertFalse(cleanup_called_2);
}, 0);
// Schedule fg and fg2 for cleanup.
gc();
})();
// Collect fg2, but fg is still alive.
gc();
setTimeout(function() {
assertTrue(cleanup_called);
assertFalse(cleanup_called_2);
}, 0);

View File

@ -4,38 +4,22 @@
// Flags: --expose-gc --noincremental-marking --no-concurrent-recompilation
(async function () {
let cleanup_called = false;
function cleanup(holdings) {
cleanup_called = true;
};
let task_1_gc = (async function () {
const fg = new FinalizationRegistry(cleanup);
(function () {
let x = {};
fg.register(x, "holdings");
x = null;
})();
// Schedule fg for cleanup.
await gc({ type: 'major', execution: 'async' });
assertFalse(cleanup_called);
let cleanup_called = false;
function cleanup(holdings) {
cleanup_called = true;
};
(function() {
let fg = new FinalizationRegistry(cleanup);
(function() {
let x = {};
fg.register(x, {});
x = null;
})();
// Schedule a task to collect fg, which should result in cleanup not called.
let task_2_gc = (async function () {
await gc({ type: 'major', execution: 'async' });
assertFalse(cleanup_called);
})();
// Wait for the two GC tasks to be executed.
await task_1_gc;
await task_2_gc;
// Check that the cleanup will not be called.
setTimeout(function () { assertFalse(cleanup_called); }, 0);
// Schedule fg for cleanup.
gc();
})();
// Collect fg, which should result in cleanup not called.
gc();
setTimeout(function() { assertFalse(cleanup_called); }, 0);

View File

@ -4,48 +4,40 @@
// Flags: --expose-gc --noincremental-marking
(async function () {
let cleanup_called = false;
let holdings_list = [];
let cleanup = function (holdings) {
assertFalse(cleanup_called);
holdings_list.push(holdings);
cleanup_called = true;
}
let fg = new FinalizationRegistry(cleanup);
let o1 = {};
let holdings = { 'a': 'this is the holdings object' };
// Ignition holds references to objects in temporary registers. These will be
// released when the function exits. So only access o inside a function to
// prevent any references to objects in temporary registers when a gc is
// triggered.
(() => { fg.register(o1, holdings); })()
// Here and below, we need to invoke GC asynchronously and wait for it to
// finish, so that it doesn't need to scan the stack. Otherwise, the objects
// may not be reclaimed because of conservative stack scanning and the test
// may not work as intended.
await gc({ type: 'major', execution: 'async' });
let cleanup_called = false;
let holdings_list = [];
let cleanup = function(holdings) {
assertFalse(cleanup_called);
holdings_list.push(holdings);
cleanup_called = true;
}
// Drop the last references to o1.
(() => { o1 = null; })()
let fg = new FinalizationRegistry(cleanup);
let o1 = {};
let holdings = {'a': 'this is the holdings object'};
// Drop the last reference to the holdings. The FinalizationRegistry keeps it
// alive, so the cleanup function will be called as normal.
holdings = null;
await gc({ type: 'major', execution: 'async' });
assertFalse(cleanup_called);
// Ignition holds references to objects in temporary registers. These will be
// released when the function exits. So only access o inside a function to
// prevent any references to objects in temporary registers when a gc is
// triggered.
(() => {fg.register(o1, holdings);})()
let timeout_func = function () {
assertTrue(cleanup_called);
assertEquals(holdings_list.length, 1);
assertEquals(holdings_list[0].a, "this is the holdings object");
}
gc();
assertFalse(cleanup_called);
setTimeout(timeout_func, 0);
// Drop the last references to o1.
(() => {o1 = null;})()
})();
// Drop the last reference to the holdings. The FinalizationRegistry keeps it
// alive, so the cleanup function will be called as normal.
holdings = null;
gc();
assertFalse(cleanup_called);
let timeout_func = function() {
assertTrue(cleanup_called);
assertEquals(holdings_list.length, 1);
assertEquals(holdings_list[0].a, "this is the holdings object");
}
setTimeout(timeout_func, 0);

View File

@ -5,78 +5,63 @@
// Flags: --expose-gc --noincremental-marking
// Flags: --no-stress-flush-code
(async function () {
let cleanup0_call_count = 0;
let cleanup0_holdings_count = 0;
let cleanup0_call_count = 0;
let cleanup1_call_count = 0;
let cleanup1_call_count = 0;
let cleanup1_holdings_count = 0;
let cleanup0 = function (holdings) {
++cleanup0_call_count;
}
let cleanup0 = function(holdings) {
++cleanup0_holdings_count;
++cleanup0_call_count;
}
let cleanup1 = function (holdings) {
++cleanup1_call_count;
}
let cleanup1 = function(holdings) {
++cleanup1_holdings_count;
++cleanup1_call_count;
}
let fg0 = new FinalizationRegistry(cleanup0);
let fg1 = new FinalizationRegistry(cleanup1);
let fg0 = new FinalizationRegistry(cleanup0);
let fg1 = new FinalizationRegistry(cleanup1);
// Register 1 weak reference for each FinalizationRegistry and kill the
// objects they point to.
(function () {
// The objects need to be inside a closure so that we can reliably kill
// them.
let objects = [];
objects[0] = {};
objects[1] = {};
fg0.register(objects[0], "holdings0-0");
fg1.register(objects[1], "holdings1-0");
// Drop the references to the objects.
objects = [];
})();
// Register 1 weak reference for each FinalizationRegistry and kill the objects they point to.
(function() {
// The objects need to be inside a closure so that we can reliably kill them.
let objects = [];
objects[0] = {};
objects[1] = {};
// Schedule a GC, which will schedule both fg0 and fg1 for cleanup.
// Here and below, we need to invoke GC asynchronously and wait for it to
// finish, so that it doesn't need to scan the stack. Otherwise, the objects
// may not be reclaimed because of conservative stack scanning and the test
// may not work as intended.
let task_1_gc = (async function () {
await gc({ type: 'major', execution: 'async' });
// Before the cleanup task has a chance to run, do the same thing again, so
// both FinalizationRegistries are (again) scheduled for cleanup. This has to
// be a IIFE function (so that we can reliably kill the objects) so we cannot
// use the same function as before.
(function () {
let objects = [];
objects[0] = {};
objects[1] = {};
fg0.register(objects[0], "holdings0-1");
fg1.register(objects[1], "holdings1-1");
objects = [];
})();
})();
// Schedule a second GC for execution after that, which will again schedule
// both fg0 and fg1 for cleanup.
let task_2_gc = (async function () {
await gc({ type: 'major', execution: 'async' });
// Check that no cleanup task has had the chance to run yet.
assertEquals(0, cleanup0_call_count);
assertEquals(0, cleanup1_call_count);
})();
// Wait for the two GCs to be executed.
await task_1_gc;
await task_2_gc;
let timeout_func = function () {
assertEquals(2, cleanup0_call_count);
assertEquals(2, cleanup1_call_count);
}
// Give the cleanup task a chance to run and check it worked correctly.
setTimeout(timeout_func, 0);
fg0.register(objects[0], "holdings0-0");
fg1.register(objects[1], "holdings1-0");
// Drop the references to the objects.
objects = [];
})();
// Will schedule both fg0 and fg1 for cleanup.
gc();
// Before the cleanup task has a chance to run, do the same thing again, so both
// FinalizationRegistries are (again) scheduled for cleanup. This has to be a IIFE function
// (so that we can reliably kill the objects) so we cannot use the same function
// as before.
(function() {
let objects = [];
objects[0] = {};
objects[1] = {};
fg0.register(objects[0], "holdings0-1");
fg1.register(objects[1], "holdings1-1");
objects = [];
})();
gc();
let timeout_func = function() {
assertEquals(2, cleanup0_call_count);
assertEquals(2, cleanup0_holdings_count);
assertEquals(2, cleanup1_call_count);
assertEquals(2, cleanup1_holdings_count);
}
// Give the cleanup task a chance to run.
setTimeout(timeout_func, 0);

View File

@ -4,42 +4,37 @@
// Flags: --expose-gc --noincremental-marking
(async function () {
let cleanup_call_count = 0;
let cleanup_holdings_count = 0;
let cleanup = function(holdings) {
++cleanup_holdings_count;
++cleanup_call_count;
}
let cleanup_call_count = 0;
let cleanup = function (holdings) {
++cleanup_call_count;
}
let fg1 = new FinalizationRegistry(cleanup);
let fg2 = new FinalizationRegistry(cleanup);
let fg1 = new FinalizationRegistry(cleanup);
let fg2 = new FinalizationRegistry(cleanup);
// Create two objects and register them in FinalizationRegistries. The objects need
// to be inside a closure so that we can reliably kill them!
// Create two objects and register them in FinalizationRegistries. The objects need
// to be inside a closure so that we can reliably kill them!
(function() {
let object1 = {};
fg1.register(object1, "holdings1");
(function () {
let object1 = {};
fg1.register(object1, "holdings1");
let object2 = {};
fg2.register(object2, "holdings2");
// object1 and object2 go out of scope.
})();
// This GC will discover dirty WeakCells and schedule cleanup.
// We need to invoke GC asynchronously and wait for it to finish, so that
// it doesn't need to scan the stack. Otherwise, the objects may not be
// reclaimed because of conservative stack scanning and the test may not
// work as intended.
await gc({ type: 'major', execution: 'async' });
assertEquals(0, cleanup_call_count);
// Assert that the cleanup function was called.
let timeout_func = function () {
assertEquals(2, cleanup_call_count);
}
setTimeout(timeout_func, 0);
let object2 = {};
fg2.register(object2, "holdings2");
// object1 and object2 go out of scope.
})();
// This GC will discover dirty WeakCells and schedule cleanup.
gc();
assertEquals(0, cleanup_call_count);
// Assert that the cleanup function was called.
let timeout_func = function() {
assertEquals(2, cleanup_call_count);
assertEquals(2, cleanup_holdings_count);
}
setTimeout(timeout_func, 0);

View File

@ -4,27 +4,20 @@
// Flags: --expose-gc --noincremental-marking
(async function () {
let call_count = 0;
let reentrant_gc = function(holdings) {
gc();
call_count++;
}
let call_count = 0;
const reentrant_gc = function (holdings) {
gc();
call_count++;
}
let fg = new FinalizationRegistry(reentrant_gc);
const fg = new FinalizationRegistry(reentrant_gc);
(function() {
fg.register({}, 42);
})();
(function () {
fg.register({}, 42);
})();
gc();
// We need to invoke GC asynchronously and wait for it to finish, so that
// it doesn't need to scan the stack. Otherwise, the objects may not be
// reclaimed because of conservative stack scanning and the test may not
// work as intended.
await gc({ type: 'major', execution: 'async' });
assertEquals(0, call_count);
setTimeout(function () { assertEquals(1, call_count); }, 0);
})();
setTimeout(function() {
assertEquals(1, call_count);
}, 0);

View File

@ -4,41 +4,33 @@
// Flags: --stress-compaction --expose-gc
(async function () {
// Test that the dirty FinalizationRegistries that are enqueued during GC have
// their slots correctly recorded by the GC.
// Test that the dirty FinalizationRegistries that are enqueued during GC have
// their slots correctly recorded by the GC.
// 1) Create many JSFinalizationRegistry objects so that they span several pages
// (page size is 256kb).
let registries = [];
for (let i = 0; i < 1024 * 8; i++) {
registries.push(new FinalizationRegistry(() => {}));
}
// 1) Create many JSFinalizationRegistry objects so that they span several pages
// (page size is 256kb).
let registries = [];
for (let i = 0; i < 1024 * 8; i++) {
registries.push(new FinalizationRegistry(() => { }));
}
// 2) Force two GCs to ensure that JSFinalizatonRegistry objects are tenured.
// Here and below, we need to invoke GC asynchronously and wait for it to
// finish, so that it doesn't need to scan the stack. Otherwise, the objects
// may not be reclaimed because of conservative stack scanning and the test
// may not work as intended.
await gc({ type: 'major', execution: 'async' });
await gc({ type: 'major', execution: 'async' });
// 3) In a function: create a dummy target and register it in all
// JSFinalizatonRegistry objects.
(function () {
let garbage = {};
registries.forEach((fr) => {
fr.register(garbage, 42);
});
garbage = null;
})();
// 4) Outside the function where the target is unreachable: force GC to collect
// the object.
await gc({ type: 'major', execution: 'async' });
// 5) Force another GC to test that the slot was correctly updated.
await gc({ type: 'major', execution: 'async' });
// 2) Force two GCs to ensure that JSFinalizatonRegistry objects are tenured.
gc();
gc();
// 3) In a function: create a dummy target and register it in all
// JSFinalizatonRegistry objects.
(function() {
let garbage = {};
registries.forEach((fr) => {
fr.register(garbage, 42);
});
garbage = null;
})();
// 4) Outside the function where the target is unreachable: force GC to collect
// the object.
gc();
// 5) Force another GC to test that the slot was correctly updated.
gc();

View File

@ -4,29 +4,36 @@
// Flags: --harmony-symbol-as-weakmap-key --expose-gc --noincremental-marking
(async function () {
(function TestWeakRefWithSymbolGC() {
let weakRef;
(function () {
{
const innerKey = Symbol('123');
weakRef = new WeakRef(innerKey);
})();
}
// Since the WeakRef was created during this turn, it is not cleared by GC.
// Here we invoke GC synchronously and, with conservative stack scanning, there is
// a chance that the object is not reclaimed now. In any case, the WeakRef should
// not be cleared.
gc();
assertNotEquals(undefined, weakRef.deref());
// Trigger GC again in next task. Now the WeakRef is cleared.
// We need to invoke GC asynchronously and wait for it to finish, so that
// it doesn't need to scan the stack. Otherwise, the objects may not be
// reclaimed because of conservative stack scanning and the test may not
// work as intended.
await gc({ type: 'major', execution: 'async' });
assertEquals(undefined, weakRef.deref());
// Next task.
setTimeout(() => {
gc();
assertEquals(undefined, weakRef.deref());
}, 0);
})();
(function TestFinalizationRegistryWithSymbolGC() {
let cleanUpCalled = false;
const fg = new FinalizationRegistry((target) => {
assertEquals('123', target);
cleanUpCalled = true;
});
(function () {
const innerKey = Symbol('123');
fg.register(innerKey, '123');
})();
gc();
assertFalse(cleanUpCalled);
// Check that cleanup callback was called in a follow up task.
setTimeout(() => {
assertTrue(cleanUpCalled);
}, 0);
})();

View File

@ -1,30 +0,0 @@
// Copyright 2023 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-symbol-as-weakmap-key --expose-gc --noincremental-marking
(async function () {
let cleanUpCalled = false;
const fg = new FinalizationRegistry((target) => {
assertEquals('123', target);
cleanUpCalled = true;
});
(function () {
const innerKey = Symbol('123');
fg.register(innerKey, '123');
})();
// We need to invoke GC asynchronously and wait for it to finish, so that
// it doesn't need to scan the stack. Otherwise, the objects may not be
// reclaimed because of conservative stack scanning and the test may not
// work as intended.
await gc({ type: 'major', execution: 'async' });
assertFalse(cleanUpCalled);
// Check that cleanup callback was called in a follow up task.
setTimeout(() => { assertTrue(cleanUpCalled); }, 0);
})();

View File

@ -14,9 +14,6 @@ let wr2;
})();
// Since the WeakRefs were created during this turn, they're not cleared by GC.
// Here and below, we invoke GC synchronously and, with conservative stack
// scanning, there is a chance that the object is not reclaimed now. In any
// case, the WeakRef should not be cleared.
gc();
(function() {
@ -26,29 +23,25 @@ gc();
// New task
setTimeout(function() {
(function () { wr1.deref(); })();
wr1.deref();
o1 = null;
gc(); // deref makes sure we don't clean up wr1
(function () { assertNotEquals(undefined, wr1.deref()); })();
// New task
setTimeout(function() {
(function () { wr2.deref(); })();
wr2.deref();
o2 = null;
gc(); // deref makes sure we don't clean up wr2
(function () { assertNotEquals(undefined, wr2.deref()); })();
// New task
(async function () {
// Trigger GC again to make sure the two WeakRefs are cleared.
// We need to invoke GC asynchronously and wait for it to finish, so that
// it doesn't need to scan the stack. Otherwise, the objects may not be
// reclaimed because of conservative stack scanning and the test may not
// work as intended.
await gc({ type: 'major', execution: 'async' });
setTimeout(function() {
assertEquals(undefined, wr1.deref());
assertEquals(undefined, wr2.deref());
})();
gc();
// New task
setTimeout(function() {
assertEquals(undefined, wr2.deref());
}, 0);
}, 0);
}, 0);
}, 0);

View File

@ -4,39 +4,34 @@
// Flags: --expose-gc --noincremental-marking
(async function () {
let cleanup_call_count = 0;
let cleanup_holdings_count = 0;
let cleanup = function(holdings) {
assertEquals(holdings, undefined);
++cleanup_holdings_count;
++cleanup_call_count;
}
let cleanup_call_count = 0;
let cleanup = function (holdings) {
assertEquals(holdings, undefined);
++cleanup_call_count;
}
let fg = new FinalizationRegistry(cleanup);
let fg = new FinalizationRegistry(cleanup);
// Create an object and register it in the FinalizationRegistry. The object needs to be inside
// a closure so that we can reliably kill them!
// Create an object and register it in the FinalizationRegistry. The object needs to be inside
// a closure so that we can reliably kill them!
(function () {
let object = {};
fg.register(object);
// object goes out of scope.
})();
// This GC will reclaim the target object and schedule cleanup.
// We need to invoke GC asynchronously and wait for it to finish, so that
// it doesn't need to scan the stack. Otherwise, the objects may not be
// reclaimed because of conservative stack scanning and the test may not
// work as intended.
await gc({ type: 'major', execution: 'async' });
assertEquals(0, cleanup_call_count);
// Assert that the cleanup function was called.
let timeout_func = function () {
assertEquals(1, cleanup_call_count);
}
setTimeout(timeout_func, 0);
(function() {
let object = {};
fg.register(object);
// object goes out of scope.
})();
// This GC will reclaim the target object and schedule cleanup.
gc();
assertEquals(0, cleanup_call_count);
// Assert that the cleanup function was called.
let timeout_func = function() {
assertEquals(1, cleanup_call_count);
assertEquals(1, cleanup_holdings_count);
}
setTimeout(timeout_func, 0);

View File

@ -4,46 +4,42 @@
// Flags: --expose-gc --noincremental-marking
(async function () {
let cleanup_call_count = 0;
let cleanup_holdings_count = 0;
let cleanup = function(holdings) {
assertEquals("holdings", holdings);
++cleanup_holdings_count;
++cleanup_call_count;
}
let cleanup_call_count = 0;
let cleanup = function (holdings) {
assertEquals("holdings", holdings);
++cleanup_call_count;
}
let fg = new FinalizationRegistry(cleanup);
let key = {"k": "this is the key"};
// Create an object and register it in the FinalizationRegistry. The object needs
// to be inside a closure so that we can reliably kill them!
let fg = new FinalizationRegistry(cleanup);
let key = { "k": "this is the key" };
// Create an object and register it in the FinalizationRegistry. The object needs
// to be inside a closure so that we can reliably kill them!
(function () {
let object = {};
fg.register(object, "holdings", key);
// object goes out of scope.
})();
// This GC will reclaim the target object and schedule cleanup.
// We need to invoke GC asynchronously and wait for it to finish, so that
// it doesn't need to scan the stack. Otherwise, the objects may not be
// reclaimed because of conservative stack scanning and the test may not
// work as intended.
await gc({ type: 'major', execution: 'async' });
assertEquals(0, cleanup_call_count);
// Assert that the cleanup function was called.
let timeout_func = function () {
assertEquals(1, cleanup_call_count);
// Unregister an already cleaned-up weak reference.
let success = fg.unregister(key);
assertFalse(success);
// Assert that it didn't do anything.
setTimeout(() => { assertEquals(1, cleanup_call_count); }, 0);
}
setTimeout(timeout_func, 0);
(function() {
let object = {};
fg.register(object, "holdings", key);
// object goes out of scope.
})();
// This GC will reclaim the target object and schedule cleanup.
gc();
assertEquals(0, cleanup_call_count);
// Assert that the cleanup function was called.
let timeout_func = function() {
assertEquals(1, cleanup_call_count);
assertEquals(1, cleanup_holdings_count);
// Unregister an already cleaned-up weak reference.
let success = fg.unregister(key);
assertFalse(success);
// Assert that it didn't do anything.
setTimeout(() => { assertEquals(1, cleanup_call_count); }, 0);
setTimeout(() => { assertEquals(1, cleanup_holdings_count); }, 0);
}
setTimeout(timeout_func, 0);

View File

@ -4,42 +4,34 @@
// Flags: --expose-gc --noincremental-marking --noincremental-marking
(async function () {
let cleanup_call_count = 0;
let cleanup = function(holdings) {
++cleanup_call_count;
}
let cleanup_call_count = 0;
let cleanup = function (holdings) {
++cleanup_call_count;
}
let fg = new FinalizationRegistry(cleanup);
let key = {"k": "this is the key"};
// Create an object and register it in the FinalizationRegistry. The object needs
// to be inside a closure so that we can reliably kill them!
let fg = new FinalizationRegistry(cleanup);
let key = { "k": "this is the key" };
// Create an object and register it in the FinalizationRegistry. The object needs
// to be inside a closure so that we can reliably kill them!
(function() {
let object = {};
fg.register(object, "my holdings", key);
(function () {
let object = {};
fg.register(object, "my holdings", key);
// Clear the WeakCell before the GC has a chance to discover it.
let success = fg.unregister(key);
assertTrue(success);
// object goes out of scope.
})();
// This GC will reclaim the target object.
// We need to invoke GC asynchronously and wait for it to finish, so that
// it doesn't need to scan the stack. Otherwise, the objects may not be
// reclaimed because of conservative stack scanning and the test may not
// work as intended.
await gc({ type: 'major', execution: 'async' });
assertEquals(0, cleanup_call_count);
// Assert that the cleanup function won't be called, since we called unregister.
let timeout_func = function () {
assertEquals(0, cleanup_call_count);
}
setTimeout(timeout_func, 0);
// Clear the WeakCell before the GC has a chance to discover it.
let success = fg.unregister(key);
assertTrue(success);
// object goes out of scope.
})();
// This GC will reclaim the target object.
gc();
assertEquals(0, cleanup_call_count);
// Assert that the cleanup function won't be called, since we called unregister.
let timeout_func = function() {
assertEquals(0, cleanup_call_count);
}
setTimeout(timeout_func, 0);

View File

@ -4,47 +4,39 @@
// Flags: --expose-gc --noincremental-marking
(async function () {
let cleanup_call_count = 0;
let cleanup = function(holdings) {
++cleanup_call_count;
}
let cleanup_call_count = 0;
let cleanup = function (holdings) {
++cleanup_call_count;
}
let fg = new FinalizationRegistry(cleanup);
let key = {"k": "this is the key"};
// Create an object and register it in the FinalizationRegistry. The object needs
// to be inside a closure so that we can reliably kill them!
let fg = new FinalizationRegistry(cleanup);
let key = { "k": "this is the key" };
// Create an object and register it in the FinalizationRegistry. The object needs
// to be inside a closure so that we can reliably kill them!
(function() {
let object = {};
fg.register(object, "holdings", key);
(function () {
let object = {};
fg.register(object, "holdings", key);
// Unregister before the GC has a chance to discover the object.
let success = fg.unregister(key);
assertTrue(success);
// Unregister before the GC has a chance to discover the object.
let success = fg.unregister(key);
assertTrue(success);
// Call unregister again (just to assert we handle this gracefully).
success = fg.unregister(key);
assertFalse(success);
// object goes out of scope.
})();
// This GC will reclaim the target object.
// We need to invoke GC asynchronously and wait for it to finish, so that
// it doesn't need to scan the stack. Otherwise, the objects may not be
// reclaimed because of conservative stack scanning and the test may not
// work as intended.
await gc({ type: 'major', execution: 'async' });
assertEquals(0, cleanup_call_count);
// Assert that the cleanup function won't be called, since the weak reference
// was unregistered.
let timeout_func = function () {
assertEquals(0, cleanup_call_count);
}
setTimeout(timeout_func, 0);
// Call unregister again (just to assert we handle this gracefully).
success = fg.unregister(key);
assertFalse(success);
// object goes out of scope.
})();
// This GC will reclaim the target object.
gc();
assertEquals(0, cleanup_call_count);
// Assert that the cleanup function won't be called, since the weak reference
// was unregistered.
let timeout_func = function() {
assertEquals(0, cleanup_call_count);
}
setTimeout(timeout_func, 0);

View File

@ -4,42 +4,37 @@
// Flags: --expose-gc --noincremental-marking
(async function () {
let cleanup_call_count = 0;
let cleanup_holdings_count = 0;
let cleanup = function(holdings) {
assertEquals(holdings, "holdings");
let success = fg.unregister(key);
assertFalse(success);
let cleanup_call_count = 0;
let cleanup = function (holdings) {
assertEquals(holdings, "holdings");
let success = fg.unregister(key);
assertFalse(success);
++cleanup_holdings_count;
++cleanup_call_count;
}
++cleanup_call_count;
}
let fg = new FinalizationRegistry(cleanup);
// Create an object and register it in the FinalizationRegistry. The object needs to be inside
// a closure so that we can reliably kill them!
let key = {"k": "this is the key"};
let fg = new FinalizationRegistry(cleanup);
// Create an object and register it in the FinalizationRegistry. The object needs to be inside
// a closure so that we can reliably kill them!
let key = { "k": "this is the key" };
(function () {
let object = {};
fg.register(object, "holdings", key);
// object goes out of scope.
})();
// This GC will discover dirty WeakCells and schedule cleanup.
// We need to invoke GC asynchronously and wait for it to finish, so that
// it doesn't need to scan the stack. Otherwise, the objects may not be
// reclaimed because of conservative stack scanning and the test may not
// work as intended.
await gc({ type: 'major', execution: 'async' });
assertEquals(0, cleanup_call_count);
// Assert that the cleanup function was called.
let timeout_func = function () {
assertEquals(1, cleanup_call_count);
}
setTimeout(timeout_func, 0);
(function() {
let object = {};
fg.register(object, "holdings", key);
// object goes out of scope.
})();
// This GC will discover dirty WeakCells and schedule cleanup.
gc();
assertEquals(0, cleanup_call_count);
// Assert that the cleanup function was called.
let timeout_func = function() {
assertEquals(1, cleanup_call_count);
assertEquals(1, cleanup_holdings_count);
}
setTimeout(timeout_func, 0);

View File

@ -4,50 +4,45 @@
// Flags: --expose-gc --noincremental-marking
(async function () {
let cleanup_call_count = 0;
let cleanup = function(holdings) {
// See which target we're cleaning up and unregister the other one.
if (holdings == 1) {
let success = fg.unregister(key2);
assertTrue(success);
} else {
assertSame(holdings, 2);
let success = fg.unregister(key1);
assertTrue(success);
}
++cleanup_call_count;
let cleanup_call_count = 0;
let cleanup_holdings_count = 0;
let cleanup = function(holdings) {
// See which target we're cleaning up and unregister the other one.
if (holdings == 1) {
let success = fg.unregister(key2);
assertTrue(success);
} else {
assertSame(holdings, 2);
let success = fg.unregister(key1);
assertTrue(success);
}
++cleanup_holdings_count;
++cleanup_call_count;
}
let fg = new FinalizationRegistry(cleanup);
let key1 = {"k": "first key"};
let key2 = {"k": "second key"};
// Create two objects and register them in the FinalizationRegistry. The objects
// need to be inside a closure so that we can reliably kill them!
let fg = new FinalizationRegistry(cleanup);
let key1 = {"k": "first key"};
let key2 = {"k": "second key"};
// Create two objects and register them in the FinalizationRegistry. The objects
// need to be inside a closure so that we can reliably kill them!
(function() {
let object1 = {};
fg.register(object1, 1, key1);
let object2 = {};
fg.register(object2, 2, key2);
// object1 and object2 go out of scope.
})();
// This GC will reclaim target objects and schedule cleanup.
// We need to invoke GC asynchronously and wait for it to finish, so that
// it doesn't need to scan the stack. Otherwise, the objects may not be
// reclaimed because of conservative stack scanning and the test may not
// work as intended.
await gc({ type: 'major', execution: 'async' });
assertEquals(0, cleanup_call_count);
// Assert that the cleanup function was called and cleaned up one holdings (but not the other one).
let timeout_func = function() {
assertEquals(1, cleanup_call_count);
}
setTimeout(timeout_func, 0);
(function() {
let object1 = {};
fg.register(object1, 1, key1);
let object2 = {};
fg.register(object2, 2, key2);
// object1 and object2 go out of scope.
})();
// This GC will reclaim target objects and schedule cleanup.
gc();
assertEquals(0, cleanup_call_count);
// Assert that the cleanup function was called and cleaned up one holdings (but not the other one).
let timeout_func = function() {
assertEquals(1, cleanup_call_count);
assertEquals(1, cleanup_holdings_count);
}
setTimeout(timeout_func, 0);

View File

@ -4,48 +4,44 @@
// Flags: --expose-gc --noincremental-marking
(async function () {
let cleanup_call_count = 0;
let cleanup_holdings_count = 0;
let cleanup = function(holdings) {
assertEquals(holdings, "holdings");
let cleanup_call_count = 0;
let cleanup = function(holdings) {
assertEquals(holdings, "holdings");
// There's one more object with the same key that we haven't
// cleaned up yet so we should be able to unregister the
// callback for that one.
let success = fg.unregister(key);
// There's one more object with the same key that we haven't
// cleaned up yet so we should be able to unregister the
// callback for that one.
let success = fg.unregister(key);
assertTrue(success);
assertTrue(success);
++cleanup_call_count;
}
++cleanup_holdings_count;
++cleanup_call_count;
}
let fg = new FinalizationRegistry(cleanup);
// Create an object and register it in the FinalizationRegistry. The object needs to be inside
// a closure so that we can reliably kill them!
let key = {"k": "this is the key"};
let fg = new FinalizationRegistry(cleanup);
// Create an object and register it in the FinalizationRegistry. The object needs to be inside
// a closure so that we can reliably kill them!
let key = {"k": "this is the key"};
(function() {
let object = {};
let object2 = {};
fg.register(object, "holdings", key);
fg.register(object2, "holdings", key);
// object goes out of scope.
})();
// This GC will discover dirty WeakCells and schedule cleanup.
// We need to invoke GC asynchronously and wait for it to finish, so that
// it doesn't need to scan the stack. Otherwise, the objects may not be
// reclaimed because of conservative stack scanning and the test may not
// work as intended.
await gc({ type: 'major', execution: 'async' });
assertEquals(0, cleanup_call_count);
// Assert that the cleanup function was called.
let timeout_func = function() {
assertEquals(1, cleanup_call_count);
}
setTimeout(timeout_func, 0);
(function() {
let object = {};
let object2 = {};
fg.register(object, "holdings", key);
fg.register(object2, "holdings", key);
// object goes out of scope.
})();
// This GC will discover dirty WeakCells and schedule cleanup.
gc();
assertEquals(0, cleanup_call_count);
// Assert that the cleanup function was called.
let timeout_func = function() {
assertEquals(1, cleanup_call_count);
assertEquals(1, cleanup_holdings_count);
}
setTimeout(timeout_func, 0);

View File

@ -4,51 +4,46 @@
// Flags: --expose-gc --noincremental-marking
(async function () {
let cleanup_call_count = 0;
let cleanup_holdings_count = 0;
let cleanup = function(holdings) {
assertEquals("holdings2", holdings);
++cleanup_holdings_count;
++cleanup_call_count;
}
let cleanup_call_count = 0;
let cleanup = function(holdings) {
assertEquals("holdings2", holdings);
++cleanup_call_count;
}
let fg = new FinalizationRegistry(cleanup);
let key1 = {"k": "key1"};
let key2 = {"k": "key2"};
// Create three objects and register them in the FinalizationRegistry. The objects
// need to be inside a closure so that we can reliably kill them!
let fg = new FinalizationRegistry(cleanup);
let key1 = {"k": "key1"};
let key2 = {"k": "key2"};
// Create three objects and register them in the FinalizationRegistry. The objects
// need to be inside a closure so that we can reliably kill them!
(function() {
let object1a = {};
fg.register(object1a, "holdings1a", key1);
(function() {
let object1a = {};
fg.register(object1a, "holdings1a", key1);
let object1b = {};
fg.register(object1b, "holdings1b", key1);
let object1b = {};
fg.register(object1b, "holdings1b", key1);
let object2 = {};
fg.register(object2, "holdings2", key2);
let object2 = {};
fg.register(object2, "holdings2", key2);
// Unregister before the GC has a chance to discover the objects.
let success = fg.unregister(key1);
assertTrue(success);
// objects go out of scope.
})();
// This GC will reclaim the target objects.
// We need to invoke GC asynchronously and wait for it to finish, so that
// it doesn't need to scan the stack. Otherwise, the objects may not be
// reclaimed because of conservative stack scanning and the test may not
// work as intended.
await gc({ type: 'major', execution: 'async' });
assertEquals(0, cleanup_call_count);
// Assert that the cleanup function will be called only for the reference which
// was not unregistered.
let timeout_func = function() {
assertEquals(1, cleanup_call_count);
}
setTimeout(timeout_func, 0);
// Unregister before the GC has a chance to discover the objects.
let success = fg.unregister(key1);
assertTrue(success);
// objects go out of scope.
})();
// This GC will reclaim the target objects.
gc();
assertEquals(0, cleanup_call_count);
// Assert that the cleanup function will be called only for the reference which
// was not unregistered.
let timeout_func = function() {
assertEquals(1, cleanup_call_count);
assertEquals(1, cleanup_holdings_count);
}
setTimeout(timeout_func, 0);

View File

@ -4,42 +4,34 @@
// Flags: --expose-gc --noincremental-marking
(async function () {
let cleanup_call_count = 0;
let cleanup = function(holdings) {
++cleanup_call_count;
}
let cleanup_call_count = 0;
let cleanup = function(holdings) {
++cleanup_call_count;
}
let key = {"k": "this is my key"};
let fg = new FinalizationRegistry(cleanup);
// Create an object and register it in the FinalizationRegistry. The object needs to be inside
// a closure so that we can reliably kill them!
let key = {"k": "this is my key"};
let fg = new FinalizationRegistry(cleanup);
// Create an object and register it in the FinalizationRegistry. The object needs to be inside
// a closure so that we can reliably kill them!
(function() {
let object = {};
fg.register(object, {}, key);
// object goes out of scope.
})();
// This GC will discover dirty WeakCells and schedule cleanup.
// We need to invoke GC asynchronously and wait for it to finish, so that
// it doesn't need to scan the stack. Otherwise, the objects may not be
// reclaimed because of conservative stack scanning and the test may not
// work as intended.
await gc({ type: 'major', execution: 'async' });
assertEquals(0, cleanup_call_count);
// Unregister the object from the FinalizationRegistry before cleanup has ran.
let success = fg.unregister(key);
assertTrue(success);
// Assert that the cleanup function won't be called.
let timeout_func = function() {
assertEquals(0, cleanup_call_count);
}
setTimeout(timeout_func, 0);
(function() {
let object = {};
fg.register(object, {}, key);
// object goes out of scope.
})();
// This GC will discover dirty WeakCells and schedule cleanup.
gc();
assertEquals(0, cleanup_call_count);
// Unregister the object from the FinalizationRegistry before cleanup has ran.
let success = fg.unregister(key);
assertTrue(success);
// Assert that the cleanup function won't be called.
let timeout_func = function() {
assertEquals(0, cleanup_call_count);
}
setTimeout(timeout_func, 0);

View File

@ -4,44 +4,36 @@
// Flags: --expose-gc --noincremental-marking
(async function () {
let cleanup_called = false;
let cleanup = function(holdings_arg) {
assertFalse(cleanup_called);
assertEquals(holdings_arg, holdings);
cleanup_called = true;
}
let fg = new FinalizationRegistry(cleanup);
let o = {};
let holdings = {'h': 55};
// Ignition holds references to objects in temporary registers. These will be
// released when the function exits. So only access o inside a function to
// prevent any references to objects in temporary registers when a gc is
// triggered.
(() => { fg.register(o, holdings); })()
// Here and below, we need to invoke GC asynchronously and wait for it to
// finish, so that it doesn't need to scan the stack. Otherwise, the objects
// may not be reclaimed because of conservative stack scanning and the test
// may not work as intended.
await gc({ type: 'major', execution: 'async' });
let cleanup_called = false;
let cleanup = function(holdings_arg) {
assertFalse(cleanup_called);
assertEquals(holdings_arg, holdings);
cleanup_called = true;
}
// Drop the last reference to o.
(() => { o = null; })()
let fg = new FinalizationRegistry(cleanup);
let o = {};
let holdings = {'h': 55};
// GC will clear the WeakCell; the cleanup function will be called the next time
// we enter the event loop.
await gc({ type: 'major', execution: 'async' });
assertFalse(cleanup_called);
// Ignition holds references to objects in temporary registers. These will be
// released when the function exits. So only access o inside a function to
// prevent any references to objects in temporary registers when a gc is
// triggered.
(() => { fg.register(o, holdings); })()
let timeout_func = function() {
assertTrue(cleanup_called);
}
gc();
assertFalse(cleanup_called);
setTimeout(timeout_func, 0);
// Drop the last reference to o.
(() => { o = null; })()
})();
// GC will clear the WeakCell; the cleanup function will be called the next time
// we enter the event loop.
gc();
assertFalse(cleanup_called);
let timeout_func = function() {
assertTrue(cleanup_called);
}
setTimeout(timeout_func, 0);

View File

@ -4,30 +4,19 @@
// Flags: --harmony-weak-refs-with-cleanup-some --expose-gc --noincremental-marking
const cleanup = function (holdings) { globalThis.FRRan = true; };
const FR = new FinalizationRegistry(cleanup);
(function () {
var FR = new FinalizationRegistry (function (holdings) { globalThis.FRRan = true; });
{
let obj = {};
// obj is its own unregister token and becomes unreachable after this
// block. If the unregister token is held strongly this test will not
// terminate.
FR.register(obj, 42, obj);
})();
}
function tryAgain() {
(async function () {
// We need to invoke GC asynchronously and wait for it to finish, so that
// it doesn't need to scan the stack. Otherwise, the objects may not be
// reclaimed because of conservative stack scanning and the test may not
// work as intended.
await gc({ type: 'major', execution: 'async' });
if (globalThis.FRRan || FR.cleanupSome()) {
return;
}
setTimeout(tryAgain, 0);
})();
gc();
if (globalThis.FRRan || FR.cleanupSome()) {
return;
}
setTimeout(tryAgain, 0);
}
tryAgain();

View File

@ -5,29 +5,22 @@
// Flags: --expose-gc --noincremental-marking
let wr;
(function () {
(function() {
let o = {};
wr = new WeakRef(o);
// Don't deref here, we want to test that the creation is enough to keep the
// WeakRef alive until the end of the turn.
})();
// Here we invoke GC synchronously and, with conservative stack scanning,
// there is a chance that the object is not reclaimed now. In any case,
// the WeakRef should not be cleared.
gc();
// Since the WeakRef was created during this turn, it is not cleared by GC.
assertNotEquals(undefined, wr.deref());
(function() {
assertNotEquals(undefined, wr.deref());
})();
// Next task.
setTimeout(() => {
(async function () {
// Trigger GC again to make sure the two WeakRefs are cleared.
// We need to invoke GC asynchronously and wait for it to finish, so that
// it doesn't need to scan the stack. Otherwise, the objects may not be
// reclaimed because of conservative stack scanning and the test may not
// work as intended.
await gc({ type: 'major', execution: 'async' });
assertEquals(undefined, wr.deref());
})();
gc();
assertEquals(undefined, wr.deref());
}, 0);

View File

@ -6,43 +6,40 @@
let wr;
let wr_control; // control WeakRef for testing what happens without deref
(function () {
(function() {
let o1 = {};
wr = new WeakRef(o1);
let o2 = {};
wr_control = new WeakRef(o2);
})();
let strong = { a: wr.deref(), b: wr_control.deref() };
let strong = {a: wr.deref(), b: wr_control.deref()};
// Here and below, we invoke GC synchronously and, with conservative stack
// scanning, there is a chance that the object is not reclaimed now. In any
// case, the WeakRefs should not be cleared.
gc();
// Next task.
setTimeout(function() {
// Call deref inside a closure, trying to avoid accidentally storing a strong
// reference into the object in the stack frame.
(function () { wr.deref(); })();
(function() {
wr.deref();
})();
strong = null;
// This GC should clear wr_control (modulo CSS), since nothing was keeping it
// alive, but it should not clear wr.
// This GC will clear wr_control.
gc();
(function () { assertNotEquals(undefined, wr.deref()); })();
// Next task.
(async function () {
// Trigger GC again to make sure the two WeakRefs are cleared.
// We need to invoke GC asynchronously and wait for it to finish, so that
// it doesn't need to scan the stack. Otherwise, the objects may not be
// reclaimed because of conservative stack scanning and the test may not
// work as intended.
await gc({ type: 'major', execution: 'async' });
assertEquals(undefined, wr.deref());
(function() {
assertNotEquals(undefined, wr.deref());
// Now the control WeakRef got cleared, since nothing was keeping it alive.
assertEquals(undefined, wr_control.deref());
})();
// Next task.
setTimeout(function() {
gc();
assertEquals(undefined, wr.deref());
}, 0);
}, 0);