Bug 1842773 - Part 16: Replace TypedArrayObject with FixedLengthTypedArrayObject...
[gecko.git] / xpcom / base / CycleCollectedJSRuntime.h
blob6f03d3ee9968b59a5c9c629700df260cd56e1569
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 "nsTHashMap.h"
23 #include "nsHashKeys.h"
24 #include "nsStringFwd.h"
25 #include "nsTHashSet.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, HoldersRequiredForGrayMarking };
93 class Iter;
95 JSHolderMap();
96 ~JSHolderMap() { MOZ_RELEASE_ASSERT(!mHasIterator); }
98 bool Has(void* aHolder) const;
99 nsScriptObjectTracer* Get(void* aHolder) const;
100 nsScriptObjectTracer* Extract(void* aHolder);
101 void Put(void* aHolder, nsScriptObjectTracer* aTracer, JS::Zone* aZone);
103 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const;
105 private:
106 struct Entry {
107 void* mHolder;
108 nsScriptObjectTracer* mTracer;
109 #ifdef DEBUG
110 JS::Zone* mZone;
111 #endif
113 Entry();
114 Entry(void* aHolder, nsScriptObjectTracer* aTracer, JS::Zone* aZone);
117 using EntryMap = mozilla::HashMap<void*, Entry*, DefaultHasher<void*>,
118 InfallibleAllocPolicy>;
120 using EntryVector = SegmentedVector<Entry, 256, InfallibleAllocPolicy>;
122 using EntryVectorMap =
123 mozilla::HashMap<JS::Zone*, UniquePtr<EntryVector>,
124 DefaultHasher<JS::Zone*>, InfallibleAllocPolicy>;
126 class EntryVectorIter;
128 bool RemoveEntry(EntryVector& aJSHolders, Entry* aEntry);
130 // A map from a holder pointer to a pointer to an entry in a vector.
131 EntryMap mJSHolderMap;
133 // A vector of holders not associated with a particular zone or that can
134 // contain pointers to GC things in more than one zone.
135 EntryVector mAnyZoneJSHolders;
137 // A map from a zone to a vector of holders that only contain pointers to GC
138 // things in that zone.
140 // Currently this will only contain wrapper cache wrappers since these are the
141 // only holders to pass a zone parameter through to AddJSHolder.
142 EntryVectorMap mPerZoneJSHolders;
144 // Iterators can mutate the element vectors by removing stale elements. Allow
145 // at most one to exist at a time.
146 bool mHasIterator = false;
149 // An iterator over an EntryVector that skips over removed entries and removes
150 // them from the map.
151 class JSHolderMap::EntryVectorIter {
152 public:
153 EntryVectorIter(JSHolderMap& aMap, EntryVector& aVector)
154 : mHolderMap(aMap), mVector(aVector), mIter(aVector.Iter()) {
155 Settle();
158 const EntryVector& Vector() const { return mVector; }
160 bool Done() const { return mIter.Done(); }
161 const Entry& Get() const { return mIter.Get(); }
162 void Next() {
163 mIter.Next();
164 Settle();
167 operator const Entry*() const { return &Get(); }
168 const Entry* operator->() const { return &Get(); }
170 private:
171 void Settle();
172 friend class JSHolderMap::Iter;
174 JSHolderMap& mHolderMap;
175 EntryVector& mVector;
176 EntryVector::IterImpl mIter;
179 class JSHolderMap::Iter {
180 public:
181 explicit Iter(JSHolderMap& aMap, WhichHolders aWhich = AllHolders);
183 ~Iter() {
184 MOZ_RELEASE_ASSERT(mHolderMap.mHasIterator);
185 mHolderMap.mHasIterator = false;
188 bool Done() const { return mIter.Done(); }
189 const Entry& Get() const { return mIter.Get(); }
190 void Next() {
191 mIter.Next();
192 Settle();
195 // If the holders have been removed from the map while the iterator is live,
196 // then the iterator may point to a removed entry. Update the iterator to make
197 // sure it points to a valid entry or is done.
198 void UpdateForRemovals();
200 operator const Entry*() const { return &Get(); }
201 const Entry* operator->() const { return &Get(); }
203 JS::Zone* Zone() const { return mZone; }
205 private:
206 void Settle();
208 JSHolderMap& mHolderMap;
209 Vector<JS::Zone*, 1, InfallibleAllocPolicy> mZones;
210 JS::Zone* mZone = nullptr;
211 EntryVectorIter mIter;
214 class CycleCollectedJSRuntime {
215 friend class JSGCThingParticipant;
216 friend class JSZoneParticipant;
217 friend class IncrementalFinalizeRunnable;
218 friend class CycleCollectedJSContext;
220 protected:
221 CycleCollectedJSRuntime(JSContext* aMainContext);
222 virtual ~CycleCollectedJSRuntime();
224 virtual void Shutdown(JSContext* cx);
226 size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
227 void UnmarkSkippableJSHolders();
229 virtual void TraverseAdditionalNativeRoots(
230 nsCycleCollectionNoteRootCallback& aCb) {}
231 virtual void TraceAdditionalNativeGrayRoots(JSTracer* aTracer) {}
233 virtual void CustomGCCallback(JSGCStatus aStatus) {}
234 virtual void CustomOutOfMemoryCallback() {}
236 CycleCollectedJSContext* GetContext() { return mContext; }
238 private:
239 void DescribeGCThing(bool aIsMarked, JS::GCCellPtr aThing,
240 nsCycleCollectionTraversalCallback& aCb) const;
242 virtual bool DescribeCustomObjects(JSObject* aObject, const JSClass* aClasp,
243 char (&aName)[72]) const {
244 return false; // We did nothing.
247 void NoteGCThingJSChildren(JS::GCCellPtr aThing,
248 nsCycleCollectionTraversalCallback& aCb) const;
250 void NoteGCThingXPCOMChildren(const JSClass* aClasp, JSObject* aObj,
251 nsCycleCollectionTraversalCallback& aCb) const;
253 virtual bool NoteCustomGCThingXPCOMChildren(
254 const JSClass* aClasp, JSObject* aObj,
255 nsCycleCollectionTraversalCallback& aCb) const {
256 return false; // We did nothing.
259 enum TraverseSelect { TRAVERSE_CPP, TRAVERSE_FULL };
261 void TraverseGCThing(TraverseSelect aTs, JS::GCCellPtr aThing,
262 nsCycleCollectionTraversalCallback& aCb);
264 void TraverseZone(JS::Zone* aZone, nsCycleCollectionTraversalCallback& aCb);
266 static void TraverseObjectShim(void* aData, JS::GCCellPtr aThing,
267 const JS::AutoRequireNoGC& nogc);
269 void TraverseNativeRoots(nsCycleCollectionNoteRootCallback& aCb);
271 static void TraceBlackJS(JSTracer* aTracer, void* aData);
273 // Trace gray JS roots until budget is exceeded and return whether we
274 // finished.
275 static bool TraceGrayJS(JSTracer* aTracer, js::SliceBudget& budget,
276 void* aData);
278 static void GCCallback(JSContext* aContext, JSGCStatus aStatus,
279 JS::GCReason aReason, void* aData);
280 static void GCSliceCallback(JSContext* aContext, JS::GCProgress aProgress,
281 const JS::GCDescription& aDesc);
282 static void GCNurseryCollectionCallback(JSContext* aContext,
283 JS::GCNurseryProgress aProgress,
284 JS::GCReason aReason, void* data);
285 static void OutOfMemoryCallback(JSContext* aContext, void* aData);
287 static bool ContextCallback(JSContext* aCx, unsigned aOperation, void* aData);
289 static void* BeforeWaitCallback(uint8_t* aMemory);
290 static void AfterWaitCallback(void* aCookie);
292 virtual void TraceNativeBlackRoots(JSTracer* aTracer){};
294 #ifdef NS_BUILD_REFCNT_LOGGING
295 void TraceAllNativeGrayRoots(JSTracer* aTracer);
296 #endif
298 bool TraceNativeGrayRoots(JSTracer* aTracer, JSHolderMap::WhichHolders aWhich,
299 js::SliceBudget& aBudget);
300 bool TraceJSHolders(JSTracer* aTracer, JSHolderMap::Iter& aIter,
301 js::SliceBudget& aBudget);
303 public:
304 enum DeferredFinalizeType {
305 // Never finalize immediately, because it would be unsafe.
306 FinalizeLater,
307 // Finalize later if we can, but it is okay to do it immediately.
308 FinalizeIncrementally,
309 // Finalize immediately, for shutdown or testing purposes.
310 FinalizeNow,
313 void FinalizeDeferredThings(DeferredFinalizeType aType);
315 virtual void PrepareForForgetSkippable() = 0;
316 virtual void BeginCycleCollectionCallback(mozilla::CCReason aReason) = 0;
317 virtual void EndCycleCollectionCallback(CycleCollectorResults& aResults) = 0;
318 virtual void DispatchDeferredDeletion(bool aContinuation,
319 bool aPurge = false) = 0;
321 // Two conditions, JSOutOfMemory and JSLargeAllocationFailure, are noted in
322 // crash reports. Here are the values that can appear in the reports:
323 enum class OOMState : uint32_t {
324 // The condition has never happened. No entry appears in the crash report.
327 // We are currently reporting the given condition.
329 // Suppose a crash report contains "JSLargeAllocationFailure:
330 // Reporting". This means we crashed while executing memory-pressure
331 // observers, trying to shake loose some memory. The large allocation in
332 // question did not return null: it is still on the stack. Had we not
333 // crashed, it would have been retried.
334 Reporting,
336 // The condition has been reported since the last GC.
338 // If a crash report contains "JSOutOfMemory: Reported", that means a small
339 // allocation failed, and then we crashed, probably due to buggy
340 // error-handling code that ran after allocation returned null.
342 // This contrasts with "Reporting" which means that no error-handling code
343 // had executed yet.
344 Reported,
346 // The condition has happened, but a GC cycle ended since then.
348 // GC is taken as a proxy for "we've been banging on the heap a good bit
349 // now and haven't crashed; the OOM was probably handled correctly".
350 Recovered
353 const char* OOMStateToString(const OOMState aOomState) const;
355 // Returns true if OOM was reported and a new successful GC cycle hasn't
356 // occurred since.
357 bool OOMReported();
359 void SetLargeAllocationFailure(OOMState aNewState);
361 void AnnotateAndSetOutOfMemory(OOMState* aStatePtr, OOMState aNewState);
362 void OnGC(JSContext* aContext, JSGCStatus aStatus, JS::GCReason aReason);
363 void OnOutOfMemory();
364 void OnLargeAllocationFailure();
366 JSRuntime* Runtime() { return mJSRuntime; }
367 const JSRuntime* Runtime() const { return mJSRuntime; }
369 bool HasPendingIdleGCTask() const {
370 // Idle GC task associates with JSRuntime.
371 MOZ_ASSERT_IF(mHasPendingIdleGCTask, Runtime());
372 return mHasPendingIdleGCTask;
374 void SetPendingIdleGCTask() {
375 // Idle GC task associates with JSRuntime.
376 MOZ_ASSERT(Runtime());
377 mHasPendingIdleGCTask = true;
379 void ClearPendingIdleGCTask() { mHasPendingIdleGCTask = false; }
381 void RunIdleTimeGCTask() {
382 if (HasPendingIdleGCTask()) {
383 JS::MaybeRunNurseryCollection(Runtime(),
384 JS::GCReason::EAGER_NURSERY_COLLECTION);
385 ClearPendingIdleGCTask();
389 bool IsIdleGCTaskNeeded() {
390 return !HasPendingIdleGCTask() && Runtime() &&
391 JS::WantEagerMinorGC(Runtime()) != JS::GCReason::NO_REASON;
394 public:
395 void AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer,
396 JS::Zone* aZone);
397 void RemoveJSHolder(void* aHolder);
398 #ifdef DEBUG
399 void AssertNoObjectsToTrace(void* aPossibleJSHolder);
400 #endif
402 nsCycleCollectionParticipant* GCThingParticipant();
403 nsCycleCollectionParticipant* ZoneParticipant();
405 nsresult TraverseRoots(nsCycleCollectionNoteRootCallback& aCb);
406 virtual bool UsefulToMergeZones() const;
407 void FixWeakMappingGrayBits() const;
408 void CheckGrayBits() const;
409 bool AreGCGrayBitsValid() const;
410 void GarbageCollect(JS::GCOptions options, JS::GCReason aReason) const;
412 // This needs to be an nsWrapperCache, not a JSObject, because we need to know
413 // when our object gets moved. But we can't trace it (and hence update our
414 // storage), because we do not want to keep it alive. nsWrapperCache handles
415 // this for us via its "object moved" handling.
416 void NurseryWrapperAdded(nsWrapperCache* aCache);
417 void JSObjectsTenured();
419 void DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
420 DeferredFinalizeFunction aFunc, void* aThing);
421 void DeferredFinalize(nsISupports* aSupports);
423 void DumpJSHeap(FILE* aFile);
425 // Add aZone to the set of zones waiting for a GC.
426 void AddZoneWaitingForGC(JS::Zone* aZone) {
427 mZonesWaitingForGC.Insert(aZone);
430 static void OnZoneDestroyed(JS::GCContext* aGcx, JS::Zone* aZone);
432 // Prepare any zones for GC that have been passed to AddZoneWaitingForGC()
433 // since the last GC or since the last call to PrepareWaitingZonesForGC(),
434 // whichever was most recent. If there were no such zones, prepare for a
435 // full GC.
436 void PrepareWaitingZonesForGC();
438 // Get the current thread's CycleCollectedJSRuntime. Returns null if there
439 // isn't one.
440 static CycleCollectedJSRuntime* Get();
442 void SetContext(CycleCollectedJSContext* aContext);
444 #ifdef NIGHTLY_BUILD
445 bool GetRecentDevError(JSContext* aContext,
446 JS::MutableHandle<JS::Value> aError);
447 void ClearRecentDevError();
448 #endif // defined(NIGHTLY_BUILD)
450 private:
451 CycleCollectedJSContext* mContext;
453 JSGCThingParticipant mGCThingCycleCollectorGlobal;
455 JSZoneParticipant mJSZoneCycleCollectorGlobal;
457 JSRuntime* mJSRuntime;
458 bool mHasPendingIdleGCTask;
460 JS::GCSliceCallback mPrevGCSliceCallback;
462 mozilla::TimeStamp mLatestNurseryCollectionStart;
464 JSHolderMap mJSHolders;
465 Maybe<JSHolderMap::Iter> mHolderIter;
467 using DeferredFinalizerTable = nsTHashMap<DeferredFinalizeFunction, void*>;
468 DeferredFinalizerTable mDeferredFinalizerTable;
470 RefPtr<IncrementalFinalizeRunnable> mFinalizeRunnable;
472 OOMState mOutOfMemoryState;
473 OOMState mLargeAllocationFailureState;
475 static const size_t kSegmentSize = 512;
476 SegmentedVector<nsWrapperCache*, kSegmentSize, InfallibleAllocPolicy>
477 mNurseryObjects;
479 nsTHashSet<JS::Zone*> mZonesWaitingForGC;
481 struct EnvironmentPreparer : public js::ScriptEnvironmentPreparer {
482 void invoke(JS::Handle<JSObject*> global, Closure& closure) override;
484 EnvironmentPreparer mEnvironmentPreparer;
486 #ifdef DEBUG
487 bool mShutdownCalled;
488 #endif
490 #ifdef NIGHTLY_BUILD
491 // Implementation of the error interceptor.
492 // Built on nightly only to avoid any possible performance impact on release
494 struct ErrorInterceptor final : public JSErrorInterceptor {
495 virtual void interceptError(JSContext* cx,
496 JS::Handle<JS::Value> exn) override;
497 void Shutdown(JSRuntime* rt);
499 // Copy of the details of the exception.
500 // We store this rather than the exception itself to avoid dealing with
501 // complicated garbage-collection scenarios, e.g. a JSContext being killed
502 // while we still hold onto an exception thrown from it.
503 struct ErrorDetails {
504 nsString mFilename;
505 nsString mMessage;
506 nsString mStack;
507 JSExnType mType;
508 uint32_t mLine;
509 uint32_t mColumn;
512 // If we have encountered at least one developer error,
513 // the first error we have encountered. Otherwise, or
514 // if we have reset since the latest error, `None`.
515 Maybe<ErrorDetails> mThrownError;
517 ErrorInterceptor mErrorInterceptor;
519 #endif // defined(NIGHTLY_BUILD)
522 void TraceScriptHolder(nsISupports* aHolder, JSTracer* aTracer);
524 } // namespace mozilla
526 #endif // mozilla_CycleCollectedJSRuntime_h