michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "vm/Runtime-inl.h" michael@0: michael@0: #include "mozilla/ArrayUtils.h" michael@0: #include "mozilla/Atomics.h" michael@0: #include "mozilla/DebugOnly.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "mozilla/ThreadLocal.h" michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #ifdef JS_CAN_CHECK_THREADSAFE_ACCESSES michael@0: # include michael@0: #endif michael@0: michael@0: #include "jsatom.h" michael@0: #include "jsdtoa.h" michael@0: #include "jsgc.h" michael@0: #include "jsmath.h" michael@0: #include "jsnativestack.h" michael@0: #include "jsobj.h" michael@0: #include "jsscript.h" michael@0: #include "jswatchpoint.h" michael@0: #include "jswrapper.h" michael@0: michael@0: #if defined(JS_ION) michael@0: # include "assembler/assembler/MacroAssembler.h" michael@0: #endif michael@0: #include "jit/arm/Simulator-arm.h" michael@0: #include "jit/AsmJSSignalHandlers.h" michael@0: #include "jit/JitCompartment.h" michael@0: #include "jit/PcScriptCache.h" michael@0: #include "js/MemoryMetrics.h" michael@0: #include "js/SliceBudget.h" michael@0: #include "yarr/BumpPointerAllocator.h" michael@0: michael@0: #include "jscntxtinlines.h" michael@0: #include "jsgcinlines.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::gc; michael@0: michael@0: using mozilla::Atomic; michael@0: using mozilla::DebugOnly; michael@0: using mozilla::NegativeInfinity; michael@0: using mozilla::PodZero; michael@0: using mozilla::PodArrayZero; michael@0: using mozilla::PositiveInfinity; michael@0: using mozilla::ThreadLocal; michael@0: using JS::GenericNaN; michael@0: using JS::DoubleNaNValue; michael@0: michael@0: /* static */ ThreadLocal js::TlsPerThreadData; michael@0: michael@0: #ifdef JS_THREADSAFE michael@0: /* static */ Atomic JSRuntime::liveRuntimesCount; michael@0: #else michael@0: /* static */ size_t JSRuntime::liveRuntimesCount; michael@0: #endif michael@0: michael@0: const JSSecurityCallbacks js::NullSecurityCallbacks = { }; michael@0: michael@0: PerThreadData::PerThreadData(JSRuntime *runtime) michael@0: : PerThreadDataFriendFields(), michael@0: runtime_(runtime), michael@0: ionTop(nullptr), michael@0: jitJSContext(nullptr), michael@0: jitStackLimit(0), michael@0: #ifdef JS_TRACE_LOGGING michael@0: traceLogger(nullptr), michael@0: #endif michael@0: activation_(nullptr), michael@0: asmJSActivationStack_(nullptr), michael@0: autoFlushICache_(nullptr), michael@0: #ifdef JS_ARM_SIMULATOR michael@0: simulator_(nullptr), michael@0: simulatorStackLimit_(0), michael@0: #endif michael@0: dtoaState(nullptr), michael@0: suppressGC(0), michael@0: activeCompilations(0) michael@0: {} michael@0: michael@0: PerThreadData::~PerThreadData() michael@0: { michael@0: if (dtoaState) michael@0: js_DestroyDtoaState(dtoaState); michael@0: michael@0: #ifdef JS_ARM_SIMULATOR michael@0: js_delete(simulator_); michael@0: #endif michael@0: } michael@0: michael@0: bool michael@0: PerThreadData::init() michael@0: { michael@0: dtoaState = js_NewDtoaState(); michael@0: if (!dtoaState) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static const JSWrapObjectCallbacks DefaultWrapObjectCallbacks = { michael@0: TransparentObjectWrapper, michael@0: nullptr michael@0: }; michael@0: michael@0: JSRuntime::JSRuntime(JSRuntime *parentRuntime, JSUseHelperThreads useHelperThreads) michael@0: : JS::shadow::Runtime( michael@0: #ifdef JSGC_GENERATIONAL michael@0: &gcStoreBuffer michael@0: #endif michael@0: ), michael@0: mainThread(this), michael@0: parentRuntime(parentRuntime), michael@0: interrupt(false), michael@0: #if defined(JS_THREADSAFE) && defined(JS_ION) michael@0: interruptPar(false), michael@0: #endif michael@0: handlingSignal(false), michael@0: interruptCallback(nullptr), michael@0: #ifdef JS_THREADSAFE michael@0: interruptLock(nullptr), michael@0: interruptLockOwner(nullptr), michael@0: exclusiveAccessLock(nullptr), michael@0: exclusiveAccessOwner(nullptr), michael@0: mainThreadHasExclusiveAccess(false), michael@0: numExclusiveThreads(0), michael@0: #else michael@0: interruptLockTaken(false), michael@0: #endif michael@0: systemZone(nullptr), michael@0: numCompartments(0), michael@0: localeCallbacks(nullptr), michael@0: defaultLocale(nullptr), michael@0: defaultVersion_(JSVERSION_DEFAULT), michael@0: #ifdef JS_THREADSAFE michael@0: ownerThread_(nullptr), michael@0: #endif michael@0: tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), michael@0: freeLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), michael@0: execAlloc_(nullptr), michael@0: bumpAlloc_(nullptr), michael@0: jitRuntime_(nullptr), michael@0: selfHostingGlobal_(nullptr), michael@0: nativeStackBase(0), michael@0: cxCallback(nullptr), michael@0: destroyCompartmentCallback(nullptr), michael@0: destroyZoneCallback(nullptr), michael@0: sweepZoneCallback(nullptr), michael@0: compartmentNameCallback(nullptr), michael@0: activityCallback(nullptr), michael@0: activityCallbackArg(nullptr), michael@0: #ifdef JS_THREADSAFE michael@0: requestDepth(0), michael@0: # ifdef DEBUG michael@0: checkRequestDepth(0), michael@0: # endif michael@0: #endif michael@0: #ifdef DEBUG michael@0: activeContext(nullptr), michael@0: #endif michael@0: gcInitialized(false), michael@0: gcSystemAvailableChunkListHead(nullptr), michael@0: gcUserAvailableChunkListHead(nullptr), michael@0: gcBytes(0), michael@0: gcMaxBytes(0), michael@0: gcMaxMallocBytes(0), michael@0: gcNumArenasFreeCommitted(0), michael@0: gcMarker(this), michael@0: gcVerifyPreData(nullptr), michael@0: gcVerifyPostData(nullptr), michael@0: gcChunkAllocationSinceLastGC(false), michael@0: gcNextFullGCTime(0), michael@0: gcLastGCTime(0), michael@0: gcJitReleaseTime(0), michael@0: gcAllocationThreshold(30 * 1024 * 1024), michael@0: gcHighFrequencyGC(false), michael@0: gcHighFrequencyTimeThreshold(1000), michael@0: gcHighFrequencyLowLimitBytes(100 * 1024 * 1024), michael@0: gcHighFrequencyHighLimitBytes(500 * 1024 * 1024), michael@0: gcHighFrequencyHeapGrowthMax(3.0), michael@0: gcHighFrequencyHeapGrowthMin(1.5), michael@0: gcLowFrequencyHeapGrowth(1.5), michael@0: gcDynamicHeapGrowth(false), michael@0: gcDynamicMarkSlice(false), michael@0: gcDecommitThreshold(32 * 1024 * 1024), michael@0: gcShouldCleanUpEverything(false), michael@0: gcGrayBitsValid(false), michael@0: gcIsNeeded(0), michael@0: gcStats(thisFromCtor()), michael@0: gcNumber(0), michael@0: gcStartNumber(0), michael@0: gcIsFull(false), michael@0: gcTriggerReason(JS::gcreason::NO_REASON), michael@0: gcStrictCompartmentChecking(false), michael@0: #ifdef DEBUG michael@0: gcDisableStrictProxyCheckingCount(0), michael@0: #endif michael@0: gcIncrementalState(gc::NO_INCREMENTAL), michael@0: gcLastMarkSlice(false), michael@0: gcSweepOnBackgroundThread(false), michael@0: gcFoundBlackGrayEdges(false), michael@0: gcSweepingZones(nullptr), michael@0: gcZoneGroupIndex(0), michael@0: gcZoneGroups(nullptr), michael@0: gcCurrentZoneGroup(nullptr), michael@0: gcSweepPhase(0), michael@0: gcSweepZone(nullptr), michael@0: gcSweepKindIndex(0), michael@0: gcAbortSweepAfterCurrentGroup(false), michael@0: gcArenasAllocatedDuringSweep(nullptr), michael@0: #ifdef DEBUG michael@0: gcMarkingValidator(nullptr), michael@0: #endif michael@0: gcInterFrameGC(0), michael@0: gcSliceBudget(SliceBudget::Unlimited), michael@0: gcIncrementalEnabled(true), michael@0: gcGenerationalDisabled(0), michael@0: gcManipulatingDeadZones(false), michael@0: gcObjectsMarkedInDeadZones(0), michael@0: gcPoke(false), michael@0: heapState(Idle), michael@0: #ifdef JSGC_GENERATIONAL michael@0: gcNursery(thisFromCtor()), michael@0: gcStoreBuffer(thisFromCtor(), gcNursery), michael@0: #endif michael@0: #ifdef JS_GC_ZEAL michael@0: gcZeal_(0), michael@0: gcZealFrequency(0), michael@0: gcNextScheduled(0), michael@0: gcDeterministicOnly(false), michael@0: gcIncrementalLimit(0), michael@0: #endif michael@0: gcValidate(true), michael@0: gcFullCompartmentChecks(false), michael@0: gcCallback(nullptr), michael@0: gcSliceCallback(nullptr), michael@0: gcFinalizeCallback(nullptr), michael@0: gcMallocBytes(0), michael@0: gcMallocGCTriggered(false), michael@0: #ifdef JS_ARM_SIMULATOR michael@0: simulatorRuntime_(nullptr), michael@0: #endif michael@0: scriptAndCountsVector(nullptr), michael@0: NaNValue(DoubleNaNValue()), michael@0: negativeInfinityValue(DoubleValue(NegativeInfinity())), michael@0: positiveInfinityValue(DoubleValue(PositiveInfinity())), michael@0: emptyString(nullptr), michael@0: debugMode(false), michael@0: spsProfiler(thisFromCtor()), michael@0: profilingScripts(false), michael@0: alwaysPreserveCode(false), michael@0: hadOutOfMemory(false), michael@0: haveCreatedContext(false), michael@0: data(nullptr), michael@0: gcLock(nullptr), michael@0: gcLockOwner(nullptr), michael@0: gcHelperThread(thisFromCtor()), michael@0: signalHandlersInstalled_(false), michael@0: defaultFreeOp_(thisFromCtor(), false), michael@0: debuggerMutations(0), michael@0: securityCallbacks(const_cast(&NullSecurityCallbacks)), michael@0: DOMcallbacks(nullptr), michael@0: destroyPrincipals(nullptr), michael@0: structuredCloneCallbacks(nullptr), michael@0: telemetryCallback(nullptr), michael@0: propertyRemovals(0), michael@0: #if !EXPOSE_INTL_API michael@0: thousandsSeparator(0), michael@0: decimalSeparator(0), michael@0: numGrouping(0), michael@0: #endif michael@0: mathCache_(nullptr), michael@0: activeCompilations_(0), michael@0: keepAtoms_(0), michael@0: trustedPrincipals_(nullptr), michael@0: beingDestroyed_(false), michael@0: atoms_(nullptr), michael@0: atomsCompartment_(nullptr), michael@0: staticStrings(nullptr), michael@0: commonNames(nullptr), michael@0: permanentAtoms(nullptr), michael@0: wrapObjectCallbacks(&DefaultWrapObjectCallbacks), michael@0: preserveWrapperCallback(nullptr), michael@0: #ifdef DEBUG michael@0: noGCOrAllocationCheck(0), michael@0: #endif michael@0: jitSupportsFloatingPoint(false), michael@0: ionPcScriptCache(nullptr), michael@0: threadPool(this), michael@0: defaultJSContextCallback(nullptr), michael@0: ctypesActivityCallback(nullptr), michael@0: forkJoinWarmup(0), michael@0: ionReturnOverride_(MagicValue(JS_ARG_POISON)), michael@0: useHelperThreads_(useHelperThreads), michael@0: parallelIonCompilationEnabled_(true), michael@0: parallelParsingEnabled_(true), michael@0: isWorkerRuntime_(false), michael@0: #ifdef DEBUG michael@0: enteredPolicy(nullptr), michael@0: #endif michael@0: largeAllocationFailureCallback(nullptr), michael@0: oomCallback(nullptr) michael@0: { michael@0: liveRuntimesCount++; michael@0: michael@0: setGCMode(JSGC_MODE_GLOBAL); michael@0: michael@0: /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */ michael@0: JS_INIT_CLIST(&onNewGlobalObjectWatchers); michael@0: michael@0: PodZero(&debugHooks); michael@0: PodArrayZero(nativeStackQuota); michael@0: PodZero(&asmJSCacheOps); michael@0: } michael@0: michael@0: static bool michael@0: JitSupportsFloatingPoint() michael@0: { michael@0: #if defined(JS_ION) michael@0: if (!JSC::MacroAssembler::supportsFloatingPoint()) michael@0: return false; michael@0: michael@0: #if defined(JS_ION) && WTF_ARM_ARCH_VERSION == 6 michael@0: if (!js::jit::hasVFP()) michael@0: return false; michael@0: #endif michael@0: michael@0: return true; michael@0: #else michael@0: return false; michael@0: #endif michael@0: } michael@0: michael@0: bool michael@0: JSRuntime::init(uint32_t maxbytes) michael@0: { michael@0: #ifdef JS_THREADSAFE michael@0: ownerThread_ = PR_GetCurrentThread(); michael@0: michael@0: interruptLock = PR_NewLock(); michael@0: if (!interruptLock) michael@0: return false; michael@0: michael@0: gcLock = PR_NewLock(); michael@0: if (!gcLock) michael@0: return false; michael@0: michael@0: exclusiveAccessLock = PR_NewLock(); michael@0: if (!exclusiveAccessLock) michael@0: return false; michael@0: #endif michael@0: michael@0: if (!mainThread.init()) michael@0: return false; michael@0: michael@0: js::TlsPerThreadData.set(&mainThread); michael@0: michael@0: if (!threadPool.init()) michael@0: return false; michael@0: michael@0: if (!js_InitGC(this, maxbytes)) michael@0: return false; michael@0: michael@0: if (!gcMarker.init(gcMode())) michael@0: return false; michael@0: michael@0: const char *size = getenv("JSGC_MARK_STACK_LIMIT"); michael@0: if (size) michael@0: SetMarkStackLimit(this, atoi(size)); michael@0: michael@0: ScopedJSDeletePtr atomsZone(new_(this)); michael@0: if (!atomsZone) michael@0: return false; michael@0: michael@0: JS::CompartmentOptions options; michael@0: ScopedJSDeletePtr atomsCompartment(new_(atomsZone.get(), options)); michael@0: if (!atomsCompartment || !atomsCompartment->init(nullptr)) michael@0: return false; michael@0: michael@0: zones.append(atomsZone.get()); michael@0: atomsZone->compartments.append(atomsCompartment.get()); michael@0: michael@0: atomsCompartment->isSystem = true; michael@0: atomsZone->isSystem = true; michael@0: atomsZone->setGCLastBytes(8192, GC_NORMAL); michael@0: michael@0: atomsZone.forget(); michael@0: this->atomsCompartment_ = atomsCompartment.forget(); michael@0: michael@0: if (!scriptDataTable_.init()) michael@0: return false; michael@0: michael@0: if (!evalCache.init()) michael@0: return false; michael@0: michael@0: /* The garbage collector depends on everything before this point being initialized. */ michael@0: gcInitialized = true; michael@0: michael@0: if (!InitRuntimeNumberState(this)) michael@0: return false; michael@0: michael@0: dateTimeInfo.updateTimeZoneAdjustment(); michael@0: michael@0: #ifdef JS_ARM_SIMULATOR michael@0: simulatorRuntime_ = js::jit::CreateSimulatorRuntime(); michael@0: if (!simulatorRuntime_) michael@0: return false; michael@0: #endif michael@0: michael@0: nativeStackBase = GetNativeStackBase(); michael@0: michael@0: jitSupportsFloatingPoint = JitSupportsFloatingPoint(); michael@0: michael@0: #ifdef JS_ION michael@0: signalHandlersInstalled_ = EnsureAsmJSSignalHandlersInstalled(this); michael@0: #endif michael@0: michael@0: if (!spsProfiler.init()) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: JSRuntime::~JSRuntime() michael@0: { michael@0: JS_ASSERT(!isHeapBusy()); michael@0: michael@0: if (gcInitialized) { michael@0: /* Free source hook early, as its destructor may want to delete roots. */ michael@0: sourceHook = nullptr; michael@0: michael@0: /* michael@0: * Cancel any pending, in progress or completed Ion compilations and michael@0: * parse tasks. Waiting for AsmJS and compression tasks is done michael@0: * synchronously (on the main thread or during parse tasks), so no michael@0: * explicit canceling is needed for these. michael@0: */ michael@0: for (CompartmentsIter comp(this, SkipAtoms); !comp.done(); comp.next()) michael@0: CancelOffThreadIonCompile(comp, nullptr); michael@0: CancelOffThreadParses(this); michael@0: michael@0: /* Clear debugging state to remove GC roots. */ michael@0: for (CompartmentsIter comp(this, SkipAtoms); !comp.done(); comp.next()) { michael@0: comp->clearTraps(defaultFreeOp()); michael@0: if (WatchpointMap *wpmap = comp->watchpointMap) michael@0: wpmap->clear(); michael@0: } michael@0: michael@0: /* Clear atoms to remove GC roots and heap allocations. */ michael@0: finishAtoms(); michael@0: michael@0: /* michael@0: * Flag us as being destroyed. This allows the GC to free things like michael@0: * interned atoms and Ion trampolines. michael@0: */ michael@0: beingDestroyed_ = true; michael@0: michael@0: /* Allow the GC to release scripts that were being profiled. */ michael@0: profilingScripts = false; michael@0: michael@0: JS::PrepareForFullGC(this); michael@0: GC(this, GC_NORMAL, JS::gcreason::DESTROY_RUNTIME); michael@0: } michael@0: michael@0: /* michael@0: * Clear the self-hosted global and delete self-hosted classes *after* michael@0: * GC, as finalizers for objects check for clasp->finalize during GC. michael@0: */ michael@0: finishSelfHosting(); michael@0: michael@0: #ifdef JS_THREADSAFE michael@0: JS_ASSERT(!exclusiveAccessOwner); michael@0: if (exclusiveAccessLock) michael@0: PR_DestroyLock(exclusiveAccessLock); michael@0: michael@0: // Avoid bogus asserts during teardown. michael@0: JS_ASSERT(!numExclusiveThreads); michael@0: mainThreadHasExclusiveAccess = true; michael@0: michael@0: JS_ASSERT(!interruptLockOwner); michael@0: if (interruptLock) michael@0: PR_DestroyLock(interruptLock); michael@0: #endif michael@0: michael@0: /* michael@0: * Even though all objects in the compartment are dead, we may have keep michael@0: * some filenames around because of gcKeepAtoms. michael@0: */ michael@0: FreeScriptData(this); michael@0: michael@0: #ifdef DEBUG michael@0: /* Don't hurt everyone in leaky ol' Mozilla with a fatal JS_ASSERT! */ michael@0: if (hasContexts()) { michael@0: unsigned cxcount = 0; michael@0: for (ContextIter acx(this); !acx.done(); acx.next()) { michael@0: fprintf(stderr, michael@0: "JS API usage error: found live context at %p\n", michael@0: (void *) acx.get()); michael@0: cxcount++; michael@0: } michael@0: fprintf(stderr, michael@0: "JS API usage error: %u context%s left in runtime upon JS_DestroyRuntime.\n", michael@0: cxcount, (cxcount == 1) ? "" : "s"); michael@0: } michael@0: #endif michael@0: michael@0: #if !EXPOSE_INTL_API michael@0: FinishRuntimeNumberState(this); michael@0: #endif michael@0: michael@0: js_FinishGC(this); michael@0: atomsCompartment_ = nullptr; michael@0: michael@0: #ifdef JS_THREADSAFE michael@0: if (gcLock) michael@0: PR_DestroyLock(gcLock); michael@0: #endif michael@0: michael@0: js_free(defaultLocale); michael@0: js_delete(bumpAlloc_); michael@0: js_delete(mathCache_); michael@0: #ifdef JS_ION michael@0: js_delete(jitRuntime_); michael@0: #endif michael@0: js_delete(execAlloc_); /* Delete after jitRuntime_. */ michael@0: michael@0: js_delete(ionPcScriptCache); michael@0: michael@0: #ifdef JSGC_GENERATIONAL michael@0: gcStoreBuffer.disable(); michael@0: gcNursery.disable(); michael@0: #endif michael@0: michael@0: #ifdef JS_ARM_SIMULATOR michael@0: js::jit::DestroySimulatorRuntime(simulatorRuntime_); michael@0: #endif michael@0: michael@0: DebugOnly oldCount = liveRuntimesCount--; michael@0: JS_ASSERT(oldCount > 0); michael@0: michael@0: #ifdef JS_THREADSAFE michael@0: js::TlsPerThreadData.set(nullptr); michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: NewObjectCache::clearNurseryObjects(JSRuntime *rt) michael@0: { michael@0: for (unsigned i = 0; i < mozilla::ArrayLength(entries); ++i) { michael@0: Entry &e = entries[i]; michael@0: JSObject *obj = reinterpret_cast(&e.templateObject); michael@0: if (IsInsideNursery(rt, e.key) || michael@0: IsInsideNursery(rt, obj->slots) || michael@0: IsInsideNursery(rt, obj->elements)) michael@0: { michael@0: PodZero(&e); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: JSRuntime::resetJitStackLimit() michael@0: { michael@0: AutoLockForInterrupt lock(this); michael@0: mainThread.setJitStackLimit(mainThread.nativeStackLimit[js::StackForUntrustedScript]); michael@0: michael@0: #ifdef JS_ARM_SIMULATOR michael@0: mainThread.setJitStackLimit(js::jit::Simulator::StackLimit()); michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::RuntimeSizes *rtSizes) michael@0: { michael@0: // Several tables in the runtime enumerated below can be used off thread. michael@0: AutoLockForExclusiveAccess lock(this); michael@0: michael@0: rtSizes->object += mallocSizeOf(this); michael@0: michael@0: rtSizes->atomsTable += atoms().sizeOfIncludingThis(mallocSizeOf); michael@0: michael@0: if (!parentRuntime) { michael@0: rtSizes->atomsTable += mallocSizeOf(staticStrings); michael@0: rtSizes->atomsTable += mallocSizeOf(commonNames); michael@0: rtSizes->atomsTable += permanentAtoms->sizeOfIncludingThis(mallocSizeOf); michael@0: } michael@0: michael@0: for (ContextIter acx(this); !acx.done(); acx.next()) michael@0: rtSizes->contexts += acx->sizeOfIncludingThis(mallocSizeOf); michael@0: michael@0: rtSizes->dtoa += mallocSizeOf(mainThread.dtoaState); michael@0: michael@0: rtSizes->temporary += tempLifoAlloc.sizeOfExcludingThis(mallocSizeOf); michael@0: michael@0: rtSizes->regexpData += bumpAlloc_ ? bumpAlloc_->sizeOfNonHeapData() : 0; michael@0: michael@0: rtSizes->interpreterStack += interpreterStack_.sizeOfExcludingThis(mallocSizeOf); michael@0: michael@0: rtSizes->mathCache += mathCache_ ? mathCache_->sizeOfIncludingThis(mallocSizeOf) : 0; michael@0: michael@0: rtSizes->sourceDataCache += sourceDataCache.sizeOfExcludingThis(mallocSizeOf); michael@0: michael@0: rtSizes->scriptData += scriptDataTable().sizeOfExcludingThis(mallocSizeOf); michael@0: for (ScriptDataTable::Range r = scriptDataTable().all(); !r.empty(); r.popFront()) michael@0: rtSizes->scriptData += mallocSizeOf(r.front()); michael@0: michael@0: if (execAlloc_) michael@0: execAlloc_->addSizeOfCode(&rtSizes->code); michael@0: #ifdef JS_ION michael@0: { michael@0: AutoLockForInterrupt lock(this); michael@0: if (jitRuntime()) { michael@0: if (JSC::ExecutableAllocator *ionAlloc = jitRuntime()->ionAlloc(this)) michael@0: ionAlloc->addSizeOfCode(&rtSizes->code); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: rtSizes->gc.marker += gcMarker.sizeOfExcludingThis(mallocSizeOf); michael@0: #ifdef JSGC_GENERATIONAL michael@0: rtSizes->gc.nurseryCommitted += gcNursery.sizeOfHeapCommitted(); michael@0: rtSizes->gc.nurseryDecommitted += gcNursery.sizeOfHeapDecommitted(); michael@0: rtSizes->gc.nurseryHugeSlots += gcNursery.sizeOfHugeSlots(mallocSizeOf); michael@0: gcStoreBuffer.addSizeOfExcludingThis(mallocSizeOf, &rtSizes->gc); michael@0: #endif michael@0: } michael@0: michael@0: static bool michael@0: SignalBasedTriggersDisabled() michael@0: { michael@0: // Don't bother trying to cache the getenv lookup; this should be called michael@0: // infrequently. michael@0: return !!getenv("JS_DISABLE_SLOW_SCRIPT_SIGNALS"); michael@0: } michael@0: michael@0: void michael@0: JSRuntime::requestInterrupt(InterruptMode mode) michael@0: { michael@0: AutoLockForInterrupt lock(this); michael@0: michael@0: /* michael@0: * Invalidate ionTop to trigger its over-recursion check. Note this must be michael@0: * set before interrupt, to avoid racing with js::InvokeInterruptCallback, michael@0: * into a weird state where interrupt is stuck at 0 but jitStackLimit is michael@0: * MAXADDR. michael@0: */ michael@0: mainThread.setJitStackLimit(-1); michael@0: michael@0: interrupt = true; michael@0: michael@0: #ifdef JS_ION michael@0: #ifdef JS_THREADSAFE michael@0: RequestInterruptForForkJoin(this, mode); michael@0: #endif michael@0: michael@0: /* michael@0: * asm.js and, optionally, normal Ion code use memory protection and signal michael@0: * handlers to halt running code. michael@0: */ michael@0: if (!SignalBasedTriggersDisabled()) { michael@0: RequestInterruptForAsmJSCode(this); michael@0: jit::RequestInterruptForIonCode(this, mode); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: JSC::ExecutableAllocator * michael@0: JSRuntime::createExecutableAllocator(JSContext *cx) michael@0: { michael@0: JS_ASSERT(!execAlloc_); michael@0: JS_ASSERT(cx->runtime() == this); michael@0: michael@0: execAlloc_ = js_new(); michael@0: if (!execAlloc_) michael@0: js_ReportOutOfMemory(cx); michael@0: return execAlloc_; michael@0: } michael@0: michael@0: WTF::BumpPointerAllocator * michael@0: JSRuntime::createBumpPointerAllocator(JSContext *cx) michael@0: { michael@0: JS_ASSERT(!bumpAlloc_); michael@0: JS_ASSERT(cx->runtime() == this); michael@0: michael@0: bumpAlloc_ = js_new(); michael@0: if (!bumpAlloc_) michael@0: js_ReportOutOfMemory(cx); michael@0: return bumpAlloc_; michael@0: } michael@0: michael@0: MathCache * michael@0: JSRuntime::createMathCache(JSContext *cx) michael@0: { michael@0: JS_ASSERT(!mathCache_); michael@0: JS_ASSERT(cx->runtime() == this); michael@0: michael@0: MathCache *newMathCache = js_new(); michael@0: if (!newMathCache) { michael@0: js_ReportOutOfMemory(cx); michael@0: return nullptr; michael@0: } michael@0: michael@0: mathCache_ = newMathCache; michael@0: return mathCache_; michael@0: } michael@0: michael@0: bool michael@0: JSRuntime::setDefaultLocale(const char *locale) michael@0: { michael@0: if (!locale) michael@0: return false; michael@0: resetDefaultLocale(); michael@0: defaultLocale = JS_strdup(this, locale); michael@0: return defaultLocale != nullptr; michael@0: } michael@0: michael@0: void michael@0: JSRuntime::resetDefaultLocale() michael@0: { michael@0: js_free(defaultLocale); michael@0: defaultLocale = nullptr; michael@0: } michael@0: michael@0: const char * michael@0: JSRuntime::getDefaultLocale() michael@0: { michael@0: if (defaultLocale) michael@0: return defaultLocale; michael@0: michael@0: char *locale, *lang, *p; michael@0: #ifdef HAVE_SETLOCALE michael@0: locale = setlocale(LC_ALL, nullptr); michael@0: #else michael@0: locale = getenv("LANG"); michael@0: #endif michael@0: // convert to a well-formed BCP 47 language tag michael@0: if (!locale || !strcmp(locale, "C")) michael@0: locale = const_cast("und"); michael@0: lang = JS_strdup(this, locale); michael@0: if (!lang) michael@0: return nullptr; michael@0: if ((p = strchr(lang, '.'))) michael@0: *p = '\0'; michael@0: while ((p = strchr(lang, '_'))) michael@0: *p = '-'; michael@0: michael@0: defaultLocale = lang; michael@0: return defaultLocale; michael@0: } michael@0: michael@0: void michael@0: JSRuntime::triggerActivityCallback(bool active) michael@0: { michael@0: if (!activityCallback) michael@0: return; michael@0: michael@0: /* michael@0: * The activity callback must not trigger a GC: it would create a cirular michael@0: * dependency between entering a request and Rooted's requirement of being michael@0: * in a request. In practice this callback already cannot trigger GC. The michael@0: * suppression serves to inform the exact rooting hazard analysis of this michael@0: * property and ensures that it remains true in the future. michael@0: */ michael@0: AutoSuppressGC suppress(this); michael@0: michael@0: activityCallback(activityCallbackArg, active); michael@0: } michael@0: michael@0: void michael@0: JSRuntime::setGCMaxMallocBytes(size_t value) michael@0: { michael@0: /* michael@0: * For compatibility treat any value that exceeds PTRDIFF_T_MAX to michael@0: * mean that value. michael@0: */ michael@0: gcMaxMallocBytes = (ptrdiff_t(value) >= 0) ? value : size_t(-1) >> 1; michael@0: resetGCMallocBytes(); michael@0: for (ZonesIter zone(this, WithAtoms); !zone.done(); zone.next()) michael@0: zone->setGCMaxMallocBytes(value); michael@0: } michael@0: michael@0: void michael@0: JSRuntime::updateMallocCounter(size_t nbytes) michael@0: { michael@0: updateMallocCounter(nullptr, nbytes); michael@0: } michael@0: michael@0: void michael@0: JSRuntime::updateMallocCounter(JS::Zone *zone, size_t nbytes) michael@0: { michael@0: /* We tolerate any thread races when updating gcMallocBytes. */ michael@0: gcMallocBytes -= ptrdiff_t(nbytes); michael@0: if (MOZ_UNLIKELY(gcMallocBytes <= 0)) michael@0: onTooMuchMalloc(); michael@0: else if (zone) michael@0: zone->updateMallocCounter(nbytes); michael@0: } michael@0: michael@0: JS_FRIEND_API(void) michael@0: JSRuntime::onTooMuchMalloc() michael@0: { michael@0: if (!CurrentThreadCanAccessRuntime(this)) michael@0: return; michael@0: michael@0: if (!gcMallocGCTriggered) michael@0: gcMallocGCTriggered = TriggerGC(this, JS::gcreason::TOO_MUCH_MALLOC); michael@0: } michael@0: michael@0: JS_FRIEND_API(void *) michael@0: JSRuntime::onOutOfMemory(void *p, size_t nbytes) michael@0: { michael@0: return onOutOfMemory(p, nbytes, nullptr); michael@0: } michael@0: michael@0: JS_FRIEND_API(void *) michael@0: JSRuntime::onOutOfMemory(void *p, size_t nbytes, JSContext *cx) michael@0: { michael@0: if (isHeapBusy()) michael@0: return nullptr; michael@0: michael@0: /* michael@0: * Retry when we are done with the background sweeping and have stopped michael@0: * all the allocations and released the empty GC chunks. michael@0: */ michael@0: JS::ShrinkGCBuffers(this); michael@0: gcHelperThread.waitBackgroundSweepOrAllocEnd(); michael@0: if (!p) michael@0: p = js_malloc(nbytes); michael@0: else if (p == reinterpret_cast(1)) michael@0: p = js_calloc(nbytes); michael@0: else michael@0: p = js_realloc(p, nbytes); michael@0: if (p) michael@0: return p; michael@0: if (cx) michael@0: js_ReportOutOfMemory(cx); michael@0: return nullptr; michael@0: } michael@0: michael@0: bool michael@0: JSRuntime::activeGCInAtomsZone() michael@0: { michael@0: Zone *zone = atomsCompartment_->zone(); michael@0: return zone->needsBarrier() || zone->isGCScheduled() || zone->wasGCStarted(); michael@0: } michael@0: michael@0: #ifdef JS_THREADSAFE michael@0: michael@0: void michael@0: JSRuntime::setUsedByExclusiveThread(Zone *zone) michael@0: { michael@0: JS_ASSERT(!zone->usedByExclusiveThread); michael@0: zone->usedByExclusiveThread = true; michael@0: numExclusiveThreads++; michael@0: } michael@0: michael@0: void michael@0: JSRuntime::clearUsedByExclusiveThread(Zone *zone) michael@0: { michael@0: JS_ASSERT(zone->usedByExclusiveThread); michael@0: zone->usedByExclusiveThread = false; michael@0: numExclusiveThreads--; michael@0: } michael@0: michael@0: bool michael@0: js::CurrentThreadCanAccessRuntime(JSRuntime *rt) michael@0: { michael@0: return rt->ownerThread_ == PR_GetCurrentThread() && !InParallelSection(); michael@0: } michael@0: michael@0: bool michael@0: js::CurrentThreadCanAccessZone(Zone *zone) michael@0: { michael@0: if (CurrentThreadCanAccessRuntime(zone->runtime_)) michael@0: return true; michael@0: if (InParallelSection()) { michael@0: DebugOnly pt = js::TlsPerThreadData.get(); michael@0: JS_ASSERT(pt && pt->associatedWith(zone->runtime_)); michael@0: return true; michael@0: } michael@0: michael@0: // Only zones in use by an exclusive thread can be used off the main thread michael@0: // or outside of PJS. We don't keep track of which thread owns such zones michael@0: // though, so this check is imperfect. michael@0: return zone->usedByExclusiveThread; michael@0: } michael@0: michael@0: #else // JS_THREADSAFE michael@0: michael@0: bool michael@0: js::CurrentThreadCanAccessRuntime(JSRuntime *rt) michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: js::CurrentThreadCanAccessZone(Zone *zone) michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: #endif // JS_THREADSAFE michael@0: michael@0: #ifdef DEBUG michael@0: michael@0: void michael@0: JSRuntime::assertCanLock(RuntimeLock which) michael@0: { michael@0: #ifdef JS_THREADSAFE michael@0: // In the switch below, each case falls through to the one below it. None michael@0: // of the runtime locks are reentrant, and when multiple locks are acquired michael@0: // it must be done in the order below. michael@0: switch (which) { michael@0: case ExclusiveAccessLock: michael@0: JS_ASSERT(exclusiveAccessOwner != PR_GetCurrentThread()); michael@0: case WorkerThreadStateLock: michael@0: JS_ASSERT(!WorkerThreadState().isLocked()); michael@0: case InterruptLock: michael@0: JS_ASSERT(!currentThreadOwnsInterruptLock()); michael@0: case GCLock: michael@0: JS_ASSERT(gcLockOwner != PR_GetCurrentThread()); michael@0: break; michael@0: default: michael@0: MOZ_CRASH(); michael@0: } michael@0: #endif // JS_THREADSAFE michael@0: } michael@0: michael@0: void michael@0: js::AssertCurrentThreadCanLock(RuntimeLock which) michael@0: { michael@0: #ifdef JS_THREADSAFE michael@0: PerThreadData *pt = TlsPerThreadData.get(); michael@0: if (pt && pt->runtime_) michael@0: pt->runtime_->assertCanLock(which); michael@0: #endif michael@0: } michael@0: michael@0: #endif // DEBUG