2013-06-18 23:02:07 +04:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
2017-02-24 00:23:45 +03:00
|
|
|
#ifndef mozilla_CycleCollectedJSContext_h
|
|
|
|
#define mozilla_CycleCollectedJSContext_h
|
2013-06-18 23:02:07 +04:00
|
|
|
|
2015-05-02 05:33:01 +03:00
|
|
|
#include <queue>
|
|
|
|
|
2019-03-19 18:14:11 +03:00
|
|
|
#include "mozilla/Attributes.h"
|
2013-06-23 16:03:39 +04:00
|
|
|
#include "mozilla/MemoryReporting.h"
|
2018-05-10 08:04:12 +03:00
|
|
|
#include "mozilla/dom/AtomList.h"
|
2019-05-07 14:39:58 +03:00
|
|
|
#include "mozilla/dom/Promise.h"
|
2020-01-15 17:22:04 +03:00
|
|
|
#include "js/GCVector.h"
|
2018-12-18 02:21:04 +03:00
|
|
|
#include "js/Promise.h"
|
2013-06-18 23:02:07 +04:00
|
|
|
|
2017-02-24 00:23:45 +03:00
|
|
|
#include "nsCOMPtr.h"
|
2013-07-09 18:28:15 +04:00
|
|
|
#include "nsTArray.h"
|
2013-06-18 23:02:14 +04:00
|
|
|
|
2013-06-18 23:02:15 +04:00
|
|
|
class nsCycleCollectionNoteRootCallback;
|
2014-10-28 15:08:19 +03:00
|
|
|
class nsIRunnable;
|
Bug 1179909: Refactor stable state handling. r=smaug
This is motivated by three separate but related problems:
1. Our concept of recursion depth is broken for things that run from AfterProcessNextEvent observers (e.g. Promises). We decrement the recursionDepth counter before firing observers, so a Promise callback running at the lowest event loop depth has a recursion depth of 0 (whereas a regular nsIRunnable would be 1). This is a problem because it's impossible to distinguish a Promise running after a sync XHR's onreadystatechange handler from a top-level event (since the former runs with depth 2 - 1 = 1, and the latter runs with just 1).
2. The nsIThreadObserver mechanism that is used by a lot of code to run "after" the current event is a poor fit for anything that runs script. First, the order the observers fire in is the order they were added, not anything fixed by spec. Additionally, running script can cause the event loop to spin, which is a big source of pain here (bholley has some nasty bug caused by this).
3. We run Promises from different points in the code for workers and main thread. The latter runs from XPConnect's nsIThreadObserver callbacks, while the former runs from a hardcoded call to run Promises in the worker event loop. What workers do is particularly problematic because it means we can't get the right recursion depth no matter what we do to nsThread.
The solve this, this patch does the following:
1. Consolidate some handling of microtasks and all handling of stable state from appshell and WorkerPrivate into CycleCollectedJSRuntime.
2. Make the recursionDepth counter only available to CycleCollectedJSRuntime (and its consumers) and remove it from the nsIThreadInternal and nsIThreadObserver APIs.
3. Adjust the recursionDepth counter so that microtasks run with the recursionDepth of the task they are associated with.
4. Introduce the concept of metastable state to replace appshell's RunBeforeNextEvent. Metastable state is reached after every microtask or task is completed. This provides the semantics that bent and I want for IndexedDB, where transactions autocommit at the end of a microtask and do not "spill" from one microtask into a subsequent microtask. This differs from appshell's RunBeforeNextEvent in two ways:
a) It fires between microtasks, which was the motivation for starting this.
b) It no longer ensures that we're at the same event loop depth in the native event queue. bent decided we don't care about this.
5. Reorder stable state to happen after microtasks such as Promises, per HTML. Right now we call the regular thread observers, including appshell, before the main thread observer (XPConnect), so stable state tasks happen before microtasks.
2015-08-11 16:10:46 +03:00
|
|
|
class nsThread;
|
2013-06-18 23:02:14 +04:00
|
|
|
|
2013-06-18 23:02:07 +04:00
|
|
|
namespace mozilla {
|
2017-10-11 15:31:38 +03:00
|
|
|
class AutoSlowOperation;
|
2013-06-18 23:02:07 +04:00
|
|
|
|
2020-07-17 20:34:46 +03:00
|
|
|
class CycleCollectedJSContext;
|
2017-02-24 00:23:45 +03:00
|
|
|
class CycleCollectedJSRuntime;
|
2013-07-09 18:28:15 +04:00
|
|
|
|
2018-02-06 00:34:05 +03:00
|
|
|
namespace dom {
|
|
|
|
class Exception;
|
2018-05-10 08:04:12 +03:00
|
|
|
class WorkerJSContext;
|
2018-04-06 00:45:56 +03:00
|
|
|
class WorkletJSContext;
|
2018-02-06 00:34:05 +03:00
|
|
|
} // namespace dom
|
|
|
|
|
2013-11-21 02:35:16 +04:00
|
|
|
// Contains various stats about the cycle collection.
|
|
|
|
struct CycleCollectorResults {
|
2014-05-07 04:25:26 +04:00
|
|
|
CycleCollectorResults() {
|
|
|
|
// Initialize here so when we increment mNumSlices the first time we're
|
|
|
|
// not using uninitialized memory.
|
|
|
|
Init();
|
|
|
|
}
|
|
|
|
|
2013-11-21 02:35:16 +04:00
|
|
|
void Init() {
|
|
|
|
mForcedGC = false;
|
|
|
|
mMergedZones = false;
|
2015-05-13 22:48:52 +03:00
|
|
|
mAnyManual = false;
|
2013-11-21 02:35:16 +04:00
|
|
|
mVisitedRefCounted = 0;
|
|
|
|
mVisitedGCed = 0;
|
|
|
|
mFreedRefCounted = 0;
|
|
|
|
mFreedGCed = 0;
|
2014-10-20 21:07:52 +04:00
|
|
|
mFreedJSZones = 0;
|
2014-05-07 04:25:26 +04:00
|
|
|
mNumSlices = 1;
|
|
|
|
// mNumSlices is initialized to one, because we call Init() after the
|
|
|
|
// per-slice increment of mNumSlices has already occurred.
|
2013-11-21 02:35:16 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool mForcedGC;
|
|
|
|
bool mMergedZones;
|
2015-05-13 22:48:52 +03:00
|
|
|
// mAnyManual is true if any slice was manually triggered, and at shutdown.
|
|
|
|
bool mAnyManual;
|
2013-11-21 02:35:16 +04:00
|
|
|
uint32_t mVisitedRefCounted;
|
|
|
|
uint32_t mVisitedGCed;
|
|
|
|
uint32_t mFreedRefCounted;
|
|
|
|
uint32_t mFreedGCed;
|
2014-10-20 21:07:52 +04:00
|
|
|
uint32_t mFreedJSZones;
|
2014-05-07 04:25:26 +04:00
|
|
|
uint32_t mNumSlices;
|
2013-11-21 02:35:16 +04:00
|
|
|
};
|
|
|
|
|
2017-10-11 15:31:38 +03:00
|
|
|
class MicroTaskRunnable {
|
|
|
|
public:
|
2018-12-13 23:05:00 +03:00
|
|
|
MicroTaskRunnable() = default;
|
2017-10-11 15:31:38 +03:00
|
|
|
NS_INLINE_DECL_REFCOUNTING(MicroTaskRunnable)
|
2019-03-19 18:14:11 +03:00
|
|
|
MOZ_CAN_RUN_SCRIPT virtual void Run(AutoSlowOperation& aAso) = 0;
|
2017-10-11 15:31:38 +03:00
|
|
|
virtual bool Suppressed() { return false; }
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2017-10-11 15:31:38 +03:00
|
|
|
protected:
|
2018-12-13 23:05:00 +03:00
|
|
|
virtual ~MicroTaskRunnable() = default;
|
2017-10-11 15:31:38 +03:00
|
|
|
};
|
|
|
|
|
2020-07-17 20:34:46 +03:00
|
|
|
// Support for JS FinalizationRegistry objects, which allow a JS callback to be
|
|
|
|
// registered that is called when objects die.
|
|
|
|
//
|
|
|
|
// We keep a vector of functions that call back into the JS engine along
|
|
|
|
// with their associated incumbent globals, one per FinalizationRegistry object
|
|
|
|
// that has pending cleanup work. These are run in their own task.
|
|
|
|
class FinalizationRegistryCleanup {
|
|
|
|
public:
|
|
|
|
explicit FinalizationRegistryCleanup(CycleCollectedJSContext* aContext);
|
|
|
|
void Init();
|
|
|
|
void Destroy();
|
|
|
|
void QueueCallback(JSFunction* aDoCleanup, JSObject* aIncumbentGlobal);
|
|
|
|
MOZ_CAN_RUN_SCRIPT void DoCleanup();
|
|
|
|
|
|
|
|
private:
|
|
|
|
static void QueueCallback(JSFunction* aDoCleanup, JSObject* aIncumbentGlobal,
|
|
|
|
void* aData);
|
|
|
|
|
|
|
|
class CleanupRunnable;
|
|
|
|
|
|
|
|
struct Callback {
|
|
|
|
JSFunction* mCallbackFunction;
|
|
|
|
JSObject* mIncumbentGlobal;
|
|
|
|
void trace(JSTracer* trc);
|
|
|
|
};
|
|
|
|
|
|
|
|
// This object is part of CycleCollectedJSContext, so it's safe to have a raw
|
|
|
|
// pointer to its containing context here.
|
|
|
|
CycleCollectedJSContext* mContext;
|
|
|
|
|
|
|
|
using CallbackVector = JS::GCVector<Callback, 0, InfallibleAllocPolicy>;
|
|
|
|
JS::PersistentRooted<CallbackVector> mCallbacks;
|
|
|
|
};
|
|
|
|
|
2016-09-14 16:47:32 +03:00
|
|
|
class CycleCollectedJSContext : dom::PerThreadAtomCache, private JS::JobQueue {
|
2017-02-24 00:23:45 +03:00
|
|
|
friend class CycleCollectedJSRuntime;
|
|
|
|
|
2013-06-18 23:02:07 +04:00
|
|
|
protected:
|
2016-09-14 16:47:32 +03:00
|
|
|
CycleCollectedJSContext();
|
|
|
|
virtual ~CycleCollectedJSContext();
|
2013-06-18 23:02:07 +04:00
|
|
|
|
2016-09-16 13:31:37 +03:00
|
|
|
MOZ_IS_CLASS_INIT
|
2019-12-16 07:14:55 +03:00
|
|
|
nsresult Initialize(JSRuntime* aParentRuntime, uint32_t aMaxBytes);
|
2016-02-14 16:30:25 +03:00
|
|
|
|
2017-02-24 00:23:45 +03:00
|
|
|
virtual CycleCollectedJSRuntime* CreateRuntime(JSContext* aCx) = 0;
|
2013-06-18 23:02:15 +04:00
|
|
|
|
2017-02-24 00:23:45 +03:00
|
|
|
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
|
2013-07-09 18:28:15 +04:00
|
|
|
|
2013-06-18 23:02:15 +04:00
|
|
|
private:
|
2016-07-02 03:00:47 +03:00
|
|
|
static JSObject* GetIncumbentGlobalCallback(JSContext* aCx);
|
2016-02-10 01:40:31 +03:00
|
|
|
static bool EnqueuePromiseJobCallback(JSContext* aCx,
|
2018-10-10 00:42:18 +03:00
|
|
|
JS::HandleObject aPromise,
|
2016-02-10 01:40:31 +03:00
|
|
|
JS::HandleObject aJob,
|
2016-03-22 18:22:23 +03:00
|
|
|
JS::HandleObject aAllocationSite,
|
2016-07-02 03:00:47 +03:00
|
|
|
JS::HandleObject aIncumbentGlobal,
|
2016-02-10 01:40:31 +03:00
|
|
|
void* aData);
|
2016-03-22 18:22:24 +03:00
|
|
|
static void PromiseRejectionTrackerCallback(
|
2019-05-19 04:18:04 +03:00
|
|
|
JSContext* aCx, bool aMutedErrors, JS::HandleObject aPromise,
|
2018-01-08 06:08:33 +03:00
|
|
|
JS::PromiseRejectionHandlingState state, void* aData);
|
2013-06-18 23:02:15 +04:00
|
|
|
|
2017-11-17 06:01:27 +03:00
|
|
|
void AfterProcessMicrotasks();
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2016-01-13 01:37:57 +03:00
|
|
|
public:
|
Bug 1179909: Refactor stable state handling. r=smaug
This is motivated by three separate but related problems:
1. Our concept of recursion depth is broken for things that run from AfterProcessNextEvent observers (e.g. Promises). We decrement the recursionDepth counter before firing observers, so a Promise callback running at the lowest event loop depth has a recursion depth of 0 (whereas a regular nsIRunnable would be 1). This is a problem because it's impossible to distinguish a Promise running after a sync XHR's onreadystatechange handler from a top-level event (since the former runs with depth 2 - 1 = 1, and the latter runs with just 1).
2. The nsIThreadObserver mechanism that is used by a lot of code to run "after" the current event is a poor fit for anything that runs script. First, the order the observers fire in is the order they were added, not anything fixed by spec. Additionally, running script can cause the event loop to spin, which is a big source of pain here (bholley has some nasty bug caused by this).
3. We run Promises from different points in the code for workers and main thread. The latter runs from XPConnect's nsIThreadObserver callbacks, while the former runs from a hardcoded call to run Promises in the worker event loop. What workers do is particularly problematic because it means we can't get the right recursion depth no matter what we do to nsThread.
The solve this, this patch does the following:
1. Consolidate some handling of microtasks and all handling of stable state from appshell and WorkerPrivate into CycleCollectedJSRuntime.
2. Make the recursionDepth counter only available to CycleCollectedJSRuntime (and its consumers) and remove it from the nsIThreadInternal and nsIThreadObserver APIs.
3. Adjust the recursionDepth counter so that microtasks run with the recursionDepth of the task they are associated with.
4. Introduce the concept of metastable state to replace appshell's RunBeforeNextEvent. Metastable state is reached after every microtask or task is completed. This provides the semantics that bent and I want for IndexedDB, where transactions autocommit at the end of a microtask and do not "spill" from one microtask into a subsequent microtask. This differs from appshell's RunBeforeNextEvent in two ways:
a) It fires between microtasks, which was the motivation for starting this.
b) It no longer ensures that we're at the same event loop depth in the native event queue. bent decided we don't care about this.
5. Reorder stable state to happen after microtasks such as Promises, per HTML. Right now we call the regular thread observers, including appshell, before the main thread observer (XPConnect), so stable state tasks happen before microtasks.
2015-08-11 16:10:46 +03:00
|
|
|
void ProcessStableStateQueue();
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2016-01-13 01:37:57 +03:00
|
|
|
private:
|
2017-11-17 06:01:27 +03:00
|
|
|
void CleanupIDBTransactions(uint32_t aRecursionDepth);
|
Bug 1179909: Refactor stable state handling. r=smaug
This is motivated by three separate but related problems:
1. Our concept of recursion depth is broken for things that run from AfterProcessNextEvent observers (e.g. Promises). We decrement the recursionDepth counter before firing observers, so a Promise callback running at the lowest event loop depth has a recursion depth of 0 (whereas a regular nsIRunnable would be 1). This is a problem because it's impossible to distinguish a Promise running after a sync XHR's onreadystatechange handler from a top-level event (since the former runs with depth 2 - 1 = 1, and the latter runs with just 1).
2. The nsIThreadObserver mechanism that is used by a lot of code to run "after" the current event is a poor fit for anything that runs script. First, the order the observers fire in is the order they were added, not anything fixed by spec. Additionally, running script can cause the event loop to spin, which is a big source of pain here (bholley has some nasty bug caused by this).
3. We run Promises from different points in the code for workers and main thread. The latter runs from XPConnect's nsIThreadObserver callbacks, while the former runs from a hardcoded call to run Promises in the worker event loop. What workers do is particularly problematic because it means we can't get the right recursion depth no matter what we do to nsThread.
The solve this, this patch does the following:
1. Consolidate some handling of microtasks and all handling of stable state from appshell and WorkerPrivate into CycleCollectedJSRuntime.
2. Make the recursionDepth counter only available to CycleCollectedJSRuntime (and its consumers) and remove it from the nsIThreadInternal and nsIThreadObserver APIs.
3. Adjust the recursionDepth counter so that microtasks run with the recursionDepth of the task they are associated with.
4. Introduce the concept of metastable state to replace appshell's RunBeforeNextEvent. Metastable state is reached after every microtask or task is completed. This provides the semantics that bent and I want for IndexedDB, where transactions autocommit at the end of a microtask and do not "spill" from one microtask into a subsequent microtask. This differs from appshell's RunBeforeNextEvent in two ways:
a) It fires between microtasks, which was the motivation for starting this.
b) It no longer ensures that we're at the same event loop depth in the native event queue. bent decided we don't care about this.
5. Reorder stable state to happen after microtasks such as Promises, per HTML. Right now we call the regular thread observers, including appshell, before the main thread observer (XPConnect), so stable state tasks happen before microtasks.
2015-08-11 16:10:46 +03:00
|
|
|
|
2015-05-13 22:48:52 +03:00
|
|
|
public:
|
2013-07-09 18:28:15 +04:00
|
|
|
enum DeferredFinalizeType {
|
|
|
|
FinalizeIncrementally,
|
|
|
|
FinalizeNow,
|
|
|
|
};
|
|
|
|
|
2018-05-10 08:04:12 +03:00
|
|
|
virtual dom::WorkerJSContext* GetAsWorkerJSContext() { return nullptr; }
|
2018-04-06 00:45:56 +03:00
|
|
|
virtual dom::WorkletJSContext* GetAsWorkletJSContext() { return nullptr; }
|
|
|
|
|
2017-02-24 00:23:45 +03:00
|
|
|
CycleCollectedJSRuntime* Runtime() const {
|
|
|
|
MOZ_ASSERT(mRuntime);
|
|
|
|
return mRuntime;
|
|
|
|
}
|
2013-06-18 23:02:14 +04:00
|
|
|
|
2018-02-06 00:34:05 +03:00
|
|
|
already_AddRefed<dom::Exception> GetPendingException() const;
|
|
|
|
void SetPendingException(dom::Exception* aException);
|
2013-09-09 07:28:50 +04:00
|
|
|
|
2017-11-17 06:01:27 +03:00
|
|
|
std::queue<RefPtr<MicroTaskRunnable>>& GetMicroTaskQueue();
|
|
|
|
std::queue<RefPtr<MicroTaskRunnable>>& GetDebuggerMicroTaskQueue();
|
2014-10-28 15:08:19 +03:00
|
|
|
|
2016-08-11 15:39:22 +03:00
|
|
|
JSContext* Context() const {
|
|
|
|
MOZ_ASSERT(mJSContext);
|
|
|
|
return mJSContext;
|
2013-08-30 13:47:19 +04:00
|
|
|
}
|
2013-09-09 07:28:50 +04:00
|
|
|
|
2016-08-11 15:39:22 +03:00
|
|
|
JS::RootingContext* RootingCx() const {
|
2016-06-24 21:19:51 +03:00
|
|
|
MOZ_ASSERT(mJSContext);
|
2016-08-11 15:39:22 +03:00
|
|
|
return JS::RootingContext::get(mJSContext);
|
2016-06-24 21:19:51 +03:00
|
|
|
}
|
|
|
|
|
2017-11-17 06:01:27 +03:00
|
|
|
void SetTargetedMicroTaskRecursionDepth(uint32_t aDepth) {
|
|
|
|
mTargetedMicroTaskRecursionDepth = aDepth;
|
2016-09-05 18:54:04 +03:00
|
|
|
}
|
|
|
|
|
2016-04-19 07:04:32 +03:00
|
|
|
protected:
|
2016-08-11 15:39:22 +03:00
|
|
|
JSContext* MaybeContext() const { return mJSContext; }
|
2016-04-19 07:04:32 +03:00
|
|
|
|
|
|
|
public:
|
Bug 1179909: Refactor stable state handling. r=smaug
This is motivated by three separate but related problems:
1. Our concept of recursion depth is broken for things that run from AfterProcessNextEvent observers (e.g. Promises). We decrement the recursionDepth counter before firing observers, so a Promise callback running at the lowest event loop depth has a recursion depth of 0 (whereas a regular nsIRunnable would be 1). This is a problem because it's impossible to distinguish a Promise running after a sync XHR's onreadystatechange handler from a top-level event (since the former runs with depth 2 - 1 = 1, and the latter runs with just 1).
2. The nsIThreadObserver mechanism that is used by a lot of code to run "after" the current event is a poor fit for anything that runs script. First, the order the observers fire in is the order they were added, not anything fixed by spec. Additionally, running script can cause the event loop to spin, which is a big source of pain here (bholley has some nasty bug caused by this).
3. We run Promises from different points in the code for workers and main thread. The latter runs from XPConnect's nsIThreadObserver callbacks, while the former runs from a hardcoded call to run Promises in the worker event loop. What workers do is particularly problematic because it means we can't get the right recursion depth no matter what we do to nsThread.
The solve this, this patch does the following:
1. Consolidate some handling of microtasks and all handling of stable state from appshell and WorkerPrivate into CycleCollectedJSRuntime.
2. Make the recursionDepth counter only available to CycleCollectedJSRuntime (and its consumers) and remove it from the nsIThreadInternal and nsIThreadObserver APIs.
3. Adjust the recursionDepth counter so that microtasks run with the recursionDepth of the task they are associated with.
4. Introduce the concept of metastable state to replace appshell's RunBeforeNextEvent. Metastable state is reached after every microtask or task is completed. This provides the semantics that bent and I want for IndexedDB, where transactions autocommit at the end of a microtask and do not "spill" from one microtask into a subsequent microtask. This differs from appshell's RunBeforeNextEvent in two ways:
a) It fires between microtasks, which was the motivation for starting this.
b) It no longer ensures that we're at the same event loop depth in the native event queue. bent decided we don't care about this.
5. Reorder stable state to happen after microtasks such as Promises, per HTML. Right now we call the regular thread observers, including appshell, before the main thread observer (XPConnect), so stable state tasks happen before microtasks.
2015-08-11 16:10:46 +03:00
|
|
|
// nsThread entrypoints
|
2019-03-19 18:14:11 +03:00
|
|
|
//
|
|
|
|
// MOZ_CAN_RUN_SCRIPT_BOUNDARY so we don't need to annotate
|
|
|
|
// nsThread::ProcessNextEvent and all its callers MOZ_CAN_RUN_SCRIPT for now.
|
|
|
|
// But we really should!
|
|
|
|
MOZ_CAN_RUN_SCRIPT_BOUNDARY
|
2017-11-17 06:01:27 +03:00
|
|
|
virtual void BeforeProcessTask(bool aMightBlock);
|
2019-03-19 18:14:11 +03:00
|
|
|
// MOZ_CAN_RUN_SCRIPT_BOUNDARY so we don't need to annotate
|
|
|
|
// nsThread::ProcessNextEvent and all its callers MOZ_CAN_RUN_SCRIPT for now.
|
|
|
|
// But we really should!
|
|
|
|
MOZ_CAN_RUN_SCRIPT_BOUNDARY
|
Bug 1179909: Refactor stable state handling. r=smaug
This is motivated by three separate but related problems:
1. Our concept of recursion depth is broken for things that run from AfterProcessNextEvent observers (e.g. Promises). We decrement the recursionDepth counter before firing observers, so a Promise callback running at the lowest event loop depth has a recursion depth of 0 (whereas a regular nsIRunnable would be 1). This is a problem because it's impossible to distinguish a Promise running after a sync XHR's onreadystatechange handler from a top-level event (since the former runs with depth 2 - 1 = 1, and the latter runs with just 1).
2. The nsIThreadObserver mechanism that is used by a lot of code to run "after" the current event is a poor fit for anything that runs script. First, the order the observers fire in is the order they were added, not anything fixed by spec. Additionally, running script can cause the event loop to spin, which is a big source of pain here (bholley has some nasty bug caused by this).
3. We run Promises from different points in the code for workers and main thread. The latter runs from XPConnect's nsIThreadObserver callbacks, while the former runs from a hardcoded call to run Promises in the worker event loop. What workers do is particularly problematic because it means we can't get the right recursion depth no matter what we do to nsThread.
The solve this, this patch does the following:
1. Consolidate some handling of microtasks and all handling of stable state from appshell and WorkerPrivate into CycleCollectedJSRuntime.
2. Make the recursionDepth counter only available to CycleCollectedJSRuntime (and its consumers) and remove it from the nsIThreadInternal and nsIThreadObserver APIs.
3. Adjust the recursionDepth counter so that microtasks run with the recursionDepth of the task they are associated with.
4. Introduce the concept of metastable state to replace appshell's RunBeforeNextEvent. Metastable state is reached after every microtask or task is completed. This provides the semantics that bent and I want for IndexedDB, where transactions autocommit at the end of a microtask and do not "spill" from one microtask into a subsequent microtask. This differs from appshell's RunBeforeNextEvent in two ways:
a) It fires between microtasks, which was the motivation for starting this.
b) It no longer ensures that we're at the same event loop depth in the native event queue. bent decided we don't care about this.
5. Reorder stable state to happen after microtasks such as Promises, per HTML. Right now we call the regular thread observers, including appshell, before the main thread observer (XPConnect), so stable state tasks happen before microtasks.
2015-08-11 16:10:46 +03:00
|
|
|
virtual void AfterProcessTask(uint32_t aRecursionDepth);
|
|
|
|
|
2017-10-30 13:07:42 +03:00
|
|
|
// Check whether we need an idle GC task.
|
2018-12-13 23:05:00 +03:00
|
|
|
void IsIdleGCTaskNeeded() const;
|
2017-10-30 13:07:42 +03:00
|
|
|
|
2018-12-13 23:05:00 +03:00
|
|
|
uint32_t RecursionDepth() const;
|
Bug 1179909: Refactor stable state handling. r=smaug
This is motivated by three separate but related problems:
1. Our concept of recursion depth is broken for things that run from AfterProcessNextEvent observers (e.g. Promises). We decrement the recursionDepth counter before firing observers, so a Promise callback running at the lowest event loop depth has a recursion depth of 0 (whereas a regular nsIRunnable would be 1). This is a problem because it's impossible to distinguish a Promise running after a sync XHR's onreadystatechange handler from a top-level event (since the former runs with depth 2 - 1 = 1, and the latter runs with just 1).
2. The nsIThreadObserver mechanism that is used by a lot of code to run "after" the current event is a poor fit for anything that runs script. First, the order the observers fire in is the order they were added, not anything fixed by spec. Additionally, running script can cause the event loop to spin, which is a big source of pain here (bholley has some nasty bug caused by this).
3. We run Promises from different points in the code for workers and main thread. The latter runs from XPConnect's nsIThreadObserver callbacks, while the former runs from a hardcoded call to run Promises in the worker event loop. What workers do is particularly problematic because it means we can't get the right recursion depth no matter what we do to nsThread.
The solve this, this patch does the following:
1. Consolidate some handling of microtasks and all handling of stable state from appshell and WorkerPrivate into CycleCollectedJSRuntime.
2. Make the recursionDepth counter only available to CycleCollectedJSRuntime (and its consumers) and remove it from the nsIThreadInternal and nsIThreadObserver APIs.
3. Adjust the recursionDepth counter so that microtasks run with the recursionDepth of the task they are associated with.
4. Introduce the concept of metastable state to replace appshell's RunBeforeNextEvent. Metastable state is reached after every microtask or task is completed. This provides the semantics that bent and I want for IndexedDB, where transactions autocommit at the end of a microtask and do not "spill" from one microtask into a subsequent microtask. This differs from appshell's RunBeforeNextEvent in two ways:
a) It fires between microtasks, which was the motivation for starting this.
b) It no longer ensures that we're at the same event loop depth in the native event queue. bent decided we don't care about this.
5. Reorder stable state to happen after microtasks such as Promises, per HTML. Right now we call the regular thread observers, including appshell, before the main thread observer (XPConnect), so stable state tasks happen before microtasks.
2015-08-11 16:10:46 +03:00
|
|
|
|
|
|
|
// Run in stable state (call through nsContentUtils)
|
|
|
|
void RunInStableState(already_AddRefed<nsIRunnable>&& aRunnable);
|
2017-11-17 06:01:27 +03:00
|
|
|
|
|
|
|
void AddPendingIDBTransaction(already_AddRefed<nsIRunnable>&& aTransaction);
|
Bug 1179909: Refactor stable state handling. r=smaug
This is motivated by three separate but related problems:
1. Our concept of recursion depth is broken for things that run from AfterProcessNextEvent observers (e.g. Promises). We decrement the recursionDepth counter before firing observers, so a Promise callback running at the lowest event loop depth has a recursion depth of 0 (whereas a regular nsIRunnable would be 1). This is a problem because it's impossible to distinguish a Promise running after a sync XHR's onreadystatechange handler from a top-level event (since the former runs with depth 2 - 1 = 1, and the latter runs with just 1).
2. The nsIThreadObserver mechanism that is used by a lot of code to run "after" the current event is a poor fit for anything that runs script. First, the order the observers fire in is the order they were added, not anything fixed by spec. Additionally, running script can cause the event loop to spin, which is a big source of pain here (bholley has some nasty bug caused by this).
3. We run Promises from different points in the code for workers and main thread. The latter runs from XPConnect's nsIThreadObserver callbacks, while the former runs from a hardcoded call to run Promises in the worker event loop. What workers do is particularly problematic because it means we can't get the right recursion depth no matter what we do to nsThread.
The solve this, this patch does the following:
1. Consolidate some handling of microtasks and all handling of stable state from appshell and WorkerPrivate into CycleCollectedJSRuntime.
2. Make the recursionDepth counter only available to CycleCollectedJSRuntime (and its consumers) and remove it from the nsIThreadInternal and nsIThreadObserver APIs.
3. Adjust the recursionDepth counter so that microtasks run with the recursionDepth of the task they are associated with.
4. Introduce the concept of metastable state to replace appshell's RunBeforeNextEvent. Metastable state is reached after every microtask or task is completed. This provides the semantics that bent and I want for IndexedDB, where transactions autocommit at the end of a microtask and do not "spill" from one microtask into a subsequent microtask. This differs from appshell's RunBeforeNextEvent in two ways:
a) It fires between microtasks, which was the motivation for starting this.
b) It no longer ensures that we're at the same event loop depth in the native event queue. bent decided we don't care about this.
5. Reorder stable state to happen after microtasks such as Promises, per HTML. Right now we call the regular thread observers, including appshell, before the main thread observer (XPConnect), so stable state tasks happen before microtasks.
2015-08-11 16:10:46 +03:00
|
|
|
|
2018-05-10 08:04:12 +03:00
|
|
|
// Get the CycleCollectedJSContext for a JSContext.
|
|
|
|
// Returns null only if Initialize() has not completed on or during
|
|
|
|
// destruction of the CycleCollectedJSContext.
|
|
|
|
static CycleCollectedJSContext* GetFor(JSContext* aCx);
|
|
|
|
|
2016-09-14 16:47:32 +03:00
|
|
|
// Get the current thread's CycleCollectedJSContext. Returns null if there
|
2013-09-09 07:28:50 +04:00
|
|
|
// isn't one.
|
2016-09-14 16:47:32 +03:00
|
|
|
static CycleCollectedJSContext* Get();
|
2013-09-09 07:28:50 +04:00
|
|
|
|
2016-03-24 18:12:00 +03:00
|
|
|
// Queue an async microtask to the current main or worker thread.
|
2017-11-17 06:01:27 +03:00
|
|
|
virtual void DispatchToMicroTask(
|
|
|
|
already_AddRefed<MicroTaskRunnable> aRunnable);
|
2016-03-24 18:12:00 +03:00
|
|
|
|
2017-10-05 18:34:12 +03:00
|
|
|
// Call EnterMicroTask when you're entering JS execution.
|
|
|
|
// Usually the best way to do this is to use nsAutoMicroTask.
|
|
|
|
void EnterMicroTask() { ++mMicroTaskLevel; }
|
|
|
|
|
2019-03-19 18:14:11 +03:00
|
|
|
MOZ_CAN_RUN_SCRIPT
|
2017-10-05 18:34:12 +03:00
|
|
|
void LeaveMicroTask() {
|
|
|
|
if (--mMicroTaskLevel == 0) {
|
2017-10-11 15:31:38 +03:00
|
|
|
PerformMicroTaskCheckPoint();
|
2017-10-05 18:34:12 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-13 23:05:00 +03:00
|
|
|
bool IsInMicroTask() const { return mMicroTaskLevel != 0; }
|
2017-10-05 18:34:12 +03:00
|
|
|
|
2018-12-13 23:05:00 +03:00
|
|
|
uint32_t MicroTaskLevel() const { return mMicroTaskLevel; }
|
2017-10-05 18:34:12 +03:00
|
|
|
|
|
|
|
void SetMicroTaskLevel(uint32_t aLevel) { mMicroTaskLevel = aLevel; }
|
|
|
|
|
2019-03-19 18:14:11 +03:00
|
|
|
MOZ_CAN_RUN_SCRIPT
|
2018-07-19 19:38:33 +03:00
|
|
|
bool PerformMicroTaskCheckPoint(bool aForce = false);
|
2017-10-11 15:31:38 +03:00
|
|
|
|
2019-03-19 18:14:11 +03:00
|
|
|
MOZ_CAN_RUN_SCRIPT
|
2017-11-17 06:01:27 +03:00
|
|
|
void PerformDebuggerMicroTaskCheckpoint();
|
2017-10-05 18:34:12 +03:00
|
|
|
|
2018-12-13 23:05:00 +03:00
|
|
|
bool IsInStableOrMetaStableState() const { return mDoingStableStates; }
|
2017-10-19 13:20:31 +03:00
|
|
|
|
2015-04-10 18:27:57 +03:00
|
|
|
// Storage for watching rejected promises waiting for some client to
|
|
|
|
// consume their rejection.
|
2016-03-22 18:22:24 +03:00
|
|
|
// Promises in this list have been rejected in the last turn of the
|
|
|
|
// event loop without the rejection being handled.
|
|
|
|
// Note that this can contain nullptrs in place of promises removed because
|
|
|
|
// they're consumed before it'd be reported.
|
|
|
|
JS::PersistentRooted<JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>>
|
|
|
|
mUncaughtRejections;
|
|
|
|
|
|
|
|
// Promises in this list have previously been reported as rejected
|
|
|
|
// (because they were in the above list), but the rejection was handled
|
|
|
|
// in the last turn of the event loop.
|
|
|
|
JS::PersistentRooted<JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>>
|
|
|
|
mConsumedRejections;
|
2015-04-10 18:27:57 +03:00
|
|
|
nsTArray<nsCOMPtr<nsISupports /* UncaughtRejectionObserver */>>
|
|
|
|
mUncaughtRejectionObservers;
|
|
|
|
|
2018-04-06 09:53:25 +03:00
|
|
|
virtual bool IsSystemCaller() const = 0;
|
|
|
|
|
2019-06-12 22:11:04 +03:00
|
|
|
// Unused on main thread. Used by AutoJSAPI on Worker and Worklet threads.
|
|
|
|
virtual void ReportError(JSErrorReport* aReport,
|
|
|
|
JS::ConstUTF8CharsZ aToStringResult) {
|
|
|
|
MOZ_ASSERT_UNREACHABLE("Not supported");
|
|
|
|
}
|
|
|
|
|
Bug 1145201: Replace EnqueuePromiseJobCallback and GetIncumbentGlobalCallback with new JobQueue abstract base class. r=arai,smaug
While the behavior of ECMAScript Promises and their associated job queue is
covered by the ECMAScript standard, the HTML specification amends that with
additional behavior the web platform requires. To support this, SpiderMonkey
provides hooks the embedding can set to replace SpiderMonkey's queue with its
own implementation.
At present, these hooks are C-style function-pointer-and-void-pointer pairs,
which are awkward to handle and mistake-prone, as passing a function the wrong
void* is not a type error. Later patches in this series must add new hooks,
making a bad situation worse.
A C++ abstract base class is a well-typed alternative. This introduces a new
`JS::JobQueue` abstract class, and adapts SpiderMonkey's internal job queue and
Gecko's customization to use it. `GetIncumbentGlobalCallback` and
`EnqueuePromiseJobCallback` become virtual methods.
Within SpiderMonkey, the patch gathers the various fields of JSContext that
implement the internal queue into their own type, js::InternalJobQueue. Various
jsfriendapi functions become veneers for calls to methods specific to the
derived class. The InternalJobQueue type itself remains private to SpiderMonkey,
as it uses types like TraceableFifo, derived from Fifo, that are not part of
SpiderMonkey's public API.
Within Gecko, CycleCollectedJSContext acquires JS::JobQueue as a private base
class, and a few static methods are cleaned up nicely.
There are a few other hooks defined in js/public/Promise.h that might make sense
to turn into virtual methods on JobQueue. For example,
DispatchToEventLoopCallback, used for resolving promises of results from
off-main-thread tasks, is probably necessarily connected to the JobQueue
implementation in use, so it might not be sensible to set one without the other.
But it was left unchanged to reduce this patch's size.
Differential Revision: https://phabricator.services.mozilla.com/D17544
--HG--
extra : moz-landing-system : lando
2019-02-12 11:16:16 +03:00
|
|
|
private:
|
|
|
|
// JS::JobQueue implementation: see js/public/Promise.h.
|
2019-02-12 11:14:34 +03:00
|
|
|
// SpiderMonkey uses some of these methods to enqueue promise resolution jobs.
|
|
|
|
// Others protect the debuggee microtask queue from the debugger's
|
|
|
|
// interruptions; see the comments on JS::AutoDebuggerJobQueueInterruption for
|
|
|
|
// details.
|
Bug 1145201: Replace EnqueuePromiseJobCallback and GetIncumbentGlobalCallback with new JobQueue abstract base class. r=arai,smaug
While the behavior of ECMAScript Promises and their associated job queue is
covered by the ECMAScript standard, the HTML specification amends that with
additional behavior the web platform requires. To support this, SpiderMonkey
provides hooks the embedding can set to replace SpiderMonkey's queue with its
own implementation.
At present, these hooks are C-style function-pointer-and-void-pointer pairs,
which are awkward to handle and mistake-prone, as passing a function the wrong
void* is not a type error. Later patches in this series must add new hooks,
making a bad situation worse.
A C++ abstract base class is a well-typed alternative. This introduces a new
`JS::JobQueue` abstract class, and adapts SpiderMonkey's internal job queue and
Gecko's customization to use it. `GetIncumbentGlobalCallback` and
`EnqueuePromiseJobCallback` become virtual methods.
Within SpiderMonkey, the patch gathers the various fields of JSContext that
implement the internal queue into their own type, js::InternalJobQueue. Various
jsfriendapi functions become veneers for calls to methods specific to the
derived class. The InternalJobQueue type itself remains private to SpiderMonkey,
as it uses types like TraceableFifo, derived from Fifo, that are not part of
SpiderMonkey's public API.
Within Gecko, CycleCollectedJSContext acquires JS::JobQueue as a private base
class, and a few static methods are cleaned up nicely.
There are a few other hooks defined in js/public/Promise.h that might make sense
to turn into virtual methods on JobQueue. For example,
DispatchToEventLoopCallback, used for resolving promises of results from
off-main-thread tasks, is probably necessarily connected to the JobQueue
implementation in use, so it might not be sensible to set one without the other.
But it was left unchanged to reduce this patch's size.
Differential Revision: https://phabricator.services.mozilla.com/D17544
--HG--
extra : moz-landing-system : lando
2019-02-12 11:16:16 +03:00
|
|
|
JSObject* getIncumbentGlobal(JSContext* cx) override;
|
|
|
|
bool enqueuePromiseJob(JSContext* cx, JS::HandleObject promise,
|
|
|
|
JS::HandleObject job, JS::HandleObject allocationSite,
|
|
|
|
JS::HandleObject incumbentGlobal) override;
|
2019-03-19 18:14:11 +03:00
|
|
|
// MOZ_CAN_RUN_SCRIPT_BOUNDARY for now so we don't have to change SpiderMonkey
|
|
|
|
// headers. The caller presumably knows this can run script (like everything
|
|
|
|
// in SpiderMonkey!) and will deal.
|
|
|
|
MOZ_CAN_RUN_SCRIPT_BOUNDARY
|
Bug 1145201: Replace EnqueuePromiseJobCallback and GetIncumbentGlobalCallback with new JobQueue abstract base class. r=arai,smaug
While the behavior of ECMAScript Promises and their associated job queue is
covered by the ECMAScript standard, the HTML specification amends that with
additional behavior the web platform requires. To support this, SpiderMonkey
provides hooks the embedding can set to replace SpiderMonkey's queue with its
own implementation.
At present, these hooks are C-style function-pointer-and-void-pointer pairs,
which are awkward to handle and mistake-prone, as passing a function the wrong
void* is not a type error. Later patches in this series must add new hooks,
making a bad situation worse.
A C++ abstract base class is a well-typed alternative. This introduces a new
`JS::JobQueue` abstract class, and adapts SpiderMonkey's internal job queue and
Gecko's customization to use it. `GetIncumbentGlobalCallback` and
`EnqueuePromiseJobCallback` become virtual methods.
Within SpiderMonkey, the patch gathers the various fields of JSContext that
implement the internal queue into their own type, js::InternalJobQueue. Various
jsfriendapi functions become veneers for calls to methods specific to the
derived class. The InternalJobQueue type itself remains private to SpiderMonkey,
as it uses types like TraceableFifo, derived from Fifo, that are not part of
SpiderMonkey's public API.
Within Gecko, CycleCollectedJSContext acquires JS::JobQueue as a private base
class, and a few static methods are cleaned up nicely.
There are a few other hooks defined in js/public/Promise.h that might make sense
to turn into virtual methods on JobQueue. For example,
DispatchToEventLoopCallback, used for resolving promises of results from
off-main-thread tasks, is probably necessarily connected to the JobQueue
implementation in use, so it might not be sensible to set one without the other.
But it was left unchanged to reduce this patch's size.
Differential Revision: https://phabricator.services.mozilla.com/D17544
--HG--
extra : moz-landing-system : lando
2019-02-12 11:16:16 +03:00
|
|
|
void runJobs(JSContext* cx) override;
|
|
|
|
bool empty() const override;
|
2019-02-12 11:14:34 +03:00
|
|
|
class SavedMicroTaskQueue;
|
|
|
|
js::UniquePtr<SavedJobQueue> saveJobQueue(JSContext*) override;
|
Bug 1145201: Replace EnqueuePromiseJobCallback and GetIncumbentGlobalCallback with new JobQueue abstract base class. r=arai,smaug
While the behavior of ECMAScript Promises and their associated job queue is
covered by the ECMAScript standard, the HTML specification amends that with
additional behavior the web platform requires. To support this, SpiderMonkey
provides hooks the embedding can set to replace SpiderMonkey's queue with its
own implementation.
At present, these hooks are C-style function-pointer-and-void-pointer pairs,
which are awkward to handle and mistake-prone, as passing a function the wrong
void* is not a type error. Later patches in this series must add new hooks,
making a bad situation worse.
A C++ abstract base class is a well-typed alternative. This introduces a new
`JS::JobQueue` abstract class, and adapts SpiderMonkey's internal job queue and
Gecko's customization to use it. `GetIncumbentGlobalCallback` and
`EnqueuePromiseJobCallback` become virtual methods.
Within SpiderMonkey, the patch gathers the various fields of JSContext that
implement the internal queue into their own type, js::InternalJobQueue. Various
jsfriendapi functions become veneers for calls to methods specific to the
derived class. The InternalJobQueue type itself remains private to SpiderMonkey,
as it uses types like TraceableFifo, derived from Fifo, that are not part of
SpiderMonkey's public API.
Within Gecko, CycleCollectedJSContext acquires JS::JobQueue as a private base
class, and a few static methods are cleaned up nicely.
There are a few other hooks defined in js/public/Promise.h that might make sense
to turn into virtual methods on JobQueue. For example,
DispatchToEventLoopCallback, used for resolving promises of results from
off-main-thread tasks, is probably necessarily connected to the JobQueue
implementation in use, so it might not be sensible to set one without the other.
But it was left unchanged to reduce this patch's size.
Differential Revision: https://phabricator.services.mozilla.com/D17544
--HG--
extra : moz-landing-system : lando
2019-02-12 11:16:16 +03:00
|
|
|
|
2013-06-18 23:02:07 +04:00
|
|
|
private:
|
2017-02-24 00:23:45 +03:00
|
|
|
CycleCollectedJSRuntime* mRuntime;
|
2013-06-18 23:02:15 +04:00
|
|
|
|
2016-06-24 21:19:51 +03:00
|
|
|
JSContext* mJSContext;
|
2013-06-18 23:02:14 +04:00
|
|
|
|
2018-02-06 00:34:05 +03:00
|
|
|
nsCOMPtr<dom::Exception> mPendingException;
|
Bug 1179909: Refactor stable state handling. r=smaug
This is motivated by three separate but related problems:
1. Our concept of recursion depth is broken for things that run from AfterProcessNextEvent observers (e.g. Promises). We decrement the recursionDepth counter before firing observers, so a Promise callback running at the lowest event loop depth has a recursion depth of 0 (whereas a regular nsIRunnable would be 1). This is a problem because it's impossible to distinguish a Promise running after a sync XHR's onreadystatechange handler from a top-level event (since the former runs with depth 2 - 1 = 1, and the latter runs with just 1).
2. The nsIThreadObserver mechanism that is used by a lot of code to run "after" the current event is a poor fit for anything that runs script. First, the order the observers fire in is the order they were added, not anything fixed by spec. Additionally, running script can cause the event loop to spin, which is a big source of pain here (bholley has some nasty bug caused by this).
3. We run Promises from different points in the code for workers and main thread. The latter runs from XPConnect's nsIThreadObserver callbacks, while the former runs from a hardcoded call to run Promises in the worker event loop. What workers do is particularly problematic because it means we can't get the right recursion depth no matter what we do to nsThread.
The solve this, this patch does the following:
1. Consolidate some handling of microtasks and all handling of stable state from appshell and WorkerPrivate into CycleCollectedJSRuntime.
2. Make the recursionDepth counter only available to CycleCollectedJSRuntime (and its consumers) and remove it from the nsIThreadInternal and nsIThreadObserver APIs.
3. Adjust the recursionDepth counter so that microtasks run with the recursionDepth of the task they are associated with.
4. Introduce the concept of metastable state to replace appshell's RunBeforeNextEvent. Metastable state is reached after every microtask or task is completed. This provides the semantics that bent and I want for IndexedDB, where transactions autocommit at the end of a microtask and do not "spill" from one microtask into a subsequent microtask. This differs from appshell's RunBeforeNextEvent in two ways:
a) It fires between microtasks, which was the motivation for starting this.
b) It no longer ensures that we're at the same event loop depth in the native event queue. bent decided we don't care about this.
5. Reorder stable state to happen after microtasks such as Promises, per HTML. Right now we call the regular thread observers, including appshell, before the main thread observer (XPConnect), so stable state tasks happen before microtasks.
2015-08-11 16:10:46 +03:00
|
|
|
nsThread* mOwningThread; // Manual refcounting to avoid include hell.
|
2014-05-22 17:18:02 +04:00
|
|
|
|
2017-11-17 06:01:27 +03:00
|
|
|
struct PendingIDBTransactionData {
|
|
|
|
nsCOMPtr<nsIRunnable> mTransaction;
|
Bug 1179909: Refactor stable state handling. r=smaug
This is motivated by three separate but related problems:
1. Our concept of recursion depth is broken for things that run from AfterProcessNextEvent observers (e.g. Promises). We decrement the recursionDepth counter before firing observers, so a Promise callback running at the lowest event loop depth has a recursion depth of 0 (whereas a regular nsIRunnable would be 1). This is a problem because it's impossible to distinguish a Promise running after a sync XHR's onreadystatechange handler from a top-level event (since the former runs with depth 2 - 1 = 1, and the latter runs with just 1).
2. The nsIThreadObserver mechanism that is used by a lot of code to run "after" the current event is a poor fit for anything that runs script. First, the order the observers fire in is the order they were added, not anything fixed by spec. Additionally, running script can cause the event loop to spin, which is a big source of pain here (bholley has some nasty bug caused by this).
3. We run Promises from different points in the code for workers and main thread. The latter runs from XPConnect's nsIThreadObserver callbacks, while the former runs from a hardcoded call to run Promises in the worker event loop. What workers do is particularly problematic because it means we can't get the right recursion depth no matter what we do to nsThread.
The solve this, this patch does the following:
1. Consolidate some handling of microtasks and all handling of stable state from appshell and WorkerPrivate into CycleCollectedJSRuntime.
2. Make the recursionDepth counter only available to CycleCollectedJSRuntime (and its consumers) and remove it from the nsIThreadInternal and nsIThreadObserver APIs.
3. Adjust the recursionDepth counter so that microtasks run with the recursionDepth of the task they are associated with.
4. Introduce the concept of metastable state to replace appshell's RunBeforeNextEvent. Metastable state is reached after every microtask or task is completed. This provides the semantics that bent and I want for IndexedDB, where transactions autocommit at the end of a microtask and do not "spill" from one microtask into a subsequent microtask. This differs from appshell's RunBeforeNextEvent in two ways:
a) It fires between microtasks, which was the motivation for starting this.
b) It no longer ensures that we're at the same event loop depth in the native event queue. bent decided we don't care about this.
5. Reorder stable state to happen after microtasks such as Promises, per HTML. Right now we call the regular thread observers, including appshell, before the main thread observer (XPConnect), so stable state tasks happen before microtasks.
2015-08-11 16:10:46 +03:00
|
|
|
uint32_t mRecursionDepth;
|
|
|
|
};
|
|
|
|
|
|
|
|
nsTArray<nsCOMPtr<nsIRunnable>> mStableStateEvents;
|
2017-11-17 06:01:27 +03:00
|
|
|
nsTArray<PendingIDBTransactionData> mPendingIDBTransactions;
|
Bug 1179909: Refactor stable state handling. r=smaug
This is motivated by three separate but related problems:
1. Our concept of recursion depth is broken for things that run from AfterProcessNextEvent observers (e.g. Promises). We decrement the recursionDepth counter before firing observers, so a Promise callback running at the lowest event loop depth has a recursion depth of 0 (whereas a regular nsIRunnable would be 1). This is a problem because it's impossible to distinguish a Promise running after a sync XHR's onreadystatechange handler from a top-level event (since the former runs with depth 2 - 1 = 1, and the latter runs with just 1).
2. The nsIThreadObserver mechanism that is used by a lot of code to run "after" the current event is a poor fit for anything that runs script. First, the order the observers fire in is the order they were added, not anything fixed by spec. Additionally, running script can cause the event loop to spin, which is a big source of pain here (bholley has some nasty bug caused by this).
3. We run Promises from different points in the code for workers and main thread. The latter runs from XPConnect's nsIThreadObserver callbacks, while the former runs from a hardcoded call to run Promises in the worker event loop. What workers do is particularly problematic because it means we can't get the right recursion depth no matter what we do to nsThread.
The solve this, this patch does the following:
1. Consolidate some handling of microtasks and all handling of stable state from appshell and WorkerPrivate into CycleCollectedJSRuntime.
2. Make the recursionDepth counter only available to CycleCollectedJSRuntime (and its consumers) and remove it from the nsIThreadInternal and nsIThreadObserver APIs.
3. Adjust the recursionDepth counter so that microtasks run with the recursionDepth of the task they are associated with.
4. Introduce the concept of metastable state to replace appshell's RunBeforeNextEvent. Metastable state is reached after every microtask or task is completed. This provides the semantics that bent and I want for IndexedDB, where transactions autocommit at the end of a microtask and do not "spill" from one microtask into a subsequent microtask. This differs from appshell's RunBeforeNextEvent in two ways:
a) It fires between microtasks, which was the motivation for starting this.
b) It no longer ensures that we're at the same event loop depth in the native event queue. bent decided we don't care about this.
5. Reorder stable state to happen after microtasks such as Promises, per HTML. Right now we call the regular thread observers, including appshell, before the main thread observer (XPConnect), so stable state tasks happen before microtasks.
2015-08-11 16:10:46 +03:00
|
|
|
uint32_t mBaseRecursionDepth;
|
|
|
|
bool mDoingStableStates;
|
|
|
|
|
2017-11-17 06:01:27 +03:00
|
|
|
// If set to none 0, microtasks will be processed only when recursion depth
|
|
|
|
// is the set value.
|
|
|
|
uint32_t mTargetedMicroTaskRecursionDepth;
|
2017-10-05 18:34:12 +03:00
|
|
|
|
|
|
|
uint32_t mMicroTaskLevel;
|
2017-11-17 06:01:27 +03:00
|
|
|
|
2017-10-11 15:31:38 +03:00
|
|
|
std::queue<RefPtr<MicroTaskRunnable>> mPendingMicroTaskRunnables;
|
2017-11-17 06:01:27 +03:00
|
|
|
std::queue<RefPtr<MicroTaskRunnable>> mDebuggerMicroTaskQueue;
|
2017-10-11 15:31:38 +03:00
|
|
|
|
2019-05-28 23:28:03 +03:00
|
|
|
// How many times the debugger has interrupted execution, possibly creating
|
|
|
|
// microtask checkpoints in places that they would not normally occur.
|
|
|
|
uint32_t mDebuggerRecursionDepth;
|
|
|
|
|
2017-10-11 15:31:38 +03:00
|
|
|
uint32_t mMicroTaskRecursionDepth;
|
2019-05-07 14:39:58 +03:00
|
|
|
|
|
|
|
// This implements about-to-be-notified rejected promises list in the spec.
|
|
|
|
// https://html.spec.whatwg.org/multipage/webappapis.html#about-to-be-notified-rejected-promises-list
|
|
|
|
typedef nsTArray<RefPtr<dom::Promise>> PromiseArray;
|
|
|
|
PromiseArray mAboutToBeNotifiedRejectedPromises;
|
|
|
|
|
|
|
|
// This is for the "outstanding rejected promises weak set" in the spec,
|
|
|
|
// https://html.spec.whatwg.org/multipage/webappapis.html#outstanding-rejected-promises-weak-set
|
|
|
|
// We use different data structure and opposite logic here to achieve the same
|
|
|
|
// effect. Basically this is used for tracking the rejected promise that does
|
|
|
|
// NOT need firing a rejectionhandled event. We will check the table to see if
|
|
|
|
// firing rejectionhandled event is required when a rejected promise is being
|
|
|
|
// handled.
|
|
|
|
//
|
|
|
|
// The rejected promise will be stored in the table if
|
|
|
|
// - it is unhandled, and
|
|
|
|
// - The unhandledrejection is not yet fired.
|
|
|
|
//
|
|
|
|
// And be removed when
|
|
|
|
// - it is handled, or
|
|
|
|
// - A unhandledrejection is fired and it isn't being handled in event
|
|
|
|
// handler.
|
|
|
|
typedef nsRefPtrHashtable<nsUint64HashKey, dom::Promise> PromiseHashtable;
|
|
|
|
PromiseHashtable mPendingUnhandledRejections;
|
|
|
|
|
|
|
|
class NotifyUnhandledRejections final : public CancelableRunnable {
|
|
|
|
public:
|
|
|
|
NotifyUnhandledRejections(CycleCollectedJSContext* aCx,
|
|
|
|
PromiseArray&& aPromises)
|
|
|
|
: CancelableRunnable("NotifyUnhandledRejections"),
|
|
|
|
mCx(aCx),
|
|
|
|
mUnhandledRejections(std::move(aPromises)) {}
|
|
|
|
|
|
|
|
NS_IMETHOD Run() final;
|
|
|
|
|
|
|
|
nsresult Cancel() final;
|
|
|
|
|
|
|
|
private:
|
|
|
|
CycleCollectedJSContext* mCx;
|
|
|
|
PromiseArray mUnhandledRejections;
|
|
|
|
};
|
2020-01-15 17:22:04 +03:00
|
|
|
|
2020-07-17 20:34:46 +03:00
|
|
|
FinalizationRegistryCleanup mFinalizationRegistryCleanup;
|
2017-10-05 18:34:12 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
class MOZ_STACK_CLASS nsAutoMicroTask {
|
|
|
|
public:
|
|
|
|
nsAutoMicroTask() {
|
|
|
|
CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
|
|
|
|
if (ccjs) {
|
|
|
|
ccjs->EnterMicroTask();
|
|
|
|
}
|
|
|
|
}
|
2019-03-19 18:14:11 +03:00
|
|
|
MOZ_CAN_RUN_SCRIPT ~nsAutoMicroTask() {
|
2017-10-05 18:34:12 +03:00
|
|
|
CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
|
|
|
|
if (ccjs) {
|
|
|
|
ccjs->LeaveMicroTask();
|
|
|
|
}
|
|
|
|
}
|
2013-06-18 23:02:07 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace mozilla
|
|
|
|
|
2017-02-24 00:23:45 +03:00
|
|
|
#endif // mozilla_CycleCollectedJSContext_h
|