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 "jsapi.h"
17 : #include "jsfriendapi.h"
18 :
19 : #include "nsCOMPtr.h"
20 : #include "nsCycleCollectionParticipant.h"
21 : #include "nsTArray.h"
22 :
23 : class nsCycleCollectionNoteRootCallback;
24 : class nsIException;
25 : class nsIRunnable;
26 : class nsThread;
27 : class nsWrapperCache;
28 :
29 : namespace mozilla {
30 :
31 : class CycleCollectedJSRuntime;
32 :
33 : // Contains various stats about the cycle collection.
34 : struct CycleCollectorResults
35 : {
36 4 : CycleCollectorResults()
37 4 : {
38 : // Initialize here so when we increment mNumSlices the first time we're
39 : // not using uninitialized memory.
40 4 : Init();
41 4 : }
42 :
43 4 : void Init()
44 : {
45 4 : mForcedGC = false;
46 4 : mMergedZones = false;
47 4 : mAnyManual = false;
48 4 : mVisitedRefCounted = 0;
49 4 : mVisitedGCed = 0;
50 4 : mFreedRefCounted = 0;
51 4 : mFreedGCed = 0;
52 4 : mFreedJSZones = 0;
53 4 : mNumSlices = 1;
54 : // mNumSlices is initialized to one, because we call Init() after the
55 : // per-slice increment of mNumSlices has already occurred.
56 4 : }
57 :
58 : bool mForcedGC;
59 : bool mMergedZones;
60 : bool mAnyManual; // true if any slice of the CC was manually triggered, or at shutdown.
61 : uint32_t mVisitedRefCounted;
62 : uint32_t mVisitedGCed;
63 : uint32_t mFreedRefCounted;
64 : uint32_t mFreedGCed;
65 : uint32_t mFreedJSZones;
66 : uint32_t mNumSlices;
67 : };
68 :
69 : class CycleCollectedJSContext
70 : : public LinkedListElement<CycleCollectedJSContext>
71 : {
72 : friend class CycleCollectedJSRuntime;
73 :
74 : protected:
75 : CycleCollectedJSContext();
76 : virtual ~CycleCollectedJSContext();
77 :
78 : MOZ_IS_CLASS_INIT
79 : nsresult Initialize(JSRuntime* aParentRuntime,
80 : uint32_t aMaxBytes,
81 : uint32_t aMaxNurseryBytes);
82 :
83 : // See explanation in mIsPrimaryContext.
84 : MOZ_IS_CLASS_INIT
85 : nsresult InitializeNonPrimary(CycleCollectedJSContext* aPrimaryContext);
86 :
87 : virtual CycleCollectedJSRuntime* CreateRuntime(JSContext* aCx) = 0;
88 :
89 : size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
90 :
91 : std::queue<nsCOMPtr<nsIRunnable>> mPromiseMicroTaskQueue;
92 : std::queue<nsCOMPtr<nsIRunnable>> mDebuggerPromiseMicroTaskQueue;
93 :
94 : private:
95 : MOZ_IS_CLASS_INIT
96 : void InitializeCommon();
97 :
98 : static JSObject* GetIncumbentGlobalCallback(JSContext* aCx);
99 : static bool EnqueuePromiseJobCallback(JSContext* aCx,
100 : JS::HandleObject aJob,
101 : JS::HandleObject aAllocationSite,
102 : JS::HandleObject aIncumbentGlobal,
103 : void* aData);
104 : static void PromiseRejectionTrackerCallback(JSContext* aCx,
105 : JS::HandleObject aPromise,
106 : PromiseRejectionHandlingState state,
107 : void* aData);
108 :
109 : void AfterProcessMicrotask(uint32_t aRecursionDepth);
110 : public:
111 : void ProcessStableStateQueue();
112 : private:
113 : void ProcessMetastableStateQueue(uint32_t aRecursionDepth);
114 :
115 : public:
116 : enum DeferredFinalizeType {
117 : FinalizeIncrementally,
118 : FinalizeNow,
119 : };
120 :
121 28217 : CycleCollectedJSRuntime* Runtime() const
122 : {
123 28217 : MOZ_ASSERT(mRuntime);
124 28217 : return mRuntime;
125 : }
126 :
127 : already_AddRefed<nsIException> GetPendingException() const;
128 : void SetPendingException(nsIException* aException);
129 :
130 : std::queue<nsCOMPtr<nsIRunnable>>& GetPromiseMicroTaskQueue();
131 : std::queue<nsCOMPtr<nsIRunnable>>& GetDebuggerPromiseMicroTaskQueue();
132 :
133 200062 : JSContext* Context() const
134 : {
135 200062 : MOZ_ASSERT(mJSContext);
136 200062 : return mJSContext;
137 : }
138 :
139 183 : JS::RootingContext* RootingCx() const
140 : {
141 183 : MOZ_ASSERT(mJSContext);
142 183 : return JS::RootingContext::get(mJSContext);
143 : }
144 :
145 31 : bool MicroTaskCheckpointDisabled() const
146 : {
147 31 : return mDisableMicroTaskCheckpoint;
148 : }
149 :
150 62 : void DisableMicroTaskCheckpoint(bool aDisable)
151 : {
152 62 : mDisableMicroTaskCheckpoint = aDisable;
153 62 : }
154 :
155 : class MOZ_RAII AutoDisableMicroTaskCheckpoint
156 : {
157 : public:
158 31 : AutoDisableMicroTaskCheckpoint()
159 31 : : mCCJSCX(CycleCollectedJSContext::Get())
160 : {
161 31 : mOldValue = mCCJSCX->MicroTaskCheckpointDisabled();
162 31 : mCCJSCX->DisableMicroTaskCheckpoint(true);
163 31 : }
164 :
165 31 : ~AutoDisableMicroTaskCheckpoint()
166 31 : {
167 31 : mCCJSCX->DisableMicroTaskCheckpoint(mOldValue);
168 31 : }
169 :
170 : CycleCollectedJSContext* mCCJSCX;
171 : bool mOldValue;
172 : };
173 :
174 : protected:
175 0 : JSContext* MaybeContext() const { return mJSContext; }
176 :
177 : public:
178 : // nsThread entrypoints
179 1265 : virtual void BeforeProcessTask(bool aMightBlock) { };
180 : virtual void AfterProcessTask(uint32_t aRecursionDepth);
181 :
182 : // microtask processor entry point
183 : void AfterProcessMicrotask();
184 :
185 : uint32_t RecursionDepth();
186 :
187 : // Run in stable state (call through nsContentUtils)
188 : void RunInStableState(already_AddRefed<nsIRunnable>&& aRunnable);
189 : // This isn't in the spec at all yet, but this gets the behavior we want for IDB.
190 : // Runs after the current microtask completes.
191 : void RunInMetastableState(already_AddRefed<nsIRunnable>&& aRunnable);
192 :
193 : // Get the current thread's CycleCollectedJSContext. Returns null if there
194 : // isn't one.
195 : static CycleCollectedJSContext* Get();
196 :
197 : // Queue an async microtask to the current main or worker thread.
198 : virtual void DispatchToMicroTask(already_AddRefed<nsIRunnable> aRunnable);
199 :
200 : // Storage for watching rejected promises waiting for some client to
201 : // consume their rejection.
202 : // Promises in this list have been rejected in the last turn of the
203 : // event loop without the rejection being handled.
204 : // Note that this can contain nullptrs in place of promises removed because
205 : // they're consumed before it'd be reported.
206 : JS::PersistentRooted<JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>> mUncaughtRejections;
207 :
208 : // Promises in this list have previously been reported as rejected
209 : // (because they were in the above list), but the rejection was handled
210 : // in the last turn of the event loop.
211 : JS::PersistentRooted<JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>> mConsumedRejections;
212 : nsTArray<nsCOMPtr<nsISupports /* UncaughtRejectionObserver */ >> mUncaughtRejectionObservers;
213 :
214 : private:
215 : // A primary context owns the mRuntime. Non-main-thread contexts should always
216 : // be primary. On the main thread, the primary context should be the first one
217 : // created and the last one destroyed. Non-primary contexts are used for
218 : // cooperatively scheduled threads.
219 : bool mIsPrimaryContext;
220 :
221 : CycleCollectedJSRuntime* mRuntime;
222 :
223 : JSContext* mJSContext;
224 :
225 : nsCOMPtr<nsIException> mPendingException;
226 : nsThread* mOwningThread; // Manual refcounting to avoid include hell.
227 :
228 0 : struct RunInMetastableStateData
229 : {
230 : nsCOMPtr<nsIRunnable> mRunnable;
231 : uint32_t mRecursionDepth;
232 : };
233 :
234 : nsTArray<nsCOMPtr<nsIRunnable>> mStableStateEvents;
235 : nsTArray<RunInMetastableStateData> mMetastableStateEvents;
236 : uint32_t mBaseRecursionDepth;
237 : bool mDoingStableStates;
238 :
239 : bool mDisableMicroTaskCheckpoint;
240 : };
241 :
242 : } // namespace mozilla
243 :
244 : #endif // mozilla_CycleCollectedJSContext_h
|