|
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/. */ |
|
6 |
|
7 #ifndef js_GCAPI_h |
|
8 #define js_GCAPI_h |
|
9 |
|
10 #include "mozilla/NullPtr.h" |
|
11 |
|
12 #include "js/HeapAPI.h" |
|
13 #include "js/RootingAPI.h" |
|
14 #include "js/Value.h" |
|
15 |
|
16 typedef enum JSGCMode { |
|
17 /* Perform only global GCs. */ |
|
18 JSGC_MODE_GLOBAL = 0, |
|
19 |
|
20 /* Perform per-compartment GCs until too much garbage has accumulated. */ |
|
21 JSGC_MODE_COMPARTMENT = 1, |
|
22 |
|
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; |
|
29 |
|
30 namespace JS { |
|
31 |
|
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) |
|
90 |
|
91 namespace gcreason { |
|
92 |
|
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, |
|
100 |
|
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 }; |
|
109 |
|
110 } /* namespace gcreason */ |
|
111 |
|
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 */ |
|
125 |
|
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); |
|
131 |
|
132 /* |
|
133 * Schedule all zones to be collected in the next GC. |
|
134 */ |
|
135 extern JS_FRIEND_API(void) |
|
136 PrepareForFullGC(JSRuntime *rt); |
|
137 |
|
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); |
|
145 |
|
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); |
|
152 |
|
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); |
|
159 |
|
160 /* |
|
161 * Non-Incremental GC: |
|
162 * |
|
163 * The following functions perform a non-incremental GC. |
|
164 */ |
|
165 |
|
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); |
|
173 |
|
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); |
|
181 |
|
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 */ |
|
204 |
|
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); |
|
216 |
|
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); |
|
225 |
|
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 */ |
|
236 |
|
237 GC_CYCLE_BEGIN, |
|
238 GC_SLICE_BEGIN, |
|
239 GC_SLICE_END, |
|
240 GC_CYCLE_END |
|
241 }; |
|
242 |
|
243 struct JS_FRIEND_API(GCDescription) { |
|
244 bool isCompartment_; |
|
245 |
|
246 GCDescription(bool isCompartment) |
|
247 : isCompartment_(isCompartment) {} |
|
248 |
|
249 jschar *formatMessage(JSRuntime *rt) const; |
|
250 jschar *formatJSON(JSRuntime *rt, uint64_t timestamp) const; |
|
251 }; |
|
252 |
|
253 typedef void |
|
254 (* GCSliceCallback)(JSRuntime *rt, GCProgress progress, const GCDescription &desc); |
|
255 |
|
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); |
|
263 |
|
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); |
|
272 |
|
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); |
|
283 |
|
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); |
|
290 |
|
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); |
|
298 |
|
299 extern JS_FRIEND_API(bool) |
|
300 IsIncrementalBarrierNeeded(JSContext *cx); |
|
301 |
|
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); |
|
308 |
|
309 extern JS_FRIEND_API(void) |
|
310 IncrementalValueBarrier(const Value &v); |
|
311 |
|
312 extern JS_FRIEND_API(void) |
|
313 IncrementalObjectBarrier(JSObject *obj); |
|
314 |
|
315 /* |
|
316 * Returns true if the most recent GC ran incrementally. |
|
317 */ |
|
318 extern JS_FRIEND_API(bool) |
|
319 WasIncrementalGC(JSRuntime *rt); |
|
320 |
|
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 */ |
|
328 |
|
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 |
|
336 |
|
337 public: |
|
338 AutoDisableGenerationalGC(JSRuntime *rt); |
|
339 ~AutoDisableGenerationalGC(); |
|
340 }; |
|
341 |
|
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); |
|
348 |
|
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(); |
|
356 |
|
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); |
|
364 |
|
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; |
|
383 |
|
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 }; |
|
395 |
|
396 class JS_PUBLIC_API(ObjectPtr) |
|
397 { |
|
398 Heap<JSObject *> value; |
|
399 |
|
400 public: |
|
401 ObjectPtr() : value(nullptr) {} |
|
402 |
|
403 ObjectPtr(JSObject *obj) : value(obj) {} |
|
404 |
|
405 /* Always call finalize before the destructor. */ |
|
406 ~ObjectPtr() { MOZ_ASSERT(!value); } |
|
407 |
|
408 void finalize(JSRuntime *rt) { |
|
409 if (IsIncrementalBarrierNeeded(rt)) |
|
410 IncrementalObjectBarrier(value); |
|
411 value = nullptr; |
|
412 } |
|
413 |
|
414 void init(JSObject *obj) { value = obj; } |
|
415 |
|
416 JSObject *get() const { return value; } |
|
417 |
|
418 void writeBarrierPre(JSRuntime *rt) { |
|
419 IncrementalObjectBarrier(value); |
|
420 } |
|
421 |
|
422 bool isAboutToBeFinalized(); |
|
423 |
|
424 ObjectPtr &operator=(JSObject *obj) { |
|
425 IncrementalObjectBarrier(value); |
|
426 value = obj; |
|
427 return *this; |
|
428 } |
|
429 |
|
430 void trace(JSTracer *trc, const char *name); |
|
431 |
|
432 JSObject &operator*() const { return *value; } |
|
433 JSObject *operator->() const { return value; } |
|
434 operator JSObject *() const { return value; } |
|
435 }; |
|
436 |
|
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); |
|
443 |
|
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); |
|
454 |
|
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 } |
|
470 |
|
471 static MOZ_ALWAYS_INLINE void |
|
472 ExposeValueToActiveJS(const Value &v) |
|
473 { |
|
474 if (v.isMarkable()) |
|
475 ExposeGCThingToActiveJS(v.toGCThing(), v.gcKind()); |
|
476 } |
|
477 |
|
478 static MOZ_ALWAYS_INLINE void |
|
479 ExposeObjectToActiveJS(JSObject *obj) |
|
480 { |
|
481 ExposeGCThingToActiveJS(obj, JSTRACE_OBJECT); |
|
482 } |
|
483 |
|
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 } |
|
501 |
|
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 } |
|
508 |
|
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); |
|
516 |
|
517 /* |
|
518 * Internal to Firefox. |
|
519 */ |
|
520 extern JS_FRIEND_API(void) |
|
521 NotifyDidPaint(JSRuntime *rt); |
|
522 |
|
523 } /* namespace JS */ |
|
524 |
|
525 #endif /* js_GCAPI_h */ |