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:
Narcis Beleuzu 2019-07-26 19:58:07 +03:00
Родитель a4cd447c57
Коммит 047fde9d4e
6 изменённых файлов: 76 добавлений и 159 удалений

Просмотреть файл

@ -1142,7 +1142,6 @@ AbortReasonOr<Ok> IonBuilder::buildInline(IonBuilder* callerBuilder,
void IonBuilder::runTask() { void IonBuilder::runTask() {
// This is the entry point when ion compiles are run offthread. // This is the entry point when ion compiles are run offthread.
JSRuntime* rt = script()->runtimeFromAnyThread(); JSRuntime* rt = script()->runtimeFromAnyThread();
AutoSetHelperThreadContext usesContext;
TraceLoggerThread* logger = TraceLoggerForCurrentThread(); TraceLoggerThread* logger = TraceLoggerForCurrentThread();
TraceLoggerEvent event(TraceLogger_AnnotateScripts, script()); TraceLoggerEvent event(TraceLogger_AnnotateScripts, script());

Просмотреть файл

@ -65,17 +65,8 @@ GlobalHelperThreadState* gHelperThreadState = nullptr;
bool js::CreateHelperThreadsState() { bool js::CreateHelperThreadsState() {
MOZ_ASSERT(!gHelperThreadState); MOZ_ASSERT(!gHelperThreadState);
UniquePtr<GlobalHelperThreadState> helperThreadState = gHelperThreadState = js_new<GlobalHelperThreadState>();
MakeUnique<GlobalHelperThreadState>(); return gHelperThreadState != nullptr;
if (!helperThreadState) {
return false;
}
gHelperThreadState = helperThreadState.release();
if (!gHelperThreadState->initializeHelperContexts()) {
js_delete(gHelperThreadState);
return false;
}
return true;
} }
void js::DestroyHelperThreadsState() { void js::DestroyHelperThreadsState() {
@ -444,43 +435,6 @@ struct MOZ_RAII AutoSetContextParse {
~AutoSetContextParse() { TlsContext.get()->setParseTask(nullptr); } ~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", static const JSClass parseTaskGlobalClass = {"internal-parse-task-global",
JSCLASS_GLOBAL_FLAGS, JSCLASS_GLOBAL_FLAGS,
&JS::DefaultGlobalClassOps}; &JS::DefaultGlobalClassOps};
@ -543,14 +497,11 @@ size_t ParseTask::sizeOfExcludingThis(
} }
void ParseTask::runTask() { void ParseTask::runTask() {
AutoSetHelperThreadContext usesContext;
JSContext* cx = TlsContext.get(); JSContext* cx = TlsContext.get();
JSRuntime* runtime = parseGlobal->runtimeFromAnyThread(); JSRuntime* runtime = parseGlobal->runtimeFromAnyThread();
AutoSetContextRuntime ascr(runtime); AutoSetContextRuntime ascr(runtime);
AutoSetContextParse parsetask(this); AutoSetContextParse parsetask(this);
gc::AutoSuppressNurseryCellAlloc noNurseryAlloc(cx);
Zone* zone = parseGlobal->zoneFromAnyThread(); Zone* zone = parseGlobal->zoneFromAnyThread();
zone->setHelperThreadOwnerContext(cx); zone->setHelperThreadOwnerContext(cx);
@ -1123,6 +1074,32 @@ bool js::CurrentThreadIsParseThread() {
} }
#endif #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() { bool GlobalHelperThreadState::ensureInitialized() {
MOZ_ASSERT(CanUseExtraThreads()); MOZ_ASSERT(CanUseExtraThreads());
@ -1187,7 +1164,6 @@ void GlobalHelperThreadState::finish() {
while (!freeList.empty()) { while (!freeList.empty()) {
jit::FreeIonBuilder(freeList.popCopy()); jit::FreeIonBuilder(freeList.popCopy());
} }
destroyHelperContexts(lock);
} }
void GlobalHelperThreadState::finishThreads() { void GlobalHelperThreadState::finishThreads() {
@ -1202,46 +1178,9 @@ void GlobalHelperThreadState::finishThreads() {
threads.reset(nullptr); threads.reset(nullptr);
} }
bool GlobalHelperThreadState::initializeHelperContexts() { void GlobalHelperThreadState::lock() { helperLock.lock(); }
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;
}
JSContext* GlobalHelperThreadState::getFirstUnusedContext( void GlobalHelperThreadState::unlock() { helperLock.unlock(); }
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);
}
}
#ifdef DEBUG #ifdef DEBUG
bool GlobalHelperThreadState::isLockedByCurrentThread() const { bool GlobalHelperThreadState::isLockedByCurrentThread() const {
@ -1359,8 +1298,8 @@ void GlobalHelperThreadState::triggerFreeUnusedMemory() {
} }
AutoLockHelperThreadState lock; AutoLockHelperThreadState lock;
for (auto& context : helperContexts_) { for (auto& thread : *threads) {
context->setFreeUnusedMemory(true); thread.shouldFreeUnusedMemory = true;
} }
notifyAll(PRODUCER, lock); notifyAll(PRODUCER, lock);
} }
@ -1768,11 +1707,11 @@ void js::GCParallelTask::runFromMainThread(JSRuntime* rt) {
void js::GCParallelTask::runFromHelperThread(AutoLockHelperThreadState& lock) { void js::GCParallelTask::runFromHelperThread(AutoLockHelperThreadState& lock) {
MOZ_ASSERT(isDispatched(lock)); MOZ_ASSERT(isDispatched(lock));
AutoSetContextRuntime ascr(runtime());
gc::AutoSetThreadIsPerformingGC performingGC;
{ {
AutoUnlockHelperThreadState parallelSection(lock); AutoUnlockHelperThreadState parallelSection(lock);
AutoSetHelperThreadContext usesContext;
AutoSetContextRuntime ascr(runtime());
gc::AutoSetThreadIsPerformingGC performingGC;
TimeStamp timeStart = ReallyNow(); TimeStamp timeStart = ReallyNow();
runTask(); runTask();
duration_ = TimeSince(timeStart); duration_ = TimeSince(timeStart);
@ -2176,6 +2115,7 @@ void HelperThread::handleIonWorkload(AutoLockHelperThreadState& locked) {
{ {
AutoUnlockHelperThreadState unlock(locked); AutoUnlockHelperThreadState unlock(locked);
AutoSetContextRuntime ascr(rt);
builder->runTask(); builder->runTask();
} }
@ -2543,9 +2483,21 @@ void HelperThread::threadLoop() {
ensureRegisteredWithProfiler(); 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) { while (!terminate) {
MOZ_ASSERT(idle()); MOZ_ASSERT(idle());
maybeFreeUnusedMemory(&cx);
// The selectors may depend on the HelperThreadState not changing // The selectors may depend on the HelperThreadState not changing
// between task selection and task execution, in particular, on new // between task selection and task execution, in particular, on new
// tasks not being added (because of the lifo structure of the work // tasks not being added (because of the lifo structure of the work
@ -2579,3 +2531,14 @@ const HelperThread::TaskSpec* HelperThread::findHighestPriorityTask(
return nullptr; 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<GCParallelTask*, 0, SystemAllocPolicy> GCParallelTaskVector;
typedef Vector<PromiseHelperTask*, 0, SystemAllocPolicy> typedef Vector<PromiseHelperTask*, 0, SystemAllocPolicy>
PromiseHelperTaskVector; PromiseHelperTaskVector;
typedef Vector<JSContext*, 0, SystemAllocPolicy> ContextVector;
// List of available threads, or null if the thread state has not been // List of available threads, or null if the thread state has not been
// initialized. // initialized.
@ -147,9 +146,6 @@ class GlobalHelperThreadState {
// GC tasks needing to be done in parallel. // GC tasks needing to be done in parallel.
GCParallelTaskVector gcParallelWorklist_; GCParallelTaskVector gcParallelWorklist_;
// Global list of JSContext for GlobalHelperThreadState to use.
ContextVector helperContexts_;
ParseTask* removeFinishedParseTask(ParseTaskKind kind, ParseTask* removeFinishedParseTask(ParseTaskKind kind,
JS::OffThreadToken* token); JS::OffThreadToken* token);
@ -171,10 +167,8 @@ class GlobalHelperThreadState {
void finish(); void finish();
void finishThreads(); void finishThreads();
MOZ_MUST_USE bool initializeHelperContexts(); void lock();
JSContext* getFirstUnusedContext(AutoLockHelperThreadState& locked); void unlock();
void destroyHelperContexts(AutoLockHelperThreadState& lock);
#ifdef DEBUG #ifdef DEBUG
bool isLockedByCurrentThread() const; bool isLockedByCurrentThread() const;
#endif #endif
@ -383,6 +377,12 @@ struct HelperThread {
*/ */
bool terminate; 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. */ /* The current task being executed by this thread, if any. */
mozilla::Maybe<HelperTaskUnion> currentTask; mozilla::Maybe<HelperTaskUnion> currentTask;
@ -465,6 +465,8 @@ struct HelperThread {
return nullptr; return nullptr;
} }
void maybeFreeUnusedMemory(JSContext* cx);
void handleWasmWorkload(AutoLockHelperThreadState& locked, void handleWasmWorkload(AutoLockHelperThreadState& locked,
wasm::CompileMode mode); 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>, struct ParseTask : public mozilla::LinkedListElement<ParseTask>,
public JS::OffThreadToken, public JS::OffThreadToken,
public RunnableTask { public RunnableTask {

Просмотреть файл

@ -105,8 +105,6 @@ js::AutoCycleDetector::~AutoCycleDetector() {
bool JSContext::init(ContextKind kind) { bool JSContext::init(ContextKind kind) {
// Skip most of the initialization if this thread will not be running JS. // Skip most of the initialization if this thread will not be running JS.
if (kind == ContextKind::MainThread) { if (kind == ContextKind::MainThread) {
TlsContext.set(this);
currentThread_ = ThisThread::GetId();
if (!regexpStack.ref().init()) { if (!regexpStack.ref().init()) {
return false; return false;
} }
@ -158,14 +156,14 @@ JSContext* js::NewContext(uint32_t maxBytes, uint32_t maxNurseryBytes,
return nullptr; return nullptr;
} }
if (!cx->init(ContextKind::MainThread)) { if (!runtime->init(cx, maxBytes, maxNurseryBytes)) {
runtime->destroyRuntime(); runtime->destroyRuntime();
js_delete(cx); js_delete(cx);
js_delete(runtime); js_delete(runtime);
return nullptr; return nullptr;
} }
if (!runtime->init(cx, maxBytes, maxNurseryBytes)) { if (!cx->init(ContextKind::MainThread)) {
runtime->destroyRuntime(); runtime->destroyRuntime();
js_delete(cx); js_delete(cx);
js_delete(runtime); js_delete(runtime);
@ -1228,7 +1226,6 @@ JSContext::JSContext(JSRuntime* runtime, const JS::ContextOptions& options)
freeLists_(this, nullptr), freeLists_(this, nullptr),
atomsZoneFreeLists_(this), atomsZoneFreeLists_(this),
defaultFreeOp_(this, runtime, true), defaultFreeOp_(this, runtime, true),
freeUnusedMemory(false),
jitActivation(this, nullptr), jitActivation(this, nullptr),
regexpStack(this), regexpStack(this),
activation_(this, nullptr), activation_(this, nullptr),
@ -1308,6 +1305,9 @@ JSContext::JSContext(JSRuntime* runtime, const JS::ContextOptions& options)
{ {
MOZ_ASSERT(static_cast<JS::RootingContext*>(this) == MOZ_ASSERT(static_cast<JS::RootingContext*>(this) ==
JS::RootingContext::get(this)); JS::RootingContext::get(this));
MOZ_ASSERT(!TlsContext.get());
TlsContext.set(this);
} }
JSContext::~JSContext() { JSContext::~JSContext() {
@ -1337,17 +1337,7 @@ JSContext::~JSContext() {
js_delete(atomsZoneFreeLists_.ref()); js_delete(atomsZoneFreeLists_.ref());
TlsContext.set(nullptr); MOZ_ASSERT(TlsContext.get() == this);
}
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();
TlsContext.set(nullptr); TlsContext.set(nullptr);
} }

Просмотреть файл

@ -176,34 +176,13 @@ struct JSContext : public JS::RootingContext,
js::ContextData<js::FreeOp> defaultFreeOp_; js::ContextData<js::FreeOp> defaultFreeOp_;
// Thread that the JSContext is currently running on, if in use.
js::Thread::Id currentThread_;
js::ParseTask* parseTask_; 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: public:
// This is used by helper threads to change the runtime their context is // This is used by helper threads to change the runtime their context is
// currently operating on. // currently operating on.
void setRuntime(JSRuntime* rt); 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 { bool isMainThreadContext() const {
return kind_ == js::ContextKind::MainThread; return kind_ == js::ContextKind::MainThread;
} }
@ -491,7 +470,7 @@ struct JSContext : public JS::RootingContext,
} }
/* Base address of the native stack for the current thread. */ /* Base address of the native stack for the current thread. */
uintptr_t nativeStackBase; const uintptr_t nativeStackBase;
public: public:
/* If non-null, report JavaScript entry points to this monitor. */ /* 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 cancel() override { cancelled_ = true; }
void runTask() override { void runTask() override {
AutoSetHelperThreadContext usesContext;
CompileTier2(*compileArgs_, bytecode_->bytes, *module_, &cancelled_); CompileTier2(*compileArgs_, bytecode_->bytes, *module_, &cancelled_);
} }
ThreadType threadType() override { ThreadType threadType() override {