GCInternals.h (9817B)
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/. */ 6 7 /* 8 * GC-internal definitions. 9 */ 10 11 #ifndef gc_GCInternals_h 12 #define gc_GCInternals_h 13 14 #include "mozilla/Maybe.h" 15 #include "mozilla/Range.h" 16 #include "mozilla/TimeStamp.h" 17 #include "mozilla/Vector.h" 18 19 #include "gc/Cell.h" 20 #include "gc/GC.h" 21 #include "gc/GCContext.h" 22 #include "gc/GCMarker.h" 23 #include "vm/GeckoProfiler.h" 24 #include "vm/HelperThreads.h" 25 #include "vm/JSContext.h" 26 27 namespace js { 28 29 class GCMarker; 30 31 namespace gc { 32 33 /* 34 * There are a couple of classes here that serve mostly as "tokens" indicating 35 * that a precondition holds. Some functions force the caller to possess such a 36 * token because they require the precondition to hold, and it is better to make 37 * the precondition explicit at the API entry point than to crash in an 38 * assertion later on when it is relied upon. 39 */ 40 41 struct MOZ_RAII AutoAssertNoNurseryAlloc { 42 #ifdef DEBUG 43 AutoAssertNoNurseryAlloc(); 44 ~AutoAssertNoNurseryAlloc(); 45 #else 46 AutoAssertNoNurseryAlloc() {} 47 #endif 48 }; 49 50 /* 51 * A class that serves as a token that the nursery in the current thread's zone 52 * group is empty. 53 */ 54 class MOZ_RAII AutoAssertEmptyNursery { 55 protected: 56 JSContext* cx; 57 58 mozilla::Maybe<AutoAssertNoNurseryAlloc> noAlloc; 59 60 // Check that the nursery is empty. 61 void checkCondition(JSContext* cx); 62 63 // For subclasses that need to empty the nursery in their constructors. 64 AutoAssertEmptyNursery() : cx(nullptr) {} 65 66 public: 67 explicit AutoAssertEmptyNursery(JSContext* cx) : cx(nullptr) { 68 checkCondition(cx); 69 } 70 71 AutoAssertEmptyNursery(const AutoAssertEmptyNursery& other) 72 : AutoAssertEmptyNursery(other.cx) {} 73 }; 74 75 /* 76 * Evict the nursery upon construction. Serves as a token indicating that the 77 * nursery is empty. (See AutoAssertEmptyNursery, above.) 78 */ 79 class MOZ_RAII AutoEmptyNursery : public AutoAssertEmptyNursery { 80 public: 81 explicit AutoEmptyNursery(JSContext* cx); 82 }; 83 84 class MOZ_RAII AutoGCSession : public AutoHeapSession { 85 public: 86 explicit AutoGCSession(GCRuntime* gc, JS::HeapState state) 87 : AutoHeapSession(gc, state) {} 88 }; 89 90 class MOZ_RAII AutoMajorGCProfilerEntry : public AutoGeckoProfilerEntry { 91 public: 92 explicit AutoMajorGCProfilerEntry(GCRuntime* gc); 93 }; 94 95 // This class should be used by any code that needs exclusive access to the heap 96 // in order to trace through it. 97 // 98 // This version also empties the nursery after finishing any ongoing GC. 99 class MOZ_RAII AutoEmptyNurseryAndPrepareForTracing : private AutoFinishGC, 100 public AutoEmptyNursery, 101 public AutoTraceSession { 102 public: 103 explicit AutoEmptyNurseryAndPrepareForTracing(JSContext* cx) 104 : AutoFinishGC(cx, JS::GCReason::PREPARE_FOR_TRACING), 105 AutoEmptyNursery(cx), 106 AutoTraceSession(cx->runtime()) {} 107 }; 108 109 // Set compartments' maybeAlive flags if anything is marked while this class is 110 // live. This is used while marking roots. 111 class AutoUpdateLiveCompartments { 112 GCRuntime* gc; 113 114 public: 115 explicit AutoUpdateLiveCompartments(GCRuntime* gc); 116 ~AutoUpdateLiveCompartments(); 117 }; 118 119 class MOZ_RAII AutoRunParallelTask : public GCParallelTask { 120 // This class takes a pointer to a member function of GCRuntime. 121 using TaskFunc = JS_MEMBER_FN_PTR_TYPE(GCRuntime, void); 122 123 TaskFunc func_; 124 AutoLockHelperThreadState& lock_; 125 126 public: 127 AutoRunParallelTask(GCRuntime* gc, TaskFunc func, gcstats::PhaseKind phase, 128 GCUse use, AutoLockHelperThreadState& lock) 129 : GCParallelTask(gc, phase, use), func_(func), lock_(lock) { 130 gc->startTask(*this, lock_); 131 } 132 133 ~AutoRunParallelTask() { gc->joinTask(*this, lock_); } 134 135 void run(AutoLockHelperThreadState& lock) override { 136 AutoUnlockHelperThreadState unlock(lock); 137 138 // The hazard analysis can't tell what the call to func_ will do but it's 139 // not allowed to GC. 140 JS::AutoSuppressGCAnalysis nogc; 141 142 // Call pointer to member function on |gc|. 143 JS_CALL_MEMBER_FN_PTR(gc, func_); 144 } 145 }; 146 147 #ifdef JS_GC_ZEAL 148 149 class MOZ_RAII AutoStopVerifyingBarriers { 150 GCRuntime* gc; 151 bool restartPreVerifier; 152 153 public: 154 AutoStopVerifyingBarriers(JSRuntime* rt, bool isShutdown) : gc(&rt->gc) { 155 if (gc->isVerifyPreBarriersEnabled()) { 156 gc->endVerifyPreBarriers(); 157 restartPreVerifier = !isShutdown; 158 } else { 159 restartPreVerifier = false; 160 } 161 } 162 163 ~AutoStopVerifyingBarriers() { 164 // Nasty special case: verification runs a minor GC, which *may* nest 165 // inside of an outer minor GC. This is not allowed by the 166 // gc::Statistics phase tree. So we pause the "real" GC, if in fact one 167 // is in progress. 168 gcstats::PhaseKind outer = gc->stats().currentPhaseKind(); 169 if (outer != gcstats::PhaseKind::NONE) { 170 gc->stats().endPhase(outer); 171 } 172 MOZ_ASSERT(gc->stats().currentPhaseKind() == gcstats::PhaseKind::NONE); 173 174 if (restartPreVerifier) { 175 gc->startVerifyPreBarriers(); 176 } 177 178 if (outer != gcstats::PhaseKind::NONE) { 179 gc->stats().beginPhase(outer); 180 } 181 } 182 }; 183 #else 184 struct MOZ_RAII AutoStopVerifyingBarriers { 185 AutoStopVerifyingBarriers(JSRuntime*, bool) {} 186 }; 187 #endif /* JS_GC_ZEAL */ 188 189 class MOZ_RAII AutoPoisonFreedJitCode { 190 JS::GCContext* const gcx; 191 192 public: 193 explicit AutoPoisonFreedJitCode(JS::GCContext* gcx) : gcx(gcx) {} 194 ~AutoPoisonFreedJitCode() { gcx->poisonJitCode(); } 195 }; 196 197 // Set/restore the GCContext GC use flag for the current thread. 198 199 class MOZ_RAII AutoSetThreadGCUse { 200 public: 201 AutoSetThreadGCUse(JS::GCContext* gcx, GCUse use) 202 : gcx(gcx), prevUse(gcx->gcUse_) { 203 gcx->gcUse_ = use; 204 } 205 explicit AutoSetThreadGCUse(GCUse use) 206 : AutoSetThreadGCUse(TlsGCContext.get(), use) {} 207 208 ~AutoSetThreadGCUse() { gcx->gcUse_ = prevUse; } 209 210 protected: 211 JS::GCContext* gcx; 212 GCUse prevUse; 213 }; 214 215 template <GCUse Use> 216 class AutoSetThreadGCUseT : public AutoSetThreadGCUse { 217 public: 218 explicit AutoSetThreadGCUseT(JS::GCContext* gcx) 219 : AutoSetThreadGCUse(gcx, Use) {} 220 AutoSetThreadGCUseT() : AutoSetThreadGCUseT(TlsGCContext.get()) {} 221 }; 222 223 using AutoSetThreadIsPerformingGC = AutoSetThreadGCUseT<GCUse::Unspecified>; 224 using AutoSetThreadIsMarking = AutoSetThreadGCUseT<GCUse::Marking>; 225 using AutoSetThreadIsFinalizing = AutoSetThreadGCUseT<GCUse::Finalizing>; 226 227 class AutoSetThreadIsSweeping : public AutoSetThreadGCUseT<GCUse::Sweeping> { 228 public: 229 explicit AutoSetThreadIsSweeping(JS::GCContext* gcx, 230 JS::Zone* sweepZone = nullptr) 231 : AutoSetThreadGCUseT(gcx) { 232 #ifdef DEBUG 233 prevZone = gcx->gcSweepZone_; 234 gcx->gcSweepZone_ = sweepZone; 235 #endif 236 } 237 explicit AutoSetThreadIsSweeping(JS::Zone* sweepZone = nullptr) 238 : AutoSetThreadIsSweeping(TlsGCContext.get(), sweepZone) {} 239 240 ~AutoSetThreadIsSweeping() { 241 #ifdef DEBUG 242 MOZ_ASSERT_IF(prevUse == GCUse::None, !prevZone); 243 gcx->gcSweepZone_ = prevZone; 244 #endif 245 } 246 247 private: 248 #ifdef DEBUG 249 JS::Zone* prevZone; 250 #endif 251 }; 252 253 class MOZ_RAII AutoDisallowPreWriteBarrier { 254 public: 255 explicit AutoDisallowPreWriteBarrier(JS::GCContext* gcx) { 256 #ifdef DEBUG 257 gcx_ = gcx; 258 MOZ_ASSERT(gcx->preWriteBarrierAllowed_); 259 gcx->preWriteBarrierAllowed_ = false; 260 #endif 261 } 262 ~AutoDisallowPreWriteBarrier() { 263 #ifdef DEBUG 264 MOZ_ASSERT(!gcx_->preWriteBarrierAllowed_); 265 gcx_->preWriteBarrierAllowed_ = true; 266 #endif 267 } 268 269 private: 270 #ifdef DEBUG 271 JS::GCContext* gcx_; 272 #endif 273 }; 274 275 #ifdef JSGC_HASH_TABLE_CHECKS 276 void CheckHashTablesAfterMovingGC(JSRuntime* rt); 277 void CheckHeapAfterGC(JSRuntime* rt); 278 #endif 279 280 struct MovingTracer final : public GenericTracerImpl<MovingTracer> { 281 explicit MovingTracer(JSRuntime* rt); 282 283 private: 284 template <typename T> 285 void onEdge(T** thingp, const char* name); 286 friend class GenericTracerImpl<MovingTracer>; 287 }; 288 289 struct MinorSweepingTracer final 290 : public GenericTracerImpl<MinorSweepingTracer> { 291 explicit MinorSweepingTracer(JSRuntime* rt); 292 293 private: 294 template <typename T> 295 void onEdge(T** thingp, const char* name); 296 friend class GenericTracerImpl<MinorSweepingTracer>; 297 }; 298 299 class MOZ_RAII AutoUpdateMarkStackRanges { 300 GCMarker& marker_; 301 302 public: 303 explicit AutoUpdateMarkStackRanges(GCMarker& marker) : marker_(marker) { 304 marker_.updateRangesAtStartOfSlice(); 305 } 306 ~AutoUpdateMarkStackRanges() { marker_.updateRangesAtEndOfSlice(); } 307 }; 308 309 extern void DelayCrossCompartmentGrayMarking(GCMarker* maybeMarker, 310 JSObject* src); 311 312 inline bool IsOOMReason(JS::GCReason reason) { 313 return reason == JS::GCReason::LAST_DITCH || 314 reason == JS::GCReason::MEM_PRESSURE; 315 } 316 317 void* AllocateTenuredCellInGC(JS::Zone* zone, AllocKind thingKind); 318 319 void ReadProfileEnv(const char* envName, const char* helpText, bool* enableOut, 320 bool* workersOut, mozilla::TimeDuration* thresholdOut); 321 322 bool ShouldPrintProfile(JSRuntime* runtime, bool enable, bool workers, 323 mozilla::TimeDuration threshold, 324 mozilla::TimeDuration duration); 325 326 using CharRange = mozilla::Range<const char>; 327 using CharRangeVector = Vector<CharRange, 0, SystemAllocPolicy>; 328 329 // Split a string on a given character and produce a vector of CharRanges that 330 // reference the input string. 331 extern bool SplitStringBy(const char* string, char delimiter, 332 CharRangeVector* resultOut); 333 extern bool SplitStringBy(const CharRange& string, char delimiter, 334 CharRangeVector* resultOut); 335 336 } /* namespace gc */ 337 } /* namespace js */ 338 339 #endif /* gc_GCInternals_h */