Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make prioritization stricter #71

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions spec/index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ spec: HTML; urlPrefix: https://html.spec.whatwg.org/multipage/;
text: associated Document; for:Window; url: window-object.html#concept-document-window
type: dfn
text: runnable; for:task; url: webappapis.html#concept-task-runnable
text: posted message task source; url: web-messaging.html#posted-message-task-source
spec: dom; urlPrefix: https://dom.spec.whatwg.org/#;
type: dfn;
text: signal; for: AbortController; url: abortcontroller-signal
Expand Down
70 changes: 50 additions & 20 deletions spec/patches.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,28 +28,58 @@ a specific [=task queue=].
With: For each [=event loop=], every [=task source=] that is not a
[=scheduler task source=] must be associated with a specific [=task queue=].

An [=event loop=] object has a numeric <dfn for="event loop">next enqueue order</dfn>, which is
is initialized to 1.

Note: The [=event loop/next enqueue order=] is a strictly increasing number that is used to
determine task execution order across [=scheduler task queues=] of the same {{TaskPriority}} across
all {{Scheduler}}s associated with the same [=event loop=]. A timestamp would also suffice as long
as it is guaranteed to be strictly increasing and unique.

### <a href="https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model">Event loop: processing model</a> ### {#sec-patches-html-event-loop-processing}

Add the following steps to the event loop processing steps, before step 1:

1. Let |queues| be the [=set=] of the [=event loop=]'s [=task queues=] that
contain at least one <a for="task">runnable</a> <a for="/">task</a>.
1. Let |schedulers| be the [=set=] of all {{Scheduler}} objects whose
[=relevant agent's=] [=agent/event loop=] is this event loop and that
[=have a runnable task=].
1. If |schedulers| and |queues| are both [=list/empty=], skip to the
<code>microtasks</code> step below.

Modify step 1 to read:

1. Let |taskQueue| be one of the following, chosen in an
[=implementation-defined=] manner:
* If |queues| is not [=list/empty=], one of the [=task queues=] in |queues|,
chosen in an [=implementation-defined=] manner.
* If |schedulers| is not [=list/empty=], the result of
[=selecting the task queue of the next scheduler task=] from one of the
{{Scheduler}}s in |schedulers|, chosen in an [=implementation-defined=]
manner.
Replace:
1. If the [=event loop=] has a [=task queue=] with at least one <a for="task">runnable</a>
<a for="/">task</a>, then:
1. Let |taskQueue| be one such [=task queue=], chosen in an [=implementation-defined=] manner.

With:
1. Let |queues| be the [=set=] of the [=event loop=]'s [=task queues=] that contain at least one
<a for="task">runnable</a> <a for="/">task</a>.
1. Let |scheduler queue| be the result of [=selecting the next scheduler queue from all schedulers=]
given the [=event loop=].
1. If |scheduler queue| is not null and |queues| is not [=list/empty=], then:
1. If |scheduler queue|'s [=scheduler task queue/priority=] is {{TaskPriority/user-blocking}},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quotes around strings like user-blocking, here and elsewhere.

then [=set/remove=] from |queues| any [=task queue=] whose [=task source=] is in
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Task queues don't have associated task sources. Instead, "every task source must be associated with a specific task queue". So e.g. a given task queue could have some timer task source-tasks, some networking tasks, etc.

I think what you want to check here is the task source of the next runnable task in the task queue. That's kind of weird, but I'm not sure there's a better option...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK, ugh I knew that :(. I'm OOO next week, I'll work on cleaning it up after. Thanks for the review - much appreciated!

«[=timer task source=], [=posted message task source=]».
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will delay behind all posted messages, not just same-Window ones. The below note doesn't seem to align with that.

If you wanted to restrict to same-window ones, you'd need to mark the queued tasks in some way, presumably at the time they're enqueued. I might try something like moneypatching https://html.spec.whatwg.org/multipage/web-messaging.html#posted-message-task-source to contain "If ..., the [task] queued in such a way is a same-window posted message task", and then checking that here.

1. Otherwise if |scheduler queue|'s [=scheduler task queue/priority=] is
{{TaskPriority/background}}, then set |scheduler queue| to null.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add a note here: something like

This means we won't select |scheduler queue|, instead selecting from the non-empty |queues| set.

1. If |scheduler queue| is not null or |queues| is not [=list/empty=], then:
1. Let |taskQueue| be one of the following, chosen in an [=implementation-defined=] manner:
* One of the [=task queues=] in |queues|, if |queues| is not [=list/empty=].
* |scheduler queue|'s [=scheduler task queue/tasks=], if |scheduler queue| is not null.


Note: This section defines the integration of {{Scheduler}} tasks and the [=event loop=].
<br/><br/>
The intention of {{TaskPriority/user-blocking}} priority is to enable developers to schedule high
priority work that still stays responsive to input and rendering, e.g. critical work spawned by an
input response that shouldn't block the next frame or key press. {{TaskPriority/user-blocking}}
tasks are specified to have higher event loop priority than other scheduling methods, specifically
{{WindowOrWorkerGlobalScope/setTimeout()|setTimeout(0)}} and same-window
{{Window/postMessage(message, options)|postMessage()}}. UAs have flexibility to prioritize between
{{TaskPriority/user-blocking}} tasks other task sources, but are encouraged to give some increased
priority to the former. One possible strategy is to give {{TaskPriority/user-blocking}}
tasks priority over everything except user input and rendering (to ensure the UI remains
responsive), as well as user-driven tasks like navigation.
<br/><br/>
{{TaskPriority/user-visible}} tasks are meant to be scheduled in a similar way to existing
scheduling mechanisms like {{WindowOrWorkerGlobalScope/setTimeout()|setTimeout(0)}} and
and same-window {{Window/postMessage(message, options)|postMessage()}}, but the relative priority
of these is unspecified (as is the case for all event loop task queues).
<br/><br/>
{{TaskPriority/background}} tasks can only run if no other tasks are <a for="task">runnable</a>.
This is similar to idle tasks, but they can run outside of idle periods.

Issue: The |taskQueue| in this step will either be a [=set=] of [=tasks=] or a
[=set=] of [=scheduler tasks=]. The steps that follow only [=set/remove=] an
Expand Down
54 changes: 22 additions & 32 deletions spec/scheduling-tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,22 +133,9 @@ single per-{{TaskPriority}} [=scheduler task queue=], and move tasks between
[=scheduler task queues=] in response to a {{TaskSignal}}'s
[=TaskSignal/priority=] changing, inserting based on
[=scheduler task/enqueue order=]. This approach would simplify
[=selecting the task queue of the next scheduler task=], but make priority
[=selecting the next scheduler queue from all schedulers=], but make priority
changes more complex.


A {{Scheduler}} object has a numeric <dfn for="Scheduler">next enqueue order</dfn>
which is initialized to 1.

Note: The [=Scheduler/next enqueue order=] is a strictly increasing number that
is used to determine task execution order across [=scheduler task queues=] of the
same {{TaskPriority}} within the same {{Scheduler}}. A logically equivalent
alternative would be to place the [=Scheduler/next enqueue order=] on the
[=event loop=], since the only requirements are that the number be strictly
increasing and not be repeated within a {{Scheduler}}.

Issue: Would it be simpler to just use a timestamp here?

The <dfn method for=Scheduler title="postTask(callback, options)">postTask(|callback|, |options|)</dfn>
method steps are to return the result of [=scheduling a postTask task=] for [=this=]
given |callback| and |options|.
Expand Down Expand Up @@ -231,7 +218,7 @@ Processing Model {#sec-scheduling-tasks-processing-model}
<div algorithm="schedule a postTask task">
To <dfn lt="schedule a postTask task|scheduling a postTask task">schedule a postTask task</dfn>
for {{Scheduler}} |scheduler| given a {{SchedulerPostTaskCallback}} |callback| and
{{SchedulerPostTaskOptions}} |options|, run the following steps:
{{SchedulerPostTaskOptions}} |options|:

1. Let |result| be [=a new promise=].
1. Let |signal| be |options|["{{SchedulerPostTaskOptions/signal}}"] if
Expand Down Expand Up @@ -294,8 +281,8 @@ see [whatwg/html#5925](https://github.com/whatwg/html/issues/5925).
1. Let |global| be the [=relevant global object=] for |scheduler|.
1. Let |document| be |global|'s <a attribute for="Window">associated `Document`</a>
if |global| is a {{Window}} object; otherwise null.
1. Let |enqueue order| be |scheduler|'s [=Scheduler/next enqueue order=].
1. Increment |scheduler|'s [=Scheduler/next enqueue order=] by 1.
1. Let |enqueue order| be |scheduler|'s [=event loop/next enqueue order=].
1. Increment |scheduler|'s [=event loop/next enqueue order=] by 1.
1. Let |task| be the result of [=queuing a scheduler task=] on |queue| given
|enqueue order|, [=the posted task task source=], and |document|, and that
performs the following steps:
Expand All @@ -309,7 +296,7 @@ see [whatwg/html#5925](https://github.com/whatwg/html/issues/5925).

Issue: Because this algorithm can be called from [=in parallel=] steps, parts
of this and other algorithms are racy. Specifically, the
[=Scheduler/next enqueue order=] should be updated atomically, and accessing
[=event loop/next enqueue order=] should be updated atomically, and accessing
the [=scheduler task queues=] should occur atomically. The latter also affects
the event loop task queues (see [this issue](https://github.com/whatwg/html/issues/6475)).
</div>
Expand All @@ -323,35 +310,38 @@ see [whatwg/html#5925](https://github.com/whatwg/html/issues/5925).

<div algorithm="get the runnable task queues">
To <dfn lt="get the runnable task queues|getting the runnable task queues">get the runnable task queues</dfn>
for a {{Scheduler}} |scheduler|, run the following steps:
for a {{Scheduler}} |scheduler|:

1. Let |queues| be the result of [=map/get the values|getting the values=] of
|scheduler|'s [=Scheduler/static priority task queue map=].
1. [=list/Extend=] |queues| with the result of [=map/get the values|getting the values=]
of |scheduler|'s [=Scheduler/dynamic priority task queue map=].
1. Let |queues| be the result of [=map/get the values|getting the values=] of |scheduler|'s
[=Scheduler/static priority task queue map=].
1. [=list/Extend=] |queues| with the result of [=map/get the values|getting the values=] of
|scheduler|'s [=Scheduler/dynamic priority task queue map=].
1. [=list/Remove=] from |queues| any |queue| such that |queue|'s [=scheduler task queue/tasks=]
do not contain a [=task/runnable=] [=scheduler task=].
1. Return |queues|.
</div>

<div algorithm>
The result of <dfn>selecting the task queue of the next scheduler task</dfn>
for {{Scheduler}} |scheduler| is a [=set=] of [=scheduler tasks=] as defined
by the following steps:
<div algorithm="select the next scheduler queue from all schedulers">
To <dfn>select the next scheduler queue from all schedulers</dfn> given an [=event loop=]
|event loop|, perform the following steps. They return a [=scheduler task queue=] or null if no
{{Scheduler}} associated with the |event loop| [=has a runnable task=].

1. Let |queues| be the result of [=getting the runnable task queues=] for |scheduler|.
1. Let |queues| be an empty [=set=].
1. Let |schedulers| be the [=set=] of all {{Scheduler}} objects whose [=relevant agent's=]
[=agent/event loop=] is |event loop| and that [=have a runnable task=].
1. For each |scheduler| in |schedulers|, [=list/extend=] |queues| with the result of [=getting the
runnable task queues=] for |scheduler|.
1. If |queues| is [=list/empty=] return null.
1. [=set/Remove=] from |queues| any |queue| such that |queue|'s [=scheduler task queue/priority=]
is [=TaskPriority/less than=] any other [=set/item=] of |queues|.
1. Let |queue| be the [=scheduler task queue=] in |queues| whose
[=scheduler task queue/first runnable task=] is the
[=scheduler task/older than|oldest=].
[=scheduler task queue/first runnable task=] is the [=scheduler task/older than|oldest=].
<br/><span class=note>Two tasks cannot have the same age since [=scheduler task/enqueue order=]
is unique.</span>
1. Return |queue|'s [=scheduler task queue/tasks=].
1. Return |queue|.

Note: The next task to run is the oldest, highest priority [=task/runnable=]
[=scheduler task=].
[=scheduler task=] from all {{Scheduler}}s associated with the [=event loop=].
</div>

Examples {#sec-scheduling-tasks-examples}
Expand Down