| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
| * vim: set ts=8 sts=4 et sw=4 tw=99: |
| * This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| #ifndef gc_Zone_h |
| #define gc_Zone_h |
| |
| #include "mozilla/Attributes.h" |
| #include "mozilla/GuardObjects.h" |
| #include "mozilla/Util.h" |
| |
| #include "jscntxt.h" |
| #include "jsfun.h" |
| #include "jsgc.h" |
| #include "jsinfer.h" |
| #include "jsobj.h" |
| |
| #include "gc/StoreBuffer.h" |
| #include "gc/FindSCCs.h" |
| #include "vm/GlobalObject.h" |
| #include "vm/RegExpObject.h" |
| #include "vm/Shape.h" |
| |
| namespace js { |
| |
| /* |
| * Encapsulates the data needed to perform allocation. Typically there is |
| * precisely one of these per zone (|cx->zone().allocator|). However, in |
| * parallel execution mode, there will be one per worker thread. |
| */ |
| class Allocator |
| { |
| /* |
| * Since allocators can be accessed from worker threads, the parent zone_ |
| * should not be accessed in general. ArenaLists is allowed to actually do |
| * the allocation, however. |
| */ |
| friend class gc::ArenaLists; |
| |
| JS::Zone *zone_; |
| |
| public: |
| explicit Allocator(JS::Zone *zone); |
| |
| js::gc::ArenaLists arenas; |
| }; |
| |
| typedef Vector<JSCompartment *, 1, SystemAllocPolicy> CompartmentVector; |
| |
| } /* namespace js */ |
| |
| namespace JS { |
| |
| /* |
| * A zone is a collection of compartments. Every compartment belongs to exactly |
| * one zone. In Firefox, there is roughly one zone per tab along with a system |
| * zone for everything else. Zones mainly serve as boundaries for garbage |
| * collection. Unlike compartments, they have no special security properties. |
| * |
| * Every GC thing belongs to exactly one zone. GC things from the same zone but |
| * different compartments can share an arena (4k page). GC things from different |
| * zones cannot be stored in the same arena. The garbage collector is capable of |
| * collecting one zone at a time; it cannot collect at the granularity of |
| * compartments. |
| * |
| * GC things are tied to zones and compartments as follows: |
| * |
| * - JSObjects belong to a compartment and cannot be shared between |
| * compartments. If an object needs to point to a JSObject in a different |
| * compartment, regardless of zone, it must go through a cross-compartment |
| * wrapper. Each compartment keeps track of its outgoing wrappers in a table. |
| * |
| * - JSStrings do not belong to any particular compartment, but they do belong |
| * to a zone. Thus, two different compartments in the same zone can point to a |
| * JSString. When a string needs to be wrapped, we copy it if it's in a |
| * different zone and do nothing if it's in the same zone. Thus, transferring |
| * strings within a zone is very efficient. |
| * |
| * - Shapes and base shapes belong to a compartment and cannot be shared between |
| * compartments. A base shape holds a pointer to its compartment. Shapes find |
| * their compartment via their base shape. JSObjects find their compartment |
| * via their shape. |
| * |
| * - Scripts are also compartment-local and cannot be shared. A script points to |
| * its compartment. |
| * |
| * - Type objects and IonCode objects belong to a compartment and cannot be |
| * shared. However, there is no mechanism to obtain their compartments. |
| * |
| * A zone remains alive as long as any GC things in the zone are alive. A |
| * compartment remains alive as long as any JSObjects, scripts, shapes, or base |
| * shapes within it are alive. |
| * |
| * We always guarantee that a zone has at least one live compartment by refusing |
| * to delete the last compartment in a live zone. (This could happen, for |
| * example, if the conservative scanner marks a string in an otherwise dead |
| * zone.) |
| */ |
| |
| struct Zone : private JS::shadow::Zone, |
| public js::gc::GraphNodeBase<JS::Zone>, |
| public js::MallocProvider<JS::Zone> |
| { |
| JSRuntime *rt; |
| js::Allocator allocator; |
| |
| js::CompartmentVector compartments; |
| |
| bool hold; |
| |
| private: |
| bool ionUsingBarriers_; |
| |
| public: |
| bool active; // GC flag, whether there are active frames |
| |
| bool needsBarrier() const { |
| return needsBarrier_; |
| } |
| |
| bool compileBarriers(bool needsBarrier) const { |
| return needsBarrier || rt->gcZeal() == js::gc::ZealVerifierPreValue; |
| } |
| |
| bool compileBarriers() const { |
| return compileBarriers(needsBarrier()); |
| } |
| |
| enum ShouldUpdateIon { |
| DontUpdateIon, |
| UpdateIon |
| }; |
| |
| void setNeedsBarrier(bool needs, ShouldUpdateIon updateIon); |
| |
| static size_t OffsetOfNeedsBarrier() { |
| return offsetof(Zone, needsBarrier_); |
| } |
| |
| js::GCMarker *barrierTracer() { |
| JS_ASSERT(needsBarrier_); |
| return &rt->gcMarker; |
| } |
| |
| public: |
| enum CompartmentGCState { |
| NoGC, |
| Mark, |
| MarkGray, |
| Sweep, |
| Finished |
| }; |
| |
| private: |
| bool gcScheduled; |
| CompartmentGCState gcState; |
| bool gcPreserveCode; |
| |
| public: |
| bool isCollecting() const { |
| if (rt->isHeapCollecting()) |
| return gcState != NoGC; |
| else |
| return needsBarrier(); |
| } |
| |
| bool isPreservingCode() const { |
| return gcPreserveCode; |
| } |
| |
| /* |
| * If this returns true, all object tracing must be done with a GC marking |
| * tracer. |
| */ |
| bool requireGCTracer() const { |
| return rt->isHeapMajorCollecting() && gcState != NoGC; |
| } |
| |
| void setGCState(CompartmentGCState state) { |
| JS_ASSERT(rt->isHeapBusy()); |
| gcState = state; |
| } |
| |
| void scheduleGC() { |
| JS_ASSERT(!rt->isHeapBusy()); |
| gcScheduled = true; |
| } |
| |
| void unscheduleGC() { |
| gcScheduled = false; |
| } |
| |
| bool isGCScheduled() const { |
| return gcScheduled; |
| } |
| |
| void setPreservingCode(bool preserving) { |
| gcPreserveCode = preserving; |
| } |
| |
| bool wasGCStarted() const { |
| return gcState != NoGC; |
| } |
| |
| bool isGCMarking() { |
| if (rt->isHeapCollecting()) |
| return gcState == Mark || gcState == MarkGray; |
| else |
| return needsBarrier(); |
| } |
| |
| bool isGCMarkingBlack() { |
| return gcState == Mark; |
| } |
| |
| bool isGCMarkingGray() { |
| return gcState == MarkGray; |
| } |
| |
| bool isGCSweeping() { |
| return gcState == Sweep; |
| } |
| |
| bool isGCFinished() { |
| return gcState == Finished; |
| } |
| |
| volatile size_t gcBytes; |
| size_t gcTriggerBytes; |
| size_t gcMaxMallocBytes; |
| double gcHeapGrowthFactor; |
| |
| bool isSystem; |
| |
| /* |
| * These flags help us to discover if a compartment that shouldn't be alive |
| * manages to outlive a GC. |
| */ |
| bool scheduledForDestruction; |
| bool maybeAlive; |
| |
| /* |
| * Malloc counter to measure memory pressure for GC scheduling. It runs from |
| * gcMaxMallocBytes down to zero. This counter should be used only when it's |
| * not possible to know the size of a free. |
| */ |
| ptrdiff_t gcMallocBytes; |
| |
| /* This compartment's gray roots. */ |
| js::Vector<js::GrayRoot, 0, js::SystemAllocPolicy> gcGrayRoots; |
| |
| /* Per-zone data for use by an embedder. */ |
| void *data; |
| |
| Zone(JSRuntime *rt); |
| ~Zone(); |
| bool init(JSContext *cx); |
| |
| void findOutgoingEdges(js::gc::ComponentFinder<JS::Zone> &finder); |
| |
| void discardJitCode(js::FreeOp *fop, bool discardConstraints); |
| |
| void sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *typePool); |
| |
| void setGCLastBytes(size_t lastBytes, js::JSGCInvocationKind gckind); |
| void reduceGCTriggerBytes(size_t amount); |
| |
| void resetGCMallocBytes(); |
| void setGCMaxMallocBytes(size_t value); |
| void updateMallocCounter(size_t nbytes) { |
| /* |
| * Note: this code may be run from worker threads. We |
| * tolerate any thread races when updating gcMallocBytes. |
| */ |
| ptrdiff_t oldCount = gcMallocBytes; |
| ptrdiff_t newCount = oldCount - ptrdiff_t(nbytes); |
| gcMallocBytes = newCount; |
| if (JS_UNLIKELY(newCount <= 0 && oldCount > 0)) |
| onTooMuchMalloc(); |
| } |
| |
| bool isTooMuchMalloc() const { |
| return gcMallocBytes <= 0; |
| } |
| |
| void onTooMuchMalloc(); |
| |
| void *onOutOfMemory(void *p, size_t nbytes) { |
| return rt->onOutOfMemory(p, nbytes); |
| } |
| void reportAllocationOverflow() { |
| js_ReportAllocationOverflow(NULL); |
| } |
| |
| void markTypes(JSTracer *trc); |
| |
| js::types::TypeZone types; |
| |
| void sweep(js::FreeOp *fop, bool releaseTypes); |
| |
| private: |
| void sweepBreakpoints(js::FreeOp *fop); |
| }; |
| |
| } /* namespace JS */ |
| |
| namespace js { |
| |
| class ZonesIter { |
| private: |
| JS::Zone **it, **end; |
| |
| public: |
| ZonesIter(JSRuntime *rt) { |
| it = rt->zones.begin(); |
| end = rt->zones.end(); |
| } |
| |
| bool done() const { return it == end; } |
| |
| void next() { |
| JS_ASSERT(!done()); |
| it++; |
| } |
| |
| JS::Zone *get() const { |
| JS_ASSERT(!done()); |
| return *it; |
| } |
| |
| operator JS::Zone *() const { return get(); } |
| JS::Zone *operator->() const { return get(); } |
| }; |
| |
| struct CompartmentsInZoneIter |
| { |
| private: |
| JSCompartment **it, **end; |
| |
| public: |
| CompartmentsInZoneIter(JS::Zone *zone) { |
| it = zone->compartments.begin(); |
| end = zone->compartments.end(); |
| } |
| |
| bool done() const { return it == end; } |
| void next() { |
| JS_ASSERT(!done()); |
| it++; |
| } |
| |
| JSCompartment *get() const { return *it; } |
| |
| operator JSCompartment *() const { return get(); } |
| JSCompartment *operator->() const { return get(); } |
| }; |
| |
| /* |
| * This iterator iterates over all the compartments in a given set of zones. The |
| * set of zones is determined by iterating ZoneIterT. |
| */ |
| template<class ZonesIterT> |
| class CompartmentsIterT |
| { |
| private: |
| ZonesIterT zone; |
| mozilla::Maybe<CompartmentsInZoneIter> comp; |
| |
| public: |
| CompartmentsIterT(JSRuntime *rt) |
| : zone(rt) |
| { |
| JS_ASSERT(!zone.done()); |
| comp.construct(zone); |
| } |
| |
| bool done() const { return zone.done(); } |
| |
| void next() { |
| JS_ASSERT(!done()); |
| JS_ASSERT(!comp.ref().done()); |
| comp.ref().next(); |
| if (comp.ref().done()) { |
| comp.destroy(); |
| zone.next(); |
| if (!zone.done()) |
| comp.construct(zone); |
| } |
| } |
| |
| JSCompartment *get() const { |
| JS_ASSERT(!done()); |
| return comp.ref(); |
| } |
| |
| operator JSCompartment *() const { return get(); } |
| JSCompartment *operator->() const { return get(); } |
| }; |
| |
| typedef CompartmentsIterT<ZonesIter> CompartmentsIter; |
| |
| } /* namespace js */ |
| |
| #endif /* gc_Zone_h */ |