Line data Source code
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/. */
6 :
7 : #ifndef mozilla_CycleCollectedJSContext_h
8 : #define mozilla_CycleCollectedJSContext_h
9 :
10 : #include <queue>
11 :
12 : #include "mozilla/DeferredFinalize.h"
13 : #include "mozilla/LinkedList.h"
14 : #include "mozilla/mozalloc.h"
15 : #include "mozilla/MemoryReporting.h"
16 : #include "mozilla/dom/AtomList.h"
17 : #include "jsapi.h"
18 : #include "jsfriendapi.h"
19 :
20 : #include "nsCOMPtr.h"
21 : #include "nsCycleCollectionParticipant.h"
22 : #include "nsTArray.h"
23 :
24 : class nsCycleCollectionNoteRootCallback;
25 : class nsIRunnable;
26 : class nsThread;
27 : class nsWrapperCache;
28 :
29 : namespace mozilla {
30 : class AutoSlowOperation;
31 :
32 : class CycleCollectedJSRuntime;
33 :
34 : namespace dom {
35 : class Exception;
36 : class WorkerJSContext;
37 : class WorkletJSContext;
38 : } // namespace dom
39 :
40 : // Contains various stats about the cycle collection.
41 : struct CycleCollectorResults
42 : {
43 : CycleCollectorResults()
44 4 : {
45 : // Initialize here so when we increment mNumSlices the first time we're
46 : // not using uninitialized memory.
47 4 : Init();
48 : }
49 :
50 0 : void Init()
51 : {
52 0 : mForcedGC = false;
53 0 : mMergedZones = false;
54 0 : mAnyManual = false;
55 0 : mVisitedRefCounted = 0;
56 0 : mVisitedGCed = 0;
57 0 : mFreedRefCounted = 0;
58 0 : mFreedGCed = 0;
59 4 : mFreedJSZones = 0;
60 4 : mNumSlices = 1;
61 : // mNumSlices is initialized to one, because we call Init() after the
62 : // per-slice increment of mNumSlices has already occurred.
63 4 : }
64 :
65 : bool mForcedGC;
66 : bool mMergedZones;
67 : bool mAnyManual; // true if any slice of the CC was manually triggered, or at shutdown.
68 : uint32_t mVisitedRefCounted;
69 : uint32_t mVisitedGCed;
70 : uint32_t mFreedRefCounted;
71 : uint32_t mFreedGCed;
72 : uint32_t mFreedJSZones;
73 : uint32_t mNumSlices;
74 : };
75 :
76 : class MicroTaskRunnable
77 : {
78 : public:
79 8310 : MicroTaskRunnable() {}
80 0 : NS_INLINE_DECL_REFCOUNTING(MicroTaskRunnable)
81 : virtual void Run(AutoSlowOperation& aAso) = 0;
82 5 : virtual bool Suppressed() { return false; }
83 : protected:
84 : virtual ~MicroTaskRunnable() {}
85 : };
86 :
87 : class CycleCollectedJSContext
88 : : dom::PerThreadAtomCache
89 : , public LinkedListElement<CycleCollectedJSContext>
90 : {
91 : friend class CycleCollectedJSRuntime;
92 :
93 : protected:
94 : CycleCollectedJSContext();
95 : virtual ~CycleCollectedJSContext();
96 :
97 : MOZ_IS_CLASS_INIT
98 : nsresult Initialize(JSRuntime* aParentRuntime,
99 : uint32_t aMaxBytes,
100 : uint32_t aMaxNurseryBytes);
101 :
102 : // See explanation in mIsPrimaryContext.
103 : MOZ_IS_CLASS_INIT
104 : nsresult InitializeNonPrimary(CycleCollectedJSContext* aPrimaryContext);
105 :
106 : virtual CycleCollectedJSRuntime* CreateRuntime(JSContext* aCx) = 0;
107 :
108 : size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
109 :
110 : private:
111 : MOZ_IS_CLASS_INIT
112 : void InitializeCommon();
113 :
114 : static JSObject* GetIncumbentGlobalCallback(JSContext* aCx);
115 : static bool EnqueuePromiseJobCallback(JSContext* aCx,
116 : JS::HandleObject aJob,
117 : JS::HandleObject aAllocationSite,
118 : JS::HandleObject aIncumbentGlobal,
119 : void* aData);
120 : static void PromiseRejectionTrackerCallback(JSContext* aCx,
121 : JS::HandleObject aPromise,
122 : JS::PromiseRejectionHandlingState state,
123 : void* aData);
124 :
125 : void AfterProcessMicrotasks();
126 : public:
127 : void ProcessStableStateQueue();
128 : private:
129 : void CleanupIDBTransactions(uint32_t aRecursionDepth);
130 :
131 : public:
132 : enum DeferredFinalizeType {
133 : FinalizeIncrementally,
134 : FinalizeNow,
135 : };
136 :
137 0 : virtual dom::WorkerJSContext* GetAsWorkerJSContext() { return nullptr; }
138 0 : virtual dom::WorkletJSContext* GetAsWorkletJSContext() { return nullptr; }
139 :
140 21779 : CycleCollectedJSRuntime* Runtime() const
141 : {
142 21779 : MOZ_ASSERT(mRuntime);
143 21779 : return mRuntime;
144 : }
145 :
146 : already_AddRefed<dom::Exception> GetPendingException() const;
147 : void SetPendingException(dom::Exception* aException);
148 :
149 : std::queue<RefPtr<MicroTaskRunnable>>& GetMicroTaskQueue();
150 : std::queue<RefPtr<MicroTaskRunnable>>& GetDebuggerMicroTaskQueue();
151 :
152 118710 : JSContext* Context() const
153 : {
154 0 : MOZ_ASSERT(mJSContext);
155 118710 : return mJSContext;
156 : }
157 :
158 1285 : JS::RootingContext* RootingCx() const
159 : {
160 1285 : MOZ_ASSERT(mJSContext);
161 1285 : return JS::RootingContext::get(mJSContext);
162 : }
163 :
164 : void SetTargetedMicroTaskRecursionDepth(uint32_t aDepth)
165 : {
166 3 : mTargetedMicroTaskRecursionDepth = aDepth;
167 : }
168 :
169 : protected:
170 : JSContext* MaybeContext() const { return mJSContext; }
171 :
172 : public:
173 : // nsThread entrypoints
174 : virtual void BeforeProcessTask(bool aMightBlock);
175 : virtual void AfterProcessTask(uint32_t aRecursionDepth);
176 :
177 : // Check whether we need an idle GC task.
178 : void IsIdleGCTaskNeeded();
179 :
180 : uint32_t RecursionDepth();
181 :
182 : // Run in stable state (call through nsContentUtils)
183 : void RunInStableState(already_AddRefed<nsIRunnable>&& aRunnable);
184 :
185 : void AddPendingIDBTransaction(already_AddRefed<nsIRunnable>&& aTransaction);
186 :
187 : // Get the CycleCollectedJSContext for a JSContext.
188 : // Returns null only if Initialize() has not completed on or during
189 : // destruction of the CycleCollectedJSContext.
190 : static CycleCollectedJSContext* GetFor(JSContext* aCx);
191 :
192 : // Get the current thread's CycleCollectedJSContext. Returns null if there
193 : // isn't one.
194 : static CycleCollectedJSContext* Get();
195 :
196 : // Queue an async microtask to the current main or worker thread.
197 : virtual void DispatchToMicroTask(already_AddRefed<MicroTaskRunnable> aRunnable);
198 :
199 : // Call EnterMicroTask when you're entering JS execution.
200 : // Usually the best way to do this is to use nsAutoMicroTask.
201 : void EnterMicroTask()
202 : {
203 4851 : ++mMicroTaskLevel;
204 : }
205 :
206 4851 : void LeaveMicroTask()
207 : {
208 4851 : if (--mMicroTaskLevel == 0) {
209 4607 : PerformMicroTaskCheckPoint();
210 : }
211 4851 : }
212 :
213 : bool IsInMicroTask()
214 : {
215 : return mMicroTaskLevel != 0;
216 : }
217 :
218 : uint32_t MicroTaskLevel()
219 : {
220 : return mMicroTaskLevel;
221 : }
222 :
223 : void SetMicroTaskLevel(uint32_t aLevel)
224 : {
225 0 : mMicroTaskLevel = aLevel;
226 : }
227 :
228 : bool PerformMicroTaskCheckPoint();
229 :
230 : void PerformDebuggerMicroTaskCheckpoint();
231 :
232 : bool IsInStableOrMetaStableState()
233 : {
234 : return mDoingStableStates;
235 : }
236 :
237 : // Storage for watching rejected promises waiting for some client to
238 : // consume their rejection.
239 : // Promises in this list have been rejected in the last turn of the
240 : // event loop without the rejection being handled.
241 : // Note that this can contain nullptrs in place of promises removed because
242 : // they're consumed before it'd be reported.
243 : JS::PersistentRooted<JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>> mUncaughtRejections;
244 :
245 : // Promises in this list have previously been reported as rejected
246 : // (because they were in the above list), but the rejection was handled
247 : // in the last turn of the event loop.
248 : JS::PersistentRooted<JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>> mConsumedRejections;
249 : nsTArray<nsCOMPtr<nsISupports /* UncaughtRejectionObserver */ >> mUncaughtRejectionObservers;
250 :
251 : virtual bool IsSystemCaller() const = 0;
252 :
253 : private:
254 : // A primary context owns the mRuntime. Non-main-thread contexts should always
255 : // be primary. On the main thread, the primary context should be the first one
256 : // created and the last one destroyed. Non-primary contexts are used for
257 : // cooperatively scheduled threads.
258 : bool mIsPrimaryContext;
259 :
260 : CycleCollectedJSRuntime* mRuntime;
261 :
262 : JSContext* mJSContext;
263 :
264 : nsCOMPtr<dom::Exception> mPendingException;
265 : nsThread* mOwningThread; // Manual refcounting to avoid include hell.
266 :
267 0 : struct PendingIDBTransactionData
268 : {
269 : nsCOMPtr<nsIRunnable> mTransaction;
270 : uint32_t mRecursionDepth;
271 : };
272 :
273 : nsTArray<nsCOMPtr<nsIRunnable>> mStableStateEvents;
274 : nsTArray<PendingIDBTransactionData> mPendingIDBTransactions;
275 : uint32_t mBaseRecursionDepth;
276 : bool mDoingStableStates;
277 :
278 : // If set to none 0, microtasks will be processed only when recursion depth
279 : // is the set value.
280 : uint32_t mTargetedMicroTaskRecursionDepth;
281 :
282 : uint32_t mMicroTaskLevel;
283 :
284 : std::queue<RefPtr<MicroTaskRunnable>> mPendingMicroTaskRunnables;
285 : std::queue<RefPtr<MicroTaskRunnable>> mDebuggerMicroTaskQueue;
286 :
287 : uint32_t mMicroTaskRecursionDepth;
288 : };
289 :
290 : class MOZ_STACK_CLASS nsAutoMicroTask
291 : {
292 : public:
293 0 : nsAutoMicroTask()
294 : {
295 0 : CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
296 443 : if (ccjs) {
297 0 : ccjs->EnterMicroTask();
298 : }
299 : }
300 : ~nsAutoMicroTask()
301 : {
302 : CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
303 : if (ccjs) {
304 : ccjs->LeaveMicroTask();
305 : }
306 : }
307 : };
308 :
309 : } // namespace mozilla
310 :
311 : #endif // mozilla_CycleCollectedJSContext_h
|