Bug 1686838 [wpt PR 27194] - [webcodecs] Deprecate VideoFrame.destroy()., a=testonly
[gecko.git] / xpcom / base / CycleCollectedJSRuntime.h
blobacfb43eaef7eab740ef9f9a6178a7413d3ea5731
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_CycleCollectedJSRuntime_h
8 #define mozilla_CycleCollectedJSRuntime_h
10 #include "mozilla/CycleCollectedJSContext.h"
11 #include "mozilla/DeferredFinalize.h"
12 #include "mozilla/HashTable.h"
13 #include "mozilla/Maybe.h"
14 #include "mozilla/MemoryReporting.h"
15 #include "mozilla/RefPtr.h"
16 #include "mozilla/SegmentedVector.h"
17 #include "jsapi.h"
18 #include "jsfriendapi.h"
19 #include "js/TypeDecls.h"
21 #include "nsCycleCollectionParticipant.h"
22 #include "nsDataHashtable.h"
23 #include "nsHashKeys.h"
24 #include "nsStringFwd.h"
25 #include "nsTHashtable.h"
27 class nsCycleCollectionNoteRootCallback;
28 class nsIException;
29 class nsWrapperCache;
31 namespace mozilla {
33 class JSGCThingParticipant : public nsCycleCollectionParticipant {
34 public:
35 constexpr JSGCThingParticipant() : nsCycleCollectionParticipant(false) {}
37 NS_IMETHOD_(void) Root(void*) override {
38 MOZ_ASSERT(false, "Don't call Root on GC things");
41 NS_IMETHOD_(void) Unlink(void*) override {
42 MOZ_ASSERT(false, "Don't call Unlink on GC things, as they may be dead");
45 NS_IMETHOD_(void) Unroot(void*) override {
46 MOZ_ASSERT(false, "Don't call Unroot on GC things, as they may be dead");
49 NS_IMETHOD_(void) DeleteCycleCollectable(void* aPtr) override {
50 MOZ_ASSERT(false, "Can't directly delete a cycle collectable GC thing");
53 NS_IMETHOD TraverseNative(void* aPtr,
54 nsCycleCollectionTraversalCallback& aCb) override;
56 NS_DECL_CYCLE_COLLECTION_CLASS_NAME_METHOD(JSGCThingParticipant)
59 class JSZoneParticipant : public nsCycleCollectionParticipant {
60 public:
61 constexpr JSZoneParticipant() : nsCycleCollectionParticipant(false) {}
63 NS_IMETHOD_(void) Root(void*) override {
64 MOZ_ASSERT(false, "Don't call Root on GC things");
67 NS_IMETHOD_(void) Unlink(void*) override {
68 MOZ_ASSERT(false, "Don't call Unlink on GC things, as they may be dead");
71 NS_IMETHOD_(void) Unroot(void*) override {
72 MOZ_ASSERT(false, "Don't call Unroot on GC things, as they may be dead");
75 NS_IMETHOD_(void) DeleteCycleCollectable(void*) override {
76 MOZ_ASSERT(false, "Can't directly delete a cycle collectable GC thing");
79 NS_IMETHOD TraverseNative(void* aPtr,
80 nsCycleCollectionTraversalCallback& aCb) override;
82 NS_DECL_CYCLE_COLLECTION_CLASS_NAME_METHOD(JSZoneParticipant)
85 class IncrementalFinalizeRunnable;
87 // A map from JS holders to tracer objects, where the values are stored in
88 // SegmentedVector to speed up iteration.
89 class JSHolderMap {
90 public:
91 enum WhichHolders { AllHolders, HoldersInCollectingZones };
93 JSHolderMap();
95 // Call functor |f| for each holder.
96 template <typename F>
97 void ForEach(F&& f, WhichHolders aWhich = AllHolders);
99 bool Has(void* aHolder) const;
100 nsScriptObjectTracer* Get(void* aHolder) const;
101 nsScriptObjectTracer* GetAndRemove(void* aHolder);
102 void Put(void* aHolder, nsScriptObjectTracer* aTracer, JS::Zone* aZone);
104 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const;
106 private:
107 struct Entry {
108 void* mHolder;
109 nsScriptObjectTracer* mTracer;
110 #ifdef DEBUG
111 JS::Zone* mZone;
112 #endif
114 Entry();
115 Entry(void* aHolder, nsScriptObjectTracer* aTracer, JS::Zone* aZone);
118 using EntryMap = mozilla::HashMap<void*, Entry*, DefaultHasher<void*>,
119 InfallibleAllocPolicy>;
121 using EntryVector = SegmentedVector<Entry, 256, InfallibleAllocPolicy>;
123 using EntryVectorMap =
124 mozilla::HashMap<JS::Zone*, UniquePtr<EntryVector>,
125 DefaultHasher<JS::Zone*>, InfallibleAllocPolicy>;
127 template <typename F>
128 void ForEach(EntryVector& aJSHolders, const F& f, JS::Zone* aZone);
130 bool RemoveEntry(EntryVector& aJSHolders, Entry* aEntry);
132 // A map from a holder pointer to a pointer to an entry in a vector.
133 EntryMap mJSHolderMap;
135 // A vector of holders not associated with a particular zone or that can
136 // contain pointers to GC things in more than one zone.
137 EntryVector mAnyZoneJSHolders;
139 // A map from a zone to a vector of holders that only contain pointers to GC
140 // things in that zone.
142 // Currently this will only contain wrapper cache wrappers since these are the
143 // only holders to pass a zone parameter through to AddJSHolder.
144 EntryVectorMap mPerZoneJSHolders;
147 class CycleCollectedJSRuntime {
148 friend class JSGCThingParticipant;
149 friend class JSZoneParticipant;
150 friend class IncrementalFinalizeRunnable;
151 friend class CycleCollectedJSContext;
153 protected:
154 CycleCollectedJSRuntime(JSContext* aMainContext);
155 virtual ~CycleCollectedJSRuntime();
157 virtual void Shutdown(JSContext* cx);
159 size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
160 void UnmarkSkippableJSHolders();
162 virtual void TraverseAdditionalNativeRoots(
163 nsCycleCollectionNoteRootCallback& aCb) {}
164 virtual void TraceAdditionalNativeGrayRoots(JSTracer* aTracer) {}
166 virtual void CustomGCCallback(JSGCStatus aStatus) {}
167 virtual void CustomOutOfMemoryCallback() {}
169 CycleCollectedJSContext* GetContext() { return mContext; }
171 private:
172 void DescribeGCThing(bool aIsMarked, JS::GCCellPtr aThing,
173 nsCycleCollectionTraversalCallback& aCb) const;
175 virtual bool DescribeCustomObjects(JSObject* aObject, const JSClass* aClasp,
176 char (&aName)[72]) const {
177 return false; // We did nothing.
180 void NoteGCThingJSChildren(JS::GCCellPtr aThing,
181 nsCycleCollectionTraversalCallback& aCb) const;
183 void NoteGCThingXPCOMChildren(const JSClass* aClasp, JSObject* aObj,
184 nsCycleCollectionTraversalCallback& aCb) const;
186 virtual bool NoteCustomGCThingXPCOMChildren(
187 const JSClass* aClasp, JSObject* aObj,
188 nsCycleCollectionTraversalCallback& aCb) const {
189 return false; // We did nothing.
192 enum TraverseSelect { TRAVERSE_CPP, TRAVERSE_FULL };
194 void TraverseGCThing(TraverseSelect aTs, JS::GCCellPtr aThing,
195 nsCycleCollectionTraversalCallback& aCb);
197 void TraverseZone(JS::Zone* aZone, nsCycleCollectionTraversalCallback& aCb);
199 static void TraverseObjectShim(void* aData, JS::GCCellPtr aThing,
200 const JS::AutoRequireNoGC& nogc);
202 void TraverseNativeRoots(nsCycleCollectionNoteRootCallback& aCb);
204 static void TraceBlackJS(JSTracer* aTracer, void* aData);
205 static void TraceGrayJS(JSTracer* aTracer, void* aData);
206 static void GCCallback(JSContext* aContext, JSGCStatus aStatus,
207 JS::GCReason aReason, void* aData);
208 static void GCSliceCallback(JSContext* aContext, JS::GCProgress aProgress,
209 const JS::GCDescription& aDesc);
210 static void GCNurseryCollectionCallback(JSContext* aContext,
211 JS::GCNurseryProgress aProgress,
212 JS::GCReason aReason);
213 static void OutOfMemoryCallback(JSContext* aContext, void* aData);
215 static bool ContextCallback(JSContext* aCx, unsigned aOperation, void* aData);
217 static void* BeforeWaitCallback(uint8_t* aMemory);
218 static void AfterWaitCallback(void* aCookie);
220 virtual void TraceNativeBlackRoots(JSTracer* aTracer){};
221 void TraceNativeGrayRoots(JSTracer* aTracer,
222 JSHolderMap::WhichHolders aWhich);
224 public:
225 void FinalizeDeferredThings(
226 CycleCollectedJSContext::DeferredFinalizeType aType);
228 virtual void PrepareForForgetSkippable() = 0;
229 virtual void BeginCycleCollectionCallback() = 0;
230 virtual void EndCycleCollectionCallback(CycleCollectorResults& aResults) = 0;
231 virtual void DispatchDeferredDeletion(bool aContinuation,
232 bool aPurge = false) = 0;
234 // Two conditions, JSOutOfMemory and JSLargeAllocationFailure, are noted in
235 // crash reports. Here are the values that can appear in the reports:
236 enum class OOMState : uint32_t {
237 // The condition has never happened. No entry appears in the crash report.
240 // We are currently reporting the given condition.
242 // Suppose a crash report contains "JSLargeAllocationFailure:
243 // Reporting". This means we crashed while executing memory-pressure
244 // observers, trying to shake loose some memory. The large allocation in
245 // question did not return null: it is still on the stack. Had we not
246 // crashed, it would have been retried.
247 Reporting,
249 // The condition has been reported since the last GC.
251 // If a crash report contains "JSOutOfMemory: Reported", that means a small
252 // allocation failed, and then we crashed, probably due to buggy
253 // error-handling code that ran after allocation returned null.
255 // This contrasts with "Reporting" which means that no error-handling code
256 // had executed yet.
257 Reported,
259 // The condition has happened, but a GC cycle ended since then.
261 // GC is taken as a proxy for "we've been banging on the heap a good bit
262 // now and haven't crashed; the OOM was probably handled correctly".
263 Recovered
266 const char* OOMStateToString(const OOMState aOomState) const;
268 void SetLargeAllocationFailure(OOMState aNewState);
270 void AnnotateAndSetOutOfMemory(OOMState* aStatePtr, OOMState aNewState);
271 void OnGC(JSContext* aContext, JSGCStatus aStatus, JS::GCReason aReason);
272 void OnOutOfMemory();
273 void OnLargeAllocationFailure();
275 JSRuntime* Runtime() { return mJSRuntime; }
276 const JSRuntime* Runtime() const { return mJSRuntime; }
278 bool HasPendingIdleGCTask() const {
279 // Idle GC task associates with JSRuntime.
280 MOZ_ASSERT_IF(mHasPendingIdleGCTask, Runtime());
281 return mHasPendingIdleGCTask;
283 void SetPendingIdleGCTask() {
284 // Idle GC task associates with JSRuntime.
285 MOZ_ASSERT(Runtime());
286 mHasPendingIdleGCTask = true;
288 void ClearPendingIdleGCTask() { mHasPendingIdleGCTask = false; }
290 void RunIdleTimeGCTask() {
291 if (HasPendingIdleGCTask()) {
292 JS::RunIdleTimeGCTask(Runtime());
293 ClearPendingIdleGCTask();
297 bool IsIdleGCTaskNeeded() {
298 return !HasPendingIdleGCTask() && Runtime() &&
299 JS::IsIdleGCTaskNeeded(Runtime());
302 public:
303 void AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer,
304 JS::Zone* aZone);
305 void RemoveJSHolder(void* aHolder);
306 #ifdef DEBUG
307 void AssertNoObjectsToTrace(void* aPossibleJSHolder);
308 #endif
310 nsCycleCollectionParticipant* GCThingParticipant();
311 nsCycleCollectionParticipant* ZoneParticipant();
313 nsresult TraverseRoots(nsCycleCollectionNoteRootCallback& aCb);
314 virtual bool UsefulToMergeZones() const;
315 void FixWeakMappingGrayBits() const;
316 void CheckGrayBits() const;
317 bool AreGCGrayBitsValid() const;
318 void GarbageCollect(JS::GCReason aReason) const;
320 // This needs to be an nsWrapperCache, not a JSObject, because we need to know
321 // when our object gets moved. But we can't trace it (and hence update our
322 // storage), because we do not want to keep it alive. nsWrapperCache handles
323 // this for us via its "object moved" handling.
324 void NurseryWrapperAdded(nsWrapperCache* aCache);
325 void NurseryWrapperPreserved(JSObject* aWrapper);
326 void JSObjectsTenured();
328 void DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
329 DeferredFinalizeFunction aFunc, void* aThing);
330 void DeferredFinalize(nsISupports* aSupports);
332 void DumpJSHeap(FILE* aFile);
334 // Add aZone to the set of zones waiting for a GC.
335 void AddZoneWaitingForGC(JS::Zone* aZone) {
336 mZonesWaitingForGC.PutEntry(aZone);
339 static void OnZoneDestroyed(JSFreeOp* aFop, JS::Zone* aZone);
341 // Prepare any zones for GC that have been passed to AddZoneWaitingForGC()
342 // since the last GC or since the last call to PrepareWaitingZonesForGC(),
343 // whichever was most recent. If there were no such zones, prepare for a
344 // full GC.
345 void PrepareWaitingZonesForGC();
347 // Get the current thread's CycleCollectedJSRuntime. Returns null if there
348 // isn't one.
349 static CycleCollectedJSRuntime* Get();
351 void SetContext(CycleCollectedJSContext* aContext);
353 #ifdef NIGHTLY_BUILD
354 bool GetRecentDevError(JSContext* aContext,
355 JS::MutableHandle<JS::Value> aError);
356 void ClearRecentDevError();
357 #endif // defined(NIGHTLY_BUILD)
359 private:
360 CycleCollectedJSContext* mContext;
362 JSGCThingParticipant mGCThingCycleCollectorGlobal;
364 JSZoneParticipant mJSZoneCycleCollectorGlobal;
366 JSRuntime* mJSRuntime;
367 bool mHasPendingIdleGCTask;
369 JS::GCSliceCallback mPrevGCSliceCallback;
370 JS::GCNurseryCollectionCallback mPrevGCNurseryCollectionCallback;
372 mozilla::TimeStamp mLatestNurseryCollectionStart;
374 JSHolderMap mJSHolders;
376 typedef nsDataHashtable<nsFuncPtrHashKey<DeferredFinalizeFunction>, void*>
377 DeferredFinalizerTable;
378 DeferredFinalizerTable mDeferredFinalizerTable;
380 RefPtr<IncrementalFinalizeRunnable> mFinalizeRunnable;
382 OOMState mOutOfMemoryState;
383 OOMState mLargeAllocationFailureState;
385 static const size_t kSegmentSize = 512;
386 SegmentedVector<nsWrapperCache*, kSegmentSize, InfallibleAllocPolicy>
387 mNurseryObjects;
388 SegmentedVector<JS::PersistentRooted<JSObject*>, kSegmentSize,
389 InfallibleAllocPolicy>
390 mPreservedNurseryObjects;
392 nsTHashtable<nsPtrHashKey<JS::Zone>> mZonesWaitingForGC;
394 struct EnvironmentPreparer : public js::ScriptEnvironmentPreparer {
395 void invoke(JS::HandleObject global, Closure& closure) override;
397 EnvironmentPreparer mEnvironmentPreparer;
399 #ifdef DEBUG
400 bool mShutdownCalled;
401 #endif
403 #ifdef NIGHTLY_BUILD
404 // Implementation of the error interceptor.
405 // Built on nightly only to avoid any possible performance impact on release
407 struct ErrorInterceptor final : public JSErrorInterceptor {
408 virtual void interceptError(JSContext* cx, JS::HandleValue exn) override;
409 void Shutdown(JSRuntime* rt);
411 // Copy of the details of the exception.
412 // We store this rather than the exception itself to avoid dealing with
413 // complicated garbage-collection scenarios, e.g. a JSContext being killed
414 // while we still hold onto an exception thrown from it.
415 struct ErrorDetails {
416 nsString mFilename;
417 nsString mMessage;
418 nsString mStack;
419 JSExnType mType;
420 uint32_t mLine;
421 uint32_t mColumn;
424 // If we have encountered at least one developer error,
425 // the first error we have encountered. Otherwise, or
426 // if we have reset since the latest error, `None`.
427 Maybe<ErrorDetails> mThrownError;
429 ErrorInterceptor mErrorInterceptor;
431 #endif // defined(NIGHTLY_BUILD)
434 void TraceScriptHolder(nsISupports* aHolder, JSTracer* aTracer);
436 } // namespace mozilla
438 #endif // mozilla_CycleCollectedJSRuntime_h