зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 3 changesets (bug 1559659) for SM bustages on Runtime.cpp . CLOSED TREE
Backed out changeset 1d509e451876 (bug 1559659) Backed out changeset f4b6a279a331 (bug 1559659) Backed out changeset fa9186737abd (bug 1559659)
This commit is contained in:
Родитель
a4cd447c57
Коммит
047fde9d4e
|
@ -1142,7 +1142,6 @@ AbortReasonOr<Ok> IonBuilder::buildInline(IonBuilder* callerBuilder,
|
|||
void IonBuilder::runTask() {
|
||||
// This is the entry point when ion compiles are run offthread.
|
||||
JSRuntime* rt = script()->runtimeFromAnyThread();
|
||||
AutoSetHelperThreadContext usesContext;
|
||||
|
||||
TraceLoggerThread* logger = TraceLoggerForCurrentThread();
|
||||
TraceLoggerEvent event(TraceLogger_AnnotateScripts, script());
|
||||
|
|
|
@ -65,17 +65,8 @@ GlobalHelperThreadState* gHelperThreadState = nullptr;
|
|||
|
||||
bool js::CreateHelperThreadsState() {
|
||||
MOZ_ASSERT(!gHelperThreadState);
|
||||
UniquePtr<GlobalHelperThreadState> helperThreadState =
|
||||
MakeUnique<GlobalHelperThreadState>();
|
||||
if (!helperThreadState) {
|
||||
return false;
|
||||
}
|
||||
gHelperThreadState = helperThreadState.release();
|
||||
if (!gHelperThreadState->initializeHelperContexts()) {
|
||||
js_delete(gHelperThreadState);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
gHelperThreadState = js_new<GlobalHelperThreadState>();
|
||||
return gHelperThreadState != nullptr;
|
||||
}
|
||||
|
||||
void js::DestroyHelperThreadsState() {
|
||||
|
@ -444,43 +435,6 @@ struct MOZ_RAII AutoSetContextParse {
|
|||
~AutoSetContextParse() { TlsContext.get()->setParseTask(nullptr); }
|
||||
};
|
||||
|
||||
// We want our default stack size limit to be approximately 2MB, to be safe, but
|
||||
// expect most threads to use much less. On Linux, however, requesting a stack
|
||||
// of 2MB or larger risks the kernel allocating an entire 2MB huge page for it
|
||||
// on first access, which we do not want. To avoid this possibility, we subtract
|
||||
// 2 standard VM page sizes from our default.
|
||||
static const uint32_t kDefaultHelperStackSize = 2048 * 1024 - 2 * 4096;
|
||||
static const uint32_t kDefaultHelperStackQuota = 1800 * 1024;
|
||||
|
||||
// TSan enforces a minimum stack size that's just slightly larger than our
|
||||
// default helper stack size. It does this to store blobs of TSan-specific
|
||||
// data on each thread's stack. Unfortunately, that means that even though
|
||||
// we'll actually receive a larger stack than we requested, the effective
|
||||
// usable space of that stack is significantly less than what we expect.
|
||||
// To offset TSan stealing our stack space from underneath us, double the
|
||||
// default.
|
||||
//
|
||||
// Note that we don't need this for ASan/MOZ_ASAN because ASan doesn't
|
||||
// require all the thread-specific state that TSan does.
|
||||
#if defined(MOZ_TSAN)
|
||||
static const uint32_t HELPER_STACK_SIZE = 2 * kDefaultHelperStackSize;
|
||||
static const uint32_t HELPER_STACK_QUOTA = 2 * kDefaultHelperStackQuota;
|
||||
#else
|
||||
static const uint32_t HELPER_STACK_SIZE = kDefaultHelperStackSize;
|
||||
static const uint32_t HELPER_STACK_QUOTA = kDefaultHelperStackQuota;
|
||||
#endif
|
||||
|
||||
AutoSetHelperThreadContext::AutoSetHelperThreadContext() {
|
||||
AutoLockHelperThreadState lock;
|
||||
cx = HelperThreadState().getFirstUnusedContext(lock);
|
||||
MOZ_ASSERT(cx);
|
||||
cx->setHelperThread(lock);
|
||||
cx->nativeStackBase = GetNativeStackBase();
|
||||
// When we set the JSContext, we need to reset the computed stack limits for
|
||||
// the current thread, so we also set the native stack quota.
|
||||
JS_SetNativeStackQuota(cx, HELPER_STACK_QUOTA);
|
||||
}
|
||||
|
||||
static const JSClass parseTaskGlobalClass = {"internal-parse-task-global",
|
||||
JSCLASS_GLOBAL_FLAGS,
|
||||
&JS::DefaultGlobalClassOps};
|
||||
|
@ -543,14 +497,11 @@ size_t ParseTask::sizeOfExcludingThis(
|
|||
}
|
||||
|
||||
void ParseTask::runTask() {
|
||||
AutoSetHelperThreadContext usesContext;
|
||||
|
||||
JSContext* cx = TlsContext.get();
|
||||
JSRuntime* runtime = parseGlobal->runtimeFromAnyThread();
|
||||
|
||||
AutoSetContextRuntime ascr(runtime);
|
||||
AutoSetContextParse parsetask(this);
|
||||
gc::AutoSuppressNurseryCellAlloc noNurseryAlloc(cx);
|
||||
|
||||
Zone* zone = parseGlobal->zoneFromAnyThread();
|
||||
zone->setHelperThreadOwnerContext(cx);
|
||||
|
@ -1123,6 +1074,32 @@ bool js::CurrentThreadIsParseThread() {
|
|||
}
|
||||
#endif
|
||||
|
||||
// We want our default stack size limit to be approximately 2MB, to be safe, but
|
||||
// expect most threads to use much less. On Linux, however, requesting a stack
|
||||
// of 2MB or larger risks the kernel allocating an entire 2MB huge page for it
|
||||
// on first access, which we do not want. To avoid this possibility, we subtract
|
||||
// 2 standard VM page sizes from our default.
|
||||
static const uint32_t kDefaultHelperStackSize = 2048 * 1024 - 2 * 4096;
|
||||
static const uint32_t kDefaultHelperStackQuota = 1800 * 1024;
|
||||
|
||||
// TSan enforces a minimum stack size that's just slightly larger than our
|
||||
// default helper stack size. It does this to store blobs of TSan-specific
|
||||
// data on each thread's stack. Unfortunately, that means that even though
|
||||
// we'll actually receive a larger stack than we requested, the effective
|
||||
// usable space of that stack is significantly less than what we expect.
|
||||
// To offset TSan stealing our stack space from underneath us, double the
|
||||
// default.
|
||||
//
|
||||
// Note that we don't need this for ASan/MOZ_ASAN because ASan doesn't
|
||||
// require all the thread-specific state that TSan does.
|
||||
#if defined(MOZ_TSAN)
|
||||
static const uint32_t HELPER_STACK_SIZE = 2 * kDefaultHelperStackSize;
|
||||
static const uint32_t HELPER_STACK_QUOTA = 2 * kDefaultHelperStackQuota;
|
||||
#else
|
||||
static const uint32_t HELPER_STACK_SIZE = kDefaultHelperStackSize;
|
||||
static const uint32_t HELPER_STACK_QUOTA = kDefaultHelperStackQuota;
|
||||
#endif
|
||||
|
||||
bool GlobalHelperThreadState::ensureInitialized() {
|
||||
MOZ_ASSERT(CanUseExtraThreads());
|
||||
|
||||
|
@ -1187,7 +1164,6 @@ void GlobalHelperThreadState::finish() {
|
|||
while (!freeList.empty()) {
|
||||
jit::FreeIonBuilder(freeList.popCopy());
|
||||
}
|
||||
destroyHelperContexts(lock);
|
||||
}
|
||||
|
||||
void GlobalHelperThreadState::finishThreads() {
|
||||
|
@ -1202,46 +1178,9 @@ void GlobalHelperThreadState::finishThreads() {
|
|||
threads.reset(nullptr);
|
||||
}
|
||||
|
||||
bool GlobalHelperThreadState::initializeHelperContexts() {
|
||||
AutoLockHelperThreadState lock;
|
||||
for (size_t i = 0; i < threadCount; i++) {
|
||||
UniquePtr<JSContext> cx =
|
||||
js::MakeUnique<JSContext>(nullptr, JS::ContextOptions());
|
||||
// To initialize context-specific protected data, the context must
|
||||
// temporarily set itself to the main thread. After initialization,
|
||||
// cx can clear itself from the thread.
|
||||
cx->setHelperThread(lock);
|
||||
if (!cx->init(ContextKind::HelperThread)) {
|
||||
return false;
|
||||
}
|
||||
cx->clearHelperThread(lock);
|
||||
if (!helperContexts_.append(cx.release())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void GlobalHelperThreadState::lock() { helperLock.lock(); }
|
||||
|
||||
JSContext* GlobalHelperThreadState::getFirstUnusedContext(
|
||||
AutoLockHelperThreadState& locked) {
|
||||
for (auto& cx : helperContexts_) {
|
||||
if (cx->contextAvailable(locked)) {
|
||||
return cx;
|
||||
}
|
||||
}
|
||||
MOZ_CRASH("Expected available JSContext");
|
||||
}
|
||||
|
||||
void GlobalHelperThreadState::destroyHelperContexts(
|
||||
AutoLockHelperThreadState& lock) {
|
||||
while (helperContexts_.length() > 0) {
|
||||
JSContext* cx = helperContexts_.popCopy();
|
||||
// Before cx can be destroyed, it has to set itself to the main thread.
|
||||
// This enables it to pass its context-specific data checks.
|
||||
cx->setHelperThread(lock);
|
||||
js_delete(cx);
|
||||
}
|
||||
}
|
||||
void GlobalHelperThreadState::unlock() { helperLock.unlock(); }
|
||||
|
||||
#ifdef DEBUG
|
||||
bool GlobalHelperThreadState::isLockedByCurrentThread() const {
|
||||
|
@ -1359,8 +1298,8 @@ void GlobalHelperThreadState::triggerFreeUnusedMemory() {
|
|||
}
|
||||
|
||||
AutoLockHelperThreadState lock;
|
||||
for (auto& context : helperContexts_) {
|
||||
context->setFreeUnusedMemory(true);
|
||||
for (auto& thread : *threads) {
|
||||
thread.shouldFreeUnusedMemory = true;
|
||||
}
|
||||
notifyAll(PRODUCER, lock);
|
||||
}
|
||||
|
@ -1768,11 +1707,11 @@ void js::GCParallelTask::runFromMainThread(JSRuntime* rt) {
|
|||
void js::GCParallelTask::runFromHelperThread(AutoLockHelperThreadState& lock) {
|
||||
MOZ_ASSERT(isDispatched(lock));
|
||||
|
||||
AutoSetContextRuntime ascr(runtime());
|
||||
gc::AutoSetThreadIsPerformingGC performingGC;
|
||||
|
||||
{
|
||||
AutoUnlockHelperThreadState parallelSection(lock);
|
||||
AutoSetHelperThreadContext usesContext;
|
||||
AutoSetContextRuntime ascr(runtime());
|
||||
gc::AutoSetThreadIsPerformingGC performingGC;
|
||||
TimeStamp timeStart = ReallyNow();
|
||||
runTask();
|
||||
duration_ = TimeSince(timeStart);
|
||||
|
@ -2176,6 +2115,7 @@ void HelperThread::handleIonWorkload(AutoLockHelperThreadState& locked) {
|
|||
|
||||
{
|
||||
AutoUnlockHelperThreadState unlock(locked);
|
||||
AutoSetContextRuntime ascr(rt);
|
||||
|
||||
builder->runTask();
|
||||
}
|
||||
|
@ -2543,9 +2483,21 @@ void HelperThread::threadLoop() {
|
|||
|
||||
ensureRegisteredWithProfiler();
|
||||
|
||||
JSContext cx(nullptr, JS::ContextOptions());
|
||||
{
|
||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
if (!cx.init(ContextKind::HelperThread)) {
|
||||
oomUnsafe.crash("HelperThread cx.init()");
|
||||
}
|
||||
}
|
||||
gc::AutoSuppressNurseryCellAlloc noNurseryAlloc(&cx);
|
||||
JS_SetNativeStackQuota(&cx, HELPER_STACK_QUOTA);
|
||||
|
||||
while (!terminate) {
|
||||
MOZ_ASSERT(idle());
|
||||
|
||||
maybeFreeUnusedMemory(&cx);
|
||||
|
||||
// The selectors may depend on the HelperThreadState not changing
|
||||
// between task selection and task execution, in particular, on new
|
||||
// tasks not being added (because of the lifo structure of the work
|
||||
|
@ -2579,3 +2531,14 @@ const HelperThread::TaskSpec* HelperThread::findHighestPriorityTask(
|
|||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void HelperThread::maybeFreeUnusedMemory(JSContext* cx) {
|
||||
MOZ_ASSERT(idle());
|
||||
|
||||
cx->tempLifoAlloc().releaseAll();
|
||||
|
||||
if (shouldFreeUnusedMemory) {
|
||||
cx->tempLifoAlloc().freeAll();
|
||||
shouldFreeUnusedMemory = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,7 +100,6 @@ class GlobalHelperThreadState {
|
|||
typedef Vector<GCParallelTask*, 0, SystemAllocPolicy> GCParallelTaskVector;
|
||||
typedef Vector<PromiseHelperTask*, 0, SystemAllocPolicy>
|
||||
PromiseHelperTaskVector;
|
||||
typedef Vector<JSContext*, 0, SystemAllocPolicy> ContextVector;
|
||||
|
||||
// List of available threads, or null if the thread state has not been
|
||||
// initialized.
|
||||
|
@ -147,9 +146,6 @@ class GlobalHelperThreadState {
|
|||
// GC tasks needing to be done in parallel.
|
||||
GCParallelTaskVector gcParallelWorklist_;
|
||||
|
||||
// Global list of JSContext for GlobalHelperThreadState to use.
|
||||
ContextVector helperContexts_;
|
||||
|
||||
ParseTask* removeFinishedParseTask(ParseTaskKind kind,
|
||||
JS::OffThreadToken* token);
|
||||
|
||||
|
@ -171,10 +167,8 @@ class GlobalHelperThreadState {
|
|||
void finish();
|
||||
void finishThreads();
|
||||
|
||||
MOZ_MUST_USE bool initializeHelperContexts();
|
||||
JSContext* getFirstUnusedContext(AutoLockHelperThreadState& locked);
|
||||
void destroyHelperContexts(AutoLockHelperThreadState& lock);
|
||||
|
||||
void lock();
|
||||
void unlock();
|
||||
#ifdef DEBUG
|
||||
bool isLockedByCurrentThread() const;
|
||||
#endif
|
||||
|
@ -383,6 +377,12 @@ struct HelperThread {
|
|||
*/
|
||||
bool terminate;
|
||||
|
||||
/*
|
||||
* Indicates that this thread should free its unused memory when it is next
|
||||
* idle.
|
||||
*/
|
||||
bool shouldFreeUnusedMemory;
|
||||
|
||||
/* The current task being executed by this thread, if any. */
|
||||
mozilla::Maybe<HelperTaskUnion> currentTask;
|
||||
|
||||
|
@ -465,6 +465,8 @@ struct HelperThread {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void maybeFreeUnusedMemory(JSContext* cx);
|
||||
|
||||
void handleWasmWorkload(AutoLockHelperThreadState& locked,
|
||||
wasm::CompileMode mode);
|
||||
|
||||
|
@ -701,21 +703,6 @@ class MOZ_RAII AutoUnlockHelperThreadState : public UnlockGuard<Mutex> {
|
|||
}
|
||||
};
|
||||
|
||||
struct MOZ_RAII AutoSetHelperThreadContext {
|
||||
JSContext* cx;
|
||||
explicit AutoSetHelperThreadContext();
|
||||
~AutoSetHelperThreadContext() {
|
||||
AutoLockHelperThreadState lock;
|
||||
cx->tempLifoAlloc().releaseAll();
|
||||
if (cx->shouldFreeUnusedMemory()) {
|
||||
cx->tempLifoAlloc().freeAll();
|
||||
cx->setFreeUnusedMemory(false);
|
||||
}
|
||||
cx->clearHelperThread(lock);
|
||||
cx = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
struct ParseTask : public mozilla::LinkedListElement<ParseTask>,
|
||||
public JS::OffThreadToken,
|
||||
public RunnableTask {
|
||||
|
|
|
@ -105,8 +105,6 @@ js::AutoCycleDetector::~AutoCycleDetector() {
|
|||
bool JSContext::init(ContextKind kind) {
|
||||
// Skip most of the initialization if this thread will not be running JS.
|
||||
if (kind == ContextKind::MainThread) {
|
||||
TlsContext.set(this);
|
||||
currentThread_ = ThisThread::GetId();
|
||||
if (!regexpStack.ref().init()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -158,14 +156,14 @@ JSContext* js::NewContext(uint32_t maxBytes, uint32_t maxNurseryBytes,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
if (!cx->init(ContextKind::MainThread)) {
|
||||
if (!runtime->init(cx, maxBytes, maxNurseryBytes)) {
|
||||
runtime->destroyRuntime();
|
||||
js_delete(cx);
|
||||
js_delete(runtime);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!runtime->init(cx, maxBytes, maxNurseryBytes)) {
|
||||
if (!cx->init(ContextKind::MainThread)) {
|
||||
runtime->destroyRuntime();
|
||||
js_delete(cx);
|
||||
js_delete(runtime);
|
||||
|
@ -1228,7 +1226,6 @@ JSContext::JSContext(JSRuntime* runtime, const JS::ContextOptions& options)
|
|||
freeLists_(this, nullptr),
|
||||
atomsZoneFreeLists_(this),
|
||||
defaultFreeOp_(this, runtime, true),
|
||||
freeUnusedMemory(false),
|
||||
jitActivation(this, nullptr),
|
||||
regexpStack(this),
|
||||
activation_(this, nullptr),
|
||||
|
@ -1308,6 +1305,9 @@ JSContext::JSContext(JSRuntime* runtime, const JS::ContextOptions& options)
|
|||
{
|
||||
MOZ_ASSERT(static_cast<JS::RootingContext*>(this) ==
|
||||
JS::RootingContext::get(this));
|
||||
|
||||
MOZ_ASSERT(!TlsContext.get());
|
||||
TlsContext.set(this);
|
||||
}
|
||||
|
||||
JSContext::~JSContext() {
|
||||
|
@ -1337,17 +1337,7 @@ JSContext::~JSContext() {
|
|||
|
||||
js_delete(atomsZoneFreeLists_.ref());
|
||||
|
||||
TlsContext.set(nullptr);
|
||||
}
|
||||
|
||||
void JSContext::setHelperThread(AutoLockHelperThreadState& locked) {
|
||||
MOZ_ASSERT_IF(!JSRuntime::hasLiveRuntimes(), !TlsContext.get());
|
||||
TlsContext.set(this);
|
||||
currentThread_ = ThisThread::GetId();
|
||||
}
|
||||
|
||||
void JSContext::clearHelperThread(AutoLockHelperThreadState& locked) {
|
||||
currentThread_ = Thread::Id();
|
||||
MOZ_ASSERT(TlsContext.get() == this);
|
||||
TlsContext.set(nullptr);
|
||||
}
|
||||
|
||||
|
|
|
@ -176,34 +176,13 @@ struct JSContext : public JS::RootingContext,
|
|||
|
||||
js::ContextData<js::FreeOp> defaultFreeOp_;
|
||||
|
||||
// Thread that the JSContext is currently running on, if in use.
|
||||
js::Thread::Id currentThread_;
|
||||
|
||||
js::ParseTask* parseTask_;
|
||||
|
||||
// When a helper thread is using a context, it may need to periodically
|
||||
// free unused memory.
|
||||
mozilla::Atomic<bool, mozilla::ReleaseAcquire> freeUnusedMemory;
|
||||
|
||||
public:
|
||||
// This is used by helper threads to change the runtime their context is
|
||||
// currently operating on.
|
||||
void setRuntime(JSRuntime* rt);
|
||||
|
||||
void setHelperThread(js::AutoLockHelperThreadState& locked);
|
||||
void clearHelperThread(js::AutoLockHelperThreadState& locked);
|
||||
|
||||
bool contextAvailable(js::AutoLockHelperThreadState& locked) {
|
||||
MOZ_ASSERT(kind_ == js::ContextKind::HelperThread);
|
||||
return currentThread_ == js::Thread::Id();
|
||||
}
|
||||
|
||||
void setFreeUnusedMemory(bool shouldFree) { freeUnusedMemory = shouldFree; }
|
||||
|
||||
bool shouldFreeUnusedMemory() const {
|
||||
return kind_ == js::ContextKind::HelperThread && freeUnusedMemory;
|
||||
}
|
||||
|
||||
bool isMainThreadContext() const {
|
||||
return kind_ == js::ContextKind::MainThread;
|
||||
}
|
||||
|
@ -491,7 +470,7 @@ struct JSContext : public JS::RootingContext,
|
|||
}
|
||||
|
||||
/* Base address of the native stack for the current thread. */
|
||||
uintptr_t nativeStackBase;
|
||||
const uintptr_t nativeStackBase;
|
||||
|
||||
public:
|
||||
/* If non-null, report JavaScript entry points to this monitor. */
|
||||
|
|
|
@ -62,7 +62,6 @@ class Module::Tier2GeneratorTaskImpl : public Tier2GeneratorTask {
|
|||
void cancel() override { cancelled_ = true; }
|
||||
|
||||
void runTask() override {
|
||||
AutoSetHelperThreadContext usesContext;
|
||||
CompileTier2(*compileArgs_, bytecode_->bytes, *module_, &cancelled_);
|
||||
}
|
||||
ThreadType threadType() override {
|
||||
|
|
Загрузка…
Ссылка в новой задаче