js/src/gc/Zone.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:dbe37a805a34
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 #include "gc/Zone.h"
8
9 #include "jsgc.h"
10
11 #ifdef JS_ION
12 #include "jit/BaselineJIT.h"
13 #include "jit/Ion.h"
14 #include "jit/JitCompartment.h"
15 #endif
16 #include "vm/Debugger.h"
17 #include "vm/Runtime.h"
18
19 #include "jsgcinlines.h"
20
21 using namespace js;
22 using namespace js::gc;
23
24 JS::Zone::Zone(JSRuntime *rt)
25 : JS::shadow::Zone(rt, &rt->gcMarker),
26 allocator(this),
27 ionUsingBarriers_(false),
28 active(false),
29 gcScheduled(false),
30 gcState(NoGC),
31 gcPreserveCode(false),
32 gcBytes(0),
33 gcTriggerBytes(0),
34 gcHeapGrowthFactor(3.0),
35 isSystem(false),
36 usedByExclusiveThread(false),
37 scheduledForDestruction(false),
38 maybeAlive(true),
39 gcMallocBytes(0),
40 gcMallocGCTriggered(false),
41 gcGrayRoots(),
42 data(nullptr),
43 types(this)
44 #ifdef JS_ION
45 , jitZone_(nullptr)
46 #endif
47 {
48 /* Ensure that there are no vtables to mess us up here. */
49 JS_ASSERT(reinterpret_cast<JS::shadow::Zone *>(this) ==
50 static_cast<JS::shadow::Zone *>(this));
51
52 setGCMaxMallocBytes(rt->gcMaxMallocBytes * 0.9);
53 }
54
55 Zone::~Zone()
56 {
57 if (this == runtimeFromMainThread()->systemZone)
58 runtimeFromMainThread()->systemZone = nullptr;
59
60 #ifdef JS_ION
61 js_delete(jitZone_);
62 #endif
63 }
64
65 void
66 Zone::setNeedsBarrier(bool needs, ShouldUpdateIon updateIon)
67 {
68 #ifdef JS_ION
69 if (updateIon == UpdateIon && needs != ionUsingBarriers_) {
70 jit::ToggleBarriers(this, needs);
71 ionUsingBarriers_ = needs;
72 }
73 #endif
74
75 if (needs && runtimeFromMainThread()->isAtomsZone(this))
76 JS_ASSERT(!runtimeFromMainThread()->exclusiveThreadsPresent());
77
78 JS_ASSERT_IF(needs, canCollect());
79 needsBarrier_ = needs;
80 }
81
82 void
83 Zone::resetGCMallocBytes()
84 {
85 gcMallocBytes = ptrdiff_t(gcMaxMallocBytes);
86 gcMallocGCTriggered = false;
87 }
88
89 void
90 Zone::setGCMaxMallocBytes(size_t value)
91 {
92 /*
93 * For compatibility treat any value that exceeds PTRDIFF_T_MAX to
94 * mean that value.
95 */
96 gcMaxMallocBytes = (ptrdiff_t(value) >= 0) ? value : size_t(-1) >> 1;
97 resetGCMallocBytes();
98 }
99
100 void
101 Zone::onTooMuchMalloc()
102 {
103 if (!gcMallocGCTriggered)
104 gcMallocGCTriggered = TriggerZoneGC(this, JS::gcreason::TOO_MUCH_MALLOC);
105 }
106
107 void
108 Zone::sweep(FreeOp *fop, bool releaseTypes, bool *oom)
109 {
110 /*
111 * Periodically release observed types for all scripts. This is safe to
112 * do when there are no frames for the zone on the stack.
113 */
114 if (active)
115 releaseTypes = false;
116
117 {
118 gcstats::AutoPhase ap(fop->runtime()->gcStats, gcstats::PHASE_DISCARD_ANALYSIS);
119 types.sweep(fop, releaseTypes, oom);
120 }
121
122 if (!fop->runtime()->debuggerList.isEmpty())
123 sweepBreakpoints(fop);
124
125 active = false;
126 }
127
128 void
129 Zone::sweepBreakpoints(FreeOp *fop)
130 {
131 /*
132 * Sweep all compartments in a zone at the same time, since there is no way
133 * to iterate over the scripts belonging to a single compartment in a zone.
134 */
135
136 gcstats::AutoPhase ap1(fop->runtime()->gcStats, gcstats::PHASE_SWEEP_TABLES);
137 gcstats::AutoPhase ap2(fop->runtime()->gcStats, gcstats::PHASE_SWEEP_TABLES_BREAKPOINT);
138
139 JS_ASSERT(isGCSweeping());
140 for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
141 JSScript *script = i.get<JSScript>();
142 JS_ASSERT(script->zone()->isGCSweeping());
143 if (!script->hasAnyBreakpointsOrStepMode())
144 continue;
145
146 bool scriptGone = IsScriptAboutToBeFinalized(&script);
147 JS_ASSERT(script == i.get<JSScript>());
148 for (unsigned i = 0; i < script->length(); i++) {
149 BreakpointSite *site = script->getBreakpointSite(script->offsetToPC(i));
150 if (!site)
151 continue;
152
153 Breakpoint *nextbp;
154 for (Breakpoint *bp = site->firstBreakpoint(); bp; bp = nextbp) {
155 nextbp = bp->nextInSite();
156 HeapPtrObject& dbgobj = bp->debugger->toJSObjectRef();
157 JS_ASSERT(dbgobj->zone()->isGCSweeping());
158 bool dying = scriptGone || IsObjectAboutToBeFinalized(&dbgobj);
159 JS_ASSERT_IF(!dying, bp->getHandler()->isMarked());
160 if (dying)
161 bp->destroy(fop);
162 }
163 }
164 }
165 }
166
167 void
168 Zone::discardJitCode(FreeOp *fop)
169 {
170 #ifdef JS_ION
171 if (!jitZone())
172 return;
173
174 if (isPreservingCode()) {
175 PurgeJITCaches(this);
176 } else {
177
178 # ifdef DEBUG
179 /* Assert no baseline scripts are marked as active. */
180 for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
181 JSScript *script = i.get<JSScript>();
182 JS_ASSERT_IF(script->hasBaselineScript(), !script->baselineScript()->active());
183 }
184 # endif
185
186 /* Mark baseline scripts on the stack as active. */
187 jit::MarkActiveBaselineScripts(this);
188
189 /* Only mark OSI points if code is being discarded. */
190 jit::InvalidateAll(fop, this);
191
192 for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
193 JSScript *script = i.get<JSScript>();
194 jit::FinishInvalidation<SequentialExecution>(fop, script);
195
196 // Preserve JIT code that have been recently used in
197 // parallel. Note that we mark their baseline scripts as active as
198 // well to preserve them.
199 if (script->hasParallelIonScript()) {
200 if (jit::ShouldPreserveParallelJITCode(runtimeFromMainThread(), script)) {
201 script->parallelIonScript()->purgeCaches();
202 script->baselineScript()->setActive();
203 } else {
204 jit::FinishInvalidation<ParallelExecution>(fop, script);
205 }
206 }
207
208 /*
209 * Discard baseline script if it's not marked as active. Note that
210 * this also resets the active flag.
211 */
212 jit::FinishDiscardBaselineScript(fop, script);
213
214 /*
215 * Use counts for scripts are reset on GC. After discarding code we
216 * need to let it warm back up to get information such as which
217 * opcodes are setting array holes or accessing getter properties.
218 */
219 script->resetUseCount();
220 }
221
222 jitZone()->optimizedStubSpace()->free();
223 }
224 #endif
225 }
226
227 uint64_t
228 Zone::gcNumber()
229 {
230 // Zones in use by exclusive threads are not collected, and threads using
231 // them cannot access the main runtime's gcNumber without racing.
232 return usedByExclusiveThread ? 0 : runtimeFromMainThread()->gcNumber;
233 }
234
235 #ifdef JS_ION
236 js::jit::JitZone *
237 Zone::createJitZone(JSContext *cx)
238 {
239 MOZ_ASSERT(!jitZone_);
240
241 if (!cx->runtime()->getJitRuntime(cx))
242 return nullptr;
243
244 jitZone_ = cx->new_<js::jit::JitZone>();
245 return jitZone_;
246 }
247 #endif
248
249 JS::Zone *
250 js::ZoneOfObject(const JSObject &obj)
251 {
252 return obj.zone();
253 }
254
255 JS::Zone *
256 js::ZoneOfObjectFromAnyThread(const JSObject &obj)
257 {
258 return obj.zoneFromAnyThread();
259 }
260
261 bool
262 Zone::hasMarkedCompartments()
263 {
264 for (CompartmentsInZoneIter comp(this); !comp.done(); comp.next()) {
265 if (comp->marked)
266 return true;
267 }
268 return false;
269 }

mercurial