| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
| * vim: set ts=8 sts=4 et sw=4 tw=99: |
| * 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/. */ |
| |
| #ifndef js_GCAPI_h |
| #define js_GCAPI_h |
| |
| #include "HeapAPI.h" |
| #include "jsfriendapi.h" |
| |
| namespace JS { |
| |
| #define GCREASONS(D) \ |
| /* Reasons internal to the JS engine */ \ |
| D(API) \ |
| D(MAYBEGC) \ |
| D(LAST_CONTEXT) \ |
| D(DESTROY_CONTEXT) \ |
| D(LAST_DITCH) \ |
| D(TOO_MUCH_MALLOC) \ |
| D(ALLOC_TRIGGER) \ |
| D(DEBUG_GC) \ |
| D(DEBUG_MODE_GC) \ |
| D(TRANSPLANT) \ |
| D(RESET) \ |
| D(OUT_OF_NURSERY) \ |
| D(EVICT_NURSERY) \ |
| D(FULL_STORE_BUFFER) \ |
| \ |
| /* These are reserved for future use. */ \ |
| D(RESERVED0) \ |
| D(RESERVED1) \ |
| D(RESERVED2) \ |
| D(RESERVED3) \ |
| D(RESERVED4) \ |
| D(RESERVED5) \ |
| D(RESERVED6) \ |
| D(RESERVED7) \ |
| D(RESERVED8) \ |
| D(RESERVED9) \ |
| D(RESERVED10) \ |
| D(RESERVED11) \ |
| D(RESERVED12) \ |
| D(RESERVED13) \ |
| D(RESERVED14) \ |
| D(RESERVED15) \ |
| D(RESERVED16) \ |
| D(RESERVED17) \ |
| D(RESERVED18) \ |
| D(RESERVED19) \ |
| \ |
| /* Reasons from Firefox */ \ |
| D(DOM_WINDOW_UTILS) \ |
| D(COMPONENT_UTILS) \ |
| D(MEM_PRESSURE) \ |
| D(CC_WAITING) \ |
| D(CC_FORCED) \ |
| D(LOAD_END) \ |
| D(POST_COMPARTMENT) \ |
| D(PAGE_HIDE) \ |
| D(NSJSCONTEXT_DESTROY) \ |
| D(SET_NEW_DOCUMENT) \ |
| D(SET_DOC_SHELL) \ |
| D(DOM_UTILS) \ |
| D(DOM_IPC) \ |
| D(DOM_WORKER) \ |
| D(INTER_SLICE_GC) \ |
| D(REFRESH_FRAME) \ |
| D(FULL_GC_TIMER) \ |
| D(SHUTDOWN_CC) \ |
| D(FINISH_LARGE_EVALUTE) |
| |
| namespace gcreason { |
| |
| /* GCReasons will end up looking like JSGC_MAYBEGC */ |
| enum Reason { |
| #define MAKE_REASON(name) name, |
| 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; as of this writing |
| * there are 26. But the cost of extra buckets seems to be low while the |
| * cost of switching histograms is high. |
| */ |
| NUM_TELEMETRY_REASONS = 100 |
| }; |
| |
| } /* namespace gcreason */ |
| |
| extern JS_FRIEND_API(void) |
| PrepareZoneForGC(Zone *zone); |
| |
| extern JS_FRIEND_API(void) |
| PrepareForFullGC(JSRuntime *rt); |
| |
| extern JS_FRIEND_API(void) |
| PrepareForIncrementalGC(JSRuntime *rt); |
| |
| extern JS_FRIEND_API(bool) |
| IsGCScheduled(JSRuntime *rt); |
| |
| extern JS_FRIEND_API(void) |
| SkipZoneForGC(Zone *zone); |
| |
| /* |
| * When triggering a GC using one of the functions below, it is first necessary |
| * to select the compartments to be collected. To do this, you can call |
| * PrepareZoneForGC on each compartment, or you can call PrepareForFullGC |
| * to select all compartments. Failing to select any compartment is an error. |
| */ |
| |
| extern JS_FRIEND_API(void) |
| GCForReason(JSRuntime *rt, gcreason::Reason reason); |
| |
| extern JS_FRIEND_API(void) |
| ShrinkingGC(JSRuntime *rt, gcreason::Reason reason); |
| |
| extern JS_FRIEND_API(void) |
| ShrinkGCBuffers(JSRuntime *rt); |
| |
| extern JS_FRIEND_API(void) |
| IncrementalGC(JSRuntime *rt, gcreason::Reason reason, int64_t millis = 0); |
| |
| extern JS_FRIEND_API(void) |
| FinishIncrementalGC(JSRuntime *rt, gcreason::Reason reason); |
| |
| enum GCProgress { |
| /* |
| * During non-incremental GC, the GC is bracketed by JSGC_CYCLE_BEGIN/END |
| * callbacks. During an incremental GC, the sequence of callbacks is as |
| * follows: |
| * JSGC_CYCLE_BEGIN, JSGC_SLICE_END (first slice) |
| * JSGC_SLICE_BEGIN, JSGC_SLICE_END (second slice) |
| * ... |
| * JSGC_SLICE_BEGIN, JSGC_CYCLE_END (last slice) |
| */ |
| |
| GC_CYCLE_BEGIN, |
| GC_SLICE_BEGIN, |
| GC_SLICE_END, |
| GC_CYCLE_END |
| }; |
| |
| struct JS_FRIEND_API(GCDescription) { |
| bool isCompartment_; |
| |
| GCDescription(bool isCompartment) |
| : isCompartment_(isCompartment) {} |
| |
| jschar *formatMessage(JSRuntime *rt) const; |
| jschar *formatJSON(JSRuntime *rt, uint64_t timestamp) const; |
| }; |
| |
| typedef void |
| (* GCSliceCallback)(JSRuntime *rt, GCProgress progress, const GCDescription &desc); |
| |
| extern JS_FRIEND_API(GCSliceCallback) |
| SetGCSliceCallback(JSRuntime *rt, GCSliceCallback callback); |
| |
| /* |
| * Signals a good place to do an incremental slice, because the browser is |
| * drawing a frame. |
| */ |
| extern JS_FRIEND_API(void) |
| NotifyDidPaint(JSRuntime *rt); |
| |
| extern JS_FRIEND_API(bool) |
| IsIncrementalGCEnabled(JSRuntime *rt); |
| |
| JS_FRIEND_API(bool) |
| IsIncrementalGCInProgress(JSRuntime *rt); |
| |
| extern JS_FRIEND_API(void) |
| DisableIncrementalGC(JSRuntime *rt); |
| |
| extern JS_FRIEND_API(void) |
| DisableGenerationalGC(JSRuntime *rt); |
| |
| extern JS_FRIEND_API(bool) |
| IsIncrementalBarrierNeeded(JSRuntime *rt); |
| |
| extern JS_FRIEND_API(bool) |
| IsIncrementalBarrierNeeded(JSContext *cx); |
| |
| extern JS_FRIEND_API(void) |
| IncrementalReferenceBarrier(void *ptr, JSGCTraceKind kind); |
| |
| extern JS_FRIEND_API(void) |
| IncrementalValueBarrier(const Value &v); |
| |
| extern JS_FRIEND_API(void) |
| IncrementalObjectBarrier(JSObject *obj); |
| |
| extern JS_FRIEND_API(void) |
| PokeGC(JSRuntime *rt); |
| |
| /* Was the most recent GC run incrementally? */ |
| extern JS_FRIEND_API(bool) |
| WasIncrementalGC(JSRuntime *rt); |
| |
| class ObjectPtr |
| { |
| JSObject *value; |
| |
| public: |
| ObjectPtr() : value(NULL) {} |
| |
| ObjectPtr(JSObject *obj) : value(obj) {} |
| |
| /* Always call finalize before the destructor. */ |
| ~ObjectPtr() { JS_ASSERT(!value); } |
| |
| void finalize(JSRuntime *rt) { |
| if (IsIncrementalBarrierNeeded(rt)) |
| IncrementalObjectBarrier(value); |
| value = NULL; |
| } |
| |
| void init(JSObject *obj) { value = obj; } |
| |
| JSObject *get() const { return value; } |
| |
| void writeBarrierPre(JSRuntime *rt) { |
| IncrementalObjectBarrier(value); |
| } |
| |
| bool isAboutToBeFinalized() { |
| return JS_IsAboutToBeFinalized(&value); |
| } |
| |
| ObjectPtr &operator=(JSObject *obj) { |
| IncrementalObjectBarrier(value); |
| value = obj; |
| return *this; |
| } |
| |
| void trace(JSTracer *trc, const char *name) { |
| JS_CallObjectTracer(trc, &value, name); |
| } |
| |
| JSObject &operator*() const { return *value; } |
| JSObject *operator->() const { return value; } |
| operator JSObject *() const { return value; } |
| }; |
| |
| /* |
| * Unsets the gray bit for anything reachable from |thing|. |kind| should not be |
| * JSTRACE_SHAPE. |thing| should be non-null. |
| */ |
| extern JS_FRIEND_API(void) |
| UnmarkGrayGCThingRecursively(void *thing, JSGCTraceKind kind); |
| |
| /* |
| * This should be called when an object that is marked gray is exposed to the JS |
| * engine (by handing it to running JS code or writing it into live JS |
| * data). During incremental GC, since the gray bits haven't been computed yet, |
| * we conservatively mark the object black. |
| */ |
| static JS_ALWAYS_INLINE void |
| ExposeGCThingToActiveJS(void *thing, JSGCTraceKind kind) |
| { |
| JS_ASSERT(kind != JSTRACE_SHAPE); |
| |
| shadow::Runtime *rt = js::gc::GetGCThingRuntime(thing); |
| #ifdef JSGC_GENERATIONAL |
| /* |
| * GC things residing in the nursery cannot be gray: they have no mark bits. |
| * All live objects in the nursery are moved to tenured at the beginning of |
| * each GC slice, so the gray marker never sees nursery things. |
| */ |
| if (uintptr_t(thing) >= rt->gcNurseryStart_ && uintptr_t(thing) < rt->gcNurseryEnd_) |
| return; |
| #endif |
| if (IsIncrementalBarrierNeededOnGCThing(rt, thing, kind)) |
| IncrementalReferenceBarrier(thing, kind); |
| else if (GCThingIsMarkedGray(thing)) |
| UnmarkGrayGCThingRecursively(thing, kind); |
| } |
| |
| static JS_ALWAYS_INLINE void |
| ExposeValueToActiveJS(const Value &v) |
| { |
| if (v.isMarkable()) |
| ExposeGCThingToActiveJS(v.toGCThing(), v.gcKind()); |
| } |
| |
| /* |
| * If a GC is currently marking, mark the object black. |
| */ |
| static JS_ALWAYS_INLINE void |
| MarkGCThingAsLive(JSRuntime *rt_, void *thing, JSGCTraceKind kind) |
| { |
| shadow::Runtime *rt = reinterpret_cast<JS::shadow::Runtime*>(rt_); |
| |
| #ifdef JSGC_GENERATIONAL |
| /* |
| * Any object in the nursery will not be freed during any GC running at that time. |
| */ |
| if (js::gc::IsInsideNursery(rt, thing)) |
| return; |
| #endif |
| if (IsIncrementalBarrierNeededOnGCThing(rt, thing, kind)) |
| IncrementalReferenceBarrier(thing, kind); |
| } |
| |
| static JS_ALWAYS_INLINE void |
| MarkStringAsLive(JSContext *cx, JSString *string) |
| { |
| MarkGCThingAsLive(js::GetRuntime(cx), string, JSTRACE_STRING); |
| } |
| |
| } /* namespace JS */ |
| |
| #endif /* js_GCAPI_h */ |