Fri, 16 Jan 2015 18:13:44 +0100
Integrate suggestion from review to improve consistency with existing code.
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
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/. */
7 #ifndef js_GCAPI_h
8 #define js_GCAPI_h
10 #include "mozilla/NullPtr.h"
12 #include "js/HeapAPI.h"
13 #include "js/RootingAPI.h"
14 #include "js/Value.h"
16 typedef enum JSGCMode {
17 /* Perform only global GCs. */
18 JSGC_MODE_GLOBAL = 0,
20 /* Perform per-compartment GCs until too much garbage has accumulated. */
21 JSGC_MODE_COMPARTMENT = 1,
23 /*
24 * Collect in short time slices rather than all at once. Implies
25 * JSGC_MODE_COMPARTMENT.
26 */
27 JSGC_MODE_INCREMENTAL = 2
28 } JSGCMode;
30 namespace JS {
32 #define GCREASONS(D) \
33 /* Reasons internal to the JS engine */ \
34 D(API) \
35 D(MAYBEGC) \
36 D(DESTROY_RUNTIME) \
37 D(DESTROY_CONTEXT) \
38 D(LAST_DITCH) \
39 D(TOO_MUCH_MALLOC) \
40 D(ALLOC_TRIGGER) \
41 D(DEBUG_GC) \
42 D(TRANSPLANT) \
43 D(RESET) \
44 D(OUT_OF_NURSERY) \
45 D(EVICT_NURSERY) \
46 D(FULL_STORE_BUFFER) \
47 \
48 /* These are reserved for future use. */ \
49 D(RESERVED0) \
50 D(RESERVED1) \
51 D(RESERVED2) \
52 D(RESERVED3) \
53 D(RESERVED4) \
54 D(RESERVED5) \
55 D(RESERVED6) \
56 D(RESERVED7) \
57 D(RESERVED8) \
58 D(RESERVED9) \
59 D(RESERVED10) \
60 D(RESERVED11) \
61 D(RESERVED12) \
62 D(RESERVED13) \
63 D(RESERVED14) \
64 D(RESERVED15) \
65 D(RESERVED16) \
66 D(RESERVED17) \
67 D(RESERVED18) \
68 D(RESERVED19) \
69 \
70 /* Reasons from Firefox */ \
71 D(DOM_WINDOW_UTILS) \
72 D(COMPONENT_UTILS) \
73 D(MEM_PRESSURE) \
74 D(CC_WAITING) \
75 D(CC_FORCED) \
76 D(LOAD_END) \
77 D(POST_COMPARTMENT) \
78 D(PAGE_HIDE) \
79 D(NSJSCONTEXT_DESTROY) \
80 D(SET_NEW_DOCUMENT) \
81 D(SET_DOC_SHELL) \
82 D(DOM_UTILS) \
83 D(DOM_IPC) \
84 D(DOM_WORKER) \
85 D(INTER_SLICE_GC) \
86 D(REFRESH_FRAME) \
87 D(FULL_GC_TIMER) \
88 D(SHUTDOWN_CC) \
89 D(FINISH_LARGE_EVALUTE)
91 namespace gcreason {
93 /* GCReasons will end up looking like JSGC_MAYBEGC */
94 enum Reason {
95 #define MAKE_REASON(name) name,
96 GCREASONS(MAKE_REASON)
97 #undef MAKE_REASON
98 NO_REASON,
99 NUM_REASONS,
101 /*
102 * For telemetry, we want to keep a fixed max bucket size over time so we
103 * don't have to switch histograms. 100 is conservative; as of this writing
104 * there are 26. But the cost of extra buckets seems to be low while the
105 * cost of switching histograms is high.
106 */
107 NUM_TELEMETRY_REASONS = 100
108 };
110 } /* namespace gcreason */
112 /*
113 * Zone GC:
114 *
115 * SpiderMonkey's GC is capable of performing a collection on an arbitrary
116 * subset of the zones in the system. This allows an embedding to minimize
117 * collection time by only collecting zones that have run code recently,
118 * ignoring the parts of the heap that are unlikely to have changed.
119 *
120 * When triggering a GC using one of the functions below, it is first necessary
121 * to select the zones to be collected. To do this, you can call
122 * PrepareZoneForGC on each zone, or you can call PrepareForFullGC to select
123 * all zones. Failing to select any zone is an error.
124 */
126 /*
127 * Schedule the given zone to be collected as part of the next GC.
128 */
129 extern JS_FRIEND_API(void)
130 PrepareZoneForGC(Zone *zone);
132 /*
133 * Schedule all zones to be collected in the next GC.
134 */
135 extern JS_FRIEND_API(void)
136 PrepareForFullGC(JSRuntime *rt);
138 /*
139 * When performing an incremental GC, the zones that were selected for the
140 * previous incremental slice must be selected in subsequent slices as well.
141 * This function selects those slices automatically.
142 */
143 extern JS_FRIEND_API(void)
144 PrepareForIncrementalGC(JSRuntime *rt);
146 /*
147 * Returns true if any zone in the system has been scheduled for GC with one of
148 * the functions above or by the JS engine.
149 */
150 extern JS_FRIEND_API(bool)
151 IsGCScheduled(JSRuntime *rt);
153 /*
154 * Undoes the effect of the Prepare methods above. The given zone will not be
155 * collected in the next GC.
156 */
157 extern JS_FRIEND_API(void)
158 SkipZoneForGC(Zone *zone);
160 /*
161 * Non-Incremental GC:
162 *
163 * The following functions perform a non-incremental GC.
164 */
166 /*
167 * Performs a non-incremental collection of all selected zones. Some objects
168 * that are unreachable from the program may still be alive afterwards because
169 * of internal references.
170 */
171 extern JS_FRIEND_API(void)
172 GCForReason(JSRuntime *rt, gcreason::Reason reason);
174 /*
175 * Perform a non-incremental collection after clearing caches and other
176 * temporary references to objects. This will remove all unreferenced objects
177 * in the system.
178 */
179 extern JS_FRIEND_API(void)
180 ShrinkingGC(JSRuntime *rt, gcreason::Reason reason);
182 /*
183 * Incremental GC:
184 *
185 * Incremental GC divides the full mark-and-sweep collection into multiple
186 * slices, allowing client JavaScript code to run between each slice. This
187 * allows interactive apps to avoid long collection pauses. Incremental GC does
188 * not make collection take less time, it merely spreads that time out so that
189 * the pauses are less noticable.
190 *
191 * For a collection to be carried out incrementally the following conditions
192 * must be met:
193 * - The collection must be run by calling JS::IncrementalGC() rather than
194 * JS_GC().
195 * - The GC mode must have been set to JSGC_MODE_INCREMENTAL with
196 * JS_SetGCParameter().
197 * - All native objects that have their own trace hook must indicate that they
198 * implement read and write barriers with the JSCLASS_IMPLEMENTS_BARRIERS
199 * flag.
200 *
201 * Note: Even if incremental GC is enabled and working correctly,
202 * non-incremental collections can still happen when low on memory.
203 */
205 /*
206 * Begin an incremental collection and perform one slice worth of work or
207 * perform a slice of an ongoing incremental collection. When this function
208 * returns, the collection is not complete. This function must be called
209 * repeatedly until !IsIncrementalGCInProgress(rt).
210 *
211 * Note: SpiderMonkey's GC is not realtime. Slices in practice may be longer or
212 * shorter than the requested interval.
213 */
214 extern JS_FRIEND_API(void)
215 IncrementalGC(JSRuntime *rt, gcreason::Reason reason, int64_t millis = 0);
217 /*
218 * If IsIncrementalGCInProgress(rt), this call finishes the ongoing collection
219 * by performing an arbitrarily long slice. If !IsIncrementalGCInProgress(rt),
220 * this is equivalent to GCForReason. When this function returns,
221 * IsIncrementalGCInProgress(rt) will always be false.
222 */
223 extern JS_FRIEND_API(void)
224 FinishIncrementalGC(JSRuntime *rt, gcreason::Reason reason);
226 enum GCProgress {
227 /*
228 * During non-incremental GC, the GC is bracketed by JSGC_CYCLE_BEGIN/END
229 * callbacks. During an incremental GC, the sequence of callbacks is as
230 * follows:
231 * JSGC_CYCLE_BEGIN, JSGC_SLICE_END (first slice)
232 * JSGC_SLICE_BEGIN, JSGC_SLICE_END (second slice)
233 * ...
234 * JSGC_SLICE_BEGIN, JSGC_CYCLE_END (last slice)
235 */
237 GC_CYCLE_BEGIN,
238 GC_SLICE_BEGIN,
239 GC_SLICE_END,
240 GC_CYCLE_END
241 };
243 struct JS_FRIEND_API(GCDescription) {
244 bool isCompartment_;
246 GCDescription(bool isCompartment)
247 : isCompartment_(isCompartment) {}
249 jschar *formatMessage(JSRuntime *rt) const;
250 jschar *formatJSON(JSRuntime *rt, uint64_t timestamp) const;
251 };
253 typedef void
254 (* GCSliceCallback)(JSRuntime *rt, GCProgress progress, const GCDescription &desc);
256 /*
257 * The GC slice callback is called at the beginning and end of each slice. This
258 * callback may be used for GC notifications as well as to perform additional
259 * marking.
260 */
261 extern JS_FRIEND_API(GCSliceCallback)
262 SetGCSliceCallback(JSRuntime *rt, GCSliceCallback callback);
264 /*
265 * Incremental GC defaults to enabled, but may be disabled for testing or in
266 * embeddings that have not yet implemented barriers on their native classes.
267 * There is not currently a way to re-enable incremental GC once it has been
268 * disabled on the runtime.
269 */
270 extern JS_FRIEND_API(void)
271 DisableIncrementalGC(JSRuntime *rt);
273 /*
274 * Returns true if incremental GC is enabled. Simply having incremental GC
275 * enabled is not sufficient to ensure incremental collections are happening.
276 * See the comment "Incremental GC" above for reasons why incremental GC may be
277 * suppressed. Inspection of the "nonincremental reason" field of the
278 * GCDescription returned by GCSliceCallback may help narrow down the cause if
279 * collections are not happening incrementally when expected.
280 */
281 extern JS_FRIEND_API(bool)
282 IsIncrementalGCEnabled(JSRuntime *rt);
284 /*
285 * Returns true while an incremental GC is ongoing, both when actively
286 * collecting and between slices.
287 */
288 JS_FRIEND_API(bool)
289 IsIncrementalGCInProgress(JSRuntime *rt);
291 /*
292 * Returns true when writes to GC things must call an incremental (pre) barrier.
293 * This is generally only true when running mutator code in-between GC slices.
294 * At other times, the barrier may be elided for performance.
295 */
296 extern JS_FRIEND_API(bool)
297 IsIncrementalBarrierNeeded(JSRuntime *rt);
299 extern JS_FRIEND_API(bool)
300 IsIncrementalBarrierNeeded(JSContext *cx);
302 /*
303 * Notify the GC that a reference to a GC thing is about to be overwritten.
304 * These methods must be called if IsIncrementalBarrierNeeded.
305 */
306 extern JS_FRIEND_API(void)
307 IncrementalReferenceBarrier(void *ptr, JSGCTraceKind kind);
309 extern JS_FRIEND_API(void)
310 IncrementalValueBarrier(const Value &v);
312 extern JS_FRIEND_API(void)
313 IncrementalObjectBarrier(JSObject *obj);
315 /*
316 * Returns true if the most recent GC ran incrementally.
317 */
318 extern JS_FRIEND_API(bool)
319 WasIncrementalGC(JSRuntime *rt);
321 /*
322 * Generational GC:
323 *
324 * Note: Generational GC is not yet enabled by default. The following class
325 * is non-functional unless SpiderMonkey was configured with
326 * --enable-gcgenerational.
327 */
329 /* Ensure that generational GC is disabled within some scope. */
330 class JS_FRIEND_API(AutoDisableGenerationalGC)
331 {
332 JSRuntime *runtime;
333 #if defined(JSGC_GENERATIONAL) && defined(JS_GC_ZEAL)
334 bool restartVerifier;
335 #endif
337 public:
338 AutoDisableGenerationalGC(JSRuntime *rt);
339 ~AutoDisableGenerationalGC();
340 };
342 /*
343 * Returns true if generational allocation and collection is currently enabled
344 * on the given runtime.
345 */
346 extern JS_FRIEND_API(bool)
347 IsGenerationalGCEnabled(JSRuntime *rt);
349 /*
350 * Returns the GC's "number". This does not correspond directly to the number
351 * of GCs that have been run, but is guaranteed to be monotonically increasing
352 * with GC activity.
353 */
354 extern JS_FRIEND_API(size_t)
355 GetGCNumber();
357 /*
358 * The GC does not immediately return the unused memory freed by a collection
359 * back to the system incase it is needed soon afterwards. This call forces the
360 * GC to return this memory immediately.
361 */
362 extern JS_FRIEND_API(void)
363 ShrinkGCBuffers(JSRuntime *rt);
365 /*
366 * Assert if any GC occured while this guard object was live. This is most
367 * useful to help the exact rooting hazard analysis in complex regions, since
368 * it cannot understand dataflow.
369 *
370 * Note: GC behavior is unpredictable even when deterministice and is generally
371 * non-deterministic in practice. The fact that this guard has not
372 * asserted is not a guarantee that a GC cannot happen in the guarded
373 * region. As a rule, anyone performing a GC unsafe action should
374 * understand the GC properties of all code in that region and ensure
375 * that the hazard analysis is correct for that code, rather than relying
376 * on this class.
377 */
378 class JS_PUBLIC_API(AutoAssertNoGC)
379 {
380 #ifdef JS_DEBUG
381 JSRuntime *runtime;
382 size_t gcNumber;
384 public:
385 AutoAssertNoGC();
386 AutoAssertNoGC(JSRuntime *rt);
387 ~AutoAssertNoGC();
388 #else
389 public:
390 /* Prevent unreferenced local warnings in opt builds. */
391 AutoAssertNoGC() {}
392 AutoAssertNoGC(JSRuntime *) {}
393 #endif
394 };
396 class JS_PUBLIC_API(ObjectPtr)
397 {
398 Heap<JSObject *> value;
400 public:
401 ObjectPtr() : value(nullptr) {}
403 ObjectPtr(JSObject *obj) : value(obj) {}
405 /* Always call finalize before the destructor. */
406 ~ObjectPtr() { MOZ_ASSERT(!value); }
408 void finalize(JSRuntime *rt) {
409 if (IsIncrementalBarrierNeeded(rt))
410 IncrementalObjectBarrier(value);
411 value = nullptr;
412 }
414 void init(JSObject *obj) { value = obj; }
416 JSObject *get() const { return value; }
418 void writeBarrierPre(JSRuntime *rt) {
419 IncrementalObjectBarrier(value);
420 }
422 bool isAboutToBeFinalized();
424 ObjectPtr &operator=(JSObject *obj) {
425 IncrementalObjectBarrier(value);
426 value = obj;
427 return *this;
428 }
430 void trace(JSTracer *trc, const char *name);
432 JSObject &operator*() const { return *value; }
433 JSObject *operator->() const { return value; }
434 operator JSObject *() const { return value; }
435 };
437 /*
438 * Unsets the gray bit for anything reachable from |thing|. |kind| should not be
439 * JSTRACE_SHAPE. |thing| should be non-null.
440 */
441 extern JS_FRIEND_API(bool)
442 UnmarkGrayGCThingRecursively(void *thing, JSGCTraceKind kind);
444 /*
445 * This should be called when an object that is marked gray is exposed to the JS
446 * engine (by handing it to running JS code or writing it into live JS
447 * data). During incremental GC, since the gray bits haven't been computed yet,
448 * we conservatively mark the object black.
449 */
450 static MOZ_ALWAYS_INLINE void
451 ExposeGCThingToActiveJS(void *thing, JSGCTraceKind kind)
452 {
453 MOZ_ASSERT(kind != JSTRACE_SHAPE);
455 shadow::Runtime *rt = js::gc::GetGCThingRuntime(thing);
456 #ifdef JSGC_GENERATIONAL
457 /*
458 * GC things residing in the nursery cannot be gray: they have no mark bits.
459 * All live objects in the nursery are moved to tenured at the beginning of
460 * each GC slice, so the gray marker never sees nursery things.
461 */
462 if (js::gc::IsInsideNursery(rt, thing))
463 return;
464 #endif
465 if (IsIncrementalBarrierNeededOnGCThing(rt, thing, kind))
466 IncrementalReferenceBarrier(thing, kind);
467 else if (GCThingIsMarkedGray(thing))
468 UnmarkGrayGCThingRecursively(thing, kind);
469 }
471 static MOZ_ALWAYS_INLINE void
472 ExposeValueToActiveJS(const Value &v)
473 {
474 if (v.isMarkable())
475 ExposeGCThingToActiveJS(v.toGCThing(), v.gcKind());
476 }
478 static MOZ_ALWAYS_INLINE void
479 ExposeObjectToActiveJS(JSObject *obj)
480 {
481 ExposeGCThingToActiveJS(obj, JSTRACE_OBJECT);
482 }
484 /*
485 * If a GC is currently marking, mark the object black.
486 */
487 static MOZ_ALWAYS_INLINE void
488 MarkGCThingAsLive(JSRuntime *rt_, void *thing, JSGCTraceKind kind)
489 {
490 shadow::Runtime *rt = shadow::Runtime::asShadowRuntime(rt_);
491 #ifdef JSGC_GENERATIONAL
492 /*
493 * Any object in the nursery will not be freed during any GC running at that time.
494 */
495 if (js::gc::IsInsideNursery(rt, thing))
496 return;
497 #endif
498 if (IsIncrementalBarrierNeededOnGCThing(rt, thing, kind))
499 IncrementalReferenceBarrier(thing, kind);
500 }
502 static MOZ_ALWAYS_INLINE void
503 MarkStringAsLive(Zone *zone, JSString *string)
504 {
505 JSRuntime *rt = JS::shadow::Zone::asShadowZone(zone)->runtimeFromMainThread();
506 MarkGCThingAsLive(rt, string, JSTRACE_STRING);
507 }
509 /*
510 * Internal to Firefox.
511 *
512 * Note: this is not related to the PokeGC in nsJSEnvironment.
513 */
514 extern JS_FRIEND_API(void)
515 PokeGC(JSRuntime *rt);
517 /*
518 * Internal to Firefox.
519 */
520 extern JS_FRIEND_API(void)
521 NotifyDidPaint(JSRuntime *rt);
523 } /* namespace JS */
525 #endif /* js_GCAPI_h */