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 // 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 fg = new FinalizationRegistry(cleanup);
let realm_global_this = Realm.eval(r, "globalThis");
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 (function() {
// to be inside a closure so that we can reliably kill them!
(function () {
let object = {}; let object = {};
fg.register(object, {}); fg.register(object, {});
// Object goes out of scope.
})();
// We need to invoke GC asynchronously and wait for it to finish, so that // object goes out of scope.
// 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. gc();
let timeout_func = function () {
// Assert that the cleanup function was called in its Realm.
let timeout_func = function() {
let stored_global = Realm.eval(r, "stored_global;"); let stored_global = Realm.eval(r, "stored_global;");
assertNotEquals(stored_global, globalThis); assertNotEquals(stored_global, globalThis);
assertEquals(stored_global, realm_global_this); assertEquals(stored_global, realm_global_this);
} }
setTimeout(timeout_func, 0); 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 // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // 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 // This test asserts that the cleanup function call, scheduled by GC, is a
// not a microtask but a normal task. // 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; let cleanedUp = false;
const microtask = () => {
assertFalse(cleanedUp);
assertFalse(microtaskInvoked);
microtaskInvoked = true;
};
let cleanedUp = false; function scheduleMicrotask(func) {
const cleanup = (holdings) => { Promise.resolve().then(func);
assertFalse(cleanedUp); }
assertTrue(microtaskInvoked);
let log = [];
let cleanup = (holdings) => {
cleanedUp = true; cleanedUp = true;
}; }
const fg = new FinalizationRegistry(cleanup); let fg = new FinalizationRegistry(cleanup);
let o = null;
(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, {});
})();
// 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);
(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 // Flags: --expose-gc --noincremental-marking
(async function () { let cleanedUp = false;
let r = Realm.create();
let FG = Realm.eval(r, "FinalizationRegistry");
Realm.detachGlobal(r);
const r = Realm.create(); let fg_not_run = new FG(() => {
const FG = Realm.eval(r, "FinalizationRegistry");
Realm.detachGlobal(r);
const cleanup_not_run = function (holdings) {
assertUnreachable(); assertUnreachable();
} });
let fg_not_run = new FG(cleanup_not_run); (() => {
fg_not_run.register({});
(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);
})(); })();
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 // 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 fg = new FinalizationRegistry(cleanup);
let realm_global_this = Realm.eval(r, "globalThis");
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 (function() {
// to be inside a closure so that we can reliably kill them!
let weak_cell;
(function () {
let object = {}; let object = {};
fg.register(object, "holdings"); fg.register(object, "holdings");
// object goes out of scope. // object goes out of scope.
})(); })();
// We need to invoke GC asynchronously and wait for it to finish, so that gc();
// 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. // Assert that the cleanup function was called in its Realm.
let timeout_func = function () { let timeout_func = function() {
let stored_global = Realm.eval(r, "stored_global;"); let stored_global = Realm.eval(r, "stored_global;");
assertNotEquals(stored_global, globalThis); assertNotEquals(stored_global, globalThis);
assertEquals(stored_global, realm_global_this); assertEquals(stored_global, realm_global_this);
} }
setTimeout(timeout_func, 0); setTimeout(timeout_func, 0);
})();

View File

@ -4,46 +4,40 @@
// Flags: --expose-gc --noincremental-marking // Flags: --expose-gc --noincremental-marking
(async function () { let cleanup_called = 0;
let holdings_list = [];
let cleanup_called = 0; let cleanup = function(holdings) {
let holdings_list = [];
let cleanup = function (holdings) {
holdings_list.push(holdings); holdings_list.push(holdings);
cleanup_called++; cleanup_called++;
} }
let fg = new FinalizationRegistry(cleanup); let fg = new FinalizationRegistry(cleanup);
let o1 = {}; let o1 = {};
let o2 = {}; let o2 = {};
// Ignition holds references to objects in temporary registers. These will be // Ignition holds references to objects in temporary registers. These will be
// released when the function exits. So only access o inside a function to // 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 // prevent any references to objects in temporary registers when a gc is
(function () { (function() {
fg.register(o1, 1); fg.register(o1, 1);
fg.register(o2, 2); fg.register(o2, 2);
})(); })();
// Here and below, we need to invoke GC asynchronously and wait for it to gc();
// finish, so that it doesn't need to scan the stack. Otherwise, the objects assertEquals(cleanup_called, 0);
// 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. // Drop the last references to o1 and o2.
(function () { (function() {
o1 = null; o1 = null;
o2 = null; o2 = null;
})(); })();
// GC will reclaim the target objects; the cleanup function will be called the // GC will reclaim the target objects; the cleanup function will be called the
// next time we enter the event loop. // next time we enter the event loop.
await gc({ type: 'major', execution: 'async' }); gc();
assertEquals(cleanup_called, 0); assertEquals(cleanup_called, 0);
let timeout_func = function () { let timeout_func = function() {
assertEquals(cleanup_called, 2); assertEquals(cleanup_called, 2);
assertEquals(holdings_list.length, 2); assertEquals(holdings_list.length, 2);
if (holdings_list[0] == 1) { if (holdings_list[0] == 1) {
@ -52,8 +46,6 @@
assertEquals(holdings_list[0], 2); assertEquals(holdings_list[0], 2);
assertEquals(holdings_list[1], 1); assertEquals(holdings_list[1], 1);
} }
} }
setTimeout(timeout_func, 0); setTimeout(timeout_func, 0);
})();

View File

@ -4,34 +4,26 @@
// Flags: --harmony-weak-refs-with-cleanup-some --expose-gc --noincremental-marking --allow-natives-syntax // 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_count = 0; let cleanup = function(holdings) {
let cleanup_holdings = [];
let cleanup = function (holdings) {
cleanup_holdings.push(holdings); cleanup_holdings.push(holdings);
++cleanup_count; ++cleanup_count;
} }
let fg = new FinalizationRegistry(cleanup); let fg = new FinalizationRegistry(cleanup);
(function () { (function() {
let o = {}; let o = {};
fg.register(o, "holdings"); fg.register(o, "holdings");
assertEquals(0, cleanup_count); 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]);
})(); })();
// 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,18 +4,16 @@
// Flags: --harmony-weak-refs-with-cleanup-some --expose-gc --noincremental-marking // Flags: --harmony-weak-refs-with-cleanup-some --expose-gc --noincremental-marking
(async function () { let cleanup_count = 0;
let cleanup_holdings = [];
let cleanup_count = 0; let cleanup = function(holdings) {
let cleanup_holdings = [];
let cleanup = function (holdings) {
cleanup_holdings.push(holdings); cleanup_holdings.push(holdings);
++cleanup_count; ++cleanup_count;
} }
let fg = new FinalizationRegistry(cleanup); let fg = new FinalizationRegistry(cleanup);
let key = { "k": "this is the key" }; let key = {"k": "this is the key"};
(function () { (function() {
let o = {}; let o = {};
weak_cell = fg.register(o, "holdings", key); weak_cell = fg.register(o, "holdings", key);
@ -23,20 +21,14 @@
fg.cleanupSome(); fg.cleanupSome();
assertEquals(0, cleanup_count); assertEquals(0, cleanup_count);
return o; 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);
fg.cleanupSome();
assertEquals(0, cleanup_count);
})(); })();
// 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 // 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_count = 0; let cleanup = function(holdings) {
let cleanup_holdings = [];
let cleanup = function (holdings) {
%AbortJS("shouldn't be called"); %AbortJS("shouldn't be called");
} }
let cleanup2 = function (holdings) { let cleanup2 = function(holdings) {
cleanup_holdings.push(holdings); cleanup_holdings.push(holdings);
++cleanup_count; ++cleanup_count;
} }
let fg = new FinalizationRegistry(cleanup); let fg = new FinalizationRegistry(cleanup);
(function () { (function() {
let o = {}; let o = {};
fg.register(o, "holdings"); fg.register(o, "holdings");
// cleanupSome won't do anything since there are no reclaimed targets. // cleanupSome won't do anything since there are no reclaimed targets.
fg.cleanupSome(cleanup2); fg.cleanupSome(cleanup2);
assertEquals(0, cleanup_count); 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' });
fg.cleanupSome(cleanup2);
assertEquals(1, cleanup_count);
assertEquals(1, cleanup_holdings.length);
assertEquals("holdings", cleanup_holdings[0]);
})(); })();
// 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 // Flags: --expose-gc --noincremental-marking
(async function () { let cleanup_called = false;
let cleanup = function(holdings) {
let cleanup_called = false;
const cleanup = function(holdings) {
assertFalse(cleanup_called); assertFalse(cleanup_called);
assertEquals("holdings", holdings); let holdings_list = [];
holdings_list.push(holdings);
assertEquals(1, holdings_list.length);
assertEquals("holdings", holdings_list[0]);
cleanup_called = true; cleanup_called = true;
} }
const fg = new FinalizationRegistry(cleanup); let fg = new FinalizationRegistry(cleanup);
let weak_ref; let weak_ref;
(function() { (function() {
const o = {}; let o = {};
weak_ref = new WeakRef(o); weak_ref = new WeakRef(o);
fg.register(o, "holdings"); fg.register(o, "holdings");
})(); })();
// Since the WeakRef was created during this turn, it is not cleared by GC. The // 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 // pointer inside the FinalizationRegistry is not cleared either, since the WeakRef
// keeps the target object alive. // keeps the target object alive.
// Here we invoke GC synchronously and, with conservative stack scanning, there is gc();
// a chance that the object is not reclaimed now. In any case, the WeakRef should (function() {
// not be cleared. assertNotEquals(undefined, weak_ref.deref());
})();
// Trigger gc in next task
setTimeout(() => {
gc(); gc();
assertNotEquals(undefined, weak_ref.deref()); // Check that cleanup callback was called in a follow up task
assertFalse(cleanup_called); setTimeout(() => {
assertTrue(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()); assertEquals(undefined, weak_ref.deref());
assertFalse(cleanup_called); }, 0);
}, 0);
// Check that the cleanup callback was called in a follow up task.
setTimeout(() => { assertTrue(cleanup_called); }, 0);
})();

View File

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

View File

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

View File

@ -5,78 +5,63 @@
// Flags: --expose-gc --noincremental-marking // Flags: --expose-gc --noincremental-marking
// Flags: --no-stress-flush-code // 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) { let cleanup0 = function(holdings) {
++cleanup0_holdings_count;
++cleanup0_call_count; ++cleanup0_call_count;
} }
let cleanup1 = function (holdings) { let cleanup1 = function(holdings) {
++cleanup1_holdings_count;
++cleanup1_call_count; ++cleanup1_call_count;
} }
let fg0 = new FinalizationRegistry(cleanup0); let fg0 = new FinalizationRegistry(cleanup0);
let fg1 = new FinalizationRegistry(cleanup1); let fg1 = new FinalizationRegistry(cleanup1);
// Register 1 weak reference for each FinalizationRegistry and kill the // Register 1 weak reference for each FinalizationRegistry and kill the objects they point to.
// objects they point to. (function() {
(function () { // The objects need to be inside a closure so that we can reliably kill them.
// The objects need to be inside a closure so that we can reliably kill
// them.
let objects = []; let objects = [];
objects[0] = {}; objects[0] = {};
objects[1] = {}; objects[1] = {};
fg0.register(objects[0], "holdings0-0"); fg0.register(objects[0], "holdings0-0");
fg1.register(objects[1], "holdings1-0"); fg1.register(objects[1], "holdings1-0");
// Drop the references to the objects. // Drop the references to the objects.
objects = []; objects = [];
})(); })();
// Schedule a GC, which will schedule both fg0 and fg1 for cleanup. // Will schedule both fg0 and fg1 for cleanup.
// Here and below, we need to invoke GC asynchronously and wait for it to gc();
// 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 // Before the cleanup task has a chance to run, do the same thing again, so both
// both FinalizationRegistries are (again) scheduled for cleanup. This has to // FinalizationRegistries are (again) scheduled for cleanup. This has to be a IIFE function
// be a IIFE function (so that we can reliably kill the objects) so we cannot // (so that we can reliably kill the objects) so we cannot use the same function
// use the same function as before. // as before.
(function () { (function() {
let objects = []; let objects = [];
objects[0] = {}; objects[0] = {};
objects[1] = {}; objects[1] = {};
fg0.register(objects[0], "holdings0-1"); fg0.register(objects[0], "holdings0-1");
fg1.register(objects[1], "holdings1-1"); fg1.register(objects[1], "holdings1-1");
objects = []; 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);
})(); })();
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,20 +4,20 @@
// Flags: --expose-gc --noincremental-marking // Flags: --expose-gc --noincremental-marking
(async function () { let cleanup_call_count = 0;
let cleanup_holdings_count = 0;
let cleanup_call_count = 0; let cleanup = function(holdings) {
let cleanup = function (holdings) { ++cleanup_holdings_count;
++cleanup_call_count; ++cleanup_call_count;
} }
let fg1 = new FinalizationRegistry(cleanup); let fg1 = new FinalizationRegistry(cleanup);
let fg2 = new FinalizationRegistry(cleanup); let fg2 = new FinalizationRegistry(cleanup);
// Create two objects and register them in FinalizationRegistries. The objects need // Create two objects and register them in FinalizationRegistries. The objects need
// to be inside a closure so that we can reliably kill them! // to be inside a closure so that we can reliably kill them!
(function () { (function() {
let object1 = {}; let object1 = {};
fg1.register(object1, "holdings1"); fg1.register(object1, "holdings1");
@ -25,21 +25,16 @@
fg2.register(object2, "holdings2"); fg2.register(object2, "holdings2");
// object1 and object2 go out of scope. // 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);
})(); })();
// 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 // Flags: --expose-gc --noincremental-marking
(async function () { let call_count = 0;
let reentrant_gc = function(holdings) {
let call_count = 0;
const reentrant_gc = function (holdings) {
gc(); gc();
call_count++; call_count++;
} }
const fg = new FinalizationRegistry(reentrant_gc); let fg = new FinalizationRegistry(reentrant_gc);
(function () { (function() {
fg.register({}, 42); fg.register({}, 42);
})(); })();
// We need to invoke GC asynchronously and wait for it to finish, so that gc();
// 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 // 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 // 1) Create many JSFinalizationRegistry objects so that they span several pages
// their slots correctly recorded by the GC. // (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 // 2) Force two GCs to ensure that JSFinalizatonRegistry objects are tenured.
// (page size is 256kb). gc();
let registries = []; gc();
for (let i = 0; i < 1024 * 8; i++) {
registries.push(new FinalizationRegistry(() => { }));
}
// 2) Force two GCs to ensure that JSFinalizatonRegistry objects are tenured. // 3) In a function: create a dummy target and register it in all
// Here and below, we need to invoke GC asynchronously and wait for it to // JSFinalizatonRegistry objects.
// finish, so that it doesn't need to scan the stack. Otherwise, the objects (function() {
// 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 = {}; let garbage = {};
registries.forEach((fr) => { registries.forEach((fr) => {
fr.register(garbage, 42); fr.register(garbage, 42);
}); });
garbage = null; 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' });
})(); })();
// 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 // Flags: --harmony-symbol-as-weakmap-key --expose-gc --noincremental-marking
(async function () { (function TestWeakRefWithSymbolGC() {
let weakRef; let weakRef;
(function () { {
const innerKey = Symbol('123'); const innerKey = Symbol('123');
weakRef = new WeakRef(innerKey); weakRef = new WeakRef(innerKey);
})(); }
// Since the WeakRef was created during this turn, it is not cleared by GC. // 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(); gc();
assertNotEquals(undefined, weakRef.deref()); assertNotEquals(undefined, weakRef.deref());
// Next task.
// Trigger GC again in next task. Now the WeakRef is cleared. setTimeout(() => {
// We need to invoke GC asynchronously and wait for it to finish, so that gc();
// 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()); 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. // 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(); gc();
(function() { (function() {
@ -26,29 +23,25 @@ gc();
// New task // New task
setTimeout(function() { setTimeout(function() {
(function () { wr1.deref(); })(); wr1.deref();
o1 = null; o1 = null;
gc(); // deref makes sure we don't clean up wr1 gc(); // deref makes sure we don't clean up wr1
(function () { assertNotEquals(undefined, wr1.deref()); })();
// New task // New task
setTimeout(function() { setTimeout(function() {
(function () { wr2.deref(); })(); wr2.deref();
o2 = null; o2 = null;
gc(); // deref makes sure we don't clean up wr2 gc(); // deref makes sure we don't clean up wr2
(function () { assertNotEquals(undefined, wr2.deref()); })();
// New task // New task
(async function () { setTimeout(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, wr1.deref()); assertEquals(undefined, wr1.deref());
gc();
// New task
setTimeout(function() {
assertEquals(undefined, wr2.deref()); assertEquals(undefined, wr2.deref());
})(); }, 0);
}, 0);
}, 0); }, 0);
}, 0); }, 0);

View File

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

View File

@ -4,19 +4,17 @@
// Flags: --expose-gc --noincremental-marking --noincremental-marking // Flags: --expose-gc --noincremental-marking --noincremental-marking
(async function () { let cleanup_call_count = 0;
let cleanup = function(holdings) {
let cleanup_call_count = 0;
let cleanup = function (holdings) {
++cleanup_call_count; ++cleanup_call_count;
} }
let fg = new FinalizationRegistry(cleanup); let fg = new FinalizationRegistry(cleanup);
let key = { "k": "this is the key" }; let key = {"k": "this is the key"};
// Create an object and register it in the FinalizationRegistry. The object needs // Create an object and register it in the FinalizationRegistry. The object needs
// to be inside a closure so that we can reliably kill them! // to be inside a closure so that we can reliably kill them!
(function () { (function() {
let object = {}; let object = {};
fg.register(object, "my holdings", key); fg.register(object, "my holdings", key);
@ -25,21 +23,15 @@
assertTrue(success); assertTrue(success);
// object goes out of scope. // 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);
})(); })();
// 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,19 +4,17 @@
// Flags: --expose-gc --noincremental-marking // Flags: --expose-gc --noincremental-marking
(async function () { let cleanup_call_count = 0;
let cleanup = function(holdings) {
let cleanup_call_count = 0;
let cleanup = function (holdings) {
++cleanup_call_count; ++cleanup_call_count;
} }
let fg = new FinalizationRegistry(cleanup); let fg = new FinalizationRegistry(cleanup);
let key = { "k": "this is the key" }; let key = {"k": "this is the key"};
// Create an object and register it in the FinalizationRegistry. The object needs // Create an object and register it in the FinalizationRegistry. The object needs
// to be inside a closure so that we can reliably kill them! // to be inside a closure so that we can reliably kill them!
(function () { (function() {
let object = {}; let object = {};
fg.register(object, "holdings", key); fg.register(object, "holdings", key);
@ -29,22 +27,16 @@
assertFalse(success); assertFalse(success);
// object goes out of scope. // 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);
})(); })();
// 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 // Flags: --expose-gc --noincremental-marking
(async function () { let cleanup_call_count = 0;
let cleanup_holdings_count = 0;
let cleanup_call_count = 0; let cleanup = function(holdings) {
let cleanup = function (holdings) {
assertEquals(holdings, "holdings"); assertEquals(holdings, "holdings");
let success = fg.unregister(key); let success = fg.unregister(key);
assertFalse(success); assertFalse(success);
++cleanup_holdings_count;
++cleanup_call_count; ++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 // Create an object and register it in the FinalizationRegistry. The object needs to be inside
// a closure so that we can reliably kill them! // a closure so that we can reliably kill them!
let key = { "k": "this is the key" }; let key = {"k": "this is the key"};
(function () { (function() {
let object = {}; let object = {};
fg.register(object, "holdings", key); fg.register(object, "holdings", key);
// object goes out of scope. // 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);
})(); })();
// 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,10 +4,9 @@
// Flags: --expose-gc --noincremental-marking // Flags: --expose-gc --noincremental-marking
(async function () { let cleanup_call_count = 0;
let cleanup_holdings_count = 0;
let cleanup_call_count = 0; let cleanup = function(holdings) {
let cleanup = function(holdings) {
// See which target we're cleaning up and unregister the other one. // See which target we're cleaning up and unregister the other one.
if (holdings == 1) { if (holdings == 1) {
let success = fg.unregister(key2); let success = fg.unregister(key2);
@ -17,37 +16,33 @@
let success = fg.unregister(key1); let success = fg.unregister(key1);
assertTrue(success); assertTrue(success);
} }
++cleanup_holdings_count;
++cleanup_call_count; ++cleanup_call_count;
} }
let fg = new FinalizationRegistry(cleanup); let fg = new FinalizationRegistry(cleanup);
let key1 = {"k": "first key"}; let key1 = {"k": "first key"};
let key2 = {"k": "second key"}; let key2 = {"k": "second key"};
// Create two objects and register them in the FinalizationRegistry. The objects // Create two objects and register them in the FinalizationRegistry. The objects
// need to be inside a closure so that we can reliably kill them! // need to be inside a closure so that we can reliably kill them!
(function() { (function() {
let object1 = {}; let object1 = {};
fg.register(object1, 1, key1); fg.register(object1, 1, key1);
let object2 = {}; let object2 = {};
fg.register(object2, 2, key2); fg.register(object2, 2, key2);
// object1 and object2 go out of scope. // 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);
})(); })();
// 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 // Flags: --expose-gc --noincremental-marking
(async function () { let cleanup_call_count = 0;
let cleanup_holdings_count = 0;
let cleanup_call_count = 0; let cleanup = function(holdings) {
let cleanup = function(holdings) {
assertEquals(holdings, "holdings"); assertEquals(holdings, "holdings");
// There's one more object with the same key that we haven't // There's one more object with the same key that we haven't
// cleaned up yet so we should be able to unregister the // cleaned up yet so we should be able to unregister the
// callback for that one. // callback for that one.
let success = fg.unregister(key); let success = fg.unregister(key);
assertTrue(success); assertTrue(success);
++cleanup_holdings_count;
++cleanup_call_count; ++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 // Create an object and register it in the FinalizationRegistry. The object needs to be inside
// a closure so that we can reliably kill them! // a closure so that we can reliably kill them!
let key = {"k": "this is the key"}; let key = {"k": "this is the key"};
(function() { (function() {
let object = {}; let object = {};
let object2 = {}; let object2 = {};
fg.register(object, "holdings", key); fg.register(object, "holdings", key);
fg.register(object2, "holdings", key); fg.register(object2, "holdings", key);
// object goes out of scope. // 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);
})(); })();
// 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,21 +4,21 @@
// Flags: --expose-gc --noincremental-marking // Flags: --expose-gc --noincremental-marking
(async function () { let cleanup_call_count = 0;
let cleanup_holdings_count = 0;
let cleanup_call_count = 0; let cleanup = function(holdings) {
let cleanup = function(holdings) {
assertEquals("holdings2", holdings); assertEquals("holdings2", holdings);
++cleanup_holdings_count;
++cleanup_call_count; ++cleanup_call_count;
} }
let fg = new FinalizationRegistry(cleanup); let fg = new FinalizationRegistry(cleanup);
let key1 = {"k": "key1"}; let key1 = {"k": "key1"};
let key2 = {"k": "key2"}; let key2 = {"k": "key2"};
// Create three objects and register them in the FinalizationRegistry. The objects // Create three objects and register them in the FinalizationRegistry. The objects
// need to be inside a closure so that we can reliably kill them! // need to be inside a closure so that we can reliably kill them!
(function() { (function() {
let object1a = {}; let object1a = {};
fg.register(object1a, "holdings1a", key1); fg.register(object1a, "holdings1a", key1);
@ -33,22 +33,17 @@
assertTrue(success); assertTrue(success);
// objects go out of scope. // 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);
})(); })();
// 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 // Flags: --expose-gc --noincremental-marking
(async function () { let cleanup_call_count = 0;
let cleanup = function(holdings) {
let cleanup_call_count = 0;
let cleanup = function(holdings) {
++cleanup_call_count; ++cleanup_call_count;
} }
let key = {"k": "this is my key"}; let key = {"k": "this is my key"};
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 // Create an object and register it in the FinalizationRegistry. The object needs to be inside
// a closure so that we can reliably kill them! // a closure so that we can reliably kill them!
(function() { (function() {
let object = {}; let object = {};
fg.register(object, {}, key); fg.register(object, {}, key);
// object goes out of scope. // 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);
})(); })();
// 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 // Flags: --expose-gc --noincremental-marking
(async function () { let cleanup_called = false;
let cleanup = function(holdings_arg) {
let cleanup_called = false;
let cleanup = function(holdings_arg) {
assertFalse(cleanup_called); assertFalse(cleanup_called);
assertEquals(holdings_arg, holdings); assertEquals(holdings_arg, holdings);
cleanup_called = true; cleanup_called = true;
} }
let fg = new FinalizationRegistry(cleanup); let fg = new FinalizationRegistry(cleanup);
let o = {}; let o = {};
let holdings = {'h': 55}; let holdings = {'h': 55};
// Ignition holds references to objects in temporary registers. These will be // Ignition holds references to objects in temporary registers. These will be
// released when the function exits. So only access o inside a function to // 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 // prevent any references to objects in temporary registers when a gc is
// triggered. // triggered.
(() => { fg.register(o, holdings); })() (() => { fg.register(o, holdings); })()
// Here and below, we need to invoke GC asynchronously and wait for it to gc();
// finish, so that it doesn't need to scan the stack. Otherwise, the objects assertFalse(cleanup_called);
// may not be reclaimed because of conservative stack scanning and the test
// may not work as intended.
await gc({ type: 'major', execution: 'async' });
assertFalse(cleanup_called);
// Drop the last reference to o. // Drop the last reference to o.
(() => { o = null; })() (() => { o = null; })()
// GC will clear the WeakCell; the cleanup function will be called the next time // GC will clear the WeakCell; the cleanup function will be called the next time
// we enter the event loop. // we enter the event loop.
await gc({ type: 'major', execution: 'async' }); gc();
assertFalse(cleanup_called); assertFalse(cleanup_called);
let timeout_func = function() { let timeout_func = function() {
assertTrue(cleanup_called); assertTrue(cleanup_called);
} }
setTimeout(timeout_func, 0); setTimeout(timeout_func, 0);
})();

View File

@ -4,30 +4,19 @@
// Flags: --harmony-weak-refs-with-cleanup-some --expose-gc --noincremental-marking // Flags: --harmony-weak-refs-with-cleanup-some --expose-gc --noincremental-marking
const cleanup = function (holdings) { globalThis.FRRan = true; }; var FR = new FinalizationRegistry (function (holdings) { globalThis.FRRan = true; });
const FR = new FinalizationRegistry(cleanup); {
(function () {
let obj = {}; let obj = {};
// obj is its own unregister token and becomes unreachable after this // obj is its own unregister token and becomes unreachable after this
// block. If the unregister token is held strongly this test will not // block. If the unregister token is held strongly this test will not
// terminate. // terminate.
FR.register(obj, 42, obj); FR.register(obj, 42, obj);
})(); }
function tryAgain() { function tryAgain() {
(async function () { 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' });
if (globalThis.FRRan || FR.cleanupSome()) { if (globalThis.FRRan || FR.cleanupSome()) {
return; return;
} }
setTimeout(tryAgain, 0); setTimeout(tryAgain, 0);
})();
} }
tryAgain(); tryAgain();

View File

@ -5,29 +5,22 @@
// Flags: --expose-gc --noincremental-marking // Flags: --expose-gc --noincremental-marking
let wr; let wr;
(function () { (function() {
let o = {}; let o = {};
wr = new WeakRef(o); wr = new WeakRef(o);
// Don't deref here, we want to test that the creation is enough to keep the // Don't deref here, we want to test that the creation is enough to keep the
// WeakRef alive until the end of the turn. // 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(); gc();
// Since the WeakRef was created during this turn, it is not cleared by 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. // Next task.
setTimeout(() => { setTimeout(() => {
(async function () { gc();
// 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()); assertEquals(undefined, wr.deref());
})();
}, 0); }, 0);

View File

@ -6,43 +6,40 @@
let wr; let wr;
let wr_control; // control WeakRef for testing what happens without deref let wr_control; // control WeakRef for testing what happens without deref
(function () { (function() {
let o1 = {}; let o1 = {};
wr = new WeakRef(o1); wr = new WeakRef(o1);
let o2 = {}; let o2 = {};
wr_control = new WeakRef(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(); gc();
// Next task. // Next task.
setTimeout(function() { setTimeout(function() {
// Call deref inside a closure, trying to avoid accidentally storing a strong // Call deref inside a closure, trying to avoid accidentally storing a strong
// reference into the object in the stack frame. // reference into the object in the stack frame.
(function () { wr.deref(); })(); (function() {
wr.deref();
})();
strong = null; strong = null;
// This GC should clear wr_control (modulo CSS), since nothing was keeping it // This GC will clear wr_control.
// alive, but it should not clear wr.
gc(); gc();
(function () { assertNotEquals(undefined, wr.deref()); })();
// Next task. (function() {
(async function () { assertNotEquals(undefined, wr.deref());
// Trigger GC again to make sure the two WeakRefs are cleared. // Now the control WeakRef got cleared, since nothing was keeping it alive.
// 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());
assertEquals(undefined, wr_control.deref()); assertEquals(undefined, wr_control.deref());
})(); })();
// Next task.
setTimeout(function() {
gc();
assertEquals(undefined, wr.deref());
}, 0);
}, 0); }, 0);