-
-
Notifications
You must be signed in to change notification settings - Fork 144
fix: don't abort on worker termination during module load #1993
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| describe("Worker terminate during module load", function () { | ||
| var ITERATIONS = 3; | ||
| // Long enough to outlast the worker's isolate setup, short enough to keep | ||
| // the spec well inside the jasmine timeout. | ||
| var TERMINATE_AFTER = 150; | ||
| var SETTLE_AFTER = 250; | ||
|
|
||
| it("should not report an error or crash when terminate() interrupts the worker's module body", function (done) { | ||
| var errors = []; | ||
|
|
||
| function iteration(remaining) { | ||
| if (remaining === 0) { | ||
| expect(errors).toEqual([]); | ||
| done(); | ||
| return; | ||
| } | ||
|
|
||
| var worker = new Worker("./workerTerminateDuringLoadWorker.js"); | ||
| worker.onerror = function (e) { | ||
| errors.push(e.message); | ||
| }; | ||
|
|
||
| setTimeout(function () { | ||
| worker.terminate(); | ||
| setTimeout(function () { | ||
| iteration(remaining - 1); | ||
| }, SETTLE_AFTER); | ||
| }, TERMINATE_AFTER); | ||
| } | ||
|
|
||
| iteration(ITERATIONS); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| // Spins at module scope so a terminate() from the parent lands while this | ||
| // module body is still executing, which is the window the test targets. | ||
| var deadline = Date.now() + 5000; | ||
| while (Date.now() < deadline) { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1496,12 +1496,14 @@ void CallbackHandlers::CallWorkerScopeOnErrorHandle(Isolate *isolate, TryCatch & | |
| auto globalObject = context->Global(); | ||
|
|
||
| // execute onerror handle if one is implemented | ||
| auto callback = globalObject->Get(context, ArgConverter::ConvertToV8String(isolate, | ||
| "onerror")).ToLocalChecked(); | ||
| auto isEmpty = callback.IsEmpty(); | ||
| Local<Value> callback; | ||
| if (!globalObject->Get(context, ArgConverter::ConvertToV8String(isolate, "onerror")) | ||
| .ToLocal(&callback)) { | ||
| return; | ||
| } | ||
|
Comment on lines
+1499
to
+1503
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== files =="
git ls-files | rg '(^|/)CallbackHandlers\.cpp$|(^|/)CallbackHandlers\.h$|test' || true
echo "== locate relevant snippet =="
rg -n "onerror|innerTc|ToLocal\\(\\)|ToLocalChecked|CallbackHandlers\\.cpp" -S . || true
echo "== inspect CallbackHandlers.cpp around snippet =="
file="$PWD/test-app/runtime/src/main/cpp/CallbackHandlers.cpp"
if [ -f "$file" ]; then
wc -l "$file"
sed -n '1450,1545p' "$file" | cat -n -v
fiRepository: NativeScript/android Length of output: 50377 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== CallbackHandlers.cpp outline and relevant sections =="
file="test-app/runtime/src/main/cpp/CallbackHandlers.cpp"
wc -l "$file"
sed -n '1400,1580p' "$file" | cat -n -v
echo "== related to worker error termination flags/constants =="
rg -n "terminate|isTerminating|OnError|ThrowError|HasCaught|innerTc|globalObject->Get|onerror" test-app/runtime/src/main/cpp/CallbackHandlers.cpp test-app/runtime/src/main/cpp -S || true
echo "== relevant tests mentioning onerror/worker error/error events =="
rg -n "onerror|workerTerminateDuringLoad|WorkerEvent|error.*worker|worker.*error|terminate" test-app/app/src/main/assets/app/test-app tests -S || trueRepository: NativeScript/android Length of output: 25669 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== WorkerWrapper PassUncaughtExceptionFromWorkerToParent implementation =="
file="test-app/runtime/src/main/cpp/WorkerWrapper.cpp"
sed -n '250,320p' "$file" | cat -n -v
echo "== GiveWorkerOnErrorAChance implementation and usage =="
sed -n '620,760p' test-app/runtime/src/main/cpp/NativeScriptException.cpp | cat -n -v
echo "== Search for non-termination reporting path after CallWorkerScopeOnErrorHandle =="
rg -n "PassUncaughtExceptionFromWorkerToParent|CallWorkerScopeOnErrorHandle\\(.*tc\\)|CallWorkerScopeOnErrorHandle\\(.*message" test-app/runtime/src/main/cpp/CallbackHandlers.cpp test-app/runtime/src/main/cpp/WorkerWrapper.cpp test-app/runtime/src/main/cpp/NativeScriptException.cpp test-app/runtime/src/main/cpp/ModuleInternal.cpp -S || true
echo "== deterministic JS semantics for try/catch + throwing getter and early return reportability =="
node - <<'JS'
let caught;
try {
const o = {};
Object.defineProperty(o, "onerror", {
get() {
throw new Error("throwing onerror getter");
}
});
let callback;
try {
callback = o.onerror;
} catch (e) {
caught = e.message;
return false;
}
if (false === callback) {
console.log("no lookup failure was suppressed");
}
} catch (e) {
console.log("outer catch", e && e.message);
}
console.log("caught?", caught);
JSRepository: NativeScript/android Length of output: 12625 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== WorketThreadCloseCallback context around original tc =="
sed -n '1368,1435p' test-app/runtime/src/main/cpp/CallbackHandlers.cpp | cat -n -v
echo "== WorkerObjectPostMessageCallback context around original tc =="
sed -n '1255,1305p' test-app/runtime/src/main/cpp/CallbackHandlers.cpp | cat -n -v
echo "== deterministic JS semantics for throw in getter caught by TryCatch and lookup failure suppressing later reporting =="
node - <<'JS'
let onerrorCalled = false;
let reported = false;
const o = {};
Object.defineProperty(o, "onerror", {
get() {
throw new Error("throwing onerror getter");
}
});
let tc;
try {
tc = { exception: null };
let callback;
try {
callback = o.onerror;
} catch (e) {
tc.exception = e;
}
if (!tc.exception) return;
reported = false;
} catch (e) {
return;
}
try {
{
callback = o.onerror;
}
} catch (e) {
reported = false;
}
console.log("onerrorCalled", onerrorCalled, "reported", reported, "tc.exception.message", tc.exception.message);
JSRepository: NativeScript/android Length of output: 6099 Do not suppress worker exceptions when A throwing global 🤖 Prompt for AI Agents |
||
| auto isFunction = callback->IsFunction(); | ||
|
|
||
| if (!isEmpty && isFunction && !tc.Message().IsEmpty()) { | ||
| if (isFunction && !tc.Message().IsEmpty()) { | ||
| auto msg = tc.Message()->Get(); | ||
| Local<Value> args1[] = {msg}; | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: NativeScript/android
Length of output: 6129
🏁 Script executed:
Repository: NativeScript/android
Length of output: 4390
Synchronize termination with module entry.
workerTerminateDuringLoadWorker.jsstarts the spinning loop at top level, but the parent starts theTERMINATE_AFTERtimer fromnew Worker(). If worker startup takes more than 150 ms,terminate()can run before the worker’s module body begins, so the test may pass without exercising termination during module evaluation.Have the worker send a “module entered” message immediately before the busy loop, and start termination only after the parent receives that message.
🧰 Tools
🪛 ast-grep (0.45.0)
[error] 22-27: React's useState should not be directly called
Context: setTimeout(function () {
worker.terminate();
setTimeout(function () {
iteration(remaining - 1);
}, SETTLE_AFTER);
}, TERMINATE_AFTER)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.
(usestate-direct-usage)
[error] 24-26: React's useState should not be directly called
Context: setTimeout(function () {
iteration(remaining - 1);
}, SETTLE_AFTER)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.
(usestate-direct-usage)
[warning] 22-27: Avoid using the initial state variable in setState
Context: setTimeout(function () {
worker.terminate();
setTimeout(function () {
iteration(remaining - 1);
}, SETTLE_AFTER);
}, TERMINATE_AFTER)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.
(setstate-same-var)
[warning] 24-26: Avoid using the initial state variable in setState
Context: setTimeout(function () {
iteration(remaining - 1);
}, SETTLE_AFTER)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.
(setstate-same-var)
🤖 Prompt for AI Agents