From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- js/src/jit-test/tests/debug/job-queue-02.js | 82 +++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 js/src/jit-test/tests/debug/job-queue-02.js (limited to 'js/src/jit-test/tests/debug/job-queue-02.js') diff --git a/js/src/jit-test/tests/debug/job-queue-02.js b/js/src/jit-test/tests/debug/job-queue-02.js new file mode 100644 index 0000000000..5f09a818af --- /dev/null +++ b/js/src/jit-test/tests/debug/job-queue-02.js @@ -0,0 +1,82 @@ +// |jit-test| error: "async tests completed successfully" +// Test that the shell's job queue doesn't skip calls to JS::JobQueueMayNotBeEmpty. + +// For expressions like `await 1`, or `await P` for some already-resolved +// promise P, there's no need to suspend the async call to determine the +// expression's value. Suspension and resumption are expensive, so it would be +// nice if we could avoid them. +// +// But of course, even when the value is known, the act of suspension itself is +// visible: an async call's first suspension returns to its (synchronous) +// caller; subsequent suspensions let other jobs run. So in general, we can't +// short-circuit such `await` expressions. +// +// However, if an async call has been resumed from the job queue (that is, this +// isn't the initial execution, with a synchronous caller expecting a promise of +// the call's final return value), and there are no other jobs following that, +// then the `await`'s reaction job would run immediately following this job --- +// which *is* indistinguishable from skipping the suspension altogether. +// +// A JS::JobQueue implementation may call JS::JobQueueIsEmpty to indicate to the +// engine that the currently running job is the last job in the queue, so this +// optimization may be considered (there are further conditions that must be met +// as well). If the JobQueue calls JobQueueIsEmpty, then it must also call +// JS::JobQueueMayNotBeEmpty when jobs are enqueued, to indicate when the +// opportunity has passed. + +var log = ''; +async function f(label, k) { + log += label + '1'; + await 1; + log += label + '2'; + await 1; + log += label + '3'; + + return k(); +} + +// Call `f` with `label` and `k`. If `skippable` is true, exercise the path that +// skips the suspension and resumption; otherwise exercise the +// non-short-circuited path. +function test(skippable, label, k) { + var resolve; + (new Promise(r => { resolve = r; })) + .then(v => { log += v + 't'; }); + assertEq(log, ''); + f(label, k); + // job queue now: f(label)'s first await's continuation + assertEq(log, label + '1'); + + if (!skippable) { + resolve('p'); + assertEq(log, label + '1'); + // job queue now: f(label)'s first await's continuation, explicit promise's reaction + } + + // Resuming f(label) will reach the second await, which should skip suspension + // or not, depending on whether we resolved that promise. +} + +// SpiderMonkey's internal 'queue is empty' flag is initially false, even though +// the queue is initially empty, because we don't yet know whether the embedding +// is going to participate in the optimization by calling +// JS::JobQueueMayNotBeEmpty and JS::JobQueueIsEmpty. But since the shell uses +// SpiderMonkey's internal job queue implementation, this call to +// `drainJobQueue` calls `JS::JobQueueIsEmpty`, and we are ready to play. +Promise.resolve(42).then(v => assertEq(v, 42)); +drainJobQueue(); + +log = ''; +test(true, 'b', continuation1); + +function continuation1() { + assertEq(log, 'b1b2b3'); + + log = ''; + test(false, 'c', continuation2); +} + +function continuation2() { + assertEq(log, 'c1c2ptc3'); + throw "async tests completed successfully"; // proof that we actually finished +} -- cgit v1.2.3