From 11268d8e69d248245d31f52dddb7cc82b4d8e37e Mon Sep 17 00:00:00 2001 From: Scott Haseley Date: Mon, 29 Apr 2024 14:45:51 -0700 Subject: [PATCH] Make task queue selection per agent rather than per scheduler (#87) --- spec/patches.md | 26 ++++++++++++++++-------- spec/scheduling-tasks.md | 44 ++++++++++++++++++---------------------- 2 files changed, 37 insertions(+), 33 deletions(-) diff --git a/spec/patches.md b/spec/patches.md index 6b990b5..acd0ad9 100644 --- a/spec/patches.md +++ b/spec/patches.md @@ -24,25 +24,33 @@ queue=]. With: For each [=event loop=], every [=task source=] that is not a [=scheduler task source=] must be associated with a specific [=task queue=]. +Add: An [=event loop=] has a numeric next enqueue order which 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. + ### Event loop: processing model ### {#sec-patches-html-event-loop-processing} -Add the following steps to the event loop processing steps, before step 1: +Add the following steps to the event loop processing steps, before step 2: 1. Let |queues| be the [=set=] of the [=event loop=]'s [=task queues=] that contain at least one [=task/runnable=] [=task=]. - 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 microtasks step - below. + 1. Let |schedulerQueue| be the result of [=selecting the next scheduler task queue from all + schedulers=]. + +Modify step 2 to read: -Modify step 1 to read: + 1. If |schedulerQueue| is not null or |queues| is not [=list/empty=]: + +Modify step 2.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. + * |schedulerQueue|'s [=scheduler task queue/tasks=], if |schedulerQueue| is not null. 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 [=set/item=], so they are diff --git a/spec/scheduling-tasks.md b/spec/scheduling-tasks.md index 0b464cb..5117cd3 100644 --- a/spec/scheduling-tasks.md +++ b/spec/scheduling-tasks.md @@ -120,20 +120,8 @@ option that is null or is an {{AbortSignal}} — are placed in these queues, An alternative, and logicially equivalent implementation, would be to maintain a 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 changes more complex. - - -A {{Scheduler}} object has a numeric next enqueue order 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? +task/enqueue order=]. This approach would simplify [=selecting the next scheduler task queue from +all schedulers=], but make priority changes more complex. The postTask(|callback|, |options|) method steps are to return the result of [=scheduling a postTask task=] for [=this=] given @@ -319,8 +307,9 @@ Issue: [=Run steps after a timeout=] doesn't necessarily account for suspension; 1. Let |global| be the [=relevant global object=] for |scheduler|. 1. Let |document| be |global|'s associated `Document` 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 |event loop| be |scheduler|'s [=relevant agent's=] [=agent/event loop=]. + 1. Let |enqueue order| be |event loop|'s [=event loop/next enqueue order=]. + 1. Increment |event loop|'s [=event loop/next enqueue order=] by 1. 1. Set |handle|'s [=task handle/task=] to the result of [=queuing a scheduler task=] on |handle|'s [=task handle/queue=] given |enqueue order|, the [=posted task task source=], and |document|, and that performs the following steps: @@ -329,7 +318,7 @@ Issue: [=Run steps after a timeout=] doesn't necessarily account for suspension; 1. Run |handle|'s [=task handle/task complete steps=]. 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 + algorithms are racy. Specifically, the [=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)). @@ -355,10 +344,15 @@ Issue: [=Run steps after a timeout=] doesn't necessarily account for suspension;
- The result of selecting the task queue of the next scheduler task for {{Scheduler}} - |scheduler| is a [=set=] of [=scheduler tasks=] as defined by the following steps: - - 1. Let |queues| be the result of [=getting the runnable task queues=] for |scheduler|. + To select the next scheduler task queue from all schedulers 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 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|. @@ -366,10 +360,12 @@ Issue: [=Run steps after a timeout=] doesn't necessarily account for suspension; runnable task=] is the [=scheduler task/older than|oldest=].
Two tasks cannot have the same age since [=scheduler task/enqueue order=] is unique. - 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=] + from all {{Scheduler}}s associated with the [=event loop=]. +
- Note: The next task to run is the oldest, highest priority [=task/runnable=] [=scheduler task=]. - ## Examples ## {#sec-scheduling-tasks-examples}