Commit 3ac21492 authored by ager@chromium.org's avatar ager@chromium.org

Oilpan: introduce sticky forcedForTesting flag to ensure that a precise

GC is performed at the next return to the event loop.

Use this in svg animation tests to be able to reliably check that
objects die when expected.

R=erik.corry@gmail.com, haraken@chromium.org, kouhei@chromium.org, oilpan-reviews@chromium.org, vegorov@chromium.org
NOTRY=true

Review URL: https://codereview.chromium.org/220203005

git-svn-id: svn://svn.chromium.org/blink/trunk@170648 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent d0639256
......@@ -12,7 +12,3 @@ crbug.com/342574 [ Mac Debug ] fast/css-generated-content/crash-selection-editin
crbug.com/350316 [ Linux Win Debug ] http/tests/eventsource/workers/eventsource-simple.html [ Crash ]
crbug.com/350316 [ Mac Debug ] http/tests/eventsource/workers/eventsource-simple.html [ Timeout ]
crbug.com/356900 svg/animations/smil-leak-element-instances-noBaseValRef.svg [ Pass Failure ]
crbug.com/356900 svg/animations/smil-leak-element-instances.svg [ Pass Failure ]
crbug.com/356900 svg/animations/smil-leak-elements.svg [ Pass Failure ]
......@@ -8,19 +8,26 @@
if (!window.internals) {
debug("This test only runs on \"content_shell --dump-render-tree\", as it requires existence of window.internals.");
} else {
gc();
var documentsBefore = window.internals.numberOfLiveDocuments();
testRunner.waitUntilDone();
window.jsTestIsAsync = true;
var documentsBefore;
var documentsAfter;
collectGarbage(function() {
documentsBefore = window.internals.numberOfLiveDocuments();
var frame = document.getElementById('frame');
frame.contentDocument.body.innerHTML = '<form></form>';
document.body.removeChild(frame);
frame = null;
gc();
var documentsAfter = window.internals.numberOfLiveDocuments();
collectGarbage(function() {
documentsAfter = window.internals.numberOfLiveDocuments();
// -1 is from removing frame itself.
shouldBe('documentsBefore - 1', 'documentsAfter');
finishJSTest();
});
});
}
</script>
</body>
......
......@@ -657,6 +657,30 @@ function shouldHaveHadError(message)
testFailed("expectError() not called before shouldHaveHadError()");
}
// With Oilpan tests that rely on garbage collection need to go through
// the event loop in order to get precise garbage collections. Oilpan
// uses conservative stack scanning when not at the event loop and that
// can artificially keep objects alive. Therefore, tests that need to check
// that something is dead need to use this asynchronous collectGarbage
// function.
function collectGarbage(callback) {
// Perform multiple GCs to break sequences of Oilpan Persistent handles
// or RefPtrs that will keep objects alive until the next GC.
// FIXME: Oilpan: Once everything is moved to the oilpan heap we can
// reduce the number of garbage collections.
GCController.collect();
setTimeout(function() {
GCController.collect();
setTimeout(function() {
GCController.collect();
setTimeout(function() {
GCController.collect();
setTimeout(callback, 0);
}, 0);
}, 0);
}, 0);
}
function gc() {
if (typeof GCController !== "undefined")
GCController.collectAll();
......
......@@ -4,6 +4,7 @@
</defs>
<g id="g"/>
<text x="50" y="50" id="log"/>
<script xlink:href="../../resources/js-test.js"></script>
<script id="script">
<![CDATA[
......@@ -33,17 +34,13 @@ function createAnimatedRectInstance() {
}
function cleanup() {
// Collect garbage before recording starting live node count, in case there are live elements from previous tests.
// FIXME: Unclear why two calls to collect() are required, see crbug.com/307614
GCController.collect();
GCController.collect();
collectGarbage(function() {
var originalLiveElements = internals.numberOfLiveNodes();
while (g.hasChildNodes())
g.removeChild(g.lastChild);
GCController.collect();
collectGarbage(function() {
// FIXME: Why 400 and not 200?
var liveDelta = originalLiveElements - internals.numberOfLiveNodes() - 400;
if (liveDelta == 0)
......@@ -52,6 +49,8 @@ function cleanup() {
log("FAIL: " + liveDelta + " extra live node(s)");
testRunner.notifyDone();
});
});
}
function addMoreInstances() {
......
......@@ -4,6 +4,7 @@
</defs>
<g id="g"/>
<text x="50" y="50" id="log"/>
<script xlink:href="../../resources/js-test.js"></script>
<script id="script">
<![CDATA[
......@@ -34,14 +35,13 @@ function createAnimatedRectInstance() {
function cleanup() {
// Collect garbage before recording starting live node count, in case there are live elements from previous tests.
GCController.collectAll();
collectGarbage(function() {
var originalLiveElements = internals.numberOfLiveNodes();
while (g.hasChildNodes())
g.removeChild(g.lastChild);
GCController.collectAll();
collectGarbage(function() {
// This is 400 instead of 200 as it creates shadow tree elements.
var liveDelta = originalLiveElements - internals.numberOfLiveNodes() - 400;
if (liveDelta == 0)
......@@ -50,6 +50,8 @@ function cleanup() {
log("FAIL: " + liveDelta + " extra live node(s)");
testRunner.notifyDone();
});
});
}
function startTest() {
......
<svg id="svg" xmlns="http://www.w3.org/2000/svg" onload="load()">
<svg id="svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" onload="load()">
<g id="g"/>
<text x="50" y="50" id="log"/>
<script xlink:href="../../resources/js-test.js"></script>
<script id="script">
<![CDATA[
......@@ -32,21 +33,20 @@ function createAnimatedRect() {
function cleanup() {
// Collect garbage before recording starting live node count, in case there are live elements from previous tests.
GCController.collectAll();
collectGarbage(function() {
var originalLiveElements = internals.numberOfLiveNodes();
while (g.hasChildNodes())
g.removeChild(g.lastChild);
GCController.collectAll();
collectGarbage(function() {
var liveDelta = originalLiveElements - internals.numberOfLiveNodes() - 200;
if (liveDelta == 0)
log("PASS");
else
log("FAIL: " + liveDelta + " extra live node(s)");
testRunner.notifyDone();
});
});
}
function startTest() {
......
......@@ -1305,6 +1305,8 @@ void Heap::prepareForGC()
void Heap::collectGarbage(ThreadState::StackState stackState, GCType gcType)
{
if (gcType == ForcedForTesting && stackState != ThreadState::NoHeapPointersOnStack)
ThreadState::current()->setForcedForTesting(true);
ThreadState::current()->clearGCRequested();
GCScope gcScope(stackState);
......
......@@ -235,6 +235,7 @@ ThreadState::ThreadState()
, m_atSafePoint(false)
, m_interruptors()
, m_gcRequested(false)
, m_forcePreciseGCForTesting(false)
, m_sweepRequested(0)
, m_sweepInProgress(false)
, m_noAllocationCount(0)
......@@ -497,6 +498,26 @@ void ThreadState::clearGCRequested()
m_gcRequested = false;
}
void ThreadState::performPendingGC(StackState stackState)
{
if (stackState == NoHeapPointersOnStack && (gcRequested() || forcePreciseGCForTesting())) {
setForcedForTesting(false);
Heap::collectGarbage(NoHeapPointersOnStack);
}
}
void ThreadState::setForcedForTesting(bool value)
{
checkThread();
m_forcePreciseGCForTesting = value;
}
bool ThreadState::forcePreciseGCForTesting()
{
checkThread();
return m_forcePreciseGCForTesting;
}
bool ThreadState::isConsistentForGC()
{
for (int i = 0; i < NumberOfHeaps; i++) {
......@@ -589,8 +610,7 @@ void ThreadState::resumeThreads()
void ThreadState::safePoint(StackState stackState)
{
checkThread();
if (stackState == NoHeapPointersOnStack && gcRequested())
Heap::collectGarbage(NoHeapPointersOnStack);
performPendingGC(stackState);
m_stackState = stackState;
s_safePointBarrier->checkAndPark(this);
m_stackState = HeapPointersOnStack;
......@@ -628,8 +648,7 @@ void ThreadState::enterSafePoint(StackState stackState, void* scopeMarker)
scopeMarker = adjustScopeMarkerForAdressSanitizer(scopeMarker);
#endif
ASSERT(stackState == NoHeapPointersOnStack || scopeMarker);
if (stackState == NoHeapPointersOnStack && gcRequested())
Heap::collectGarbage(NoHeapPointersOnStack);
performPendingGC(stackState);
checkThread();
ASSERT(!m_atSafePoint);
m_atSafePoint = true;
......
......@@ -278,6 +278,15 @@ public:
void setGCRequested();
void clearGCRequested();
// Was the last GC forced for testing? This is set when garbage collection
// is forced for testing and there are pointers on the stack. It remains
// set until a garbage collection is triggered with no pointers on the stack.
// This is used for layout tests that trigger GCs and check if objects are
// dead at a given point in time. That only reliably works when we get
// precise GCs with no conservative stack scanning.
void setForcedForTesting(bool);
bool forcePreciseGCForTesting();
bool sweepRequested();
void setSweepRequested();
void clearSweepRequested();
......@@ -497,6 +506,8 @@ private:
m_safePointScopeMarker = 0;
}
void performPendingGC(StackState);
// Finds the Blink HeapPage in this thread-specific heap
// corresponding to a given address. Return 0 if the address is
// not contained in any of the pages. This does not consider
......@@ -540,6 +551,7 @@ private:
bool m_atSafePoint;
Vector<Interruptor*> m_interruptors;
bool m_gcRequested;
bool m_forcePreciseGCForTesting;
volatile int m_sweepRequested;
bool m_sweepInProgress;
size_t m_noAllocationCount;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment