mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-08 08:48:39 +09:00
58 GC no crashing.
Compiles link no crashing.
This commit is contained in:
parent
f9e762101d
commit
c5d270f172
12 changed files with 320 additions and 144 deletions
|
|
@ -144,6 +144,12 @@ class PersistentRootedBase : public MutableWrappedPtrOperations<T, Wrapper> {};
|
|||
|
||||
static void* const ConstNullValue = nullptr;
|
||||
|
||||
template <typename T>
|
||||
class FakeRooted;
|
||||
|
||||
template <typename T>
|
||||
class FakeMutableHandle;
|
||||
|
||||
namespace gc {
|
||||
struct Cell;
|
||||
template<typename T>
|
||||
|
|
@ -889,64 +895,6 @@ class HandleBase<JSObject*, Container> : public WrappedPtrOperations<JSObject*,
|
|||
JS::Handle<U*> as() const;
|
||||
};
|
||||
|
||||
/** Interface substitute for Rooted<T> which does not root the variable's memory. */
|
||||
template <typename T>
|
||||
class MOZ_RAII FakeRooted : public RootedBase<T, FakeRooted<T>>
|
||||
{
|
||||
public:
|
||||
using ElementType = T;
|
||||
|
||||
template <typename CX>
|
||||
explicit FakeRooted(CX* cx) : ptr(JS::GCPolicy<T>::initial()) {}
|
||||
|
||||
template <typename CX>
|
||||
FakeRooted(CX* cx, T initial) : ptr(initial) {}
|
||||
|
||||
DECLARE_POINTER_CONSTREF_OPS(T);
|
||||
DECLARE_POINTER_ASSIGN_OPS(FakeRooted, T);
|
||||
DECLARE_NONPOINTER_ACCESSOR_METHODS(ptr);
|
||||
DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(ptr);
|
||||
|
||||
private:
|
||||
T ptr;
|
||||
|
||||
void set(const T& value) {
|
||||
ptr = value;
|
||||
}
|
||||
|
||||
FakeRooted(const FakeRooted&) = delete;
|
||||
};
|
||||
|
||||
/** Interface substitute for MutableHandle<T> which is not required to point to rooted memory. */
|
||||
template <typename T>
|
||||
class FakeMutableHandle : public js::MutableHandleBase<T, FakeMutableHandle<T>>
|
||||
{
|
||||
public:
|
||||
using ElementType = T;
|
||||
|
||||
MOZ_IMPLICIT FakeMutableHandle(T* t) {
|
||||
ptr = t;
|
||||
}
|
||||
|
||||
MOZ_IMPLICIT FakeMutableHandle(FakeRooted<T>* root) {
|
||||
ptr = root->address();
|
||||
}
|
||||
|
||||
void set(const T& v) {
|
||||
*ptr = v;
|
||||
}
|
||||
|
||||
DECLARE_POINTER_CONSTREF_OPS(T);
|
||||
DECLARE_NONPOINTER_ACCESSOR_METHODS(*ptr);
|
||||
DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(*ptr);
|
||||
|
||||
private:
|
||||
FakeMutableHandle() {}
|
||||
DELETE_ASSIGNMENT_OPS(FakeMutableHandle, T);
|
||||
|
||||
T* ptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* Types for a variable that either should or shouldn't be rooted, depending on
|
||||
* the template parameter allowGC. Used for implementing functions that can
|
||||
|
|
@ -986,27 +934,6 @@ template <typename T> class MaybeRooted<T, CanGC>
|
|||
}
|
||||
};
|
||||
|
||||
template <typename T> class MaybeRooted<T, NoGC>
|
||||
{
|
||||
public:
|
||||
typedef const T& HandleType;
|
||||
typedef FakeRooted<T> RootType;
|
||||
typedef FakeMutableHandle<T> MutableHandleType;
|
||||
|
||||
static JS::Handle<T> toHandle(HandleType v) {
|
||||
MOZ_CRASH("Bad conversion");
|
||||
}
|
||||
|
||||
static JS::MutableHandle<T> toMutableHandle(MutableHandleType v) {
|
||||
MOZ_CRASH("Bad conversion");
|
||||
}
|
||||
|
||||
template <typename T2>
|
||||
static inline T2* downcastHandle(HandleType v) {
|
||||
return &v->template as<T2>();
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
namespace JS {
|
||||
|
|
@ -1519,6 +1446,4 @@ operator!=(const T& a, std::nullptr_t b) {
|
|||
return !(a == b);
|
||||
}
|
||||
|
||||
#undef DELETE_ASSIGNMENT_OPS
|
||||
|
||||
#endif /* js_RootingAPI_h */
|
||||
|
|
|
|||
|
|
@ -2161,7 +2161,7 @@ GCMarker::enterWeakMarkingMode()
|
|||
if (weakMapAction() == ExpandWeakMaps) {
|
||||
tag_ = TracerKindTag::WeakMarking;
|
||||
|
||||
for (SweepGroupZonesIter zone(runtime()); !zone.done(); zone.next()) {
|
||||
for (GCSweepGroupIter zone(runtime()); !zone.done(); zone.next()) {
|
||||
for (WeakMapBase* m : zone->gcWeakMapList()) {
|
||||
if (m->marked)
|
||||
(void) m->traceEntries(this);
|
||||
|
|
@ -2610,12 +2610,25 @@ js::TenuringTracer::moveToTenuredSlow(JSObject* src)
|
|||
// shape list. This is updated in Nursery::sweepDictionaryModeObjects().
|
||||
}
|
||||
|
||||
JSObjectMovedOp op = dst->getClass()->extObjectMovedOp();
|
||||
MOZ_ASSERT_IF(src->is<ProxyObject>(), op == proxy_ObjectMoved);
|
||||
if (op) {
|
||||
if (src->is<InlineTypedObject>()) {
|
||||
InlineTypedObject::objectMovedDuringMinorGC(this, dst, src);
|
||||
} else if (src->is<TypedArrayObject>()) {
|
||||
tenuredSize += TypedArrayObject::objectMovedDuringMinorGC(this, dst, src, dstKind);
|
||||
} else if (src->is<UnboxedArrayObject>()) {
|
||||
tenuredSize += UnboxedArrayObject::objectMovedDuringMinorGC(this, dst, src, dstKind);
|
||||
} else if (src->is<ArgumentsObject>()) {
|
||||
tenuredSize += ArgumentsObject::objectMovedDuringMinorGC(this, dst, src);
|
||||
} else if (src->is<ProxyObject>()) {
|
||||
// Objects in the nursery are never swapped so the proxy must have an
|
||||
// inline ProxyValueArray.
|
||||
MOZ_ASSERT(src->as<ProxyObject>().usingInlineValueArray());
|
||||
dst->as<ProxyObject>().setInlineValueArray();
|
||||
if (JSObjectMovedOp op = dst->getClass()->extObjectMovedOp())
|
||||
op(dst, src);
|
||||
} else if (JSObjectMovedOp op = dst->getClass()->extObjectMovedOp()) {
|
||||
// Tell the hazard analysis that the object moved hook can't GC.
|
||||
JS::AutoSuppressGCAnalysis nogc;
|
||||
tenuredSize += op(dst, src);
|
||||
op(dst, src);
|
||||
} else {
|
||||
MOZ_ASSERT_IF(src->getClass()->hasFinalize(),
|
||||
CanNurseryAllocateFinalizedClass(src->getClass()));
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ class RegExpObject;
|
|||
class SavedFrame;
|
||||
class Scope;
|
||||
class EnvironmentObject;
|
||||
class RequestedModuleObject;
|
||||
class ScriptSourceObject;
|
||||
class Shape;
|
||||
class SharedArrayBufferObject;
|
||||
|
|
@ -88,7 +87,6 @@ class JitCode;
|
|||
D(js::PropertyName*) \
|
||||
D(js::RegExpObject*) \
|
||||
D(js::RegExpShared*) \
|
||||
D(js::RequestedModuleObject*) \
|
||||
D(js::SavedFrame*) \
|
||||
D(js::Scope*) \
|
||||
D(js::ScriptSourceObject*) \
|
||||
|
|
|
|||
|
|
@ -27,9 +27,11 @@ StoreBuffer::GenericBuffer::trace(StoreBuffer* owner, JSTracer* trc)
|
|||
return;
|
||||
|
||||
for (LifoAlloc::Enum e(*storage_); !e.empty();) {
|
||||
unsigned size = *e.read<unsigned>();
|
||||
BufferableRef* edge = e.read<BufferableRef>(size);
|
||||
unsigned size = *e.get<unsigned>();
|
||||
e.popFront<unsigned>();
|
||||
BufferableRef* edge = e.get<BufferableRef>(size);
|
||||
edge->trace(trc);
|
||||
e.popFront(size);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,6 +45,9 @@ class ZoneHeapThreshold
|
|||
|
||||
double gcHeapGrowthFactor() const { return gcHeapGrowthFactor_; }
|
||||
size_t gcTriggerBytes() const { return gcTriggerBytes_; }
|
||||
size_t AllocThresholdFactorTriggerBytes(GCSchedulingTunables& tunables) const {
|
||||
return gcTriggerBytes_ * tunables.allocThresholdFactor();
|
||||
}
|
||||
double eagerAllocTrigger(bool highFrequencyGC) const;
|
||||
|
||||
void updateAfterGC(size_t lastBytes, JSGCInvocationKind gckind,
|
||||
|
|
|
|||
|
|
@ -925,7 +925,7 @@ GCRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes)
|
|||
return false;
|
||||
|
||||
{
|
||||
AutoLockGC lock(rt);
|
||||
AutoLockGCBgAlloc lock(rt);
|
||||
|
||||
/*
|
||||
* Separate gcMaxMallocBytes from gcMaxBytes but initialize to maxbytes
|
||||
|
|
@ -5954,8 +5954,9 @@ AddSweepAction(bool* ok, SweepAction::Func func, AllocKind kind = AllocKind::LIM
|
|||
*ok = SweepPhases.back().emplaceBack(func, kind);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
GCRuntime::initializeSweepActions()
|
||||
template <typename... Args>
|
||||
static UniquePtr<SweepAction<Args...>>
|
||||
RepeatForSweepGroup(JSRuntime* rt, UniquePtr<SweepAction<Args...>> action)
|
||||
{
|
||||
bool ok = true;
|
||||
|
||||
|
|
@ -5963,14 +5964,17 @@ GCRuntime::initializeSweepActions()
|
|||
return js::MakeUnique<Action>(rt, Move(action));
|
||||
}
|
||||
|
||||
AddSweepAction(&ok, GCRuntime::sweepTypeInformation);
|
||||
AddSweepAction(&ok, GCRuntime::mergeSweptObjectArenas);
|
||||
template <typename... Args>
|
||||
static UniquePtr<typename RemoveLastTemplateParameter<SweepAction<Args...>>::Type>
|
||||
ForEachZoneInSweepGroup(JSRuntime* rt, UniquePtr<SweepAction<Args...>> action)
|
||||
{
|
||||
if (!action)
|
||||
return nullptr;
|
||||
|
||||
for (const auto& finalizePhase : IncrementalFinalizePhases) {
|
||||
AddSweepPhase(&ok);
|
||||
for (auto kind : finalizePhase.kinds)
|
||||
AddSweepAction(&ok, GCRuntime::finalizeAllocKind, kind);
|
||||
}
|
||||
using Action = typename RemoveLastTemplateParameter<
|
||||
SweepActionForEach<GCSweepGroupIter, JSRuntime*, Args...>>::Type;
|
||||
return js::MakeUnique<Action>(rt, Move(action));
|
||||
}
|
||||
|
||||
AddSweepPhase(&ok);
|
||||
AddSweepAction(&ok, GCRuntime::sweepShapeTree);
|
||||
|
|
@ -5989,7 +5993,7 @@ GCRuntime::initSweepActions()
|
|||
using sweepaction::Call;
|
||||
|
||||
sweepActions.ref() =
|
||||
RepeatForZoneGroup(rt,
|
||||
RepeatForSweepGroup(rt,
|
||||
Sequence(
|
||||
Call(&GCRuntime::endMarkingSweepGroup),
|
||||
Call(&GCRuntime::beginSweepingSweepGroup),
|
||||
|
|
@ -5998,17 +6002,17 @@ GCRuntime::initSweepActions()
|
|||
#endif
|
||||
Call(&GCRuntime::sweepAtomsTable),
|
||||
Call(&GCRuntime::sweepWeakCaches),
|
||||
ForEachZoneInZoneGroup(rt,
|
||||
ForEachZoneInSweepGroup(rt,
|
||||
ForEachAllocKind(ForegroundObjectFinalizePhase.kinds,
|
||||
Call(&GCRuntime::finalizeAllocKind))),
|
||||
ForEachZoneInZoneGroup(rt,
|
||||
ForEachZoneInSweepGroup(rt,
|
||||
Sequence(
|
||||
Call(&GCRuntime::sweepTypeInformation),
|
||||
Call(&GCRuntime::mergeSweptObjectArenas))),
|
||||
ForEachZoneInZoneGroup(rt,
|
||||
ForEachZoneInSweepGroup(rt,
|
||||
ForEachAllocKind(ForegroundNonObjectFinalizePhase.kinds,
|
||||
Call(&GCRuntime::finalizeAllocKind))),
|
||||
ForEachZoneInZoneGroup(rt,
|
||||
ForEachZoneInSweepGroup(rt,
|
||||
Call(&GCRuntime::sweepShapeTree)),
|
||||
Call(&GCRuntime::endSweepingSweepGroup)));
|
||||
|
||||
|
|
|
|||
|
|
@ -306,12 +306,6 @@ GetGCKindBytes(AllocKind thingKind)
|
|||
return sizeof(JSObject_Slots0) + GetGCKindSlots(thingKind) * sizeof(Value);
|
||||
}
|
||||
|
||||
// Class to assist in triggering background chunk allocation. This cannot be done
|
||||
// while holding the GC or worker thread state lock due to lock ordering issues.
|
||||
// As a result, the triggering is delayed using this class until neither of the
|
||||
// above locks is held.
|
||||
class AutoMaybeStartBackgroundAllocation;
|
||||
|
||||
/*
|
||||
* A single segment of a SortedArenaList. Each segment has a head and a tail,
|
||||
* which track the start and end of a segment for O(1) append and concatenation.
|
||||
|
|
@ -827,8 +821,7 @@ class ArenaLists
|
|||
inline void mergeSweptArenas(AllocKind thingKind);
|
||||
|
||||
TenuredCell* allocateFromArena(JS::Zone* zone, AllocKind thingKind,
|
||||
ShouldCheckThresholds checkThresholds,
|
||||
AutoMaybeStartBackgroundAllocation& maybeStartBGAlloc);
|
||||
ShouldCheckThresholds checkThresholds);
|
||||
inline TenuredCell* allocateFromArenaInner(JS::Zone* zone, Arena* arena, AllocKind kind);
|
||||
|
||||
inline void normalizeBackgroundFinalizeState(AllocKind thingKind);
|
||||
|
|
|
|||
139
js/src/threading/ProtectedData.cpp
Normal file
139
js/src/threading/ProtectedData.cpp
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#include "threading/ProtectedData.h"
|
||||
|
||||
#include "jscntxt.h"
|
||||
|
||||
#include "gc/Heap.h"
|
||||
#include "vm/HelperThreads.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
#ifdef JS_HAS_PROTECTED_DATA_CHECKS
|
||||
|
||||
/* static */ mozilla::Atomic<size_t> AutoNoteSingleThreadedRegion::count(0);
|
||||
|
||||
template <AllowedHelperThread Helper>
|
||||
static inline bool
|
||||
OnHelperThread()
|
||||
{
|
||||
if (Helper == AllowedHelperThread::IonCompile || Helper == AllowedHelperThread::GCTaskOrIonCompile) {
|
||||
if (CurrentThreadIsIonCompiling())
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Helper == AllowedHelperThread::GCTask || Helper == AllowedHelperThread::GCTaskOrIonCompile) {
|
||||
if (TlsContext.get()->performingGC || TlsContext.get()->runtime()->gc.onBackgroundThread())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
CheckThreadLocal::check() const
|
||||
{
|
||||
JSContext* cx = TlsContext.get();
|
||||
MOZ_ASSERT(cx);
|
||||
|
||||
// As for CheckZoneGroup, in a cooperatively scheduled runtime the active
|
||||
// thread is permitted access to thread local state for other suspended
|
||||
// threads in the same runtime.
|
||||
if (cx->isCooperativelyScheduled())
|
||||
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
|
||||
else
|
||||
MOZ_ASSERT(id == ThisThread::GetId());
|
||||
}
|
||||
|
||||
template <AllowedHelperThread Helper>
|
||||
void
|
||||
CheckActiveThread<Helper>::check() const
|
||||
{
|
||||
// When interrupting a thread on Windows, changes are made to the runtime
|
||||
// and active thread's state from another thread while the active thread is
|
||||
// suspended. We need a way to mark these accesses as being tantamount to
|
||||
// accesses by the active thread. See bug 1323066.
|
||||
#ifndef XP_WIN
|
||||
if (OnHelperThread<Helper>())
|
||||
return;
|
||||
|
||||
JSContext* cx = TlsContext.get();
|
||||
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
|
||||
#endif // XP_WIN
|
||||
}
|
||||
|
||||
template class CheckActiveThread<AllowedHelperThread::None>;
|
||||
template class CheckActiveThread<AllowedHelperThread::GCTask>;
|
||||
template class CheckActiveThread<AllowedHelperThread::IonCompile>;
|
||||
|
||||
template <AllowedHelperThread Helper>
|
||||
void
|
||||
CheckZoneGroup<Helper>::check() const
|
||||
{
|
||||
if (OnHelperThread<Helper>())
|
||||
return;
|
||||
|
||||
JSContext* cx = TlsContext.get();
|
||||
if (group) {
|
||||
if (group->usedByHelperThread()) {
|
||||
MOZ_ASSERT(group->ownedByCurrentThread());
|
||||
} else {
|
||||
// This check is disabled on windows for the same reason as in
|
||||
// CheckActiveThread.
|
||||
#ifndef XP_WIN
|
||||
// In a cooperatively scheduled runtime the active thread is
|
||||
// permitted access to all zone groups --- even those it has not
|
||||
// entered --- for GC and similar purposes. Since all other
|
||||
// cooperative threads are suspended, these accesses are threadsafe
|
||||
// if the zone group is not in use by a helper thread.
|
||||
//
|
||||
// A corollary to this is that suspended cooperative threads may
|
||||
// not access anything in a zone group, even zone groups they own,
|
||||
// because they're not allowed to interact with the JS API.
|
||||
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
// |group| will be null for data in the atoms zone. This is protected
|
||||
// by the exclusive access lock.
|
||||
MOZ_ASSERT(cx->runtime()->currentThreadHasExclusiveAccess());
|
||||
}
|
||||
}
|
||||
|
||||
template class CheckZoneGroup<AllowedHelperThread::None>;
|
||||
template class CheckZoneGroup<AllowedHelperThread::GCTask>;
|
||||
template class CheckZoneGroup<AllowedHelperThread::IonCompile>;
|
||||
template class CheckZoneGroup<AllowedHelperThread::GCTaskOrIonCompile>;
|
||||
|
||||
template <GlobalLock Lock, AllowedHelperThread Helper>
|
||||
void
|
||||
CheckGlobalLock<Lock, Helper>::check() const
|
||||
{
|
||||
if (OnHelperThread<Helper>())
|
||||
return;
|
||||
|
||||
switch (Lock) {
|
||||
case GlobalLock::GCLock:
|
||||
MOZ_ASSERT(TlsContext.get()->runtime()->gc.currentThreadHasLockedGC());
|
||||
break;
|
||||
case GlobalLock::ExclusiveAccessLock:
|
||||
MOZ_ASSERT(TlsContext.get()->runtime()->currentThreadHasExclusiveAccess());
|
||||
break;
|
||||
case GlobalLock::HelperThreadLock:
|
||||
MOZ_ASSERT(HelperThreadState().isLockedByCurrentThread());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template class CheckGlobalLock<GlobalLock::GCLock, AllowedHelperThread::None>;
|
||||
template class CheckGlobalLock<GlobalLock::ExclusiveAccessLock, AllowedHelperThread::None>;
|
||||
template class CheckGlobalLock<GlobalLock::ExclusiveAccessLock, AllowedHelperThread::GCTask>;
|
||||
template class CheckGlobalLock<GlobalLock::HelperThreadLock, AllowedHelperThread::None>;
|
||||
|
||||
#endif // JS_HAS_PROTECTED_DATA_CHECKS
|
||||
|
||||
} // namespace js
|
||||
|
|
@ -31,6 +31,7 @@ using namespace js;
|
|||
|
||||
using mozilla::ArrayLength;
|
||||
using mozilla::DebugOnly;
|
||||
using mozilla::Maybe;
|
||||
using mozilla::Unused;
|
||||
using mozilla::TimeDuration;
|
||||
|
||||
|
|
@ -483,6 +484,14 @@ js::CancelOffThreadParses(JSRuntime* rt)
|
|||
if (!found)
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
GlobalHelperThreadState::ParseTaskVector& worklist = HelperThreadState().parseWorklist(lock);
|
||||
for (size_t i = 0; i < worklist.length(); i++) {
|
||||
ParseTask* task = worklist[i];
|
||||
MOZ_ASSERT(!task->runtimeMatches(rt));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -536,8 +545,29 @@ EnsureParserCreatedClasses(JSContext* cx, ParseTaskKind kind)
|
|||
return true;
|
||||
}
|
||||
|
||||
class AutoClearUsedByHelperThread
|
||||
{
|
||||
ZoneGroup* group;
|
||||
|
||||
public:
|
||||
AutoClearUsedByHelperThread(JSObject* global)
|
||||
: group(global->zone()->group())
|
||||
{}
|
||||
|
||||
void forget() {
|
||||
group = nullptr;
|
||||
}
|
||||
|
||||
~AutoClearUsedByHelperThread() {
|
||||
if (group)
|
||||
group->clearUsedByHelperThread();
|
||||
}
|
||||
};
|
||||
|
||||
static JSObject*
|
||||
CreateGlobalForOffThreadParse(JSContext* cx, ParseTaskKind kind, const gc::AutoSuppressGC& nogc)
|
||||
CreateGlobalForOffThreadParse(JSContext* cx, ParseTaskKind kind,
|
||||
Maybe<AutoClearUsedByHelperThread>& clearUseGuard,
|
||||
const gc::AutoSuppressGC& nogc)
|
||||
{
|
||||
JSCompartment* currentCompartment = cx->compartment();
|
||||
|
||||
|
|
@ -560,7 +590,13 @@ CreateGlobalForOffThreadParse(JSContext* cx, ParseTaskKind kind, const gc::AutoS
|
|||
|
||||
JS_SetCompartmentPrincipals(global->compartment(), currentCompartment->principals());
|
||||
|
||||
// Initialize all classes required for parsing while still on the main
|
||||
// Mark this zone group as created for a helper thread. This prevents it
|
||||
// from being collected until clearUsedByHelperThread() is called.
|
||||
ZoneGroup* group = global->zone()->group();
|
||||
group->setCreatedForHelperThread();
|
||||
clearUseGuard.emplace(global);
|
||||
|
||||
// Initialize all classes required for parsing while still on the active
|
||||
// thread, for both the target and the new global so that prototype
|
||||
// pointers can be changed infallibly after parsing finishes.
|
||||
if (!EnsureParserCreatedClasses(cx, kind))
|
||||
|
|
@ -577,19 +613,18 @@ CreateGlobalForOffThreadParse(JSContext* cx, ParseTaskKind kind, const gc::AutoS
|
|||
static bool
|
||||
QueueOffThreadParseTask(JSContext* cx, ParseTask* task)
|
||||
{
|
||||
if (OffThreadParsingMustWaitForGC(cx->runtime())) {
|
||||
AutoLockHelperThreadState lock;
|
||||
if (!HelperThreadState().parseWaitingOnGC(lock).append(task)) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
AutoLockHelperThreadState lock;
|
||||
if (!HelperThreadState().parseWorklist(lock).append(task)) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
AutoLockHelperThreadState lock;
|
||||
|
||||
bool mustWait = OffThreadParsingMustWaitForGC(cx->runtime());
|
||||
|
||||
auto& queue = mustWait ? HelperThreadState().parseWaitingOnGC(lock)
|
||||
: HelperThreadState().parseWorklist(lock);
|
||||
if (!queue.append(task)) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mustWait) {
|
||||
task->activate(cx->runtime());
|
||||
HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER, lock);
|
||||
}
|
||||
|
|
@ -608,7 +643,8 @@ StartOffThreadParseTask(JSContext* cx, const ReadOnlyCompileOptions& options,
|
|||
gc::AutoAssertNoNurseryAlloc noNurseryAlloc(cx->runtime());
|
||||
AutoSuppressAllocationMetadataBuilder suppressMetadata(cx);
|
||||
|
||||
JSObject* global = CreateGlobalForOffThreadParse(cx, kind, nogc);
|
||||
Maybe<AutoClearUsedByHelperThread> clearUseGuard;
|
||||
JSObject* global = CreateGlobalForOffThreadParse(cx, kind, clearUseGuard, nogc);
|
||||
if (!global)
|
||||
return false;
|
||||
|
||||
|
|
@ -628,6 +664,7 @@ StartOffThreadParseTask(JSContext* cx, const ReadOnlyCompileOptions& options,
|
|||
return false;
|
||||
|
||||
task.forget();
|
||||
clearUseGuard->forget();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -693,8 +730,8 @@ js::EnqueuePendingParseTasksAfterGC(JSRuntime* rt)
|
|||
if (newTasks.empty())
|
||||
return;
|
||||
|
||||
// This logic should mirror the contents of the !activeGCInAtomsZone()
|
||||
// branch in StartOffThreadParseScript:
|
||||
// This logic should mirror the contents of the
|
||||
// !OffThreadParsingMustWaitForGC() branch in QueueOffThreadParseTask:
|
||||
|
||||
for (size_t i = 0; i < newTasks.length(); i++)
|
||||
newTasks[i]->activate(rt);
|
||||
|
|
@ -1130,7 +1167,9 @@ js::GCParallelTask::~GCParallelTask()
|
|||
// base class can't ensure that the task is done using the members. All we
|
||||
// can do now is check that someone has previously stopped the task.
|
||||
#ifdef DEBUG
|
||||
AutoLockHelperThreadState helperLock;
|
||||
Maybe<AutoLockHelperThreadState> helperLock;
|
||||
if (!HelperThreadState().isLockedByCurrentThread())
|
||||
helperLock.emplace();
|
||||
MOZ_ASSERT(state == NotStarted);
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -297,6 +297,10 @@ class RegExpZone
|
|||
bool get(JSContext* cx, HandleAtom source, JSString* maybeOpt,
|
||||
MutableHandleRegExpShared shared);
|
||||
|
||||
#ifdef DEBUG
|
||||
void clear() { set_.clear(); }
|
||||
#endif
|
||||
|
||||
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime)
|
|||
#ifdef DEBUG
|
||||
mainThreadHasExclusiveAccess(false),
|
||||
#endif
|
||||
numExclusiveThreads(0),
|
||||
numActiveHelperThreadZones(0),
|
||||
numCompartments(0),
|
||||
localeCallbacks(nullptr),
|
||||
defaultLocale(nullptr),
|
||||
|
|
@ -868,11 +868,21 @@ JSRuntime::setUsedByExclusiveThread(Zone* zone)
|
|||
void
|
||||
JSRuntime::clearUsedByExclusiveThread(Zone* zone)
|
||||
{
|
||||
MOZ_ASSERT(zone->usedByExclusiveThread);
|
||||
zone->usedByExclusiveThread = false;
|
||||
numExclusiveThreads--;
|
||||
if (gc.fullGCForAtomsRequested() && !keepAtoms())
|
||||
gc.triggerFullGCForAtoms();
|
||||
MOZ_ASSERT(!zone->group()->usedByHelperThread());
|
||||
MOZ_ASSERT(!zone->wasGCStarted());
|
||||
zone->group()->setUsedByHelperThread();
|
||||
numActiveHelperThreadZones++;
|
||||
}
|
||||
|
||||
void
|
||||
JSRuntime::clearUsedByHelperThread(Zone* zone)
|
||||
{
|
||||
MOZ_ASSERT(zone->group()->usedByHelperThread());
|
||||
zone->group()->clearUsedByHelperThread();
|
||||
numActiveHelperThreadZones--;
|
||||
JSContext* cx = TlsContext.get();
|
||||
if (gc.fullGCForAtomsRequested() && cx->canCollectAtoms())
|
||||
gc.triggerFullGCForAtoms(cx);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
|||
|
|
@ -676,8 +676,8 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||
bool mainThreadHasExclusiveAccess;
|
||||
#endif
|
||||
|
||||
/* Number of non-main threads with an ExclusiveContext. */
|
||||
size_t numExclusiveThreads;
|
||||
/* Number of zones which may be operated on by non-cooperating helper threads. */
|
||||
js::UnprotectedData<size_t> numActiveHelperThreadZones;
|
||||
|
||||
friend class js::AutoLockForExclusiveAccess;
|
||||
|
||||
|
|
@ -685,8 +685,8 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||
void setUsedByExclusiveThread(JS::Zone* zone);
|
||||
void clearUsedByExclusiveThread(JS::Zone* zone);
|
||||
|
||||
bool exclusiveThreadsPresent() const {
|
||||
return numExclusiveThreads > 0;
|
||||
bool hasHelperThreadZones() const {
|
||||
return numActiveHelperThreadZones > 0;
|
||||
}
|
||||
|
||||
// How many compartments there are across all zones. This number includes
|
||||
|
|
@ -1414,8 +1414,10 @@ FreeOp::appendJitPoisonRange(const jit::JitPoisonRange& range)
|
|||
/*
|
||||
* RAII class that takes the GC lock while it is live.
|
||||
*
|
||||
* Note that the lock may be temporarily released by use of AutoUnlockGC when
|
||||
* passed a non-const reference to this class.
|
||||
* Usually functions will pass const references of this class. However
|
||||
* non-const references can be used to either temporarily release the lock by
|
||||
* use of AutoUnlockGC or to start background allocation when the lock is
|
||||
* released.
|
||||
*/
|
||||
class MOZ_RAII AutoLockGC
|
||||
{
|
||||
|
|
@ -1429,7 +1431,7 @@ class MOZ_RAII AutoLockGC
|
|||
}
|
||||
|
||||
~AutoLockGC() {
|
||||
unlock();
|
||||
lockGuard_.reset();
|
||||
}
|
||||
|
||||
void lock() {
|
||||
|
|
@ -1446,6 +1448,9 @@ class MOZ_RAII AutoLockGC
|
|||
return lockGuard_.ref();
|
||||
}
|
||||
|
||||
protected:
|
||||
JSRuntime* runtime() const { return runtime_; }
|
||||
|
||||
private:
|
||||
JSRuntime* runtime_;
|
||||
mozilla::Maybe<js::LockGuard<js::Mutex>> lockGuard_;
|
||||
|
|
@ -1455,6 +1460,47 @@ class MOZ_RAII AutoLockGC
|
|||
AutoLockGC& operator=(const AutoLockGC&) = delete;
|
||||
};
|
||||
|
||||
/*
|
||||
* Same as AutoLockGC except it can optionally start a background chunk
|
||||
* allocation task when the lock is released.
|
||||
*/
|
||||
class MOZ_RAII AutoLockGCBgAlloc : public AutoLockGC
|
||||
{
|
||||
public:
|
||||
explicit AutoLockGCBgAlloc(JSRuntime* rt)
|
||||
: AutoLockGC(rt)
|
||||
, startBgAlloc(false)
|
||||
{}
|
||||
|
||||
~AutoLockGCBgAlloc() {
|
||||
unlock();
|
||||
|
||||
/*
|
||||
* We have to do this after releasing the lock because it may acquire
|
||||
* the helper lock which could cause lock inversion if we still held
|
||||
* the GC lock.
|
||||
*/
|
||||
if (startBgAlloc)
|
||||
runtime()->gc.startBackgroundAllocTaskIfIdle();
|
||||
}
|
||||
|
||||
/*
|
||||
* This can be used to start a background allocation task (if one isn't
|
||||
* already running) that allocates chunks and makes them available in the
|
||||
* free chunks list. This happens after the lock is released in order to
|
||||
* avoid lock inversion.
|
||||
*/
|
||||
void tryToStartBackgroundAllocation() {
|
||||
startBgAlloc = true;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// true if we should start a background chunk allocation task after the
|
||||
// lock is released.
|
||||
bool startBgAlloc;
|
||||
};
|
||||
|
||||
class MOZ_RAII AutoUnlockGC
|
||||
{
|
||||
public:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue