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