| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 7a96d82 commit b1828b3
10 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -121,6 +121,18 @@ added: | |||
| 121 | 121 | ||
| 122 | 122 | Returns a new `AbortSignal` which will be aborted in `delay` milliseconds. | |
| 123 | 123 | ||
| 124 | + #### Static method: `AbortSignal.any(signals)` | ||
| 125 | + | ||
| 126 | + <!-- YAML | ||
| 127 | + added: REPLACEME | ||
| 128 | + --> | ||
| 129 | + | ||
| 130 | + * `signals` {AbortSignal\[]} The `AbortSignal`s of which to compose a new `AbortSignal`. | ||
| 131 | + | ||
| 132 | + Returns a new `AbortSignal` which will be aborted if any of the provided | ||
| 133 | + signals are aborted. Its [`abortSignal.reason`][] will be set to whichever | ||
| 134 | + one of the `signals` caused it to be aborted. | ||
| 135 | + | ||
| 124 | 136 | #### Event: `'abort'` | |
| 125 | 137 | ||
| 126 | 138 | <!-- YAML | |
@@ -1026,6 +1038,7 @@ A browser-compatible implementation of [`WritableStreamDefaultWriter`][]. | |||
| 1026 | 1038 | [`WritableStream`]: webstreams.md#class-writablestream | |
| 1027 | 1039 | [`__dirname`]: modules.md#__dirname | |
| 1028 | 1040 | [`__filename`]: modules.md#__filename | |
| 1041 | + [`abortSignal.reason`]: #abortsignalreason | ||
| 1029 | 1042 | [`buffer.atob()`]: buffer.md#bufferatobdata | |
| 1030 | 1043 | [`buffer.btoa()`]: buffer.md#bufferbtoadata | |
| 1031 | 1044 | [`clearImmediate`]: timers.md#clearimmediateimmediate | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -42,6 +42,7 @@ const { | |||
| 42 | 42 | ||
| 43 | 43 | const { | |
| 44 | 44 | validateAbortSignal, | |
| 45 | + validateAbortSignalArray, | ||
| 45 | 46 | validateObject, | |
| 46 | 47 | validateUint32, | |
| 47 | 48 | } = require('internal/validators'); | |
@@ -54,6 +55,7 @@ const { | |||
| 54 | 55 | clearTimeout, | |
| 55 | 56 | setTimeout, | |
| 56 | 57 | } = require('timers'); | |
| 58 | + const assert = require('internal/assert'); | ||
| 57 | 59 | ||
| 58 | 60 | const { | |
| 59 | 61 | messaging_deserialize_symbol: kDeserialize, | |
@@ -80,13 +82,16 @@ function lazyMakeTransferable(obj) { | |||
| 80 | 82 | } | |
| 81 | 83 | ||
| 82 | 84 | const clearTimeoutRegistry = new SafeFinalizationRegistry(clearTimeout); | |
| 83 | - const timeOutSignals = new SafeSet(); | ||
| 85 | + const gcPersistentSignals = new SafeSet(); | ||
| 84 | 86 | ||
| 85 | 87 | const kAborted = Symbol('kAborted'); | |
| 86 | 88 | const kReason = Symbol('kReason'); | |
| 87 | 89 | const kCloneData = Symbol('kCloneData'); | |
| 88 | 90 | const kTimeout = Symbol('kTimeout'); | |
| 89 | 91 | const kMakeTransferable = Symbol('kMakeTransferable'); | |
| 92 | + const kComposite = Symbol('kComposite'); | ||
| 93 | + const kSourceSignals = Symbol('kSourceSignals'); | ||
| 94 | + const kDependantSignals = Symbol('kDependantSignals'); | ||
| 90 | 95 | ||
| 91 | 96 | function customInspect(self, obj, depth, options) { | |
| 92 | 97 | if (depth < 0) | |
@@ -116,7 +121,7 @@ function setWeakAbortSignalTimeout(weakRef, delay) { | |||
| 116 | 121 | const timeout = setTimeout(() => { | |
| 117 | 122 | const signal = weakRef.deref(); | |
| 118 | 123 | if (signal !== undefined) { | |
| 119 | - timeOutSignals.delete(signal); | ||
| 124 | + gcPersistentSignals.delete(signal); | ||
| 120 | 125 | abortSignal( | |
| 121 | 126 | signal, | |
| 122 | 127 | new DOMException( | |
@@ -185,25 +190,68 @@ class AbortSignal extends EventTarget { | |||
| 185 | 190 | return signal; | |
| 186 | 191 | } | |
| 187 | 192 | ||
| 193 | + /** | ||
| 194 | + * @param {AbortSignal[]} signals | ||
| 195 | + * @returns {AbortSignal} | ||
| 196 | + */ | ||
| 197 | + static any(signals) { | ||
| 198 | + validateAbortSignalArray(signals, 'signals'); | ||
| 199 | + const resultSignal = createAbortSignal({ composite: true }); | ||
| 200 | + const resultSignalWeakRef = new WeakRef(resultSignal); | ||
| 201 | + resultSignal[kSourceSignals] = new SafeSet(); | ||
| 202 | + for (let i = 0; i < signals.length; i++) { | ||
| 203 | + const signal = signals[i]; | ||
| 204 | + if (signal.aborted) { | ||
| 205 | + abortSignal(resultSignal, signal.reason); | ||
| 206 | + return resultSignal; | ||
| 207 | + } | ||
| 208 | + signal[kDependantSignals] ??= new SafeSet(); | ||
| 209 | + if (!signal[kComposite]) { | ||
| 210 | + resultSignal[kSourceSignals].add(new WeakRef(signal)); | ||
| 211 | + signal[kDependantSignals].add(resultSignalWeakRef); | ||
| 212 | + } else if (!signal[kSourceSignals]) { | ||
| 213 | + continue; | ||
| 214 | + } else { | ||
| 215 | + for (const sourceSignal of signal[kSourceSignals]) { | ||
| 216 | + const sourceSignalRef = sourceSignal.deref(); | ||
| 217 | + if (!sourceSignalRef) { | ||
| 218 | + continue; | ||
| 219 | + } | ||
| 220 | + assert(!sourceSignalRef.aborted); | ||
| 221 | + assert(!sourceSignalRef[kComposite]); | ||
| 222 | + | ||
| 223 | + if (resultSignal[kSourceSignals].has(sourceSignal)) { | ||
| 224 | + continue; | ||
| 225 | + } | ||
| 226 | + resultSignal[kSourceSignals].add(sourceSignal); | ||
| 227 | + sourceSignalRef[kDependantSignals].add(resultSignalWeakRef); | ||
| 228 | + } | ||
| 229 | + } | ||
| 230 | + } | ||
| 231 | + return resultSignal; | ||
| 232 | + } | ||
| 233 | + | ||
| 188 | 234 | [kNewListener](size, type, listener, once, capture, passive, weak) { | |
| 189 | 235 | super[kNewListener](size, type, listener, once, capture, passive, weak); | |
| 190 | - if (this[kTimeout] && | ||
| 236 | + const isTimeoutOrNonEmptyCompositeSignal = this[kTimeout] || (this[kComposite] && this[kSourceSignals]?.size); | ||
| 237 | + if (isTimeoutOrNonEmptyCompositeSignal && | ||
| 191 | 238 | type === 'abort' && | |
| 192 | 239 | !this.aborted && | |
| 193 | 240 | !weak && | |
| 194 | 241 | size === 1) { | |
| 195 | - // If this is a timeout signal, and we're adding a non-weak abort | ||
| 242 | + // If this is a timeout signal, or a non-empty composite signal, and we're adding a non-weak abort | ||
| 196 | 243 | // listener, then we don't want it to be gc'd while the listener | |
| 197 | 244 | // is attached and the timer still hasn't fired. So, we retain a | |
| 198 | 245 | // strong ref that is held for as long as the listener is registered. | |
| 199 | - timeOutSignals.add(this); | ||
| 246 | + gcPersistentSignals.add(this); | ||
| 200 | 247 | } | |
| 201 | 248 | } | |
| 202 | 249 | ||
| 203 | 250 | [kRemoveListener](size, type, listener, capture) { | |
| 204 | 251 | super[kRemoveListener](size, type, listener, capture); | |
| 205 | - if (this[kTimeout] && type === 'abort' && size === 0) { | ||
| 206 | - timeOutSignals.delete(this); | ||
| 252 | + const isTimeoutOrNonEmptyCompositeSignal = this[kTimeout] || (this[kComposite] && this[kSourceSignals]?.size); | ||
| 253 | + if (isTimeoutOrNonEmptyCompositeSignal && type === 'abort' && size === 0) { | ||
| 254 | + gcPersistentSignals.delete(this); | ||
| 207 | 255 | } | |
| 208 | 256 | } | |
| 209 | 257 | ||
@@ -287,7 +335,8 @@ defineEventHandler(AbortSignal.prototype, 'abort'); | |||
| 287 | 335 | * @param {{ | |
| 288 | 336 | * aborted? : boolean, | |
| 289 | 337 | * reason? : any, | |
| 290 | - * transferable? : boolean | ||
| 338 | + * transferable? : boolean, | ||
| 339 | + * composite? : boolean, | ||
| 291 | 340 | * }} [init] | |
| 292 | 341 | * @returns {AbortSignal} | |
| 293 | 342 | */ | |
@@ -296,11 +345,13 @@ function createAbortSignal(init = kEmptyObject) { | |||
| 296 | 345 | aborted = false, | |
| 297 | 346 | reason = undefined, | |
| 298 | 347 | transferable = false, | |
| 348 | + composite = false, | ||
| 299 | 349 | } = init; | |
| 300 | 350 | const signal = new EventTarget(); | |
| 301 | 351 | ObjectSetPrototypeOf(signal, AbortSignal.prototype); | |
| 302 | 352 | signal[kAborted] = aborted; | |
| 303 | 353 | signal[kReason] = reason; | |
| 354 | + signal[kComposite] = composite; | ||
| 304 | 355 | return transferable ? lazyMakeTransferable(signal) : signal; | |
| 305 | 356 | } | |
| 306 | 357 | ||
@@ -312,6 +363,10 @@ function abortSignal(signal, reason) { | |||
| 312 | 363 | [kTrustEvent]: true, | |
| 313 | 364 | }); | |
| 314 | 365 | signal.dispatchEvent(event); | |
| 366 | + signal[kDependantSignals]?.forEach((s) => { | ||
| 367 | + const signalRef = s.deref(); | ||
| 368 | + if (signalRef) abortSignal(signalRef, reason); | ||
| 369 | + }); | ||
| 315 | 370 | } | |
| 316 | 371 | ||
| 317 | 372 | class AbortController { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -324,6 +324,26 @@ function validateBooleanArray(value, name) { | |||
| 324 | 324 | } | |
| 325 | 325 | } | |
| 326 | 326 | ||
| 327 | + /** | ||
| 328 | + * @callback validateAbortSignalArray | ||
| 329 | + * @param {*} value | ||
| 330 | + * @param {string} name | ||
| 331 | + * @returns {asserts value is AbortSignal[]} | ||
| 332 | + */ | ||
| 333 | + | ||
| 334 | + /** @type {validateAbortSignalArray} */ | ||
| 335 | + function validateAbortSignalArray(value, name) { | ||
| 336 | + validateArray(value, name); | ||
| 337 | + for (let i = 0; i < value.length; i++) { | ||
| 338 | + const signal = value[i]; | ||
| 339 | + const indexedName = `${name}[${i}]`; | ||
| 340 | + if (signal == null) { | ||
| 341 | + throw new ERR_INVALID_ARG_TYPE(indexedName, 'AbortSignal', signal); | ||
| 342 | + } | ||
| 343 | + validateAbortSignal(signal, indexedName); | ||
| 344 | + } | ||
| 345 | + } | ||
| 346 | + | ||
| 327 | 347 | /** | |
| 328 | 348 | * @param {*} signal | |
| 329 | 349 | * @param {string} [name='signal'] | |
@@ -534,6 +554,7 @@ module.exports = { | |||
| 534 | 554 | validateArray, | |
| 535 | 555 | validateStringArray, | |
| 536 | 556 | validateBooleanArray, | |
| 557 | + validateAbortSignalArray, | ||
| 537 | 558 | validateBoolean, | |
| 538 | 559 | validateBuffer, | |
| 539 | 560 | validateDictionary, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -680,7 +680,7 @@ class WPTRunner { | |||
| 680 | 680 | ||
| 681 | 681 | process.on('exit', () => { | |
| 682 | 682 | for (const spec of this.inProgress) { | |
| 683 | - this.fail(spec, { name: 'Unknown' }, kIncomplete); | ||
| 683 | + this.fail(spec, { name: 'Incomplete' }, kIncomplete); | ||
| 684 | 684 | } | |
| 685 | 685 | inspect.defaultOptions.depth = Infinity; | |
| 686 | 686 | // Sorts the rules to have consistent output | |
@@ -796,9 +796,11 @@ class WPTRunner { | |||
| 796 | 796 | * @param {object} harnessStatus - The status object returned by WPT harness. | |
| 797 | 797 | */ | |
| 798 | 798 | completionCallback(spec, harnessStatus) { | |
| 799 | + const status = this.getTestStatus(harnessStatus.status); | ||
| 800 | + | ||
| 799 | 801 | // Treat it like a test case failure | |
| 800 | - if (harnessStatus.status === 2) { | ||
| 801 | - this.resultCallback(spec, { status: 2, name: 'Unknown' }); | ||
| 802 | + if (status === kTimeout) { | ||
| 803 | + this.fail(spec, { name: 'WPT testharness timeout' }, kTimeout); | ||
| 802 | 804 | } | |
| 803 | 805 | this.inProgress.delete(spec); | |
| 804 | 806 | // Always force termination of the worker. Some tests allocate resources | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -48,8 +48,17 @@ add_result_callback((result) => { | |||
| 48 | 48 | }); | |
| 49 | 49 | }); | |
| 50 | 50 | ||
| 51 | + // Keep the event loop alive | ||
| 52 | + const timeout = setTimeout(() => { | ||
| 53 | + parentPort.postMessage({ | ||
| 54 | + type: 'completion', | ||
| 55 | + status: { status: 2 }, | ||
| 56 | + }); | ||
| 57 | + }, 2 ** 31 - 1); // Max timeout is 2^31-1, when overflown the timeout is set to 1. | ||
| 58 | + | ||
| 51 | 59 | // eslint-disable-next-line no-undef | |
| 52 | 60 | add_completion_callback((_, status) => { | |
| 61 | + clearTimeout(timeout); | ||
| 53 | 62 | parentPort.postMessage({ | |
| 54 | 63 | type: 'completion', | |
| 55 | 64 | status, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -12,7 +12,7 @@ Last update: | |||
| 12 | 12 | ||
| 13 | 13 | - common: https://github.com/web-platform-tests/wpt/tree/dbd648158d/common | |
| 14 | 14 | - console: https://github.com/web-platform-tests/wpt/tree/767ae35464/console | |
| 15 | - - dom/abort: https://github.com/web-platform-tests/wpt/tree/8fadb38120/dom/abort | ||
| 15 | + - dom/abort: https://github.com/web-platform-tests/wpt/tree/d1f1ecbd52/dom/abort | ||
| 16 | 16 | - dom/events: https://github.com/web-platform-tests/wpt/tree/ab8999891c/dom/events | |
| 17 | 17 | - encoding: https://github.com/web-platform-tests/wpt/tree/0c1b9d1622/encoding | |
| 18 | 18 | - fetch/data-urls/resources: https://github.com/web-platform-tests/wpt/tree/7c79d998ff/fetch/data-urls/resources | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,4 @@ | |||
| 1 | + // META: script=./resources/abort-signal-any-tests.js | ||
| 2 | + | ||
| 3 | + abortSignalAnySignalOnlyTests(AbortSignal); | ||
| 4 | + abortSignalAnyTests(AbortSignal, AbortController); | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments