Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/js/src/jit-test/tests/gc/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 19 kB image not shown  

Quelle  weak-marking-03.js   Sprache: JAVA

 
// |jit-test| allow-unhandlable-oom

// weakmap marking tests that use the testing mark queue to force an ordering
// of marking.

// We are carefully controlling the sequence of GC events.
gczeal(0);

// If a command-line parameter is given, use it as a substring restriction on
// the tests to run.
var testRestriction = scriptArgs[0];
printErr(`testRestriction is ${testRestriction || '(run all tests)'}`);

function runtest(func) {
  if (testRestriction && ! func.name.includes(testRestriction)) {
    print("\Skipping " + func.name);
  } else {
    print("\nRunning " + func.name);
    func();
  }
}

function reportMarks(prefix = "") {
  const marks = getMarks();
  const current = currentgc();
  const markstr = marks.join("/");
  print(`${prefix}[${current.incrementalState}/${current.sweepGroup}@${current.queuePos}] ${markstr}`);
  return markstr;
}

function startGCMarking() {
  startgc(100000);
  while (gcstate() === "Prepare" || gcstate() === "MarkRoots") {
    gcslice(100000);
  }
}

function purgeKey() {
  const m = new WeakMap();
  const vals = {};
  vals.key = Object.create(null);
  vals.val = Object.create(null);
  m.set(vals.key, vals.val);

  minorgc();

  addMarkObservers([m, vals.key, vals.val]);

  enqueueMark(m);
  enqueueMark("yield");

  enqueueMark(vals.key);
  enqueueMark("yield");

  vals.key = vals.val = null;

  startGCMarking();
  // getMarks() returns map/key/value
  assertEq(getMarks().join("/"), "black/unmarked/unmarked",
           "marked the map black");

  gcslice(100000);
  assertEq(getMarks().join("/"), "black/black/unmarked",
           "key is now marked");

  // Trigger purgeWeakKey: the key is in weakkeys (because it was unmarked when
  // the map was marked), and now we're removing it.
  m.delete(nondeterministicGetWeakMapKeys(m)[0]);

  finishgc(); // Finish the GC
  assertEq(getMarks().join("/"), "black/black/black",
           "at end, value is marked too");

  clearMarkQueue();
  clearMarkObservers();
}

if (this.enqueueMark)
  runtest(purgeKey);

function removeKey() {
  reportMarks("removeKey start: ");

  const m = new WeakMap();
  const vals = {};
  vals.key = Object.create(null);
  vals.val = Object.create(null);
  m.set(vals.key, vals.val);

  minorgc();

  addMarkObservers([m, vals.key, vals.val]);

  enqueueMark(m);
  enqueueMark("yield");

  startGCMarking();
  reportMarks("first: ");
  var marks = getMarks();
  assertEq(marks[0], "black""map is black");
  assertEq(marks[1], "unmarked""key not marked yet");
  assertEq(marks[2], "unmarked""value not marked yet");
  m.delete(vals.key);

  finishgc(); // Finish the GC
  reportMarks("done: ");
  marks = getMarks();
  assertEq(marks[0], "black""map is black");
  assertEq(marks[1], "black""key is black");
  assertEq(marks[2], "black""value is black");

  // Do it again, but this time, remove all other roots.
  m.set(vals.key, vals.val);
  vals.key = vals.val = null;
  startGCMarking();
  marks = getMarks();
  assertEq(marks[0], "black""map is black");
  assertEq(marks[1], "unmarked""key not marked yet");
  assertEq(marks[2], "unmarked""value not marked yet");

  // This was meant to test the weakmap deletion barrier, which would remove
  // the key from weakkeys. Unfortunately, JS-exposed WeakMaps now have a read
  // barrier on lookup that marks the key, and deletion requires a lookup.
  m.delete(nondeterministicGetWeakMapKeys(m)[0]);

  finishgc();
  marks = getMarks();
  assertEq(marks[0], "black""map is black");
  assertEq(marks[1], "black""key was blackened by lookup read barrier during deletion");
  assertEq(marks[2], "black""value is black because map and key are black");

  clearMarkQueue();
  clearMarkObservers();
}

if (this.enqueueMark)
  runtest(removeKey);

// Test:
//   1. mark the map
//     - that inserts the delegate into weakKeys
//   2. nuke the CCW key
//     - removes the delegate from weakKeys
//   3. mark the key
//   4. enter weak marking mode
//
// The problem it's attempting to recreate is that entering weak marking mode
// will no longer mark the value, because there's no delegate to trigger it,
// and the key was not added to weakKeys (because at the time the map was
// scanned, the key had a delegate, so it was added to weakKeys instead.)
function nukeMarking() {
  const g1 = newGlobal({newCompartment: true});

  const vals = {};
  vals.map = new WeakMap();
  vals.key = g1.eval("Object.create(null)");
  vals.val = Object.create(null);
  vals.map.set(vals.key, vals.val);
  vals.val = null;
  gc();

  // Set up the sequence of marking events.
  enqueueMark(vals.map);
  enqueueMark("yield");
  // We will nuke the key's delegate here.
  enqueueMark(vals.key);
  enqueueMark("enter-weak-marking-mode");

  // Okay, run through the GC now.
  startgc(1000000);
  while (gcstate() === "Prepare" || gcstate() === "MarkRoots") {
    gcslice(100000);
  }
  assertEq(gcstate(), "Mark""expected to yield after marking map");
  // We should have marked the map and then yielded back here.
  nukeCCW(vals.key);
  // Finish up the GC.
  gcslice();

  clearMarkQueue();
}

if (this.enqueueMark)
  runtest(nukeMarking);

// Similar to the above, but trying to get a different failure:
//  - start marking
//  - find a map, add its key to ephemeronEdges
//  - nuke the key (and all other CCWs between the key -> delegate zones)
//  - when sweeping, we will no longer have any edges between the key
//    and delegate zones. So they will be placed in separate sweep groups.
//  - for this test, the delegate zone must be swept after the key zone
//  - make sure we don't try to mark back in the key zone (due to an
//    ephemeron edge) while sweeping the delegate zone. In a DEBUG build,
//    this would assert.
function nukeMarkingSweepGroups() {
  // Create g1 before host, because that will result in the right zone
  // ordering to trigger the bug.
  const g1 = newGlobal({newCompartment: true});
  const host = newGlobal({newCompartment: true});
  host.g1 = g1;
  host.eval(`
  const vals = {};
  vals.map = new WeakMap();
  vals.key = g1.eval("Object.create(null)");
  vals.val = Object.create(null);
  vals.map.set(vals.key, vals.val);
  vals.val = null;
  gc();

  // Set up the sequence of marking events.
  enqueueMark(vals.map);
  enqueueMark("yield");
  // We will nuke the key's delegate here.
  enqueueMark(vals.key);
  enqueueMark("enter-weak-marking-mode");

  // Okay, run through the GC now.
  startgc(1);
  while (gcstate() !== "Mark") {
    gcslice(100000);
  }
  assertEq(gcstate(), "Mark""expected to yield after marking map");
  // We should have marked the map and then yielded back here.
  nukeAllCCWs();
  // Finish up the GC.
  while (gcstate() === "Mark") {
    gcslice(1000);
  }
  gcslice();

  clearMarkQueue();
  `);
}

if (this.enqueueMark)
  runtest(nukeMarkingSweepGroups);

function transplantMarking() {
  const g1 = newGlobal({newCompartment: true});

  const vals = {};
  vals.map = new WeakMap();
  let {object, transplant} = transplantableObject();
  vals.key = object;
  object = null;
  vals.val = Object.create(null);
  vals.map.set(vals.key, vals.val);
  vals.val = null;
  gc();

  // Set up the sequence of marking events.
  enqueueMark(vals.map);
  enqueueMark("yield");
  // We will transplant the key here.
  enqueueMark(vals.key);
  enqueueMark("enter-weak-marking-mode");

  // Okay, run through the GC now.
  startgc(1000000);
  while (gcstate() !== "Mark") {
    gcslice(100000);
  }
  assertEq(gcstate(), "Mark""expected to yield after marking map");
  // We should have marked the map and then yielded back here.
  transplant(g1);
  // Finish up the GC.
  gcslice();

  clearMarkQueue();
}

if (this.enqueueMark)
  runtest(transplantMarking);

// 1. Mark the map
//   => add delegate to weakKeys
// 2. Mark the delegate black
//   => do nothing because we are not in weak marking mode
// 3. Mark the key gray
//   => mark value gray, not that we really care
// 4. Enter weak marking mode
//   => black delegate darkens the key from gray to black
function grayMarkingMapFirst() {
  const g = newGlobal({newCompartment: true});
  const vals = {};
  vals.map = new WeakMap();
  vals.key = g.eval("Object.create(null)");
  vals.val = Object.create(null);
  vals.map.set(vals.key, vals.val);

  g.delegate = vals.key;
  g.eval("dummy = Object.create(null)");
  g.eval("grayRoot().push(delegate, dummy)");
  addMarkObservers([vals.map, vals.key]);
  g.addMarkObservers([vals.key, g.dummy]);
  addMarkObservers([vals.val]);

  gc();

  enqueueMark(vals.map);
  enqueueMark("yield"); // checkpoint 1

  g.enqueueMark(vals.key);
  enqueueMark("yield"); // checkpoint 2

  vals.val = null;
  vals.key = null;
  g.delegate = null;
  g.dummy = null;

  const showmarks = () => {
    print("[map,key,delegate,graydummy,value] marked " + JSON.stringify(getMarks()));
  };

  print("Starting incremental GC");
  startGCMarking();
  // Checkpoint 1, after marking map
  showmarks();
  var marks = getMarks();
  assertEq(marks[0], "black""map is black");
  assertEq(marks[1], "unmarked""key is not marked yet");
  assertEq(marks[2], "unmarked""delegate is not marked yet");

  gcslice(100000);
  // Checkpoint 2, after marking delegate
  showmarks();
  marks = getMarks();
  assertEq(marks[0], "black""map is black");
  assertEq(marks[1], "unmarked""key is not marked yet");
  assertEq(marks[2], "black""delegate is black");

  gcslice();
  // GC complete. Key was marked black (b/c of delegate), then gray marking saw
  // it was already black and skipped it.
  showmarks();
  marks = getMarks();
  assertEq(marks[0], "black""map is black");
  assertEq(marks[1], "black""delegate marked key black because of weakmap");
  assertEq(marks[2], "black""delegate is still black");
  assertEq(marks[3], "gray""basic gray marking is working");
  assertEq(marks[4], "black""black map + black delegate => black value");

  clearMarkQueue();
  clearMarkObservers();
  grayRoot().length = 0;
  g.eval("grayRoot().length = 0");
}

if (this.enqueueMark)
  runtest(grayMarkingMapFirst);

function grayMarkingMapLast() {
  const g = newGlobal({newCompartment: true});
  const vals = {};
  vals.map = new WeakMap();
  vals.key = g.eval("Object.create(null)");
  vals.val = Object.create(null);
  vals.map.set(vals.key, vals.val);

  vals.map2 = new WeakMap();
  vals.key2 = g.eval("Object.create(null)");
  vals.val2 = Object.create(null);
  vals.map2.set(vals.key2, vals.val2);

  g.delegate = vals.key;
  g.eval("grayRoot().push(delegate)");
  addMarkObservers([vals.map, vals.key]);
  g.addMarkObservers([vals.key]);
  addMarkObservers([vals.val]);

  grayRoot().push(vals.key2);
  addMarkObservers([vals.map2, vals.key2]);
  g.addMarkObservers([vals.key2]);
  addMarkObservers([vals.val2]);

  const labels = ["map""key""delegate""value""map2""key2""delegate2""value2"];

  gc();

  g.enqueueMark(vals.key);
  g.enqueueMark(vals.key2);
  enqueueMark("yield"); // checkpoint 1

  vals.val = null;
  vals.key = null;
  g.delegate = null;

  vals.map2 = null// Important! Second map is never marked, keeps nothing alive.
  vals.key2 = null;
  vals.val2 = null;
  g.delegate2 = null;

  const labeledMarks = () => {
    const info = {};
    const marks = getMarks();
    for (let i = 0; i < labels.length; i++)
      info[labels[i]] = marks[i];
    return info;
  };

  const showmarks = () => {
    print("Marks:");
    for (const [label, mark] of Object.entries(labeledMarks()))
      print(`  ${label}: ${mark}`);
  };

  print("Starting incremental GC");
  startGCMarking();
  // Checkpoint 1, after marking key
  showmarks();
  var marks = labeledMarks();
  assertEq(marks.map, "unmarked""map is unmarked");
  assertEq(marks.key, "unmarked""key is not marked yet");
  assertEq(marks.delegate, "black""delegate is black");
  assertEq(marks.map2, "unmarked""map2 is unmarked");
  assertEq(marks.key2, "unmarked""key2 is not marked yet");
  assertEq(marks.delegate2, "black""delegate2 is black");

  gcslice();
  // GC complete. When entering weak marking mode, black delegate propagated to
  // key.
  showmarks();
  marks = labeledMarks();
  assertEq(marks.map, "black""map is black");
  assertEq(marks.key, "black""delegate marked key black because of weakmap");
  assertEq(marks.delegate, "black""delegate is still black");
  assertEq(marks.value, "black""black map + black delegate => black value");
  assertEq(marks.map2, "dead""map2 is dead");
  assertEq(marks.key2, "gray""key2 marked gray, map2 had no effect");
  assertEq(marks.delegate2, "black""delegate artificially marked black via mark queue");
  assertEq(marks.value2, "dead""dead map + black delegate => dead value");

  clearMarkQueue();
  clearMarkObservers();
  grayRoot().length = 0;
  g.eval("grayRoot().length = 0");

  return vals; // To prevent the JIT from optimizing out vals.
}

if (this.enqueueMark)
  runtest(grayMarkingMapLast);

function grayMapKey() {
  const vals = {};
  vals.m = new WeakMap();
  vals.key = Object.create(null);
  vals.val = Object.create(null);
  vals.m.set(vals.key, vals.val);

  // Maps are allocated black, so we won't be able to mark it gray during the
  // first GC.
  gc();

  addMarkObservers([vals.m, vals.key, vals.val]);

  // Wait until we can mark gray (ie, sweeping). Mark the map gray and yield.
  // This should happen all in one slice.
  enqueueMark("set-color-gray");
  enqueueMark(vals.m);
  enqueueMark("unset-color");
  enqueueMark("yield");

  // Make the weakmap no longer reachable from the roots, so we can mark it
  // gray.
  vals.m = null;

  enqueueMark(vals.key);
  enqueueMark("yield");

  vals.key = vals.val = null;

  startGCMarking();
  assertEq(getMarks().join("/"), "gray/unmarked/unmarked",
           "marked the map gray");

  gcslice(100000);
  assertEq(getMarks().join("/"), "gray/black/unmarked",
           "key is now marked black");

  finishgc(); // Finish the GC

  assertEq(getMarks().join("/"), "gray/black/gray",
           "at end: black/gray => gray");

  clearMarkQueue();
  clearMarkObservers();
}

if (this.enqueueMark)
  runtest(grayMapKey);

function grayKeyMap() {
  const vals = {};
  vals.m = new WeakMap();
  vals.key = Object.create(null);
  vals.val = Object.create(null);
  vals.m.set(vals.key, vals.val);

  addMarkObservers([vals.m, vals.key, vals.val]);

  enqueueMark(vals.key);
  enqueueMark("yield");

  // Wait until we are gray marking.
  enqueueMark("set-color-gray");
  enqueueMark(vals.m);
  enqueueMark("unset-color");
  enqueueMark("yield");

  enqueueMark("set-color-black");
  enqueueMark(vals.m);
  enqueueMark("unset-color");

  // Make the weakmap no longer reachable from the roots, so we can mark it
  // gray.
  vals.m = null;

  vals.key = vals.val = null;

  // Only mark this zone, to avoid interference from other tests that may have
  // created additional zones.
  schedulezone(vals);

  startGCMarking();
  // getMarks() returns map/key/value
  reportMarks("1: ");
  assertEq(getMarks().join("/"), "unmarked/black/unmarked",
           "marked key black");

  // We always yield before sweeping (in the absence of zeal), so we will see
  // the unmarked state another time.
  gcslice(100000);
  reportMarks("2: ");
  assertEq(getMarks().join("/"), "unmarked/black/unmarked",
           "marked key black, yield before sweeping");

  gcslice(100000);
  reportMarks("3: ");
  assertEq(getMarks().join("/"), "gray/black/gray",
           "marked the map gray, which marked the value when map scanned");

  finishgc(); // Finish the GC
  reportMarks("4: ");
  assertEq(getMarks().join("/"), "black/black/black",
           "further marked the map black, so value should also be blackened");

  clearMarkQueue();
  clearMarkObservers();
}

if (this.enqueueMark)
  runtest(grayKeyMap);

// Cause a key to be marked black *during gray marking*, by first marking a
// delegate black, then marking the map and key gray. When the key is scanned,
// it should be seen to be a CCW of a black delegate and so should itself be
// marked black.
//
// The bad behavior being prevented is:
//
//  1. You wrap an object in a CCW and use it as a weakmap key to some
//     information.
//  2. You keep a strong reference to the object (in its compartment).
//  3. The only references to the CCW are gray, and are in fact part of a cycle.
//  4. The CC runs and discards the CCW.
//  5. You look up the object in the weakmap again. This creates a new wrapper
//     to use as a key. It is not in the weakmap, so the information you stored
//     before is not found. (It may have even been collected, if you had no
//     other references to it.)
//
function blackDuringGray() {
  const g = newGlobal({newCompartment: true});
  const vals = {};
  vals.map = new WeakMap();
  vals.key = g.eval("Object.create(null)");
  vals.val = Object.create(null);
  vals.map.set(vals.key, vals.val);

  g.delegate = vals.key;
  addMarkObservers([vals.map, vals.key]);
  g.addMarkObservers([vals.key]);
  addMarkObservers([vals.val]);
  // Mark observers: map, key, delegate, value

  gc();

  g.enqueueMark(vals.key); // Mark the delegate black
  enqueueMark("yield"); // checkpoint 1

  // Mark the map gray. This will scan through all entries, find our key, and
  // mark it black because its delegate is black.
  enqueueMark("set-color-gray");
  enqueueMark(vals.map); // Mark the map gray

  vals.map = null;
  vals.val = null;
  vals.key = null;
  g.delegate = null;

  const showmarks = () => {
    print("[map,key,delegate,value] marked " + JSON.stringify(getMarks()));
  };

  print("Starting incremental GC");
  startGCMarking();
  // Checkpoint 1, after marking delegate black
  showmarks();
  var marks = getMarks();
  assertEq(marks[0], "unmarked""map is not marked yet");
  assertEq(marks[1], "unmarked""key is not marked yet");
  assertEq(marks[2], "black""delegate is black");
  assertEq(marks[3], "unmarked""values is not marked yet");

  finishgc();
  showmarks();
  marks = getMarks();
  assertEq(marks[0], "gray""map is gray");
  assertEq(marks[1], "gray""gray map + black delegate should mark key gray");
  assertEq(marks[2], "black""delegate is still black");
  assertEq(marks[3], "gray""gray map + gray key => gray value");

  clearMarkQueue();
  clearMarkObservers();
  grayRoot().length = 0;
  g.eval("grayRoot().length = 0");
}

if (this.enqueueMark)
  runtest(blackDuringGray);

// Same as above, except relying on the implicit edge from delegate -> key.
function blackDuringGrayImplicit() {
  const g = newGlobal({newCompartment: true});
  const vals = {};
  vals.map = new WeakMap();
  vals.key = g.eval("Object.create(null)");
  vals.val = Object.create(null);
  vals.map.set(vals.key, vals.val);

  g.delegate = vals.key;
  addMarkObservers([vals.map, vals.key]);
  g.addMarkObservers([vals.key]);
  addMarkObservers([vals.val]);
  // Mark observers: map, key, delegate, value

  gc();

  // Mark the map gray. This will scan through all entries, find our key, and
  // add implicit edges from delegate -> key and delegate -> value.
  enqueueMark("set-color-gray");
  enqueueMark(vals.map); // Mark the map gray
  enqueueMark("yield"); // checkpoint 1

  enqueueMark("set-color-black");
  g.enqueueMark(vals.key); // Mark the delegate black, propagating to key.

  vals.map = null;
  vals.val = null;
  vals.key = null;
  g.delegate = null;

  const showmarks = () => {
    print("[map,key,delegate,value] marked " + JSON.stringify(getMarks()));
  };

  print("Starting incremental GC");
  startGCMarking();
  // Checkpoint 1, after marking map gray
  showmarks();
  var marks = getMarks();
  assertEq(marks[0], "gray""map is gray");
  assertEq(marks[1], "unmarked""key is not marked yet");
  assertEq(marks[2], "unmarked""delegate is not marked yet");
  assertEq(marks[3], "unmarked""value is not marked yet");

  finishgc();
  showmarks();
  marks = getMarks();
  assertEq(marks[0], "gray""map is gray");
  assertEq(marks[1], "gray""gray map + black delegate should mark key gray");
  assertEq(marks[2], "black""delegate is black");
  assertEq(marks[3], "gray""gray map + gray key => gray value via delegate -> value");

  clearMarkQueue();
  clearMarkObservers();
  grayRoot().length = 0;
  g.eval("grayRoot().length = 0");
}

if (this.enqueueMark)
  runtest(blackDuringGrayImplicit);

Messung V0.5
C=83 H=91 G=86

¤ Dauer der Verarbeitung: 0.38 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.