Verified Commit 6102df3b authored by Georg Koppen's avatar Georg Koppen Committed by Pier Angelo Vendrame
Browse files

Revert "Bug 1724777, optimize suppressed MicroTask handling, r=mccr8 a=RyanVM"

This reverts commit 1eb13643.

This fixes tor-browser#40721, tor-browser#40698, and #40706.
parent 74e68ed3
......@@ -15690,18 +15690,6 @@ nsAutoSyncOperation::~nsAutoSyncOperation() {
}
}
 
void Document::SetIsInSyncOperation(bool aSync) {
if (CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get()) {
ccjs->UpdateMicroTaskSuppressionGeneration();
}
if (aSync) {
++mInSyncOperationCount;
} else {
--mInSyncOperationCount;
}
}
gfxUserFontSet* Document::GetUserFontSet() {
if (!mFontFaceSet) {
return nullptr;
......
......@@ -3214,7 +3214,13 @@ class Document : public nsINode,
bool IsInSyncOperation() { return mInSyncOperationCount != 0; }
void SetIsInSyncOperation(bool aSync);
void SetIsInSyncOperation(bool aSync) {
if (aSync) {
++mInSyncOperationCount;
} else {
--mInSyncOperationCount;
}
}
bool CreatingStaticClone() const { return mCreatingStaticClone; }
......
......@@ -769,8 +769,6 @@ skip-if = debug == false
[test_shared_compartment2.html]
[test_structuredclone_backref.html]
[test_style_cssText.html]
[test_suppressed_microtasks.html]
skip-if = debug || asan || verify || toolkit == 'android' # The test needs to run reasonably fast.
[test_text_wholeText.html]
[test_textnode_normalize_in_selection.html]
[test_textnode_split_in_selection.html]
......
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Test microtask suppression</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
<script>
SimpleTest.waitForExplicitFinish();
var previousTask = -1;
function test() {
let win = window.open("about:blank");
win.onload = function() {
win.onmessage = function() {
win.start = win.performance.now();
win.didRunMicrotask = false;
win.onmessage = function() {
ok(win.didRunMicrotask, "Should have run a microtask.");
let period = win.performance.now() - win.start;
win.opener.ok(
period < 200,
"Running a task should be fast. Took " + period + "ms.");
win.onmessage = null;
}
win.queueMicrotask(function() { win.didRunMicrotask = true; });
win.postMessage("measurementMessage", "*");
}
win.postMessage("initialMessage", "*");
const last = 500000;
for (let i = 0; i < last + 1; ++i) {
window.queueMicrotask(function() {
// Check that once microtasks are unsuppressed, they are handled in
// the correct order.
if (previousTask != i - 1) {
// Explicitly optimize out cases which pass.
ok(false, "Microtasks should be handled in order.");
}
previousTask = i;
if (i == last) {
win.close();
SimpleTest.finish();
}
});
}
// Synchronous XMLHttpRequest suppresses microtasks.
var xhr = new XMLHttpRequest();
xhr.open("GET", "slow.sjs", false);
xhr.send();
is(previousTask, -1, "Shouldn't have run microtasks during a sync XHR.");
}
}
</script>
</head>
<body onload="test()">
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test"></pre>
</body>
</html>
......@@ -931,7 +931,7 @@ class WorkerJSContext final : public mozilla::CycleCollectedJSContext {
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(runnable);
std::deque<RefPtr<MicroTaskRunnable>>* microTaskQueue = nullptr;
std::queue<RefPtr<MicroTaskRunnable>>* microTaskQueue = nullptr;
JSContext* cx = Context();
NS_ASSERTION(cx, "This should never be null!");
......@@ -953,7 +953,7 @@ class WorkerJSContext final : public mozilla::CycleCollectedJSContext {
}
JS::JobQueueMayNotBeEmpty(cx);
microTaskQueue->push_back(std::move(runnable));
microTaskQueue->push(std::move(runnable));
}
bool IsSystemCaller() const override {
......
......@@ -4313,7 +4313,7 @@ void WorkerPrivate::EnterDebuggerEventLoop() {
{
MutexAutoLock lock(mMutex);
std::deque<RefPtr<MicroTaskRunnable>>& debuggerMtQueue =
std::queue<RefPtr<MicroTaskRunnable>>& debuggerMtQueue =
ccjscx->GetDebuggerMicroTaskQueue();
while (mControlQueue.IsEmpty() &&
!(debuggerRunnablesPending = !mDebuggerQueue.IsEmpty()) &&
......
......@@ -159,7 +159,7 @@ class WorkletJSContext final : public CycleCollectedJSContext {
#endif
JS::JobQueueMayNotBeEmpty(cx);
GetMicroTaskQueue().push_back(std::move(runnable));
GetMicroTaskQueue().push(std::move(runnable));
}
bool IsSystemCaller() const override {
......
......@@ -61,7 +61,6 @@ CycleCollectedJSContext::CycleCollectedJSContext()
mDoingStableStates(false),
mTargetedMicroTaskRecursionDepth(0),
mMicroTaskLevel(0),
mSuppressionGeneration(0),
mDebuggerRecursionDepth(0),
mMicroTaskRecursionDepth(0),
mFinalizationRegistryCleanup(this) {
......@@ -292,7 +291,7 @@ class CycleCollectedJSContext::SavedMicroTaskQueue
private:
CycleCollectedJSContext* ccjs;
std::deque<RefPtr<MicroTaskRunnable>> mQueue;
std::queue<RefPtr<MicroTaskRunnable>> mQueue;
};
js::UniquePtr<JS::JobQueue::SavedJobQueue>
......@@ -380,13 +379,13 @@ void CycleCollectedJSContext::SetPendingException(Exception* aException) {
mPendingException = aException;
}
std::deque<RefPtr<MicroTaskRunnable>>&
std::queue<RefPtr<MicroTaskRunnable>>&
CycleCollectedJSContext::GetMicroTaskQueue() {
MOZ_ASSERT(mJSContext);
return mPendingMicroTaskRunnables;
}
std::deque<RefPtr<MicroTaskRunnable>>&
std::queue<RefPtr<MicroTaskRunnable>>&
CycleCollectedJSContext::GetDebuggerMicroTaskQueue() {
MOZ_ASSERT(mJSContext);
return mDebuggerMicroTaskQueue;
......@@ -563,7 +562,7 @@ void CycleCollectedJSContext::DispatchToMicroTask(
JS::JobQueueMayNotBeEmpty(Context());
LogMicroTaskRunnable::LogDispatch(runnable.get());
mPendingMicroTaskRunnables.push_back(std::move(runnable));
mPendingMicroTaskRunnables.push(std::move(runnable));
}
class AsyncMutationHandler final : public mozilla::Runnable {
......@@ -582,25 +581,6 @@ class AsyncMutationHandler final : public mozilla::Runnable {
}
};
SuppressedMicroTasks::SuppressedMicroTasks(CycleCollectedJSContext* aContext)
: mContext(aContext),
mSuppressionGeneration(aContext->mSuppressionGeneration) {}
bool SuppressedMicroTasks::Suppressed() {
if (mSuppressionGeneration == mContext->mSuppressionGeneration) {
return true;
}
for (std::deque<RefPtr<MicroTaskRunnable>>::reverse_iterator it =
mSuppressedMicroTaskRunnables.rbegin();
it != mSuppressedMicroTaskRunnables.rend(); ++it) {
mContext->GetMicroTaskQueue().push_front(*it);
}
mContext->mSuppressedMicroTasks = nullptr;
return false;
}
bool CycleCollectedJSContext::PerformMicroTaskCheckPoint(bool aForce) {
if (mPendingMicroTaskRunnables.empty() && mDebuggerMicroTaskQueue.empty()) {
AfterProcessMicrotasks();
......@@ -636,14 +616,15 @@ bool CycleCollectedJSContext::PerformMicroTaskCheckPoint(bool aForce) {
bool didProcess = false;
AutoSlowOperation aso;
std::queue<RefPtr<MicroTaskRunnable>> suppressed;
for (;;) {
RefPtr<MicroTaskRunnable> runnable;
if (!mDebuggerMicroTaskQueue.empty()) {
runnable = std::move(mDebuggerMicroTaskQueue.front());
mDebuggerMicroTaskQueue.pop_front();
mDebuggerMicroTaskQueue.pop();
} else if (!mPendingMicroTaskRunnables.empty()) {
runnable = std::move(mPendingMicroTaskRunnables.front());
mPendingMicroTaskRunnables.pop_front();
mPendingMicroTaskRunnables.pop();
} else {
break;
}
......@@ -654,16 +635,10 @@ bool CycleCollectedJSContext::PerformMicroTaskCheckPoint(bool aForce) {
// all suppressed tasks in mDebuggerMicroTaskQueue unexpectedly.
MOZ_ASSERT(NS_IsMainThread());
JS::JobQueueMayNotBeEmpty(Context());
if (runnable != mSuppressedMicroTasks) {
if (!mSuppressedMicroTasks) {
mSuppressedMicroTasks = new SuppressedMicroTasks(this);
}
mSuppressedMicroTasks->mSuppressedMicroTaskRunnables.push_back(
runnable);
}
suppressed.push(runnable);
} else {
if (mPendingMicroTaskRunnables.empty() &&
mDebuggerMicroTaskQueue.empty() && !mSuppressedMicroTasks) {
mDebuggerMicroTaskQueue.empty() && suppressed.empty()) {
JS::JobQueueIsEmpty(Context());
}
didProcess = true;
......@@ -678,9 +653,7 @@ bool CycleCollectedJSContext::PerformMicroTaskCheckPoint(bool aForce) {
// Note, it is possible that we end up keeping these suppressed tasks around
// for some time, but no longer than spinning the event loop nestedly
// (sync XHR, alert, etc.)
if (mSuppressedMicroTasks) {
mPendingMicroTaskRunnables.push_back(mSuppressedMicroTasks);
}
mPendingMicroTaskRunnables.swap(suppressed);
AfterProcessMicrotasks();
......@@ -695,7 +668,7 @@ void CycleCollectedJSContext::PerformDebuggerMicroTaskCheckpoint() {
for (;;) {
// For a debugger microtask checkpoint, we always use the debugger microtask
// queue.
std::deque<RefPtr<MicroTaskRunnable>>* microtaskQueue =
std::queue<RefPtr<MicroTaskRunnable>>* microtaskQueue =
&GetDebuggerMicroTaskQueue();
if (microtaskQueue->empty()) {
......@@ -708,7 +681,7 @@ void CycleCollectedJSContext::PerformDebuggerMicroTaskCheckpoint() {
LogMicroTaskRunnable::Run log(runnable.get());
// This function can re-enter, so we remove the element before calling.
microtaskQueue->pop_front();
microtaskQueue->pop();
if (mPendingMicroTaskRunnables.empty() && mDebuggerMicroTaskQueue.empty()) {
JS::JobQueueIsEmpty(Context());
......
......@@ -7,7 +7,7 @@
#ifndef mozilla_CycleCollectedJSContext_h
#define mozilla_CycleCollectedJSContext_h
#include <deque>
#include <queue>
#include "mozilla/Attributes.h"
#include "mozilla/MemoryReporting.h"
......@@ -81,20 +81,6 @@ class MicroTaskRunnable {
virtual ~MicroTaskRunnable() = default;
};
// Store the suppressed mictotasks in another microtask so that operations
// for the microtask queue as a whole keep working.
class SuppressedMicroTasks : public MicroTaskRunnable {
public:
explicit SuppressedMicroTasks(CycleCollectedJSContext* aContext);
MOZ_CAN_RUN_SCRIPT_BOUNDARY void Run(AutoSlowOperation& aAso) final {}
virtual bool Suppressed();
CycleCollectedJSContext* mContext;
uint64_t mSuppressionGeneration;
std::deque<RefPtr<MicroTaskRunnable>> mSuppressedMicroTaskRunnables;
};
// Support for JS FinalizationRegistry objects, which allow a JS callback to be
// registered that is called when objects die.
//
......@@ -131,7 +117,6 @@ class FinalizationRegistryCleanup {
class CycleCollectedJSContext : dom::PerThreadAtomCache, private JS::JobQueue {
friend class CycleCollectedJSRuntime;
friend class SuppressedMicroTasks;
protected:
CycleCollectedJSContext();
......@@ -181,8 +166,8 @@ class CycleCollectedJSContext : dom::PerThreadAtomCache, private JS::JobQueue {
already_AddRefed<dom::Exception> GetPendingException() const;
void SetPendingException(dom::Exception* aException);
std::deque<RefPtr<MicroTaskRunnable>>& GetMicroTaskQueue();
std::deque<RefPtr<MicroTaskRunnable>>& GetDebuggerMicroTaskQueue();
std::queue<RefPtr<MicroTaskRunnable>>& GetMicroTaskQueue();
std::queue<RefPtr<MicroTaskRunnable>>& GetDebuggerMicroTaskQueue();
JSContext* Context() const {
MOZ_ASSERT(mJSContext);
......@@ -198,8 +183,6 @@ class CycleCollectedJSContext : dom::PerThreadAtomCache, private JS::JobQueue {
mTargetedMicroTaskRecursionDepth = aDepth;
}
void UpdateMicroTaskSuppressionGeneration() { ++mSuppressionGeneration; }
protected:
JSContext* MaybeContext() const { return mJSContext; }
......@@ -333,10 +316,8 @@ class CycleCollectedJSContext : dom::PerThreadAtomCache, private JS::JobQueue {
uint32_t mMicroTaskLevel;
std::deque<RefPtr<MicroTaskRunnable>> mPendingMicroTaskRunnables;
std::deque<RefPtr<MicroTaskRunnable>> mDebuggerMicroTaskQueue;
RefPtr<SuppressedMicroTasks> mSuppressedMicroTasks;
uint64_t mSuppressionGeneration;
std::queue<RefPtr<MicroTaskRunnable>> mPendingMicroTaskRunnables;
std::queue<RefPtr<MicroTaskRunnable>> mDebuggerMicroTaskQueue;
// How many times the debugger has interrupted execution, possibly creating
// microtask checkpoints in places that they would not normally occur.
......
Supports Markdown
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