diff options
Diffstat (limited to 'js/public/GCAPI.h')
-rw-r--r-- | js/public/GCAPI.h | 1182 |
1 files changed, 1182 insertions, 0 deletions
diff --git a/js/public/GCAPI.h b/js/public/GCAPI.h new file mode 100644 index 0000000000..e2211f13c9 --- /dev/null +++ b/js/public/GCAPI.h @@ -0,0 +1,1182 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * High-level interface to the JS garbage collector. + */ + +#ifndef js_GCAPI_h +#define js_GCAPI_h + +#include "mozilla/TimeStamp.h" +#include "mozilla/Vector.h" + +#include "js/GCAnnotations.h" +#include "js/TypeDecls.h" +#include "js/UniquePtr.h" +#include "js/Utility.h" + +class JS_PUBLIC_API JSTracer; + +namespace js { +namespace gc { +class GCRuntime; +} // namespace gc +namespace gcstats { +struct Statistics; +} // namespace gcstats +} // namespace js + +/** + * Kinds of js_GC invocation. + */ +typedef enum JSGCInvocationKind { + /* Normal invocation. */ + GC_NORMAL = 0, + + /* Minimize GC triggers and release empty GC chunks right away. */ + GC_SHRINK = 1 +} JSGCInvocationKind; + +typedef enum JSGCParamKey { + /** + * Maximum nominal heap before last ditch GC. + * + * Soft limit on the number of bytes we are allowed to allocate in the GC + * heap. Attempts to allocate gcthings over this limit will return null and + * subsequently invoke the standard OOM machinery, independent of available + * physical memory. + * + * Pref: javascript.options.mem.max + * Default: 0xffffffff + */ + JSGC_MAX_BYTES = 0, + + /** + * Maximum size of the generational GC nurseries. + * + * This will be rounded to the nearest gc::ChunkSize. + * + * Pref: javascript.options.mem.nursery.max_kb + * Default: JS::DefaultNurseryMaxBytes + */ + JSGC_MAX_NURSERY_BYTES = 2, + + /** Amount of bytes allocated by the GC. */ + JSGC_BYTES = 3, + + /** Number of times GC has been invoked. Includes both major and minor GC. */ + JSGC_NUMBER = 4, + + /** + * Whether incremental GC is enabled. If not, GC will always run to + * completion. + * + * prefs: javascript.options.mem.gc_incremental. + * Default: false + */ + JSGC_INCREMENTAL_GC_ENABLED = 5, + + /** + * Whether per-zone GC is enabled. If not, all zones are collected every time. + * + * prefs: javascript.options.mem.gc_per_zone + * Default: false + */ + JSGC_PER_ZONE_GC_ENABLED = 6, + + /** Number of cached empty GC chunks. */ + JSGC_UNUSED_CHUNKS = 7, + + /** Total number of allocated GC chunks. */ + JSGC_TOTAL_CHUNKS = 8, + + /** + * Max milliseconds to spend in an incremental GC slice. + * + * Pref: javascript.options.mem.gc_incremental_slice_ms + * Default: DefaultTimeBudgetMS. + */ + JSGC_SLICE_TIME_BUDGET_MS = 9, + + /** + * Maximum size the GC mark stack can grow to. + * + * Pref: none + * Default: MarkStack::DefaultCapacity + */ + JSGC_MARK_STACK_LIMIT = 10, + + /** + * The "do we collect?" decision depends on various parameters and can be + * summarised as: + * + * ZoneSize > Max(ThresholdBase, LastSize) * GrowthFactor * ThresholdFactor + * + * Where + * ZoneSize: Current size of this zone. + * LastSize: Heap size immediately after the most recent collection. + * ThresholdBase: The JSGC_ALLOCATION_THRESHOLD parameter + * GrowthFactor: A number above 1, calculated based on some of the + * following parameters. + * See computeZoneHeapGrowthFactorForHeapSize() in GC.cpp + * ThresholdFactor: 1.0 to trigger an incremental collections or between + * JSGC_SMALL_HEAP_INCREMENTAL_LIMIT and + * JSGC_LARGE_HEAP_INCREMENTAL_LIMIT to trigger a + * non-incremental collection. + * + * The RHS of the equation above is calculated and sets + * zone->gcHeapThreshold.bytes(). When gcHeapSize.bytes() exeeds + * gcHeapThreshold.bytes() for a zone, the zone may be scheduled for a GC. + */ + + /** + * GCs less than this far apart in milliseconds will be considered + * 'high-frequency GCs'. + * + * Pref: javascript.options.mem.gc_high_frequency_time_limit_ms + * Default: HighFrequencyThreshold + */ + JSGC_HIGH_FREQUENCY_TIME_LIMIT = 11, + + /** + * Upper limit for classifying a heap as small (MB). + * + * Dynamic heap growth thresholds are based on whether the heap is small, + * medium or large. Heaps smaller than this size are classified as small; + * larger heaps are classified as medium or large. + * + * Pref: javascript.options.mem.gc_small_heap_size_max_mb + * Default: SmallHeapSizeMaxBytes + */ + JSGC_SMALL_HEAP_SIZE_MAX = 12, + + /** + * Lower limit for classifying a heap as large (MB). + * + * Dynamic heap growth thresholds are based on whether the heap is small, + * medium or large. Heaps larger than this size are classified as large; + * smaller heaps are classified as small or medium. + * + * Pref: javascript.options.mem.gc_large_heap_size_min_mb + * Default: LargeHeapSizeMinBytes + */ + JSGC_LARGE_HEAP_SIZE_MIN = 13, + + /** + * Heap growth factor for small heaps in the high-frequency GC state. + * + * Pref: javascript.options.mem.gc_high_frequency_small_heap_growth + * Default: HighFrequencySmallHeapGrowth + */ + JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH = 14, + + /** + * Heap growth factor for large heaps in the high-frequency GC state. + * + * Pref: javascript.options.mem.gc_high_frequency_large_heap_growth + * Default: HighFrequencyLargeHeapGrowth + */ + JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH = 15, + + /** + * Heap growth factor for low frequency GCs. + * + * This factor is applied regardless of the size of the heap when not in the + * high-frequency GC state. + * + * Pref: javascript.options.mem.gc_low_frequency_heap_growth + * Default: LowFrequencyHeapGrowth + */ + JSGC_LOW_FREQUENCY_HEAP_GROWTH = 16, + + /** + * Lower limit for collecting a zone. + * + * Zones smaller than this size will not normally be collected. + * + * Pref: javascript.options.mem.gc_allocation_threshold_mb + * Default GCZoneAllocThresholdBase + */ + JSGC_ALLOCATION_THRESHOLD = 19, + + /** + * We try to keep at least this many unused chunks in the free chunk pool at + * all times, even after a shrinking GC. + * + * Pref: javascript.options.mem.gc_min_empty_chunk_count + * Default: MinEmptyChunkCount + */ + JSGC_MIN_EMPTY_CHUNK_COUNT = 21, + + /** + * We never keep more than this many unused chunks in the free chunk + * pool. + * + * Pref: javascript.options.mem.gc_min_empty_chunk_count + * Default: MinEmptyChunkCount + */ + JSGC_MAX_EMPTY_CHUNK_COUNT = 22, + + /** + * Whether compacting GC is enabled. + * + * Pref: javascript.options.mem.gc_compacting + * Default: CompactingEnabled + */ + JSGC_COMPACTING_ENABLED = 23, + + /** + * Limit of how far over the incremental trigger threshold we allow the heap + * to grow before finishing a collection non-incrementally, for small heaps. + * + * We trigger an incremental GC when a trigger threshold is reached but the + * collection may not be fast enough to keep up with the mutator. At some + * point we finish the collection non-incrementally. + * + * Default: SmallHeapIncrementalLimit + * Pref: javascript.options.mem.gc_small_heap_incremental_limit + */ + JSGC_SMALL_HEAP_INCREMENTAL_LIMIT = 25, + + /** + * Limit of how far over the incremental trigger threshold we allow the heap + * to grow before finishing a collection non-incrementally, for large heaps. + * + * Default: LargeHeapIncrementalLimit + * Pref: javascript.options.mem.gc_large_heap_incremental_limit + */ + JSGC_LARGE_HEAP_INCREMENTAL_LIMIT = 26, + + /** + * Attempt to run a minor GC in the idle time if the free space falls + * below this number of bytes. + * + * Default: NurseryChunkUsableSize / 4 + * Pref: None + */ + JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION = 27, + + /** + * If this percentage of the nursery is tenured and the nursery is at least + * 4MB, then proceed to examine which groups we should pretenure. + * + * Default: PretenureThreshold + * Pref: None + */ + JSGC_PRETENURE_THRESHOLD = 28, + + /** + * If the above condition is met, then any object group that tenures more than + * this number of objects will be pretenured (if it can be). + * + * Default: PretenureGroupThreshold + * Pref: None + */ + JSGC_PRETENURE_GROUP_THRESHOLD = 29, + + /** + * Attempt to run a minor GC in the idle time if the free space falls + * below this percentage (from 0 to 99). + * + * Default: 25 + * Pref: None + */ + JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION_PERCENT = 30, + + /** + * Minimum size of the generational GC nurseries. + * + * This value will be rounded to the nearest Nursery::SubChunkStep if below + * gc::ChunkSize, otherwise it'll be rounded to the nearest gc::ChunkSize. + * + * Default: Nursery::SubChunkLimit + * Pref: javascript.options.mem.nursery.min_kb + */ + JSGC_MIN_NURSERY_BYTES = 31, + + /** + * The minimum time to allow between triggering last ditch GCs in seconds. + * + * Default: 60 seconds + * Pref: None + */ + JSGC_MIN_LAST_DITCH_GC_PERIOD = 32, + + /** + * The delay (in heapsize kilobytes) between slices of an incremental GC. + * + * Default: ZoneAllocDelayBytes + */ + JSGC_ZONE_ALLOC_DELAY_KB = 33, + + /* + * The current size of the nursery. + * + * This parameter is read-only. + */ + JSGC_NURSERY_BYTES = 34, + + /** + * Retained size base value for calculating malloc heap threshold. + * + * Default: MallocThresholdBase + */ + JSGC_MALLOC_THRESHOLD_BASE = 35, + + /** + * Growth factor for calculating malloc heap threshold. + * + * Default: MallocGrowthFactor + */ + JSGC_MALLOC_GROWTH_FACTOR = 36, + + /** + * Whether incremental weakmap marking is enabled. + * + * Pref: javascript.options.mem.incremental_weakmap + * Default: IncrementalWeakMarkEnabled + */ + JSGC_INCREMENTAL_WEAKMAP_ENABLED = 37, + + /** + * The chunk size in bytes for this system. + * + * This parameter is read-only. + */ + JSGC_CHUNK_BYTES = 38, + + /** + * The number of background threads to use for parallel GC work for each CPU + * core, expressed as an integer percentage. + * + * Pref: javascript.options.mem.gc_helper_thread_ratio + */ + JSGC_HELPER_THREAD_RATIO = 39, + + /** + * The maximum number of background threads to use for parallel GC work. + * + * Pref: javascript.options.mem.gc_max_helper_threads + */ + JSGC_MAX_HELPER_THREADS = 40, + + /** + * The number of background threads to use for parallel GC work. + * + * This parameter is read-only and is set based on the + * JSGC_HELPER_THREAD_RATIO and JSGC_MAX_HELPER_THREADS parameters. + */ + JSGC_HELPER_THREAD_COUNT = 41, + + /** + * If the percentage of the tenured strings exceeds this threshold, string + * will be allocated in tenured heap instead. (Default is allocated in + * nursery.) + */ + JSGC_PRETENURE_STRING_THRESHOLD = 42, + + /** + * If the finalization rate of the tenured strings exceeds this threshold, + * string will be allocated in nursery. + */ + JSGC_STOP_PRETENURE_STRING_THRESHOLD = 43, + + /** + * A number that is incremented on every major GC slice. + */ + JSGC_MAJOR_GC_NUMBER = 44, + + /** + * A number that is incremented on every minor GC. + */ + JSGC_MINOR_GC_NUMBER = 45, +} JSGCParamKey; + +/* + * Generic trace operation that calls JS::TraceEdge on each traceable thing's + * location reachable from data. + */ +typedef void (*JSTraceDataOp)(JSTracer* trc, void* data); + +typedef enum JSGCStatus { JSGC_BEGIN, JSGC_END } JSGCStatus; + +typedef void (*JSObjectsTenuredCallback)(JSContext* cx, void* data); + +typedef enum JSFinalizeStatus { + /** + * Called when preparing to sweep a group of zones, before anything has been + * swept. The collector will not yield to the mutator before calling the + * callback with JSFINALIZE_GROUP_START status. + */ + JSFINALIZE_GROUP_PREPARE, + + /** + * Called after preparing to sweep a group of zones. Weak references to + * unmarked things have been removed at this point, but no GC things have + * been swept. The collector may yield to the mutator after this point. + */ + JSFINALIZE_GROUP_START, + + /** + * Called after sweeping a group of zones. All dead GC things have been + * swept at this point. + */ + JSFINALIZE_GROUP_END, + + /** + * Called at the end of collection when everything has been swept. + */ + JSFINALIZE_COLLECTION_END +} JSFinalizeStatus; + +typedef void (*JSFinalizeCallback)(JSFreeOp* fop, JSFinalizeStatus status, + void* data); + +typedef void (*JSWeakPointerZonesCallback)(JSContext* cx, void* data); + +typedef void (*JSWeakPointerCompartmentCallback)(JSContext* cx, + JS::Compartment* comp, + void* data); + +/* + * This is called to tell the embedding that a FinalizationRegistry object has + * cleanup work, and that the engine should be called back at an appropriate + * later time to perform this cleanup, by calling the function |doCleanup|. + * + * This callback must not do anything that could cause GC. + */ +using JSHostCleanupFinalizationRegistryCallback = + void (*)(JSFunction* doCleanup, JSObject* incumbentGlobal, void* data); + +/** + * Each external string has a pointer to JSExternalStringCallbacks. Embedders + * can use this to implement custom finalization or memory reporting behavior. + */ +struct JSExternalStringCallbacks { + /** + * Finalizes external strings created by JS_NewExternalString. The finalizer + * can be called off the main thread. + */ + virtual void finalize(char16_t* chars) const = 0; + + /** + * Callback used by memory reporting to ask the embedder how much memory an + * external string is keeping alive. The embedder is expected to return a + * value that corresponds to the size of the allocation that will be released + * by the finalizer callback above. + * + * Implementations of this callback MUST NOT do anything that can cause GC. + */ + virtual size_t sizeOfBuffer(const char16_t* chars, + mozilla::MallocSizeOf mallocSizeOf) const = 0; +}; + +namespace JS { + +#define GCREASONS(D) \ + /* Reasons internal to the JS engine */ \ + D(API, 0) \ + D(EAGER_ALLOC_TRIGGER, 1) \ + D(DESTROY_RUNTIME, 2) \ + D(ROOTS_REMOVED, 3) \ + D(LAST_DITCH, 4) \ + D(TOO_MUCH_MALLOC, 5) \ + D(ALLOC_TRIGGER, 6) \ + D(DEBUG_GC, 7) \ + D(COMPARTMENT_REVIVED, 8) \ + D(RESET, 9) \ + D(OUT_OF_NURSERY, 10) \ + D(EVICT_NURSERY, 11) \ + D(DELAYED_ATOMS_GC, 12) \ + D(SHARED_MEMORY_LIMIT, 13) \ + D(IDLE_TIME_COLLECTION, 14) \ + D(BG_TASK_FINISHED, 15) \ + D(ABORT_GC, 16) \ + D(FULL_WHOLE_CELL_BUFFER, 17) \ + D(FULL_GENERIC_BUFFER, 18) \ + D(FULL_VALUE_BUFFER, 19) \ + D(FULL_CELL_PTR_OBJ_BUFFER, 20) \ + D(FULL_SLOT_BUFFER, 21) \ + D(FULL_SHAPE_BUFFER, 22) \ + D(TOO_MUCH_WASM_MEMORY, 23) \ + D(DISABLE_GENERATIONAL_GC, 24) \ + D(FINISH_GC, 25) \ + D(PREPARE_FOR_TRACING, 26) \ + D(UNUSED4, 27) \ + D(FULL_CELL_PTR_STR_BUFFER, 28) \ + D(TOO_MUCH_JIT_CODE, 29) \ + D(FULL_CELL_PTR_BIGINT_BUFFER, 30) \ + D(INIT_SELF_HOSTING, 31) \ + D(NURSERY_MALLOC_BUFFERS, 32) \ + \ + /* Reasons from Firefox */ \ + D(DOM_WINDOW_UTILS, FIRST_FIREFOX_REASON) \ + D(COMPONENT_UTILS, 34) \ + D(MEM_PRESSURE, 35) \ + D(CC_FINISHED, 36) \ + D(CC_FORCED, 37) \ + D(LOAD_END, 38) \ + D(UNUSED3, 39) \ + D(PAGE_HIDE, 40) \ + D(NSJSCONTEXT_DESTROY, 41) \ + D(WORKER_SHUTDOWN, 42) \ + D(SET_DOC_SHELL, 43) \ + D(DOM_UTILS, 44) \ + D(DOM_IPC, 45) \ + D(DOM_WORKER, 46) \ + D(INTER_SLICE_GC, 47) \ + D(UNUSED1, 48) \ + D(FULL_GC_TIMER, 49) \ + D(SHUTDOWN_CC, 50) \ + D(UNUSED2, 51) \ + D(USER_INACTIVE, 52) \ + D(XPCONNECT_SHUTDOWN, 53) \ + D(DOCSHELL, 54) \ + D(HTML_PARSER, 55) + +enum class GCReason { + FIRST_FIREFOX_REASON = 33, + +#define MAKE_REASON(name, val) name = val, + GCREASONS(MAKE_REASON) +#undef MAKE_REASON + NO_REASON, + NUM_REASONS, + + /* + * For telemetry, we want to keep a fixed max bucket size over time so we + * don't have to switch histograms. 100 is conservative; but the cost of extra + * buckets seems to be low while the cost of switching histograms is high. + */ + NUM_TELEMETRY_REASONS = 100 +}; + +/** + * Get a statically allocated C string explaining the given GC reason. + */ +extern JS_PUBLIC_API const char* ExplainGCReason(JS::GCReason reason); + +/** + * Return true if the GC reason is internal to the JS engine. + */ +extern JS_PUBLIC_API bool InternalGCReason(JS::GCReason reason); + +/* + * Zone GC: + * + * SpiderMonkey's GC is capable of performing a collection on an arbitrary + * subset of the zones in the system. This allows an embedding to minimize + * collection time by only collecting zones that have run code recently, + * ignoring the parts of the heap that are unlikely to have changed. + * + * When triggering a GC using one of the functions below, it is first necessary + * to select the zones to be collected. To do this, you can call + * PrepareZoneForGC on each zone, or you can call PrepareForFullGC to select + * all zones. Failing to select any zone is an error. + */ + +/** + * Schedule the given zone to be collected as part of the next GC. + */ +extern JS_PUBLIC_API void PrepareZoneForGC(JSContext* cx, Zone* zone); + +/** + * Schedule all zones to be collected in the next GC. + */ +extern JS_PUBLIC_API void PrepareForFullGC(JSContext* cx); + +/** + * When performing an incremental GC, the zones that were selected for the + * previous incremental slice must be selected in subsequent slices as well. + * This function selects those slices automatically. + */ +extern JS_PUBLIC_API void PrepareForIncrementalGC(JSContext* cx); + +/** + * Returns true if any zone in the system has been scheduled for GC with one of + * the functions above or by the JS engine. + */ +extern JS_PUBLIC_API bool IsGCScheduled(JSContext* cx); + +/** + * Undoes the effect of the Prepare methods above. The given zone will not be + * collected in the next GC. + */ +extern JS_PUBLIC_API void SkipZoneForGC(JSContext* cx, Zone* zone); + +/* + * Non-Incremental GC: + * + * The following functions perform a non-incremental GC. + */ + +/** + * Performs a non-incremental collection of all selected zones. + * + * If the gckind argument is GC_NORMAL, then some objects that are unreachable + * from the program may still be alive afterwards because of internal + * references; if GC_SHRINK is passed then caches and other temporary references + * to objects will be cleared and all unreferenced objects will be removed from + * the system. + */ +extern JS_PUBLIC_API void NonIncrementalGC(JSContext* cx, + JSGCInvocationKind gckind, + GCReason reason); + +/* + * Incremental GC: + * + * Incremental GC divides the full mark-and-sweep collection into multiple + * slices, allowing client JavaScript code to run between each slice. This + * allows interactive apps to avoid long collection pauses. Incremental GC does + * not make collection take less time, it merely spreads that time out so that + * the pauses are less noticable. + * + * For a collection to be carried out incrementally the following conditions + * must be met: + * - The collection must be run by calling JS::IncrementalGC() rather than + * JS_GC(). + * - The GC parameter JSGC_INCREMENTAL_GC_ENABLED must be true. + * + * Note: Even if incremental GC is enabled and working correctly, + * non-incremental collections can still happen when low on memory. + */ + +/** + * Begin an incremental collection and perform one slice worth of work. When + * this function returns, the collection may not be complete. + * IncrementalGCSlice() must be called repeatedly until + * !IsIncrementalGCInProgress(cx). + * + * Note: SpiderMonkey's GC is not realtime. Slices in practice may be longer or + * shorter than the requested interval. + */ +extern JS_PUBLIC_API void StartIncrementalGC(JSContext* cx, + JSGCInvocationKind gckind, + GCReason reason, + int64_t millis = 0); + +/** + * Perform a slice of an ongoing incremental collection. When this function + * returns, the collection may not be complete. It must be called repeatedly + * until !IsIncrementalGCInProgress(cx). + * + * Note: SpiderMonkey's GC is not realtime. Slices in practice may be longer or + * shorter than the requested interval. + */ +extern JS_PUBLIC_API void IncrementalGCSlice(JSContext* cx, GCReason reason, + int64_t millis = 0); + +/** + * Return whether an incremental GC has work to do on the foreground thread and + * would make progress if a slice was run now. If this returns false then the GC + * is waiting for background threads to finish their work and a slice started + * now would return immediately. + */ +extern JS_PUBLIC_API bool IncrementalGCHasForegroundWork(JSContext* cx); + +/** + * If IsIncrementalGCInProgress(cx), this call finishes the ongoing collection + * by performing an arbitrarily long slice. If !IsIncrementalGCInProgress(cx), + * this is equivalent to NonIncrementalGC. When this function returns, + * IsIncrementalGCInProgress(cx) will always be false. + */ +extern JS_PUBLIC_API void FinishIncrementalGC(JSContext* cx, GCReason reason); + +/** + * If IsIncrementalGCInProgress(cx), this call aborts the ongoing collection and + * performs whatever work needs to be done to return the collector to its idle + * state. This may take an arbitrarily long time. When this function returns, + * IsIncrementalGCInProgress(cx) will always be false. + */ +extern JS_PUBLIC_API void AbortIncrementalGC(JSContext* cx); + +namespace dbg { + +// The `JS::dbg::GarbageCollectionEvent` class is essentially a view of the +// `js::gcstats::Statistics` data without the uber implementation-specific bits. +// It should generally be palatable for web developers. +class GarbageCollectionEvent { + // The major GC number of the GC cycle this data pertains to. + uint64_t majorGCNumber_; + + // Reference to a non-owned, statically allocated C string. This is a very + // short reason explaining why a GC was triggered. + const char* reason; + + // Reference to a nullable, non-owned, statically allocated C string. If the + // collection was forced to be non-incremental, this is a short reason of + // why the GC could not perform an incremental collection. + const char* nonincrementalReason; + + // Represents a single slice of a possibly multi-slice incremental garbage + // collection. + struct Collection { + mozilla::TimeStamp startTimestamp; + mozilla::TimeStamp endTimestamp; + }; + + // The set of garbage collection slices that made up this GC cycle. + mozilla::Vector<Collection> collections; + + GarbageCollectionEvent(const GarbageCollectionEvent& rhs) = delete; + GarbageCollectionEvent& operator=(const GarbageCollectionEvent& rhs) = delete; + + public: + explicit GarbageCollectionEvent(uint64_t majorGCNum) + : majorGCNumber_(majorGCNum), + reason(nullptr), + nonincrementalReason(nullptr), + collections() {} + + using Ptr = js::UniquePtr<GarbageCollectionEvent>; + static Ptr Create(JSRuntime* rt, ::js::gcstats::Statistics& stats, + uint64_t majorGCNumber); + + JSObject* toJSObject(JSContext* cx) const; + + uint64_t majorGCNumber() const { return majorGCNumber_; } +}; + +} // namespace dbg + +enum GCProgress { + /* + * During GC, the GC is bracketed by GC_CYCLE_BEGIN/END callbacks. Each + * slice between those (whether an incremental or the sole non-incremental + * slice) is bracketed by GC_SLICE_BEGIN/GC_SLICE_END. + */ + + GC_CYCLE_BEGIN, + GC_SLICE_BEGIN, + GC_SLICE_END, + GC_CYCLE_END +}; + +struct JS_PUBLIC_API GCDescription { + bool isZone_; + bool isComplete_; + JSGCInvocationKind invocationKind_; + GCReason reason_; + + GCDescription(bool isZone, bool isComplete, JSGCInvocationKind kind, + GCReason reason) + : isZone_(isZone), + isComplete_(isComplete), + invocationKind_(kind), + reason_(reason) {} + + char16_t* formatSliceMessage(JSContext* cx) const; + char16_t* formatSummaryMessage(JSContext* cx) const; + + mozilla::TimeStamp startTime(JSContext* cx) const; + mozilla::TimeStamp endTime(JSContext* cx) const; + mozilla::TimeStamp lastSliceStart(JSContext* cx) const; + mozilla::TimeStamp lastSliceEnd(JSContext* cx) const; + + JS::UniqueChars sliceToJSONProfiler(JSContext* cx) const; + JS::UniqueChars formatJSONProfiler(JSContext* cx) const; + + JS::dbg::GarbageCollectionEvent::Ptr toGCEvent(JSContext* cx) const; +}; + +extern JS_PUBLIC_API UniqueChars MinorGcToJSON(JSContext* cx); + +typedef void (*GCSliceCallback)(JSContext* cx, GCProgress progress, + const GCDescription& desc); + +/** + * The GC slice callback is called at the beginning and end of each slice. This + * callback may be used for GC notifications as well as to perform additional + * marking. + */ +extern JS_PUBLIC_API GCSliceCallback +SetGCSliceCallback(JSContext* cx, GCSliceCallback callback); + +/** + * Describes the progress of an observed nursery collection. + */ +enum class GCNurseryProgress { + /** + * The nursery collection is starting. + */ + GC_NURSERY_COLLECTION_START, + /** + * The nursery collection is ending. + */ + GC_NURSERY_COLLECTION_END +}; + +/** + * A nursery collection callback receives the progress of the nursery collection + * and the reason for the collection. + */ +using GCNurseryCollectionCallback = void (*)(JSContext* cx, + GCNurseryProgress progress, + GCReason reason); + +/** + * Set the nursery collection callback for the given runtime. When set, it will + * be called at the start and end of every nursery collection. + */ +extern JS_PUBLIC_API GCNurseryCollectionCallback SetGCNurseryCollectionCallback( + JSContext* cx, GCNurseryCollectionCallback callback); + +typedef void (*DoCycleCollectionCallback)(JSContext* cx); + +/** + * The purge gray callback is called after any COMPARTMENT_REVIVED GC in which + * the majority of compartments have been marked gray. + */ +extern JS_PUBLIC_API DoCycleCollectionCallback +SetDoCycleCollectionCallback(JSContext* cx, DoCycleCollectionCallback callback); + +/** + * Incremental GC defaults to enabled, but may be disabled for testing or in + * embeddings that have not yet implemented barriers on their native classes. + * There is not currently a way to re-enable incremental GC once it has been + * disabled on the runtime. + */ +extern JS_PUBLIC_API void DisableIncrementalGC(JSContext* cx); + +/** + * Returns true if incremental GC is enabled. Simply having incremental GC + * enabled is not sufficient to ensure incremental collections are happening. + * See the comment "Incremental GC" above for reasons why incremental GC may be + * suppressed. Inspection of the "nonincremental reason" field of the + * GCDescription returned by GCSliceCallback may help narrow down the cause if + * collections are not happening incrementally when expected. + */ +extern JS_PUBLIC_API bool IsIncrementalGCEnabled(JSContext* cx); + +/** + * Returns true while an incremental GC is ongoing, both when actively + * collecting and between slices. + */ +extern JS_PUBLIC_API bool IsIncrementalGCInProgress(JSContext* cx); + +/** + * Returns true while an incremental GC is ongoing, both when actively + * collecting and between slices. + */ +extern JS_PUBLIC_API bool IsIncrementalGCInProgress(JSRuntime* rt); + +/** + * Returns true if the most recent GC ran incrementally. + */ +extern JS_PUBLIC_API bool WasIncrementalGC(JSRuntime* rt); + +/* + * Generational GC: + * + * Note: Generational GC is not yet enabled by default. The following class + * is non-functional unless SpiderMonkey was configured with + * --enable-gcgenerational. + */ + +/** Ensure that generational GC is disabled within some scope. */ +class JS_PUBLIC_API AutoDisableGenerationalGC { + JSContext* cx; + + public: + explicit AutoDisableGenerationalGC(JSContext* cx); + ~AutoDisableGenerationalGC(); +}; + +/** + * Returns true if generational allocation and collection is currently enabled + * on the given runtime. + */ +extern JS_PUBLIC_API bool IsGenerationalGCEnabled(JSRuntime* rt); + +/** + * Pass a subclass of this "abstract" class to callees to require that they + * never GC. Subclasses can use assertions or the hazard analysis to ensure no + * GC happens. + */ +class JS_PUBLIC_API AutoRequireNoGC { + protected: + AutoRequireNoGC() = default; + ~AutoRequireNoGC() = default; +}; + +/** + * Diagnostic assert (see MOZ_DIAGNOSTIC_ASSERT) that GC cannot occur while this + * class is live. This class does not disable the static rooting hazard + * analysis. + * + * This works by entering a GC unsafe region, which is checked on allocation and + * on GC. + */ +class JS_PUBLIC_API AutoAssertNoGC : public AutoRequireNoGC { +#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED + JSContext* cx_; + + public: + // This gets the context from TLS if it is not passed in. + explicit AutoAssertNoGC(JSContext* cx = nullptr); + ~AutoAssertNoGC(); +#else + public: + explicit AutoAssertNoGC(JSContext* cx = nullptr) {} + ~AutoAssertNoGC() {} +#endif +}; + +/** + * Disable the static rooting hazard analysis in the live region and assert in + * debug builds if any allocation that could potentially trigger a GC occurs + * while this guard object is live. This is most useful to help the exact + * rooting hazard analysis in complex regions, since it cannot understand + * dataflow. + * + * Note: GC behavior is unpredictable even when deterministic and is generally + * non-deterministic in practice. The fact that this guard has not + * asserted is not a guarantee that a GC cannot happen in the guarded + * region. As a rule, anyone performing a GC unsafe action should + * understand the GC properties of all code in that region and ensure + * that the hazard analysis is correct for that code, rather than relying + * on this class. + */ +#ifdef DEBUG +class JS_PUBLIC_API AutoSuppressGCAnalysis : public AutoAssertNoGC { + public: + explicit AutoSuppressGCAnalysis(JSContext* cx = nullptr) + : AutoAssertNoGC(cx) {} +} JS_HAZ_GC_SUPPRESSED; +#else +class JS_PUBLIC_API AutoSuppressGCAnalysis : public AutoRequireNoGC { + public: + explicit AutoSuppressGCAnalysis(JSContext* cx = nullptr) {} +} JS_HAZ_GC_SUPPRESSED; +#endif + +/** + * Assert that code is only ever called from a GC callback, disable the static + * rooting hazard analysis and assert if any allocation that could potentially + * trigger a GC occurs while this guard object is live. + * + * This is useful to make the static analysis ignore code that runs in GC + * callbacks. + */ +class JS_PUBLIC_API AutoAssertGCCallback : public AutoSuppressGCAnalysis { + public: +#ifdef DEBUG + AutoAssertGCCallback(); +#else + AutoAssertGCCallback() {} +#endif +}; + +/** + * Place AutoCheckCannotGC in scopes that you believe can never GC. These + * annotations will be verified both dynamically via AutoAssertNoGC, and + * statically with the rooting hazard analysis (implemented by making the + * analysis consider AutoCheckCannotGC to be a GC pointer, and therefore + * complain if it is live across a GC call.) It is useful when dealing with + * internal pointers to GC things where the GC thing itself may not be present + * for the static analysis: e.g. acquiring inline chars from a JSString* on the + * heap. + * + * We only do the assertion checking in DEBUG builds. + */ +#ifdef DEBUG +class JS_PUBLIC_API AutoCheckCannotGC : public AutoAssertNoGC { + public: + explicit AutoCheckCannotGC(JSContext* cx = nullptr) : AutoAssertNoGC(cx) {} +} JS_HAZ_GC_INVALIDATED; +#else +class JS_PUBLIC_API AutoCheckCannotGC : public AutoRequireNoGC { + public: + explicit AutoCheckCannotGC(JSContext* cx = nullptr) {} +} JS_HAZ_GC_INVALIDATED; +#endif + +extern JS_PUBLIC_API void SetLowMemoryState(JSContext* cx, bool newState); + +/* + * Internal to Firefox. + */ +extern JS_FRIEND_API void NotifyGCRootsRemoved(JSContext* cx); + +} /* namespace JS */ + +typedef void (*JSGCCallback)(JSContext* cx, JSGCStatus status, + JS::GCReason reason, void* data); + +/** + * Register externally maintained GC roots. + * + * traceOp: the trace operation. For each root the implementation should call + * JS::TraceEdge whenever the root contains a traceable thing. + * data: the data argument to pass to each invocation of traceOp. + */ +extern JS_PUBLIC_API bool JS_AddExtraGCRootsTracer(JSContext* cx, + JSTraceDataOp traceOp, + void* data); + +/** Undo a call to JS_AddExtraGCRootsTracer. */ +extern JS_PUBLIC_API void JS_RemoveExtraGCRootsTracer(JSContext* cx, + JSTraceDataOp traceOp, + void* data); + +extern JS_PUBLIC_API void JS_GC(JSContext* cx, + JS::GCReason reason = JS::GCReason::API); + +extern JS_PUBLIC_API void JS_MaybeGC(JSContext* cx); + +extern JS_PUBLIC_API void JS_SetGCCallback(JSContext* cx, JSGCCallback cb, + void* data); + +extern JS_PUBLIC_API void JS_SetObjectsTenuredCallback( + JSContext* cx, JSObjectsTenuredCallback cb, void* data); + +extern JS_PUBLIC_API bool JS_AddFinalizeCallback(JSContext* cx, + JSFinalizeCallback cb, + void* data); + +extern JS_PUBLIC_API void JS_RemoveFinalizeCallback(JSContext* cx, + JSFinalizeCallback cb); + +/* + * Weak pointers and garbage collection + * + * Weak pointers are by their nature not marked as part of garbage collection, + * but they may need to be updated in two cases after a GC: + * + * 1) Their referent was found not to be live and is about to be finalized + * 2) Their referent has been moved by a compacting GC + * + * To handle this, any part of the system that maintain weak pointers to + * JavaScript GC things must register a callback with + * JS_(Add,Remove)WeakPointer{ZoneGroup,Compartment}Callback(). This callback + * must then call JS_UpdateWeakPointerAfterGC() on all weak pointers it knows + * about. + * + * Since sweeping is incremental, we have several callbacks to avoid repeatedly + * having to visit all embedder structures. The WeakPointerZonesCallback is + * called once for each strongly connected group of zones, whereas the + * WeakPointerCompartmentCallback is called once for each compartment that is + * visited while sweeping. Structures that cannot contain references in more + * than one compartment should sweep the relevant per-compartment structures + * using the latter callback to minimizer per-slice overhead. + * + * The argument to JS_UpdateWeakPointerAfterGC() is an in-out param. If the + * referent is about to be finalized the pointer will be set to null. If the + * referent has been moved then the pointer will be updated to point to the new + * location. + * + * Callers of this method are responsible for updating any state that is + * dependent on the object's address. For example, if the object's address is + * used as a key in a hashtable, then the object must be removed and + * re-inserted with the correct hash. + */ + +extern JS_PUBLIC_API bool JS_AddWeakPointerZonesCallback( + JSContext* cx, JSWeakPointerZonesCallback cb, void* data); + +extern JS_PUBLIC_API void JS_RemoveWeakPointerZonesCallback( + JSContext* cx, JSWeakPointerZonesCallback cb); + +extern JS_PUBLIC_API bool JS_AddWeakPointerCompartmentCallback( + JSContext* cx, JSWeakPointerCompartmentCallback cb, void* data); + +extern JS_PUBLIC_API void JS_RemoveWeakPointerCompartmentCallback( + JSContext* cx, JSWeakPointerCompartmentCallback cb); + +namespace JS { +template <typename T> +class Heap; +} + +extern JS_PUBLIC_API void JS_UpdateWeakPointerAfterGC( + JS::Heap<JSObject*>* objp); + +extern JS_PUBLIC_API void JS_UpdateWeakPointerAfterGCUnbarriered( + JSObject** objp); + +extern JS_PUBLIC_API void JS_SetGCParameter(JSContext* cx, JSGCParamKey key, + uint32_t value); + +extern JS_PUBLIC_API void JS_ResetGCParameter(JSContext* cx, JSGCParamKey key); + +extern JS_PUBLIC_API uint32_t JS_GetGCParameter(JSContext* cx, + JSGCParamKey key); + +extern JS_PUBLIC_API void JS_SetGCParametersBasedOnAvailableMemory( + JSContext* cx, uint32_t availMem); + +/** + * Create a new JSString whose chars member refers to external memory, i.e., + * memory requiring application-specific finalization. + */ +extern JS_PUBLIC_API JSString* JS_NewExternalString( + JSContext* cx, const char16_t* chars, size_t length, + const JSExternalStringCallbacks* callbacks); + +/** + * Create a new JSString whose chars member may refer to external memory. + * If a new external string is allocated, |*allocatedExternal| is set to true. + * Otherwise the returned string is either not an external string or an + * external string allocated by a previous call and |*allocatedExternal| is set + * to false. If |*allocatedExternal| is false, |fin| won't be called. + */ +extern JS_PUBLIC_API JSString* JS_NewMaybeExternalString( + JSContext* cx, const char16_t* chars, size_t length, + const JSExternalStringCallbacks* callbacks, bool* allocatedExternal); + +/** + * Return the 'callbacks' arg passed to JS_NewExternalString or + * JS_NewMaybeExternalString. + */ +extern JS_PUBLIC_API const JSExternalStringCallbacks* +JS_GetExternalStringCallbacks(JSString* str); + +namespace JS { + +extern JS_PUBLIC_API bool IsIdleGCTaskNeeded(JSRuntime* rt); + +extern JS_PUBLIC_API void RunIdleTimeGCTask(JSRuntime* rt); + +extern JS_PUBLIC_API void SetHostCleanupFinalizationRegistryCallback( + JSContext* cx, JSHostCleanupFinalizationRegistryCallback cb, void* data); + +/** + * Clear kept alive objects in JS WeakRef. + * https://tc39.es/proposal-weakrefs/#sec-clear-kept-objects + */ +extern JS_PUBLIC_API void ClearKeptObjects(JSContext* cx); + +extern JS_PUBLIC_API bool ZoneIsCollecting(Zone* zone); + +} // namespace JS + +namespace js { +namespace gc { + +/** + * Create an object providing access to the garbage collector's internal notion + * of the current state of memory (both GC heap memory and GCthing-controlled + * malloc memory. + */ +extern JS_PUBLIC_API JSObject* NewMemoryInfoObject(JSContext* cx); + +/* + * Run the finalizer of a nursery-allocated JSObject that is known to be dead. + * + * This is a dangerous operation - only use this if you know what you're doing! + * + * This is used by the browser to implement nursery-allocated wrapper cached + * wrappers. + */ +extern JS_PUBLIC_API void FinalizeDeadNurseryObject(JSContext* cx, + JSObject* obj); + +} /* namespace gc */ +} /* namespace js */ + +#endif /* js_GCAPI_h */ |