Skip to content

More Array builtins never consult the interrupt handler (follow-up to #1672) #1753

Description

@yspbwx2010

#1674 added js_poll_interrupts() to the element loops of indexOf, lastIndexOf, includes, every/some/forEach/map/filter, reduce/reduceRight and the find family. Several other Array builtins walk [0, length) in C the same way, without calling into JS, and still never poll. Neither Ctrl-C in the REPL nor an embedder's interrupt handler can stop them.

On a sparse array, or an array-like with a large length and no elements, most of these loops allocate nothing per iteration, so a memory limit does not stop them either. At a length of 232-1 they run for minutes (join took four to five minutes here), and the ones that also accept array-likes with lengths up to 253-1 effectively never return.

To reproduce, start the v0.17.0 qjs REPL, enter a line and press Ctrl-C after a second:

while (true) {}                                     // InternalError: interrupted
Array.prototype.join.call({length: 2**32-1}, "")    // Ctrl-C has no effect
Array.prototype.slice.call({length: 2**32-1})       // Ctrl-C has no effect
new Array(2**32-1).reverse()                        // Ctrl-C has no effect

The last three keep running after Ctrl-C, so each one needs a fresh qjs. The same can be seen with run-test262 and the qjs:set-interrupt-handler flag from #1674.

These are the loops without a poll, with line numbers at v0.17.0. Master has the same line numbers, since the only change to quickjs.c after the tag is in js_string_repeat.

builtins loop allocates per element
join, toLocaleString js_array_join, quickjs.c:44093 no with an empty separator, otherwise one character
reverse js_array_reverse, 44271 (generic path) no
copyWithin, shift, unshift, splice JS_CopySubArray, 43023 no
slice, splice js_array_slice, 44425 (generic copy) and 44445 (splice deleting the old tail) no
concat js_array_concat, 43446 (spreadable argument) no
sort js_array_sort, 44777 (gather) and 44824 (deleting trailing holes) no
flat, flatMap JS_FlattenIntoArray, 44594 no
fill js_array_fill, 43799 yes
Array.from (array-like source) js_array_from, 43181 yes

fill and Array.from define a property per index, so a memory limit stops them eventually, but until then they ignore the handler too. pop and push do not loop over the length. with, toReversed, toSorted and toSpliced allocate a dense result of the full length up front (above 2**31-1 that is a RangeError), so their loops are bounded by what can actually be allocated.

The fix is a poll at the top of each of these loops, as in #1674. I have a patch with tests and can open a PR.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions