Bug 1716940 - Pass external thread stack size through to the JS engine r=sfink,bas

This adds plumbing to make the JS engine set the stack quota based on the
actual stack size for external thread pool threads (and internal thread pool
ones).

The quota is calculated as 90% of the size, which is currently hardcoded into
the constants.

Differential Revision: https://phabricator.services.mozilla.com/D118183
This commit is contained in:
Jon Coppeard 2021-06-17 16:14:19 +00:00
Родитель 0ccfec97f9
Коммит b4a00f01a6
8 изменённых файлов: 57 добавлений и 40 удалений

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

@ -21,7 +21,7 @@ namespace JS {
*/
using HelperThreadTaskCallback = void (*)();
extern JS_PUBLIC_API void SetHelperThreadTaskCallback(
HelperThreadTaskCallback callback, size_t threadCount);
HelperThreadTaskCallback callback, size_t threadCount, size_t stackSize);
// Function to call from external thread pool to run a helper thread task.
extern JS_PUBLIC_API void RunHelperThreadTask();

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

@ -85,6 +85,9 @@ class GlobalHelperThreadState {
// Number of threads to create. May be accessed without locking.
size_t threadCount;
// Thread stack quota to use when running tasks.
size_t stackQuota;
bool terminating_ = false;
typedef Vector<jit::IonCompileTask*, 0, SystemAllocPolicy>
@ -199,7 +202,8 @@ class GlobalHelperThreadState {
void setCpuCount(size_t count);
void setDispatchTaskCallback(JS::HelperThreadTaskCallback callback,
size_t threadCount);
size_t threadCount, size_t stackSize,
const AutoLockHelperThreadState& lock);
[[nodiscard]] bool ensureContextList(size_t count,
const AutoLockHelperThreadState& lock);

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

@ -134,23 +134,31 @@ void JS::SetProfilingThreadCallbacks(
HelperThreadState().unregisterThread = unregisterThread;
}
static size_t ThreadStackQuotaForSize(size_t size) {
// Set the stack quota to 10% less that the actual size.
return size_t(double(size) * 0.9);
}
// Bug 1630189: Without MOZ_NEVER_INLINE, Windows PGO builds have a linking
// error for HelperThreadTaskCallback.
JS_PUBLIC_API MOZ_NEVER_INLINE void JS::SetHelperThreadTaskCallback(
HelperThreadTaskCallback callback, size_t threadCount) {
HelperThreadState().setDispatchTaskCallback(callback, threadCount);
HelperThreadTaskCallback callback, size_t threadCount, size_t stackSize) {
AutoLockHelperThreadState lock;
HelperThreadState().setDispatchTaskCallback(callback, threadCount, stackSize,
lock);
}
void GlobalHelperThreadState::setDispatchTaskCallback(
JS::HelperThreadTaskCallback callback, size_t threadCount) {
AutoLockHelperThreadState lock;
JS::HelperThreadTaskCallback callback, size_t threadCount, size_t stackSize,
const AutoLockHelperThreadState& lock) {
MOZ_ASSERT(!isInitialized(lock));
MOZ_ASSERT(!dispatchTaskCallback);
MOZ_ASSERT(threadCount != 0);
MOZ_ASSERT(stackSize >= 16 * 1024);
dispatchTaskCallback = callback;
this->threadCount = threadCount;
useInternalThreadPool_ = false;
this->stackQuota = ThreadStackQuotaForSize(stackSize);
}
bool js::StartOffThreadWasmCompile(wasm::CompileTask* task,
@ -498,7 +506,7 @@ AutoSetHelperThreadContext::AutoSetHelperThreadContext(
cx->setHelperThread(lock);
// 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);
JS_SetNativeStackQuota(cx, HelperThreadState().stackQuota);
}
AutoSetHelperThreadContext::~AutoSetHelperThreadContext() {
@ -1300,14 +1308,15 @@ bool GlobalHelperThreadState::ensureInitialized() {
i = 0;
}
useInternalThreadPool_ = !dispatchTaskCallback;
if (useInternalThreadPool(lock)) {
if (!InternalThreadPool::Initialize(threadCount, lock)) {
return false;
}
dispatchTaskCallback = InternalThreadPool::DispatchTask;
}
MOZ_ASSERT(dispatchTaskCallback);
if (!ensureThreadCount(threadCount, lock)) {
finishThreads(lock);
return false;

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

@ -14,6 +14,29 @@
#include "util/NativeStack.h"
#include "vm/HelperThreadState.h"
// 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;
// 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;
#else
static const uint32_t HELPER_STACK_SIZE = kDefaultHelperStackSize;
#endif
// These macros are identical in function to the same-named ones in
// GeckoProfiler.h, but they are defined separately because SpiderMonkey can't
// use GeckoProfiler.h.
@ -91,6 +114,8 @@ bool InternalThreadPool::Initialize(size_t threadCount,
}
Instance = instance.release();
HelperThreadState().setDispatchTaskCallback(DispatchTask, threadCount,
HELPER_STACK_SIZE, lock);
return true;
}

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

@ -18,32 +18,6 @@
#include "threading/ConditionVariable.h"
#include "threading/ProtectedData.h"
// 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
namespace js {
class AutoLockHelperThreadState;
@ -60,8 +34,6 @@ class InternalThreadPool {
static bool IsInitialized() { return Instance; }
static InternalThreadPool& Get();
static void DispatchTask();
bool ensureThreadCount(size_t threadCount, AutoLockHelperThreadState& lock);
size_t threadCount(const AutoLockHelperThreadState& lock);
@ -69,6 +41,8 @@ class InternalThreadPool {
const AutoLockHelperThreadState& lock) const;
private:
static void DispatchTask();
void dispatchTask();
void shutDown(AutoLockHelperThreadState& lock);

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

@ -1195,7 +1195,8 @@ static bool CreateSelfHostedSharedMemory(JSContext* aCx,
nsresult XPCJSContext::Initialize() {
if (StaticPrefs::javascript_options_external_thread_pool_DoNotUseDirectly()) {
size_t threadCount = TaskController::GetPoolThreadCount();
SetHelperThreadTaskCallback(&DispatchOffThreadTask, threadCount);
size_t stackSize = TaskController::GetThreadStackSize();
SetHelperThreadTaskCallback(&DispatchOffThreadTask, threadCount, stackSize);
}
nsresult rv =

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

@ -160,11 +160,14 @@ void TaskController::InitializeThreadPool() {
mPoolThreads.push_back(
{PR_CreateThread(PR_USER_THREAD, ThreadFuncPoolThread, index,
PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
PR_JOINABLE_THREAD, 512u * 1024u),
PR_JOINABLE_THREAD, sStackSize),
nullptr});
}
}
/* static */
size_t TaskController::GetThreadStackSize() { return sStackSize; }
void TaskController::SetPerformanceCounterState(
PerformanceCounterState* aPerformanceCounterState) {
mPerformanceCounterState = aPerformanceCounterState;

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

@ -322,6 +322,7 @@ class TaskController {
bool MTTaskRunnableProcessedTask() { return mMTTaskRunnableProcessedTask; }
static int32_t GetPoolThreadCount();
static size_t GetThreadStackSize();
private:
friend void ThreadFuncPoolThread(void* aIndex);