Skip to content

Commit 921209f

Browse files
fix(worker): let terminate() stop a worker that called close()
close() only ends the worker once its running callback returns, and terminate() returned early for a closing worker, so a worker that kept running after close() could not be stopped. terminate() now interrupts it like any other running worker.
1 parent a9a5ea4 commit 921209f

4 files changed

Lines changed: 43 additions & 4 deletions

File tree

‎test-app/app/src/main/assets/app/mainpage.js‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ require("./tests/testWebAssembly");
2727
require("./tests/testEventLoop");
2828
require("./tests/testMultithreadedJavascript");
2929
require("./tests/testWorkerTerminateDuringLoad");
30+
require("./tests/testWorkerTerminateAfterClose");
3031
require("./tests/testWorkerOptions");
3132
require("./tests/testWorkerResourceLimits");
3233
require("./tests/testInterfaceDefaultMethods");
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
describe("Worker terminate after close", function () {
2+
var SETTLE_AFTER = 300;
3+
4+
it("stops a worker that keeps running after it called close()", function (done) {
5+
var counter = new Int32Array(new SharedArrayBuffer(4));
6+
var worker = new Worker("./workerCloseThenSpinWorker.js");
7+
worker.postMessage(counter.buffer);
8+
var started = Date.now();
9+
10+
(function waitForSpin() {
11+
if (Atomics.load(counter, 0) === 0) {
12+
if (Date.now() - started > 5000) {
13+
fail("the worker never started running");
14+
done();
15+
return;
16+
}
17+
setTimeout(waitForSpin, 20);
18+
return;
19+
}
20+
worker.terminate();
21+
setTimeout(function () {
22+
var afterTerminate = Atomics.load(counter, 0);
23+
setTimeout(function () {
24+
expect(Atomics.load(counter, 0)).toBe(afterTerminate);
25+
done();
26+
}, SETTLE_AFTER);
27+
}, SETTLE_AFTER);
28+
})();
29+
});
30+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// close() lets the running callback finish, so this one never returns unless
2+
// terminate() interrupts it.
3+
onmessage = function (event) {
4+
var counter = new Int32Array(event.data);
5+
close();
6+
for (;;) {
7+
Atomics.add(counter, 0, 1);
8+
}
9+
};

‎test-app/runtime/src/main/cpp/WorkerWrapper.cpp‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,7 @@ void WorkerWrapper::PostMessageToParent(std::shared_ptr<worker::Message> message
142142
}
143143

144144
void WorkerWrapper::Terminate() {
145-
if (isClosing_ || isDisposed_) {
146-
// The worker is already shutting down on its own; nothing to do.
145+
if (isDisposed_) {
147146
return;
148147
}
149148

@@ -160,8 +159,8 @@ void WorkerWrapper::Terminate() {
160159
std::lock_guard<std::mutex> lock(workerIsolateMutex_);
161160
Isolate* isolate = workerIsolate_.load();
162161
if (isolate != nullptr) {
163-
// The only v8 call that is legal from another thread - interrupts any
164-
// JS currently running on the worker (e.g. a busy loop).
162+
// Legal from any thread: interrupts any JS currently running on the
163+
// worker (e.g. a busy loop, including one that follows close()).
165164
isolate->TerminateExecution();
166165
// A pump parked with nothing queued runs no JS, so the interrupt
167166
// above never materializes for it - the loop's own flag ends it.

0 commit comments

Comments
 (0)