-
Notifications
You must be signed in to change notification settings - Fork 29.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
messaging: fix edge cases with transferring ports
Currently, transferring the port on which postMessage is called causes a segmentation fault, and transferring the target port causes a subsequent port.onmessage setting to throw, or a deadlock if onmessage is set before the postMessage. Fix both of these behaviors and align the methods more closely with the normative definitions in the HTML Standard. Also, per spec postMessage must not throw just because the ports are disentangled. Implement that behavior. PR-URL: #21540 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: James M Snell <[email protected]>
- Loading branch information
Showing
5 changed files
with
213 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// Flags: --experimental-worker | ||
'use strict'; | ||
|
||
const common = require('../common'); | ||
const assert = require('assert'); | ||
const { MessageChannel } = require('worker_threads'); | ||
|
||
// This tests various behaviors around transferring MessagePorts with closing | ||
// or closed handles. | ||
|
||
const { port1, port2 } = new MessageChannel(); | ||
|
||
const arrayBuf = new ArrayBuffer(10); | ||
port1.onmessage = common.mustNotCall(); | ||
port2.onmessage = common.mustNotCall(); | ||
|
||
function testSingle(closedPort, potentiallyOpenPort) { | ||
assert.throws(common.mustCall(() => { | ||
potentiallyOpenPort.postMessage(null, [arrayBuf, closedPort]); | ||
}), common.mustCall((err) => { | ||
assert.strictEqual(err.name, 'DataCloneError'); | ||
assert.strictEqual(err.message, | ||
'MessagePort in transfer list is already detached'); | ||
assert.strictEqual(err.code, 25); | ||
assert.ok(err instanceof Error); | ||
|
||
const DOMException = err.constructor; | ||
assert.ok(err instanceof DOMException); | ||
assert.strictEqual(DOMException.name, 'DOMException'); | ||
|
||
return true; | ||
})); | ||
|
||
// arrayBuf must not be transferred, even though it is present earlier in the | ||
// transfer list than the closedPort. | ||
assert.strictEqual(arrayBuf.byteLength, 10); | ||
} | ||
|
||
function testBothClosed() { | ||
testSingle(port1, port2); | ||
testSingle(port2, port1); | ||
} | ||
|
||
// Even though the port handles may not be completely closed in C++ land, the | ||
// observable behavior must be that the closing/detachment is synchronous and | ||
// instant. | ||
|
||
port1.close(common.mustCall(testBothClosed)); | ||
testSingle(port1, port2); | ||
port2.close(common.mustCall(testBothClosed)); | ||
testBothClosed(); | ||
|
||
setTimeout(common.mustNotCall('The communication channel is still open'), | ||
common.platformTimeout(1000)).unref(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// Flags: --experimental-worker | ||
'use strict'; | ||
|
||
const common = require('../common'); | ||
const assert = require('assert'); | ||
const { MessageChannel } = require('worker_threads'); | ||
|
||
const { port1, port2 } = new MessageChannel(); | ||
|
||
assert.throws(common.mustCall(() => { | ||
port1.postMessage(null, [port1]); | ||
}), common.mustCall((err) => { | ||
assert.strictEqual(err.name, 'DataCloneError'); | ||
assert.strictEqual(err.message, 'Transfer list contains source port'); | ||
assert.strictEqual(err.code, 25); | ||
assert.ok(err instanceof Error); | ||
|
||
const DOMException = err.constructor; | ||
assert.ok(err instanceof DOMException); | ||
assert.strictEqual(DOMException.name, 'DOMException'); | ||
|
||
return true; | ||
})); | ||
|
||
// The failed transfer should not affect the ports in anyway. | ||
port2.onmessage = common.mustCall((message) => { | ||
assert.strictEqual(message, 2); | ||
port1.close(); | ||
|
||
setTimeout(common.mustNotCall('The communication channel is still open'), | ||
common.platformTimeout(1000)).unref(); | ||
}); | ||
port1.postMessage(2); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// Flags: --experimental-worker | ||
'use strict'; | ||
|
||
const common = require('../common'); | ||
const assert = require('assert'); | ||
const { MessageChannel } = require('worker_threads'); | ||
|
||
const { port1, port2 } = new MessageChannel(); | ||
|
||
const arrayBuf = new ArrayBuffer(10); | ||
|
||
common.expectWarning('Warning', | ||
'The target port was posted to itself, and the ' + | ||
'communication channel was lost', | ||
common.noWarnCode); | ||
port2.onmessage = common.mustNotCall(); | ||
port2.postMessage(null, [port1, arrayBuf]); | ||
|
||
// arrayBuf must be transferred, despite the fact that port2 never received the | ||
// message. | ||
assert.strictEqual(arrayBuf.byteLength, 0); | ||
|
||
setTimeout(common.mustNotCall('The communication channel is still open'), | ||
common.platformTimeout(1000)).unref(); |