diff options
author | Gaming4JC <g4jc@hyperbola.info> | 2020-01-22 21:07:19 -0500 |
---|---|---|
committer | Gaming4JC <g4jc@hyperbola.info> | 2020-01-26 15:50:50 -0500 |
commit | 2077cdb41e3ef814cbbef482774d8bbec464fb1c (patch) | |
tree | 4646b9f4e1568da54c06adcf6ed3a4c27b9d7e60 /xpcom | |
parent | 43a8113072b96affb2b7a3a7a4e965547d3d0c41 (diff) | |
download | uxp-2077cdb41e3ef814cbbef482774d8bbec464fb1c.tar.gz |
Bug 1406922 - Make CycleCollectedJSContext to handle microtasks and make MutationObserver to use them
Tag UXP Issue #1344
Diffstat (limited to 'xpcom')
-rw-r--r-- | xpcom/base/CycleCollectedJSContext.cpp | 67 | ||||
-rw-r--r-- | xpcom/base/CycleCollectedJSContext.h | 22 |
2 files changed, 83 insertions, 6 deletions
diff --git a/xpcom/base/CycleCollectedJSContext.cpp b/xpcom/base/CycleCollectedJSContext.cpp index 0a85ae6ac6..033ca562a5 100644 --- a/xpcom/base/CycleCollectedJSContext.cpp +++ b/xpcom/base/CycleCollectedJSContext.cpp @@ -440,6 +440,7 @@ CycleCollectedJSContext::CycleCollectedJSContext() , mDoingStableStates(false) , mDisableMicroTaskCheckpoint(false) , mMicroTaskLevel(0) + , mMicroTaskRecursionDepth(0) , mOutOfMemoryState(OOMState::OK) , mLargeAllocationFailureState(OOMState::OK) { @@ -1380,8 +1381,8 @@ CycleCollectedJSContext::AfterProcessTask(uint32_t aRecursionDepth) // Step 4.1: Execute microtasks. if (!mDisableMicroTaskCheckpoint) { + PerformMicroTaskCheckPoint(); if (NS_IsMainThread()) { - PerformMainThreadMicroTaskCheckpoint(); Promise::PerformMicroTaskCheckpoint(); } else { Promise::PerformWorkerMicroTaskCheckpoint(); @@ -1661,12 +1662,70 @@ CycleCollectedJSContext::DispatchToMicroTask(already_AddRefed<nsIRunnable> aRunn mPromiseMicroTaskQueue.push(runnable.forget()); } +class AsyncMutationHandler final : public mozilla::Runnable +{ +public: + NS_IMETHOD Run() override + { + CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get(); + if (ccjs) { + ccjs->PerformMicroTaskCheckPoint(); + } + return NS_OK; + } +}; + void -CycleCollectedJSContext::PerformMainThreadMicroTaskCheckpoint() +CycleCollectedJSContext::PerformMicroTaskCheckPoint() { - MOZ_ASSERT(NS_IsMainThread()); + if (mPendingMicroTaskRunnables.empty()) { + // Nothing to do, return early. + return; + } + + uint32_t currentDepth = RecursionDepth(); + if (mMicroTaskRecursionDepth >= currentDepth) { + // We are already executing microtasks for the current recursion depth. + return; + } - nsDOMMutationObserver::HandleMutations(); + if (NS_IsMainThread() && !nsContentUtils::IsSafeToRunScript()) { + // Special case for main thread where DOM mutations may happen when + // it is not safe to run scripts. + nsContentUtils::AddScriptRunner(new AsyncMutationHandler()); + return; + } + + mozilla::AutoRestore<uint32_t> restore(mMicroTaskRecursionDepth); + MOZ_ASSERT(currentDepth > 0); + mMicroTaskRecursionDepth = currentDepth; + + AutoSlowOperation aso; + + std::queue<RefPtr<MicroTaskRunnable>> suppressed; + while (!mPendingMicroTaskRunnables.empty()) { + RefPtr<MicroTaskRunnable> runnable = + mPendingMicroTaskRunnables.front().forget(); + mPendingMicroTaskRunnables.pop(); + if (runnable->Suppressed()) { + suppressed.push(runnable); + } else { + runnable->Run(aso); + } + } + + // Put back the suppressed microtasks so that they will be run later. + // 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.) + mPendingMicroTaskRunnables.swap(suppressed); +} + +void +CycleCollectedJSContext::DispatchMicroTaskRunnable( + already_AddRefed<MicroTaskRunnable> aRunnable) +{ + mPendingMicroTaskRunnables.push(aRunnable); } void diff --git a/xpcom/base/CycleCollectedJSContext.h b/xpcom/base/CycleCollectedJSContext.h index 2197eae920..4cd1479ed7 100644 --- a/xpcom/base/CycleCollectedJSContext.h +++ b/xpcom/base/CycleCollectedJSContext.h @@ -33,6 +33,7 @@ struct Class; } // namespace js namespace mozilla { +class AutoSlowOperation; class JSGCThingParticipant: public nsCycleCollectionParticipant { @@ -134,6 +135,17 @@ struct CycleCollectorResults uint32_t mNumSlices; }; +class MicroTaskRunnable +{ +public: + MicroTaskRunnable() {} + NS_INLINE_DECL_REFCOUNTING(MicroTaskRunnable) + virtual void Run(AutoSlowOperation& aAso) = 0; + virtual bool Suppressed() { return false; } +protected: + virtual ~MicroTaskRunnable() {} +}; + class CycleCollectedJSContext { friend class JSGCThingParticipant; @@ -412,7 +424,7 @@ public: void LeaveMicroTask() { if (--mMicroTaskLevel == 0) { - PerformMainThreadMicroTaskCheckpoint(); + PerformMicroTaskCheckPoint(); } } @@ -431,7 +443,9 @@ public: mMicroTaskLevel = aLevel; } - void PerformMainThreadMicroTaskCheckpoint(); + void PerformMicroTaskCheckPoint(); + + void DispatchMicroTaskRunnable(already_AddRefed<MicroTaskRunnable> aRunnable); // Storage for watching rejected promises waiting for some client to // consume their rejection. @@ -484,6 +498,10 @@ private: bool mDisableMicroTaskCheckpoint; uint32_t mMicroTaskLevel; + std::queue<RefPtr<MicroTaskRunnable>> mPendingMicroTaskRunnables; + + uint32_t mMicroTaskRecursionDepth; + OOMState mOutOfMemoryState; OOMState mLargeAllocationFailureState; |