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/TimeStamp.h"
16 
17 #include "gc/GC.h"
18 #include "vm/GeckoProfiler.h"
19 #include "vm/HelperThreads.h"
20 #include "vm/JSContext.h"
21 
22 namespace js {
23 namespace gc {
24 
25 /*
26  * There are a couple of classes here that serve mostly as "tokens" indicating
27  * that a precondition holds. Some functions force the caller to possess such a
28  * token because they require the precondition to hold, and it is better to make
29  * the precondition explicit at the API entry point than to crash in an
30  * assertion later on when it is relied upon.
31  */
32 
33 struct MOZ_RAII AutoAssertNoNurseryAlloc {
34 #ifdef DEBUG
35   AutoAssertNoNurseryAlloc();
36   ~AutoAssertNoNurseryAlloc();
37 #else
38   AutoAssertNoNurseryAlloc() {}
39 #endif
40 };
41 
42 /*
43  * A class that serves as a token that the nursery in the current thread's zone
44  * group is empty.
45  */
46 class MOZ_RAII AutoAssertEmptyNursery {
47  protected:
48   JSContext* cx;
49 
50   mozilla::Maybe<AutoAssertNoNurseryAlloc> noAlloc;
51 
52   // Check that the nursery is empty.
53   void checkCondition(JSContext* cx);
54 
55   // For subclasses that need to empty the nursery in their constructors.
AutoAssertEmptyNursery()56   AutoAssertEmptyNursery() : cx(nullptr) {}
57 
58  public:
AutoAssertEmptyNursery(JSContext * cx)59   explicit AutoAssertEmptyNursery(JSContext* cx) : cx(nullptr) {
60     checkCondition(cx);
61   }
62 
AutoAssertEmptyNursery(const AutoAssertEmptyNursery & other)63   AutoAssertEmptyNursery(const AutoAssertEmptyNursery& other)
64       : AutoAssertEmptyNursery(other.cx) {}
65 };
66 
67 /*
68  * Evict the nursery upon construction. Serves as a token indicating that the
69  * nursery is empty. (See AutoAssertEmptyNursery, above.)
70  */
71 class MOZ_RAII AutoEmptyNursery : public AutoAssertEmptyNursery {
72  public:
73   explicit AutoEmptyNursery(JSContext* cx);
74 };
75 
76 // Abstract base class for exclusive heap access for tracing or GC.
77 class MOZ_RAII AutoHeapSession {
78  public:
79   ~AutoHeapSession();
80 
81  protected:
82   AutoHeapSession(GCRuntime* gc, JS::HeapState state);
83 
84  private:
85   AutoHeapSession(const AutoHeapSession&) = delete;
86   void operator=(const AutoHeapSession&) = delete;
87 
88   GCRuntime* gc;
89   JS::HeapState prevState;
90   mozilla::Maybe<AutoGeckoProfilerEntry> profilingStackFrame;
91 };
92 
93 class MOZ_RAII AutoGCSession : public AutoHeapSession {
94  public:
AutoGCSession(GCRuntime * gc,JS::HeapState state)95   explicit AutoGCSession(GCRuntime* gc, JS::HeapState state)
96       : AutoHeapSession(gc, state) {}
97 };
98 
99 class MOZ_RAII AutoMajorGCProfilerEntry : public AutoGeckoProfilerEntry {
100  public:
101   explicit AutoMajorGCProfilerEntry(GCRuntime* gc);
102 };
103 
104 class MOZ_RAII AutoTraceSession : public AutoHeapSession {
105  public:
AutoTraceSession(JSRuntime * rt)106   explicit AutoTraceSession(JSRuntime* rt)
107       : AutoHeapSession(&rt->gc, JS::HeapState::Tracing) {}
108 };
109 
110 struct MOZ_RAII AutoFinishGC {
AutoFinishGCAutoFinishGC111   explicit AutoFinishGC(JSContext* cx, JS::GCReason reason) {
112     FinishGC(cx, reason);
113   }
114 };
115 
116 // This class should be used by any code that needs exclusive access to the heap
117 // in order to trace through it.
118 class MOZ_RAII AutoPrepareForTracing : private AutoFinishGC,
119                                        public AutoTraceSession {
120  public:
AutoPrepareForTracing(JSContext * cx)121   explicit AutoPrepareForTracing(JSContext* cx)
122       : AutoFinishGC(cx, JS::GCReason::PREPARE_FOR_TRACING),
123         AutoTraceSession(cx->runtime()) {}
124 };
125 
126 // This class should be used by any code that needs exclusive access to the heap
127 // in order to trace through it.
128 //
129 // This version also empties the nursery after finishing any ongoing GC.
130 class MOZ_RAII AutoEmptyNurseryAndPrepareForTracing : private AutoFinishGC,
131                                                       public AutoEmptyNursery,
132                                                       public AutoTraceSession {
133  public:
AutoEmptyNurseryAndPrepareForTracing(JSContext * cx)134   explicit AutoEmptyNurseryAndPrepareForTracing(JSContext* cx)
135       : AutoFinishGC(cx, JS::GCReason::PREPARE_FOR_TRACING),
136         AutoEmptyNursery(cx),
137         AutoTraceSession(cx->runtime()) {}
138 };
139 
140 /*
141  * Temporarily disable incremental barriers.
142  */
143 class AutoDisableBarriers {
144  public:
145   explicit AutoDisableBarriers(GCRuntime* gc);
146   ~AutoDisableBarriers();
147 
148  private:
149   GCRuntime* gc;
150 };
151 
152 // Set compartments' maybeAlive flags if anything is marked while this class is
153 // live. This is used while marking roots.
154 class AutoUpdateLiveCompartments {
155   GCRuntime* gc;
156 
157  public:
158   explicit AutoUpdateLiveCompartments(GCRuntime* gc);
159   ~AutoUpdateLiveCompartments();
160 };
161 
162 class MOZ_RAII AutoRunParallelTask : public GCParallelTask {
163   // This class takes a pointer to a member function of GCRuntime.
164   using TaskFunc = JS_MEMBER_FN_PTR_TYPE(GCRuntime, void);
165 
166   TaskFunc func_;
167   AutoLockHelperThreadState& lock_;
168 
169  public:
AutoRunParallelTask(GCRuntime * gc,TaskFunc func,gcstats::PhaseKind phase,AutoLockHelperThreadState & lock)170   AutoRunParallelTask(GCRuntime* gc, TaskFunc func, gcstats::PhaseKind phase,
171                       AutoLockHelperThreadState& lock)
172       : GCParallelTask(gc, phase), func_(func), lock_(lock) {
173     gc->startTask(*this, lock_);
174   }
175 
~AutoRunParallelTask()176   ~AutoRunParallelTask() { gc->joinTask(*this, lock_); }
177 
run(AutoLockHelperThreadState & lock)178   void run(AutoLockHelperThreadState& lock) override {
179     AutoUnlockHelperThreadState unlock(lock);
180 
181     // The hazard analysis can't tell what the call to func_ will do but it's
182     // not allowed to GC.
183     JS::AutoSuppressGCAnalysis nogc;
184 
185     // Call pointer to member function on |gc|.
186     JS_CALL_MEMBER_FN_PTR(gc, func_);
187   }
188 };
189 
190 GCAbortReason IsIncrementalGCUnsafe(JSRuntime* rt);
191 
192 #ifdef JS_GC_ZEAL
193 
194 class MOZ_RAII AutoStopVerifyingBarriers {
195   GCRuntime* gc;
196   bool restartPreVerifier;
197 
198  public:
AutoStopVerifyingBarriers(JSRuntime * rt,bool isShutdown)199   AutoStopVerifyingBarriers(JSRuntime* rt, bool isShutdown) : gc(&rt->gc) {
200     if (gc->isVerifyPreBarriersEnabled()) {
201       gc->endVerifyPreBarriers();
202       restartPreVerifier = !isShutdown;
203     } else {
204       restartPreVerifier = false;
205     }
206   }
207 
~AutoStopVerifyingBarriers()208   ~AutoStopVerifyingBarriers() {
209     // Nasty special case: verification runs a minor GC, which *may* nest
210     // inside of an outer minor GC. This is not allowed by the
211     // gc::Statistics phase tree. So we pause the "real" GC, if in fact one
212     // is in progress.
213     gcstats::PhaseKind outer = gc->stats().currentPhaseKind();
214     if (outer != gcstats::PhaseKind::NONE) {
215       gc->stats().endPhase(outer);
216     }
217     MOZ_ASSERT(gc->stats().currentPhaseKind() == gcstats::PhaseKind::NONE);
218 
219     if (restartPreVerifier) {
220       gc->startVerifyPreBarriers();
221     }
222 
223     if (outer != gcstats::PhaseKind::NONE) {
224       gc->stats().beginPhase(outer);
225     }
226   }
227 };
228 #else
229 struct MOZ_RAII AutoStopVerifyingBarriers {
AutoStopVerifyingBarriersAutoStopVerifyingBarriers230   AutoStopVerifyingBarriers(JSRuntime*, bool) {}
231 };
232 #endif /* JS_GC_ZEAL */
233 
234 class MOZ_RAII AutoDisableCompartmentCheckTracer {
235 #ifdef DEBUG
236   JSContext* cx_;
237   bool prev_;
238 
239  public:
AutoDisableCompartmentCheckTracer()240   AutoDisableCompartmentCheckTracer()
241       : cx_(TlsContext.get()), prev_(cx_->disableCompartmentCheckTracer) {
242     cx_->disableCompartmentCheckTracer = true;
243   }
~AutoDisableCompartmentCheckTracer()244   ~AutoDisableCompartmentCheckTracer() {
245     cx_->disableCompartmentCheckTracer = prev_;
246   }
247 #else
248  public:
249   AutoDisableCompartmentCheckTracer(){};
250 #endif
251 };
252 
253 #ifdef JSGC_HASH_TABLE_CHECKS
254 void CheckHashTablesAfterMovingGC(JSRuntime* rt);
255 void CheckHeapAfterGC(JSRuntime* rt);
256 #endif
257 
258 struct MovingTracer final : public GenericTracerImpl<MovingTracer> {
259   explicit MovingTracer(JSRuntime* rt);
260 
261  private:
262   template <typename T>
263   T* onEdge(T* thingp);
264   friend class GenericTracerImpl<MovingTracer>;
265 };
266 
267 struct MinorSweepingTracer final
268     : public GenericTracerImpl<MinorSweepingTracer> {
269   explicit MinorSweepingTracer(JSRuntime* rt);
270 
271  private:
272   template <typename T>
273   T* onEdge(T* thingp);
274   friend class GenericTracerImpl<MinorSweepingTracer>;
275 };
276 
277 extern void DelayCrossCompartmentGrayMarking(JSObject* src);
278 
IsOOMReason(JS::GCReason reason)279 inline bool IsOOMReason(JS::GCReason reason) {
280   return reason == JS::GCReason::LAST_DITCH ||
281          reason == JS::GCReason::MEM_PRESSURE;
282 }
283 
284 TenuredCell* AllocateCellInGC(JS::Zone* zone, AllocKind thingKind);
285 
286 void ReadProfileEnv(const char* envName, const char* helpText, bool* enableOut,
287                     bool* workersOut, mozilla::TimeDuration* thresholdOut);
288 
289 bool ShouldPrintProfile(JSRuntime* runtime, bool enable, bool workers,
290                         mozilla::TimeDuration threshold,
291                         mozilla::TimeDuration duration);
292 
293 } /* namespace gc */
294 } /* namespace js */
295 
296 #endif /* gc_GCInternals_h */
297