From bf1398f7d22eb683278df32b7b16e11ad170cccd Mon Sep 17 00:00:00 2001 From: "Nicolas B. Pierron" Date: Mon, 15 Oct 2018 16:41:33 +0200 Subject: [PATCH] Bug 1497906 - Add THREAD_TYPE_CURRENT to simulate errors on a single worker thread. r=jonco --- js/public/Utility.h | 18 +++++-- js/src/builtin/TestingFunctions.cpp | 21 +++++--- .../tests/self-test/oom-test-bug1497906.js | 27 +++++++++++ js/src/jsutil.cpp | 48 ++++++++++++++++++- js/src/shell/js.cpp | 14 +++++- js/src/tests/shell/mailbox.js | 4 +- 6 files changed, 119 insertions(+), 13 deletions(-) create mode 100644 js/src/jit-test/tests/self-test/oom-test-bug1497906.js diff --git a/js/public/Utility.h b/js/public/Utility.h index ca961ff51070..490130da6003 100644 --- a/js/public/Utility.h +++ b/js/public/Utility.h @@ -72,6 +72,7 @@ enum ThreadType { THREAD_TYPE_ION_FREE, // 9 THREAD_TYPE_WASM_TIER2, // 10 THREAD_TYPE_WORKER, // 11 + THREAD_TYPE_CURRENT, // 12 -- Special code to only track the current thread. THREAD_TYPE_MAX // Used to check shell function arguments }; @@ -92,16 +93,24 @@ namespace oom { // like. Testing worker threads is not supported. const ThreadType FirstThreadTypeToTest = THREAD_TYPE_MAIN; const ThreadType LastThreadTypeToTest = THREAD_TYPE_WASM_TIER2; +const ThreadType WorkerFirstThreadTypeToTest = THREAD_TYPE_CURRENT; +const ThreadType WorkerLastThreadTypeToTest = THREAD_TYPE_CURRENT; extern bool InitThreadType(void); extern void SetThreadType(ThreadType); extern JS_FRIEND_API(uint32_t) GetThreadType(void); +extern JS_FRIEND_API(uint32_t) GetAllocationThreadType(void); +extern JS_FRIEND_API(uint32_t) GetStackCheckThreadType(void); +extern JS_FRIEND_API(uint32_t) GetInterruptCheckThreadType(void); # else inline bool InitThreadType(void) { return true; } inline void SetThreadType(ThreadType t) {}; inline uint32_t GetThreadType(void) { return 0; } +inline uint32_t GetAllocationThreadType(void) { return 0; } +inline uint32_t GetStackCheckThreadType(void) { return 0; } +inline uint32_t GetInterruptCheckThreadType(void) { return 0; } # endif @@ -143,7 +152,8 @@ ResetSimulatedOOM(); inline bool IsThreadSimulatingOOM() { - return js::oom::targetThread && js::oom::targetThread == js::oom::GetThreadType(); + return js::oom::targetThread && + js::oom::targetThread == js::oom::GetAllocationThreadType(); } inline bool @@ -191,7 +201,8 @@ ResetSimulatedStackOOM(); inline bool IsThreadSimulatingStackOOM() { - return js::oom::stackTargetThread && js::oom::stackTargetThread == js::oom::GetThreadType(); + return js::oom::stackTargetThread && + js::oom::stackTargetThread == js::oom::GetStackCheckThreadType(); } inline bool @@ -240,7 +251,8 @@ ResetSimulatedInterrupt(); inline bool IsThreadSimulatingInterrupt() { - return js::oom::interruptTargetThread && js::oom::interruptTargetThread == js::oom::GetThreadType(); + return js::oom::interruptTargetThread && + js::oom::interruptTargetThread == js::oom::GetInterruptCheckThreadType(); } inline bool diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index fd13b6d8f973..6be930929c5c 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -1894,7 +1894,7 @@ RunIterativeFailureTest(JSContext* cx, const IterativeFailureTestParams& params, simulator.setup(cx); - for (unsigned thread = params.threadStart; thread < params.threadEnd; thread++) { + for (unsigned thread = params.threadStart; thread <= params.threadEnd; thread++) { if (params.verbose) { fprintf(stderr, "thread %d\n", thread); } @@ -2035,20 +2035,29 @@ ParseIterativeFailureTestParams(JSContext* cx, const CallArgs& args, params->expectExceptionOnFailure = false; } - // Test all threads by default. - params->threadStart = oom::FirstThreadTypeToTest; - params->threadEnd = oom::LastThreadTypeToTest; + // Test all threads by default except worker threads, except if we are + // running in a worker thread in which case only the worker thread which + // requested the simulation is tested. + if (js::oom::GetThreadType() == js::THREAD_TYPE_WORKER) { + params->threadStart = oom::WorkerFirstThreadTypeToTest; + params->threadEnd = oom::WorkerLastThreadTypeToTest; + } else { + params->threadStart = oom::FirstThreadTypeToTest; + params->threadEnd = oom::LastThreadTypeToTest; + } // Test a single thread type if specified by the OOM_THREAD environment variable. int threadOption = 0; if (EnvVarAsInt("OOM_THREAD", &threadOption)) { - if (threadOption < oom::FirstThreadTypeToTest || threadOption > oom::LastThreadTypeToTest) { + if (threadOption < oom::FirstThreadTypeToTest || threadOption > oom::LastThreadTypeToTest || + threadOption != js::THREAD_TYPE_CURRENT) + { JS_ReportErrorASCII(cx, "OOM_THREAD value out of range."); return false; } params->threadStart = threadOption; - params->threadEnd = threadOption + 1; + params->threadEnd = threadOption; } params->verbose = EnvVarIsDefined("OOM_VERBOSE"); diff --git a/js/src/jit-test/tests/self-test/oom-test-bug1497906.js b/js/src/jit-test/tests/self-test/oom-test-bug1497906.js new file mode 100644 index 000000000000..1bc865882bba --- /dev/null +++ b/js/src/jit-test/tests/self-test/oom-test-bug1497906.js @@ -0,0 +1,27 @@ +// |jit-test| skip-if: !('oomTest' in this && 'stackTest' in this) || helperThreadCount() === 0 + +// Check the we can run a stack test on all threads except worker threads while +// a worker thread is running a OOM test at the same time. +// +// This test case uses setSharedObject to "synchronize" between a worker thread +// and the main thread. This worker thread enters the oomTest section, while the +// main thread waits. Then the worker thread remains in the oomTest section +// until the main thread finish entering and existing its stackTest section. + +setSharedObject(0); +evalInWorker(` + oomTest(() => { + if (getSharedObject() < 2) { + setSharedObject(1); + while (getSharedObject() != 2) { + } + } + }); +`); + +while (getSharedObject() != 1) { + // poor-man wait condition. +} + +stackTest(() => 42); +setSharedObject(2); diff --git a/js/src/jsutil.cpp b/js/src/jsutil.cpp index 9f6e5f92a2bb..8759dce74b75 100644 --- a/js/src/jsutil.cpp +++ b/js/src/jsutil.cpp @@ -38,25 +38,32 @@ MOZ_THREAD_LOCAL(uint32_t) threadType; JS_PUBLIC_DATA(uint64_t) maxAllocations = UINT64_MAX; JS_PUBLIC_DATA(uint64_t) counter = 0; JS_PUBLIC_DATA(bool) failAlways = true; +MOZ_THREAD_LOCAL(bool) isAllocationThread; JS_PUBLIC_DATA(uint32_t) stackTargetThread = 0; JS_PUBLIC_DATA(uint64_t) maxStackChecks = UINT64_MAX; JS_PUBLIC_DATA(uint64_t) stackCheckCounter = 0; JS_PUBLIC_DATA(bool) stackCheckFailAlways = true; +MOZ_THREAD_LOCAL(bool) isStackCheckThread; JS_PUBLIC_DATA(uint32_t) interruptTargetThread = 0; JS_PUBLIC_DATA(uint64_t) maxInterruptChecks = UINT64_MAX; JS_PUBLIC_DATA(uint64_t) interruptCheckCounter = 0; JS_PUBLIC_DATA(bool) interruptCheckFailAlways = true; +MOZ_THREAD_LOCAL(bool) isInterruptCheckThread; bool InitThreadType(void) { - return threadType.init(); + return threadType.init() && isAllocationThread.init() && + isStackCheckThread.init() && isInterruptCheckThread.init(); } void SetThreadType(ThreadType type) { threadType.set(type); + isAllocationThread.set(false); + isStackCheckThread.set(false); + isInterruptCheckThread.set(false); } uint32_t @@ -64,10 +71,35 @@ GetThreadType(void) { return threadType.get(); } +uint32_t +GetAllocationThreadType(void) { + if (isAllocationThread.get()) { + return js::THREAD_TYPE_CURRENT; + } + return threadType.get(); +} + +uint32_t +GetStackCheckThreadType(void) { + if (isStackCheckThread.get()) { + return js::THREAD_TYPE_CURRENT; + } + return threadType.get(); +} + +uint32_t +GetInterruptCheckThreadType(void) { + if (isInterruptCheckThread.get()) { + return js::THREAD_TYPE_CURRENT; + } + return threadType.get(); +} + static inline bool IsHelperThreadType(uint32_t thread) { - return thread != THREAD_TYPE_NONE && thread != THREAD_TYPE_MAIN; + return thread != THREAD_TYPE_NONE && thread != THREAD_TYPE_MAIN && + thread != THREAD_TYPE_CURRENT; } void @@ -82,6 +114,9 @@ SimulateOOMAfter(uint64_t allocations, uint32_t thread, bool always) MOZ_ASSERT(counter + allocations > counter); MOZ_ASSERT(thread > js::THREAD_TYPE_NONE && thread < js::THREAD_TYPE_MAX); targetThread = thread; + if (thread == js::THREAD_TYPE_CURRENT) { + isAllocationThread.set(true); + } maxAllocations = counter + allocations; failAlways = always; } @@ -96,6 +131,7 @@ ResetSimulatedOOM() } targetThread = THREAD_TYPE_NONE; + isAllocationThread.set(false); maxAllocations = UINT64_MAX; failAlways = false; } @@ -112,6 +148,9 @@ SimulateStackOOMAfter(uint64_t checks, uint32_t thread, bool always) MOZ_ASSERT(stackCheckCounter + checks > stackCheckCounter); MOZ_ASSERT(thread > js::THREAD_TYPE_NONE && thread < js::THREAD_TYPE_MAX); stackTargetThread = thread; + if (thread == js::THREAD_TYPE_CURRENT) { + isStackCheckThread.set(true); + } maxStackChecks = stackCheckCounter + checks; stackCheckFailAlways = always; } @@ -126,6 +165,7 @@ ResetSimulatedStackOOM() } stackTargetThread = THREAD_TYPE_NONE; + isStackCheckThread.set(false); maxStackChecks = UINT64_MAX; stackCheckFailAlways = false; } @@ -142,6 +182,9 @@ SimulateInterruptAfter(uint64_t checks, uint32_t thread, bool always) MOZ_ASSERT(interruptCheckCounter + checks > interruptCheckCounter); MOZ_ASSERT(thread > js::THREAD_TYPE_NONE && thread < js::THREAD_TYPE_MAX); interruptTargetThread = thread; + if (thread == js::THREAD_TYPE_CURRENT) { + isInterruptCheckThread.set(true); + } maxInterruptChecks = interruptCheckCounter + checks; interruptCheckFailAlways = always; } @@ -156,6 +199,7 @@ ResetSimulatedInterrupt() } interruptTargetThread = THREAD_TYPE_NONE; + isInterruptCheckThread.set(false); maxInterruptChecks = UINT64_MAX; interruptCheckFailAlways = false; } diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 3338b2d28c2b..635ff3370715 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -6401,7 +6401,8 @@ enum class MailboxTag { Empty, SharedArrayBuffer, WasmMemory, - WasmModule + WasmModule, + Number, }; struct SharedObjectMailbox @@ -6412,6 +6413,7 @@ struct SharedObjectMailbox uint32_t length; } sarb; const wasm::Module* module; + double number; }; SharedObjectMailbox() : tag(MailboxTag::Empty) {} @@ -6441,6 +6443,7 @@ DestructSharedObjectMailbox() auto mbx = sharedObjectMailbox->lock(); switch (mbx->tag) { case MailboxTag::Empty: + case MailboxTag::Number: break; case MailboxTag::SharedArrayBuffer: case MailboxTag::WasmMemory: @@ -6470,6 +6473,10 @@ GetSharedObject(JSContext* cx, unsigned argc, Value* vp) case MailboxTag::Empty: { break; } + case MailboxTag::Number: { + args.rval().setNumber(mbx->val.number); + return true; + } case MailboxTag::SharedArrayBuffer: case MailboxTag::WasmMemory: { // Flag was set in the sender; ensure it is set in the receiver. @@ -6589,6 +6596,10 @@ SetSharedObject(JSContext* cx, unsigned argc, Value* vp) JS_ReportErrorASCII(cx, "Invalid argument to SetSharedObject"); return false; } + } else if (args.get(0).isNumber()) { + tag = MailboxTag::Number; + value.number = args.get(0).toNumber(); + // Nothing } else if (args.get(0).isNullOrUndefined()) { // Nothing } else { @@ -6601,6 +6612,7 @@ SetSharedObject(JSContext* cx, unsigned argc, Value* vp) switch (mbx->tag) { case MailboxTag::Empty: + case MailboxTag::Number: break; case MailboxTag::SharedArrayBuffer: case MailboxTag::WasmMemory: diff --git a/js/src/tests/shell/mailbox.js b/js/src/tests/shell/mailbox.js index 35ac2bbf7dbf..e7d9415cb66e 100644 --- a/js/src/tests/shell/mailbox.js +++ b/js/src/tests/shell/mailbox.js @@ -33,6 +33,9 @@ mem[0] = 314159; assertEq(w[0], 314159); // Shares memory (locally) with what we put in? mem[0] = 0; +setSharedObject(3.14); // Share numbers +assertEq(getSharedObject(), 3.14); + setSharedObject(null); // Setting to null clears to null assertEq(getSharedObject(), null); @@ -51,7 +54,6 @@ assertThrowsInstanceOf(() => setSharedObject([1,2]), Error); assertThrowsInstanceOf(() => setSharedObject(new ArrayBuffer(10)), Error); assertThrowsInstanceOf(() => setSharedObject(new Int32Array(10)), Error); assertThrowsInstanceOf(() => setSharedObject(false), Error); -assertThrowsInstanceOf(() => setSharedObject(3.14), Error); assertThrowsInstanceOf(() => setSharedObject(mem), Error); assertThrowsInstanceOf(() => setSharedObject("abracadabra"), Error); assertThrowsInstanceOf(() => setSharedObject(() => 37), Error);