diff options
Diffstat (limited to 'js/src/gc/GCInternals.h')
-rw-r--r-- | js/src/gc/GCInternals.h | 344 |
1 files changed, 344 insertions, 0 deletions
diff --git a/js/src/gc/GCInternals.h b/js/src/gc/GCInternals.h new file mode 100644 index 0000000000..c234ad4b2b --- /dev/null +++ b/js/src/gc/GCInternals.h @@ -0,0 +1,344 @@ +/* -*- 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/. */ + +/* + * GC-internal definitions. + */ + +#ifndef gc_GCInternals_h +#define gc_GCInternals_h + +#include "mozilla/Maybe.h" +#include "mozilla/TimeStamp.h" + +#include "gc/Cell.h" +#include "gc/GC.h" +#include "gc/GCContext.h" +#include "vm/GeckoProfiler.h" +#include "vm/HelperThreads.h" +#include "vm/JSContext.h" + +namespace js { +namespace gc { + +/* + * There are a couple of classes here that serve mostly as "tokens" indicating + * that a precondition holds. Some functions force the caller to possess such a + * token because they require the precondition to hold, and it is better to make + * the precondition explicit at the API entry point than to crash in an + * assertion later on when it is relied upon. + */ + +struct MOZ_RAII AutoAssertNoNurseryAlloc { +#ifdef DEBUG + AutoAssertNoNurseryAlloc(); + ~AutoAssertNoNurseryAlloc(); +#else + AutoAssertNoNurseryAlloc() {} +#endif +}; + +/* + * A class that serves as a token that the nursery in the current thread's zone + * group is empty. + */ +class MOZ_RAII AutoAssertEmptyNursery { + protected: + JSContext* cx; + + mozilla::Maybe<AutoAssertNoNurseryAlloc> noAlloc; + + // Check that the nursery is empty. + void checkCondition(JSContext* cx); + + // For subclasses that need to empty the nursery in their constructors. + AutoAssertEmptyNursery() : cx(nullptr) {} + + public: + explicit AutoAssertEmptyNursery(JSContext* cx) : cx(nullptr) { + checkCondition(cx); + } + + AutoAssertEmptyNursery(const AutoAssertEmptyNursery& other) + : AutoAssertEmptyNursery(other.cx) {} +}; + +/* + * Evict the nursery upon construction. Serves as a token indicating that the + * nursery is empty. (See AutoAssertEmptyNursery, above.) + */ +class MOZ_RAII AutoEmptyNursery : public AutoAssertEmptyNursery { + public: + explicit AutoEmptyNursery(JSContext* cx); +}; + +// Abstract base class for exclusive heap access for tracing or GC. +class MOZ_RAII AutoHeapSession { + public: + ~AutoHeapSession(); + + protected: + AutoHeapSession(GCRuntime* gc, JS::HeapState state); + + private: + AutoHeapSession(const AutoHeapSession&) = delete; + void operator=(const AutoHeapSession&) = delete; + + GCRuntime* gc; + JS::HeapState prevState; + mozilla::Maybe<AutoGeckoProfilerEntry> profilingStackFrame; +}; + +class MOZ_RAII AutoGCSession : public AutoHeapSession { + public: + explicit AutoGCSession(GCRuntime* gc, JS::HeapState state) + : AutoHeapSession(gc, state) {} +}; + +class MOZ_RAII AutoMajorGCProfilerEntry : public AutoGeckoProfilerEntry { + public: + explicit AutoMajorGCProfilerEntry(GCRuntime* gc); +}; + +class MOZ_RAII AutoTraceSession : public AutoHeapSession { + public: + explicit AutoTraceSession(JSRuntime* rt) + : AutoHeapSession(&rt->gc, JS::HeapState::Tracing) {} +}; + +struct MOZ_RAII AutoFinishGC { + explicit AutoFinishGC(JSContext* cx, JS::GCReason reason) { + FinishGC(cx, reason); + } +}; + +// This class should be used by any code that needs exclusive access to the heap +// in order to trace through it. +class MOZ_RAII AutoPrepareForTracing : private AutoFinishGC, + public AutoTraceSession { + public: + explicit AutoPrepareForTracing(JSContext* cx) + : AutoFinishGC(cx, JS::GCReason::PREPARE_FOR_TRACING), + AutoTraceSession(cx->runtime()) {} +}; + +// This class should be used by any code that needs exclusive access to the heap +// in order to trace through it. +// +// This version also empties the nursery after finishing any ongoing GC. +class MOZ_RAII AutoEmptyNurseryAndPrepareForTracing : private AutoFinishGC, + public AutoEmptyNursery, + public AutoTraceSession { + public: + explicit AutoEmptyNurseryAndPrepareForTracing(JSContext* cx) + : AutoFinishGC(cx, JS::GCReason::PREPARE_FOR_TRACING), + AutoEmptyNursery(cx), + AutoTraceSession(cx->runtime()) {} +}; + +/* + * Temporarily disable incremental barriers. + */ +class AutoDisableBarriers { + public: + explicit AutoDisableBarriers(GCRuntime* gc); + ~AutoDisableBarriers(); + + private: + GCRuntime* gc; +}; + +// Set compartments' maybeAlive flags if anything is marked while this class is +// live. This is used while marking roots. +class AutoUpdateLiveCompartments { + GCRuntime* gc; + + public: + explicit AutoUpdateLiveCompartments(GCRuntime* gc); + ~AutoUpdateLiveCompartments(); +}; + +class MOZ_RAII AutoRunParallelTask : public GCParallelTask { + // This class takes a pointer to a member function of GCRuntime. + using TaskFunc = JS_MEMBER_FN_PTR_TYPE(GCRuntime, void); + + TaskFunc func_; + AutoLockHelperThreadState& lock_; + + public: + AutoRunParallelTask(GCRuntime* gc, TaskFunc func, gcstats::PhaseKind phase, + GCUse use, AutoLockHelperThreadState& lock) + : GCParallelTask(gc, phase, use), func_(func), lock_(lock) { + gc->startTask(*this, lock_); + } + + ~AutoRunParallelTask() { gc->joinTask(*this, lock_); } + + void run(AutoLockHelperThreadState& lock) override { + AutoUnlockHelperThreadState unlock(lock); + + // The hazard analysis can't tell what the call to func_ will do but it's + // not allowed to GC. + JS::AutoSuppressGCAnalysis nogc; + + // Call pointer to member function on |gc|. + JS_CALL_MEMBER_FN_PTR(gc, func_); + } +}; + +GCAbortReason IsIncrementalGCUnsafe(JSRuntime* rt); + +#ifdef JS_GC_ZEAL + +class MOZ_RAII AutoStopVerifyingBarriers { + GCRuntime* gc; + bool restartPreVerifier; + + public: + AutoStopVerifyingBarriers(JSRuntime* rt, bool isShutdown) : gc(&rt->gc) { + if (gc->isVerifyPreBarriersEnabled()) { + gc->endVerifyPreBarriers(); + restartPreVerifier = !isShutdown; + } else { + restartPreVerifier = false; + } + } + + ~AutoStopVerifyingBarriers() { + // Nasty special case: verification runs a minor GC, which *may* nest + // inside of an outer minor GC. This is not allowed by the + // gc::Statistics phase tree. So we pause the "real" GC, if in fact one + // is in progress. + gcstats::PhaseKind outer = gc->stats().currentPhaseKind(); + if (outer != gcstats::PhaseKind::NONE) { + gc->stats().endPhase(outer); + } + MOZ_ASSERT(gc->stats().currentPhaseKind() == gcstats::PhaseKind::NONE); + + if (restartPreVerifier) { + gc->startVerifyPreBarriers(); + } + + if (outer != gcstats::PhaseKind::NONE) { + gc->stats().beginPhase(outer); + } + } +}; +#else +struct MOZ_RAII AutoStopVerifyingBarriers { + AutoStopVerifyingBarriers(JSRuntime*, bool) {} +}; +#endif /* JS_GC_ZEAL */ + +class MOZ_RAII AutoPoisonFreedJitCode { + JS::GCContext* const gcx; + + public: + explicit AutoPoisonFreedJitCode(JS::GCContext* gcx) : gcx(gcx) {} + ~AutoPoisonFreedJitCode() { gcx->poisonJitCode(); } +}; + +// Set/restore the GCContext GC use flag for the current thread. + +class MOZ_RAII AutoSetThreadGCUse { + public: + AutoSetThreadGCUse(JS::GCContext* gcx, GCUse use) + : gcx(gcx), prevUse(gcx->gcUse_) { + gcx->gcUse_ = use; + } + explicit AutoSetThreadGCUse(GCUse use) + : AutoSetThreadGCUse(TlsGCContext.get(), use) {} + + ~AutoSetThreadGCUse() { gcx->gcUse_ = prevUse; } + + protected: + JS::GCContext* gcx; + GCUse prevUse; +}; + +template <GCUse Use> +class AutoSetThreadGCUseT : public AutoSetThreadGCUse { + public: + explicit AutoSetThreadGCUseT(JS::GCContext* gcx) + : AutoSetThreadGCUse(gcx, Use) {} + AutoSetThreadGCUseT() : AutoSetThreadGCUseT(TlsGCContext.get()) {} +}; + +using AutoSetThreadIsPerformingGC = AutoSetThreadGCUseT<GCUse::Unspecified>; +using AutoSetThreadIsMarking = AutoSetThreadGCUseT<GCUse::Marking>; +using AutoSetThreadIsFinalizing = AutoSetThreadGCUseT<GCUse::Finalizing>; + +class AutoSetThreadIsSweeping : public AutoSetThreadGCUseT<GCUse::Sweeping> { + public: + explicit AutoSetThreadIsSweeping(JS::GCContext* gcx, + JS::Zone* sweepZone = nullptr) + : AutoSetThreadGCUseT(gcx) { +#ifdef DEBUG + prevZone = gcx->gcSweepZone_; + gcx->gcSweepZone_ = sweepZone; +#endif + } + explicit AutoSetThreadIsSweeping(JS::Zone* sweepZone = nullptr) + : AutoSetThreadIsSweeping(TlsGCContext.get(), sweepZone) {} + + ~AutoSetThreadIsSweeping() { +#ifdef DEBUG + MOZ_ASSERT_IF(prevUse == GCUse::None, !prevZone); + gcx->gcSweepZone_ = prevZone; +#endif + } + + private: +#ifdef DEBUG + JS::Zone* prevZone; +#endif +}; + +#ifdef JSGC_HASH_TABLE_CHECKS +void CheckHashTablesAfterMovingGC(JSRuntime* rt); +void CheckHeapAfterGC(JSRuntime* rt); +#endif + +struct MovingTracer final : public GenericTracerImpl<MovingTracer> { + explicit MovingTracer(JSRuntime* rt); + + private: + template <typename T> + void onEdge(T** thingp, const char* name); + friend class GenericTracerImpl<MovingTracer>; +}; + +struct MinorSweepingTracer final + : public GenericTracerImpl<MinorSweepingTracer> { + explicit MinorSweepingTracer(JSRuntime* rt); + + private: + template <typename T> + void onEdge(T** thingp, const char* name); + friend class GenericTracerImpl<MinorSweepingTracer>; +}; + +extern void DelayCrossCompartmentGrayMarking(GCMarker* maybeMarker, + JSObject* src); + +inline bool IsOOMReason(JS::GCReason reason) { + return reason == JS::GCReason::LAST_DITCH || + reason == JS::GCReason::MEM_PRESSURE; +} + +void* AllocateCellInGC(JS::Zone* zone, AllocKind thingKind); + +void ReadProfileEnv(const char* envName, const char* helpText, bool* enableOut, + bool* workersOut, mozilla::TimeDuration* thresholdOut); + +bool ShouldPrintProfile(JSRuntime* runtime, bool enable, bool workers, + mozilla::TimeDuration threshold, + mozilla::TimeDuration duration); + +} /* namespace gc */ +} /* namespace js */ + +#endif /* gc_GCInternals_h */ |