Bug 1867190 - Add prefs for PHC probablities r=glandium
[gecko.git] / xpcom / base / CycleCollectedJSContext.h
blobbbe47a57a5cdb24a11e4bd41b3f6b560dfd463bb
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_CycleCollectedJSContext_h
8 #define mozilla_CycleCollectedJSContext_h
10 #include <deque>
12 #include "mozilla/Attributes.h"
13 #include "mozilla/MemoryReporting.h"
14 #include "mozilla/dom/AtomList.h"
15 #include "mozilla/dom/Promise.h"
16 #include "js/GCVector.h"
17 #include "js/Promise.h"
19 #include "nsCOMPtr.h"
20 #include "nsRefPtrHashtable.h"
21 #include "nsTArray.h"
23 class nsCycleCollectionNoteRootCallback;
24 class nsIRunnable;
25 class nsThread;
27 namespace mozilla {
28 class AutoSlowOperation;
30 class CycleCollectedJSContext;
31 class CycleCollectedJSRuntime;
33 namespace dom {
34 class Exception;
35 class WorkerJSContext;
36 class WorkletJSContext;
37 } // namespace dom
39 // Contains various stats about the cycle collection.
40 struct CycleCollectorResults {
41 CycleCollectorResults() {
42 // Initialize here so when we increment mNumSlices the first time we're
43 // not using uninitialized memory.
44 Init();
47 void Init() {
48 mForcedGC = false;
49 mSuspectedAtCCStart = 0;
50 mMergedZones = false;
51 mAnyManual = false;
52 mVisitedRefCounted = 0;
53 mVisitedGCed = 0;
54 mFreedRefCounted = 0;
55 mFreedGCed = 0;
56 mFreedJSZones = 0;
57 mNumSlices = 1;
58 // mNumSlices is initialized to one, because we call Init() after the
59 // per-slice increment of mNumSlices has already occurred.
62 bool mForcedGC;
63 bool mMergedZones;
64 // mAnyManual is true if any slice was manually triggered, and at shutdown.
65 bool mAnyManual;
66 uint32_t mSuspectedAtCCStart;
67 uint32_t mVisitedRefCounted;
68 uint32_t mVisitedGCed;
69 uint32_t mFreedRefCounted;
70 uint32_t mFreedGCed;
71 uint32_t mFreedJSZones;
72 uint32_t mNumSlices;
75 class MicroTaskRunnable {
76 public:
77 MicroTaskRunnable() = default;
78 NS_INLINE_DECL_REFCOUNTING(MicroTaskRunnable)
79 MOZ_CAN_RUN_SCRIPT virtual void Run(AutoSlowOperation& aAso) = 0;
80 virtual bool Suppressed() { return false; }
82 protected:
83 virtual ~MicroTaskRunnable() = default;
86 // Store the suppressed mictotasks in another microtask so that operations
87 // for the microtask queue as a whole keep working.
88 class SuppressedMicroTasks : public MicroTaskRunnable {
89 public:
90 explicit SuppressedMicroTasks(CycleCollectedJSContext* aContext);
92 MOZ_CAN_RUN_SCRIPT_BOUNDARY void Run(AutoSlowOperation& aAso) final {}
93 virtual bool Suppressed();
95 CycleCollectedJSContext* mContext;
96 uint64_t mSuppressionGeneration;
97 std::deque<RefPtr<MicroTaskRunnable>> mSuppressedMicroTaskRunnables;
100 // Support for JS FinalizationRegistry objects, which allow a JS callback to be
101 // registered that is called when objects die.
103 // We keep a vector of functions that call back into the JS engine along
104 // with their associated incumbent globals, one per FinalizationRegistry object
105 // that has pending cleanup work. These are run in their own task.
106 class FinalizationRegistryCleanup {
107 public:
108 explicit FinalizationRegistryCleanup(CycleCollectedJSContext* aContext);
109 void Init();
110 void Destroy();
111 void QueueCallback(JSFunction* aDoCleanup, JSObject* aIncumbentGlobal);
112 MOZ_CAN_RUN_SCRIPT void DoCleanup();
114 private:
115 static void QueueCallback(JSFunction* aDoCleanup, JSObject* aIncumbentGlobal,
116 void* aData);
118 class CleanupRunnable;
120 struct Callback {
121 JSFunction* mCallbackFunction;
122 JSObject* mIncumbentGlobal;
123 void trace(JSTracer* trc);
126 // This object is part of CycleCollectedJSContext, so it's safe to have a raw
127 // pointer to its containing context here.
128 CycleCollectedJSContext* mContext;
130 using CallbackVector = JS::GCVector<Callback, 0, InfallibleAllocPolicy>;
131 JS::PersistentRooted<CallbackVector> mCallbacks;
134 class CycleCollectedJSContext : dom::PerThreadAtomCache, private JS::JobQueue {
135 friend class CycleCollectedJSRuntime;
136 friend class SuppressedMicroTasks;
138 protected:
139 CycleCollectedJSContext();
140 virtual ~CycleCollectedJSContext();
142 MOZ_IS_CLASS_INIT
143 nsresult Initialize(JSRuntime* aParentRuntime, uint32_t aMaxBytes);
145 virtual CycleCollectedJSRuntime* CreateRuntime(JSContext* aCx) = 0;
147 size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
149 private:
150 static void PromiseRejectionTrackerCallback(
151 JSContext* aCx, bool aMutedErrors, JS::Handle<JSObject*> aPromise,
152 JS::PromiseRejectionHandlingState state, void* aData);
154 void AfterProcessMicrotasks();
156 public:
157 void ProcessStableStateQueue();
159 private:
160 void CleanupIDBTransactions(uint32_t aRecursionDepth);
162 public:
163 virtual dom::WorkerJSContext* GetAsWorkerJSContext() { return nullptr; }
164 virtual dom::WorkletJSContext* GetAsWorkletJSContext() { return nullptr; }
166 CycleCollectedJSRuntime* Runtime() const {
167 MOZ_ASSERT(mRuntime);
168 return mRuntime;
171 already_AddRefed<dom::Exception> GetPendingException() const;
172 void SetPendingException(dom::Exception* aException);
174 std::deque<RefPtr<MicroTaskRunnable>>& GetMicroTaskQueue();
175 std::deque<RefPtr<MicroTaskRunnable>>& GetDebuggerMicroTaskQueue();
177 JSContext* Context() const {
178 MOZ_ASSERT(mJSContext);
179 return mJSContext;
182 JS::RootingContext* RootingCx() const {
183 MOZ_ASSERT(mJSContext);
184 return JS::RootingContext::get(mJSContext);
187 void SetTargetedMicroTaskRecursionDepth(uint32_t aDepth) {
188 mTargetedMicroTaskRecursionDepth = aDepth;
191 void UpdateMicroTaskSuppressionGeneration() { ++mSuppressionGeneration; }
193 protected:
194 JSContext* MaybeContext() const { return mJSContext; }
196 public:
197 // nsThread entrypoints
199 // MOZ_CAN_RUN_SCRIPT_BOUNDARY so we don't need to annotate
200 // nsThread::ProcessNextEvent and all its callers MOZ_CAN_RUN_SCRIPT for now.
201 // But we really should!
202 MOZ_CAN_RUN_SCRIPT_BOUNDARY
203 virtual void BeforeProcessTask(bool aMightBlock);
204 // MOZ_CAN_RUN_SCRIPT_BOUNDARY so we don't need to annotate
205 // nsThread::ProcessNextEvent and all its callers MOZ_CAN_RUN_SCRIPT for now.
206 // But we really should!
207 MOZ_CAN_RUN_SCRIPT_BOUNDARY
208 virtual void AfterProcessTask(uint32_t aRecursionDepth);
210 // Check whether any eager thresholds have been reached, which would mean
211 // an idle GC task (minor or major) would be useful.
212 virtual void MaybePokeGC();
214 uint32_t RecursionDepth() const;
216 // Run in stable state (call through nsContentUtils)
217 void RunInStableState(already_AddRefed<nsIRunnable>&& aRunnable);
219 void AddPendingIDBTransaction(already_AddRefed<nsIRunnable>&& aTransaction);
221 // Get the CycleCollectedJSContext for a JSContext.
222 // Returns null only if Initialize() has not completed on or during
223 // destruction of the CycleCollectedJSContext.
224 static CycleCollectedJSContext* GetFor(JSContext* aCx);
226 // Get the current thread's CycleCollectedJSContext. Returns null if there
227 // isn't one.
228 static CycleCollectedJSContext* Get();
230 // Queue an async microtask to the current main or worker thread.
231 virtual void DispatchToMicroTask(
232 already_AddRefed<MicroTaskRunnable> aRunnable);
234 // Call EnterMicroTask when you're entering JS execution.
235 // Usually the best way to do this is to use nsAutoMicroTask.
236 void EnterMicroTask() { ++mMicroTaskLevel; }
238 MOZ_CAN_RUN_SCRIPT
239 void LeaveMicroTask() {
240 if (--mMicroTaskLevel == 0) {
241 PerformMicroTaskCheckPoint();
245 uint32_t MicroTaskLevel() const { return mMicroTaskLevel; }
247 void SetMicroTaskLevel(uint32_t aLevel) { mMicroTaskLevel = aLevel; }
249 MOZ_CAN_RUN_SCRIPT
250 bool PerformMicroTaskCheckPoint(bool aForce = false);
252 MOZ_CAN_RUN_SCRIPT
253 void PerformDebuggerMicroTaskCheckpoint();
255 bool IsInStableOrMetaStableState() const { return mDoingStableStates; }
257 // Storage for watching rejected promises waiting for some client to
258 // consume their rejection.
259 // Promises in this list have been rejected in the last turn of the
260 // event loop without the rejection being handled.
261 // Note that this can contain nullptrs in place of promises removed because
262 // they're consumed before it'd be reported.
263 JS::PersistentRooted<JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>>
264 mUncaughtRejections;
266 // Promises in this list have previously been reported as rejected
267 // (because they were in the above list), but the rejection was handled
268 // in the last turn of the event loop.
269 JS::PersistentRooted<JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>>
270 mConsumedRejections;
271 nsTArray<nsCOMPtr<nsISupports /* UncaughtRejectionObserver */>>
272 mUncaughtRejectionObservers;
274 virtual bool IsSystemCaller() const = 0;
276 // Unused on main thread. Used by AutoJSAPI on Worker and Worklet threads.
277 virtual void ReportError(JSErrorReport* aReport,
278 JS::ConstUTF8CharsZ aToStringResult) {
279 MOZ_ASSERT_UNREACHABLE("Not supported");
282 private:
283 // JS::JobQueue implementation: see js/public/Promise.h.
284 // SpiderMonkey uses some of these methods to enqueue promise resolution jobs.
285 // Others protect the debuggee microtask queue from the debugger's
286 // interruptions; see the comments on JS::AutoDebuggerJobQueueInterruption for
287 // details.
288 JSObject* getIncumbentGlobal(JSContext* cx) override;
289 bool enqueuePromiseJob(JSContext* cx, JS::Handle<JSObject*> promise,
290 JS::Handle<JSObject*> job,
291 JS::Handle<JSObject*> allocationSite,
292 JS::Handle<JSObject*> incumbentGlobal) override;
293 // MOZ_CAN_RUN_SCRIPT_BOUNDARY for now so we don't have to change SpiderMonkey
294 // headers. The caller presumably knows this can run script (like everything
295 // in SpiderMonkey!) and will deal.
296 MOZ_CAN_RUN_SCRIPT_BOUNDARY
297 void runJobs(JSContext* cx) override;
298 bool empty() const override;
299 class SavedMicroTaskQueue;
300 js::UniquePtr<SavedJobQueue> saveJobQueue(JSContext*) override;
302 private:
303 CycleCollectedJSRuntime* mRuntime;
305 JSContext* mJSContext;
307 nsCOMPtr<dom::Exception> mPendingException;
308 nsThread* mOwningThread; // Manual refcounting to avoid include hell.
310 struct PendingIDBTransactionData {
311 nsCOMPtr<nsIRunnable> mTransaction;
312 uint32_t mRecursionDepth;
315 nsTArray<nsCOMPtr<nsIRunnable>> mStableStateEvents;
316 nsTArray<PendingIDBTransactionData> mPendingIDBTransactions;
317 uint32_t mBaseRecursionDepth;
318 bool mDoingStableStates;
320 // If set to none 0, microtasks will be processed only when recursion depth
321 // is the set value.
322 uint32_t mTargetedMicroTaskRecursionDepth;
324 uint32_t mMicroTaskLevel;
326 std::deque<RefPtr<MicroTaskRunnable>> mPendingMicroTaskRunnables;
327 std::deque<RefPtr<MicroTaskRunnable>> mDebuggerMicroTaskQueue;
328 RefPtr<SuppressedMicroTasks> mSuppressedMicroTasks;
329 uint64_t mSuppressionGeneration;
331 // How many times the debugger has interrupted execution, possibly creating
332 // microtask checkpoints in places that they would not normally occur.
333 uint32_t mDebuggerRecursionDepth;
335 uint32_t mMicroTaskRecursionDepth;
337 // This implements about-to-be-notified rejected promises list in the spec.
338 // https://html.spec.whatwg.org/multipage/webappapis.html#about-to-be-notified-rejected-promises-list
339 typedef nsTArray<RefPtr<dom::Promise>> PromiseArray;
340 PromiseArray mAboutToBeNotifiedRejectedPromises;
342 // This is for the "outstanding rejected promises weak set" in the spec,
343 // https://html.spec.whatwg.org/multipage/webappapis.html#outstanding-rejected-promises-weak-set
344 // We use different data structure and opposite logic here to achieve the same
345 // effect. Basically this is used for tracking the rejected promise that does
346 // NOT need firing a rejectionhandled event. We will check the table to see if
347 // firing rejectionhandled event is required when a rejected promise is being
348 // handled.
350 // The rejected promise will be stored in the table if
351 // - it is unhandled, and
352 // - The unhandledrejection is not yet fired.
354 // And be removed when
355 // - it is handled, or
356 // - A unhandledrejection is fired and it isn't being handled in event
357 // handler.
358 typedef nsRefPtrHashtable<nsUint64HashKey, dom::Promise> PromiseHashtable;
359 PromiseHashtable mPendingUnhandledRejections;
361 class NotifyUnhandledRejections final : public CancelableRunnable {
362 public:
363 explicit NotifyUnhandledRejections(PromiseArray&& aPromises)
364 : CancelableRunnable("NotifyUnhandledRejections"),
365 mUnhandledRejections(std::move(aPromises)) {}
367 NS_IMETHOD Run() final;
369 nsresult Cancel() final;
371 private:
372 PromiseArray mUnhandledRejections;
375 FinalizationRegistryCleanup mFinalizationRegistryCleanup;
378 class MOZ_STACK_CLASS nsAutoMicroTask {
379 public:
380 nsAutoMicroTask() {
381 CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
382 if (ccjs) {
383 ccjs->EnterMicroTask();
386 MOZ_CAN_RUN_SCRIPT ~nsAutoMicroTask() {
387 CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
388 if (ccjs) {
389 ccjs->LeaveMicroTask();
394 } // namespace mozilla
396 #endif // mozilla_CycleCollectedJSContext_h