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_CycleCollectedJSRuntime_h
8 : #define mozilla_CycleCollectedJSRuntime_h
9 :
10 : #include <queue>
11 :
12 : #include "mozilla/CycleCollectedJSContext.h"
13 : #include "mozilla/DeferredFinalize.h"
14 : #include "mozilla/LinkedList.h"
15 : #include "mozilla/mozalloc.h"
16 : #include "mozilla/MemoryReporting.h"
17 : #include "mozilla/SegmentedVector.h"
18 : #include "jsapi.h"
19 : #include "jsfriendapi.h"
20 :
21 : #include "nsCycleCollectionParticipant.h"
22 : #include "nsDataHashtable.h"
23 : #include "nsHashKeys.h"
24 : #include "nsTHashtable.h"
25 :
26 : class nsCycleCollectionNoteRootCallback;
27 : class nsIException;
28 : class nsIRunnable;
29 : class nsWrapperCache;
30 :
31 : namespace js {
32 : struct Class;
33 : } // namespace js
34 :
35 : namespace mozilla {
36 :
37 4 : class JSGCThingParticipant: public nsCycleCollectionParticipant
38 : {
39 : public:
40 : constexpr JSGCThingParticipant()
41 : : nsCycleCollectionParticipant(false) {}
42 :
43 0 : NS_IMETHOD_(void) Root(void*) override
44 : {
45 0 : MOZ_ASSERT(false, "Don't call Root on GC things");
46 : }
47 :
48 0 : NS_IMETHOD_(void) Unlink(void*) override
49 : {
50 0 : MOZ_ASSERT(false, "Don't call Unlink on GC things, as they may be dead");
51 : }
52 :
53 0 : NS_IMETHOD_(void) Unroot(void*) override
54 : {
55 0 : MOZ_ASSERT(false, "Don't call Unroot on GC things, as they may be dead");
56 : }
57 :
58 0 : NS_IMETHOD_(void) DeleteCycleCollectable(void* aPtr) override
59 : {
60 0 : MOZ_ASSERT(false, "Can't directly delete a cycle collectable GC thing");
61 : }
62 :
63 : NS_IMETHOD TraverseNative(void* aPtr, nsCycleCollectionTraversalCallback& aCb)
64 : override;
65 :
66 0 : NS_DECL_CYCLE_COLLECTION_CLASS_NAME_METHOD(JSGCThingParticipant)
67 : };
68 :
69 4 : class JSZoneParticipant : public nsCycleCollectionParticipant
70 : {
71 : public:
72 : constexpr JSZoneParticipant(): nsCycleCollectionParticipant(false)
73 : {
74 : }
75 :
76 0 : NS_IMETHOD_(void) Root(void*) override
77 : {
78 0 : MOZ_ASSERT(false, "Don't call Root on GC things");
79 : }
80 :
81 0 : NS_IMETHOD_(void) Unlink(void*) override
82 : {
83 0 : MOZ_ASSERT(false, "Don't call Unlink on GC things, as they may be dead");
84 : }
85 :
86 0 : NS_IMETHOD_(void) Unroot(void*) override
87 : {
88 0 : MOZ_ASSERT(false, "Don't call Unroot on GC things, as they may be dead");
89 : }
90 :
91 0 : NS_IMETHOD_(void) DeleteCycleCollectable(void*) override
92 : {
93 0 : MOZ_ASSERT(false, "Can't directly delete a cycle collectable GC thing");
94 : }
95 :
96 : NS_IMETHOD TraverseNative(void* aPtr, nsCycleCollectionTraversalCallback& aCb)
97 : override;
98 :
99 0 : NS_DECL_CYCLE_COLLECTION_CLASS_NAME_METHOD(JSZoneParticipant)
100 : };
101 :
102 : class IncrementalFinalizeRunnable;
103 :
104 : struct JSHolderInfo
105 : {
106 : void* mHolder;
107 : nsScriptObjectTracer* mTracer;
108 : };
109 :
110 : class CycleCollectedJSRuntime
111 : {
112 : friend class JSGCThingParticipant;
113 : friend class JSZoneParticipant;
114 : friend class IncrementalFinalizeRunnable;
115 : friend class CycleCollectedJSContext;
116 : protected:
117 : CycleCollectedJSRuntime(JSContext* aMainContext);
118 : virtual ~CycleCollectedJSRuntime();
119 :
120 : virtual void Shutdown(JSContext* cx);
121 :
122 : size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
123 : void UnmarkSkippableJSHolders();
124 :
125 : virtual void
126 0 : TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback& aCb) {}
127 0 : virtual void TraceAdditionalNativeGrayRoots(JSTracer* aTracer) {}
128 :
129 0 : virtual void CustomGCCallback(JSGCStatus aStatus) {}
130 0 : virtual void CustomOutOfMemoryCallback() {}
131 :
132 1 : LinkedList<CycleCollectedJSContext>& Contexts()
133 : {
134 1 : return mContexts;
135 : }
136 :
137 : private:
138 : void
139 : DescribeGCThing(bool aIsMarked, JS::GCCellPtr aThing,
140 : nsCycleCollectionTraversalCallback& aCb) const;
141 :
142 : virtual bool
143 0 : DescribeCustomObjects(JSObject* aObject, const js::Class* aClasp,
144 : char (&aName)[72]) const
145 : {
146 0 : return false; // We did nothing.
147 : }
148 :
149 : void
150 : NoteGCThingJSChildren(JS::GCCellPtr aThing,
151 : nsCycleCollectionTraversalCallback& aCb) const;
152 :
153 : void
154 : NoteGCThingXPCOMChildren(const js::Class* aClasp, JSObject* aObj,
155 : nsCycleCollectionTraversalCallback& aCb) const;
156 :
157 : virtual bool
158 0 : NoteCustomGCThingXPCOMChildren(const js::Class* aClasp, JSObject* aObj,
159 : nsCycleCollectionTraversalCallback& aCb) const
160 : {
161 0 : return false; // We did nothing.
162 : }
163 :
164 : enum TraverseSelect {
165 : TRAVERSE_CPP,
166 : TRAVERSE_FULL
167 : };
168 :
169 : void
170 : TraverseGCThing(TraverseSelect aTs, JS::GCCellPtr aThing,
171 : nsCycleCollectionTraversalCallback& aCb);
172 :
173 : void
174 : TraverseZone(JS::Zone* aZone, nsCycleCollectionTraversalCallback& aCb);
175 :
176 : static void
177 : TraverseObjectShim(void* aData, JS::GCCellPtr aThing);
178 :
179 : void TraverseNativeRoots(nsCycleCollectionNoteRootCallback& aCb);
180 :
181 : static void TraceBlackJS(JSTracer* aTracer, void* aData);
182 : static void TraceGrayJS(JSTracer* aTracer, void* aData);
183 : static void GCCallback(JSContext* aContext, JSGCStatus aStatus, void* aData);
184 : static void GCSliceCallback(JSContext* aContext, JS::GCProgress aProgress,
185 : const JS::GCDescription& aDesc);
186 : static void GCNurseryCollectionCallback(JSContext* aContext,
187 : JS::GCNurseryProgress aProgress,
188 : JS::gcreason::Reason aReason);
189 : static void OutOfMemoryCallback(JSContext* aContext, void* aData);
190 : /**
191 : * Callback for reporting external string memory.
192 : */
193 : static size_t SizeofExternalStringCallback(JSString* aStr,
194 : mozilla::MallocSizeOf aMallocSizeOf);
195 :
196 : static bool ContextCallback(JSContext* aCx, unsigned aOperation,
197 : void* aData);
198 :
199 0 : virtual void TraceNativeBlackRoots(JSTracer* aTracer) { };
200 : void TraceNativeGrayRoots(JSTracer* aTracer);
201 :
202 : public:
203 : void FinalizeDeferredThings(CycleCollectedJSContext::DeferredFinalizeType aType);
204 :
205 : virtual void PrepareForForgetSkippable() = 0;
206 : virtual void BeginCycleCollectionCallback() = 0;
207 : virtual void EndCycleCollectionCallback(CycleCollectorResults& aResults) = 0;
208 : virtual void DispatchDeferredDeletion(bool aContinuation, bool aPurge = false) = 0;
209 :
210 : // Two conditions, JSOutOfMemory and JSLargeAllocationFailure, are noted in
211 : // crash reports. Here are the values that can appear in the reports:
212 : enum class OOMState : uint32_t {
213 : // The condition has never happened. No entry appears in the crash report.
214 : OK,
215 :
216 : // We are currently reporting the given condition.
217 : //
218 : // Suppose a crash report contains "JSLargeAllocationFailure:
219 : // Reporting". This means we crashed while executing memory-pressure
220 : // observers, trying to shake loose some memory. The large allocation in
221 : // question did not return null: it is still on the stack. Had we not
222 : // crashed, it would have been retried.
223 : Reporting,
224 :
225 : // The condition has been reported since the last GC.
226 : //
227 : // If a crash report contains "JSOutOfMemory: Reported", that means a small
228 : // allocation failed, and then we crashed, probably due to buggy
229 : // error-handling code that ran after allocation returned null.
230 : //
231 : // This contrasts with "Reporting" which means that no error-handling code
232 : // had executed yet.
233 : Reported,
234 :
235 : // The condition has happened, but a GC cycle ended since then.
236 : //
237 : // GC is taken as a proxy for "we've been banging on the heap a good bit
238 : // now and haven't crashed; the OOM was probably handled correctly".
239 : Recovered
240 : };
241 :
242 : void SetLargeAllocationFailure(OOMState aNewState);
243 :
244 : void AnnotateAndSetOutOfMemory(OOMState* aStatePtr, OOMState aNewState);
245 : void OnGC(JSContext* aContext, JSGCStatus aStatus);
246 : void OnOutOfMemory();
247 : void OnLargeAllocationFailure();
248 :
249 1788 : JSRuntime* Runtime() { return mJSRuntime; }
250 :
251 : public:
252 : void AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer);
253 : void RemoveJSHolder(void* aHolder);
254 : #ifdef DEBUG
255 : bool IsJSHolder(void* aHolder);
256 : void AssertNoObjectsToTrace(void* aPossibleJSHolder);
257 : #endif
258 :
259 : nsCycleCollectionParticipant* GCThingParticipant();
260 : nsCycleCollectionParticipant* ZoneParticipant();
261 :
262 : nsresult TraverseRoots(nsCycleCollectionNoteRootCallback& aCb);
263 : virtual bool UsefulToMergeZones() const;
264 : void FixWeakMappingGrayBits() const;
265 : void CheckGrayBits() const;
266 : bool AreGCGrayBitsValid() const;
267 : void GarbageCollect(uint32_t aReason) const;
268 :
269 : // This needs to be an nsWrapperCache, not a JSObject, because we need to know
270 : // when our object gets moved. But we can't trace it (and hence update our
271 : // storage), because we do not want to keep it alive. nsWrapperCache handles
272 : // this for us via its "object moved" handling.
273 : void NurseryWrapperAdded(nsWrapperCache* aCache);
274 : void NurseryWrapperPreserved(JSObject* aWrapper);
275 : void JSObjectsTenured();
276 :
277 : void DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
278 : DeferredFinalizeFunction aFunc,
279 : void* aThing);
280 : void DeferredFinalize(nsISupports* aSupports);
281 :
282 : void DumpJSHeap(FILE* aFile);
283 :
284 : // Add aZone to the set of zones waiting for a GC.
285 11 : void AddZoneWaitingForGC(JS::Zone* aZone)
286 : {
287 11 : mZonesWaitingForGC.PutEntry(aZone);
288 11 : }
289 :
290 : // Prepare any zones for GC that have been passed to AddZoneWaitingForGC()
291 : // since the last GC or since the last call to PrepareWaitingZonesForGC(),
292 : // whichever was most recent. If there were no such zones, prepare for a
293 : // full GC.
294 : void PrepareWaitingZonesForGC();
295 :
296 : // Get the current thread's CycleCollectedJSRuntime. Returns null if there
297 : // isn't one.
298 : static CycleCollectedJSRuntime* Get();
299 :
300 : void AddContext(CycleCollectedJSContext* aContext);
301 : void RemoveContext(CycleCollectedJSContext* aContext);
302 :
303 : private:
304 : LinkedList<CycleCollectedJSContext> mContexts;
305 :
306 : JSGCThingParticipant mGCThingCycleCollectorGlobal;
307 :
308 : JSZoneParticipant mJSZoneCycleCollectorGlobal;
309 :
310 : JSRuntime* mJSRuntime;
311 :
312 : JS::GCSliceCallback mPrevGCSliceCallback;
313 : JS::GCNurseryCollectionCallback mPrevGCNurseryCollectionCallback;
314 :
315 : mozilla::TimeStamp mLatestNurseryCollectionStart;
316 :
317 : SegmentedVector<JSHolderInfo, 1024, InfallibleAllocPolicy> mJSHolders;
318 : nsDataHashtable<nsPtrHashKey<void>, JSHolderInfo*> mJSHolderMap;
319 :
320 : typedef nsDataHashtable<nsFuncPtrHashKey<DeferredFinalizeFunction>, void*>
321 : DeferredFinalizerTable;
322 : DeferredFinalizerTable mDeferredFinalizerTable;
323 :
324 : RefPtr<IncrementalFinalizeRunnable> mFinalizeRunnable;
325 :
326 : OOMState mOutOfMemoryState;
327 : OOMState mLargeAllocationFailureState;
328 :
329 : static const size_t kSegmentSize = 512;
330 : SegmentedVector<nsWrapperCache*, kSegmentSize, InfallibleAllocPolicy>
331 : mNurseryObjects;
332 : SegmentedVector<JS::PersistentRooted<JSObject*>, kSegmentSize,
333 : InfallibleAllocPolicy>
334 : mPreservedNurseryObjects;
335 :
336 : nsTHashtable<nsPtrHashKey<JS::Zone>> mZonesWaitingForGC;
337 :
338 4 : struct EnvironmentPreparer : public js::ScriptEnvironmentPreparer {
339 : void invoke(JS::HandleObject scope, Closure& closure) override;
340 : };
341 : EnvironmentPreparer mEnvironmentPreparer;
342 : };
343 :
344 : void TraceScriptHolder(nsISupports* aHolder, JSTracer* aTracer);
345 :
346 : // Returns true if the JS::TraceKind is one the cycle collector cares about.
347 2592 : inline bool AddToCCKind(JS::TraceKind aKind)
348 : {
349 0 : return aKind == JS::TraceKind::Object ||
350 0 : aKind == JS::TraceKind::Script ||
351 2592 : aKind == JS::TraceKind::Scope ||
352 2592 : aKind == JS::TraceKind::RegExpShared;
353 : }
354 :
355 : bool
356 : GetBuildId(JS::BuildIdCharVector* aBuildID);
357 :
358 : } // namespace mozilla
359 :
360 : #endif // mozilla_CycleCollectedJSRuntime_h
|