Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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_Tracer_h
8 #define js_Tracer_h
10 #include "mozilla/DebugOnly.h"
12 #include "js/GCAPI.h"
13 #include "js/SliceBudget.h"
14 #include "js/TracingAPI.h"
16 namespace js {
17 class GCMarker;
18 class ObjectImpl;
19 namespace gc {
20 class ArenaHeader;
21 }
22 namespace jit {
23 class JitCode;
24 }
25 namespace types {
26 class TypeObject;
27 }
29 static const size_t NON_INCREMENTAL_MARK_STACK_BASE_CAPACITY = 4096;
30 static const size_t INCREMENTAL_MARK_STACK_BASE_CAPACITY = 32768;
32 /*
33 * When the native stack is low, the GC does not call JS_TraceChildren to mark
34 * the reachable "children" of the thing. Rather the thing is put aside and
35 * JS_TraceChildren is called later with more space on the C stack.
36 *
37 * To implement such delayed marking of the children with minimal overhead for
38 * the normal case of sufficient native stack, the code adds a field per arena.
39 * The field markingDelay->link links all arenas with delayed things into a
40 * stack list with the pointer to stack top in GCMarker::unmarkedArenaStackTop.
41 * GCMarker::delayMarkingChildren adds arenas to the stack as necessary while
42 * markDelayedChildren pops the arenas from the stack until it empties.
43 */
44 class MarkStack
45 {
46 friend class GCMarker;
48 uintptr_t *stack_;
49 uintptr_t *tos_;
50 uintptr_t *end_;
52 // The capacity we start with and reset() to.
53 size_t baseCapacity_;
54 size_t maxCapacity_;
56 public:
57 MarkStack(size_t maxCapacity)
58 : stack_(nullptr),
59 tos_(nullptr),
60 end_(nullptr),
61 baseCapacity_(0),
62 maxCapacity_(maxCapacity)
63 {}
65 ~MarkStack() {
66 js_free(stack_);
67 }
69 size_t capacity() { return end_ - stack_; }
71 ptrdiff_t position() const { return tos_ - stack_; }
73 void setStack(uintptr_t *stack, size_t tosIndex, size_t capacity) {
74 stack_ = stack;
75 tos_ = stack + tosIndex;
76 end_ = stack + capacity;
77 }
79 bool init(JSGCMode gcMode);
81 void setBaseCapacity(JSGCMode mode);
82 size_t maxCapacity() const { return maxCapacity_; }
83 void setMaxCapacity(size_t maxCapacity);
85 bool push(uintptr_t item) {
86 if (tos_ == end_) {
87 if (!enlarge(1))
88 return false;
89 }
90 JS_ASSERT(tos_ < end_);
91 *tos_++ = item;
92 return true;
93 }
95 bool push(uintptr_t item1, uintptr_t item2, uintptr_t item3) {
96 uintptr_t *nextTos = tos_ + 3;
97 if (nextTos > end_) {
98 if (!enlarge(3))
99 return false;
100 nextTos = tos_ + 3;
101 }
102 JS_ASSERT(nextTos <= end_);
103 tos_[0] = item1;
104 tos_[1] = item2;
105 tos_[2] = item3;
106 tos_ = nextTos;
107 return true;
108 }
110 bool isEmpty() const {
111 return tos_ == stack_;
112 }
114 uintptr_t pop() {
115 JS_ASSERT(!isEmpty());
116 return *--tos_;
117 }
119 void reset();
121 /* Grow the stack, ensuring there is space for at least count elements. */
122 bool enlarge(unsigned count);
124 void setGCMode(JSGCMode gcMode);
126 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
127 };
129 class GCMarker : public JSTracer
130 {
131 public:
132 explicit GCMarker(JSRuntime *rt);
133 bool init(JSGCMode gcMode);
135 void setMaxCapacity(size_t maxCap) { stack.setMaxCapacity(maxCap); }
136 size_t maxCapacity() const { return stack.maxCapacity(); }
138 void start();
139 void stop();
140 void reset();
142 void pushObject(ObjectImpl *obj) {
143 pushTaggedPtr(ObjectTag, obj);
144 }
146 void pushType(types::TypeObject *type) {
147 pushTaggedPtr(TypeTag, type);
148 }
150 void pushJitCode(jit::JitCode *code) {
151 pushTaggedPtr(JitCodeTag, code);
152 }
154 uint32_t getMarkColor() const {
155 return color;
156 }
158 /*
159 * Care must be taken changing the mark color from gray to black. The cycle
160 * collector depends on the invariant that there are no black to gray edges
161 * in the GC heap. This invariant lets the CC not trace through black
162 * objects. If this invariant is violated, the cycle collector may free
163 * objects that are still reachable.
164 */
165 void setMarkColorGray() {
166 JS_ASSERT(isDrained());
167 JS_ASSERT(color == gc::BLACK);
168 color = gc::GRAY;
169 }
171 void setMarkColorBlack() {
172 JS_ASSERT(isDrained());
173 JS_ASSERT(color == gc::GRAY);
174 color = gc::BLACK;
175 }
177 inline void delayMarkingArena(gc::ArenaHeader *aheader);
178 void delayMarkingChildren(const void *thing);
179 void markDelayedChildren(gc::ArenaHeader *aheader);
180 bool markDelayedChildren(SliceBudget &budget);
181 bool hasDelayedChildren() const {
182 return !!unmarkedArenaStackTop;
183 }
185 bool isDrained() {
186 return isMarkStackEmpty() && !unmarkedArenaStackTop;
187 }
189 bool drainMarkStack(SliceBudget &budget);
191 /*
192 * Gray marking must be done after all black marking is complete. However,
193 * we do not have write barriers on XPConnect roots. Therefore, XPConnect
194 * roots must be accumulated in the first slice of incremental GC. We
195 * accumulate these roots in the each compartment's gcGrayRoots vector and
196 * then mark them later, after black marking is complete for each
197 * compartment. This accumulation can fail, but in that case we switch to
198 * non-incremental GC.
199 */
200 bool hasBufferedGrayRoots() const;
201 void startBufferingGrayRoots();
202 void endBufferingGrayRoots();
203 void resetBufferedGrayRoots();
204 void markBufferedGrayRoots(JS::Zone *zone);
206 static void GrayCallback(JSTracer *trc, void **thing, JSGCTraceKind kind);
208 void setGCMode(JSGCMode mode) { stack.setGCMode(mode); }
210 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
212 /* This is public exclusively for ScanRope. */
213 MarkStack stack;
215 private:
216 #ifdef DEBUG
217 void checkZone(void *p);
218 #else
219 void checkZone(void *p) {}
220 #endif
222 /*
223 * We use a common mark stack to mark GC things of different types and use
224 * the explicit tags to distinguish them when it cannot be deduced from
225 * the context of push or pop operation.
226 */
227 enum StackTag {
228 ValueArrayTag,
229 ObjectTag,
230 TypeTag,
231 XmlTag,
232 SavedValueArrayTag,
233 JitCodeTag,
234 LastTag = JitCodeTag
235 };
237 static const uintptr_t StackTagMask = 7;
238 static_assert(StackTagMask >= uintptr_t(LastTag), "The tag mask must subsume the tags.");
239 static_assert(StackTagMask <= gc::CellMask, "The tag mask must be embeddable in a Cell*.");
241 void pushTaggedPtr(StackTag tag, void *ptr) {
242 checkZone(ptr);
243 uintptr_t addr = reinterpret_cast<uintptr_t>(ptr);
244 JS_ASSERT(!(addr & StackTagMask));
245 if (!stack.push(addr | uintptr_t(tag)))
246 delayMarkingChildren(ptr);
247 }
249 void pushValueArray(JSObject *obj, void *start, void *end) {
250 checkZone(obj);
252 JS_ASSERT(start <= end);
253 uintptr_t tagged = reinterpret_cast<uintptr_t>(obj) | GCMarker::ValueArrayTag;
254 uintptr_t startAddr = reinterpret_cast<uintptr_t>(start);
255 uintptr_t endAddr = reinterpret_cast<uintptr_t>(end);
257 /*
258 * Push in the reverse order so obj will be on top. If we cannot push
259 * the array, we trigger delay marking for the whole object.
260 */
261 if (!stack.push(endAddr, startAddr, tagged))
262 delayMarkingChildren(obj);
263 }
265 bool isMarkStackEmpty() {
266 return stack.isEmpty();
267 }
269 bool restoreValueArray(JSObject *obj, void **vpp, void **endp);
270 void saveValueRanges();
271 inline void processMarkStackTop(SliceBudget &budget);
272 void processMarkStackOther(uintptr_t tag, uintptr_t addr);
274 void appendGrayRoot(void *thing, JSGCTraceKind kind);
276 /* The color is only applied to objects and functions. */
277 uint32_t color;
279 /* Pointer to the top of the stack of arenas we are delaying marking on. */
280 js::gc::ArenaHeader *unmarkedArenaStackTop;
282 /* Count of arenas that are currently in the stack. */
283 mozilla::DebugOnly<size_t> markLaterArenas;
285 enum GrayBufferState {
286 GRAY_BUFFER_UNUSED,
287 GRAY_BUFFER_OK,
288 GRAY_BUFFER_FAILED
289 };
290 GrayBufferState grayBufferState;
292 /* Assert that start and stop are called with correct ordering. */
293 mozilla::DebugOnly<bool> started;
294 };
296 void
297 SetMarkStackLimit(JSRuntime *rt, size_t limit);
299 } /* namespace js */
301 /*
302 * Macro to test if a traversal is the marking phase of the GC.
303 */
304 #define IS_GC_MARKING_TRACER(trc) \
305 ((trc)->callback == nullptr || (trc)->callback == GCMarker::GrayCallback)
307 #endif /* js_Tracer_h */