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

Add specification for scheduler.yield() method #88

Merged
merged 3 commits into from
May 28, 2024

Conversation

shaseley
Copy link
Collaborator

@shaseley shaseley commented Apr 8, 2024

Implementation notes:

  • Refactor existing spec to share as much as possible between yield() and postTask():
    • Extract out "scheduling state" struct to hold abort and priority sources and compute this based the options passed to postTask() and yield()
    • Change map data structures and related algorithms to additionally be keyed on "is continuation" boolean (the queues need to be separate since the effective priorities are different)
    • Unify postTask() and yield() priority with notion of an "effective priority", which is used for picking the next task to run (yield() effective priority for a given task priority is just higher than postTask() equivalent).
  • Implement inheritance by adding notion of "current scheduling state" to the event loop
    • Set before running postTask() callback and idle callbacks
    • Stored/restored in HostMakeJobCallback and HostCallJobCallback (i.e. propagate to microtasks from .then() call)

Note: things get a little awkward regarding continuation vs. task priority IDL since we can't yet inherit from enums (ContinuationPriority is TaskPriority + "inherit"). And we sometimes pass one type as another where they are compatible (i.e. same string values permitted).


Preview | Diff

@shaseley shaseley changed the title Add specification for scheduler.yield() method (WIP) Add specification for scheduler.yield() method May 3, 2024
@shaseley shaseley requested review from clelland and mmocny May 3, 2024 23:59
spec/introduction.md Outdated Show resolved Hide resolved
spec/controlling-tasks.md Outdated Show resolved Hide resolved
spec/scheduling-tasks.md Outdated Show resolved Hide resolved
spec/scheduling-tasks.md Outdated Show resolved Hide resolved
spec/scheduling-tasks.md Outdated Show resolved Hide resolved
spec/scheduling-tasks.md Outdated Show resolved Hide resolved
@@ -207,6 +279,20 @@ A <dfn>task handle</dfn> is a [=struct=] with the following [=struct/items=]:
[=task/runnable=].
</div>

<div algorithm>
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't know if it's better or not, but this could also be a simple lookup from a table with 6 rows in it, rather than an algorithm.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah that's probably easier to read. I don't know how it should be formatted, however (using defaults for now).

spec/scheduling-tasks.md Outdated Show resolved Hide resolved
1. Otherwise if |priorityOption| is not null, then set |result|'s [=scheduling state/priority
source=] to the result of [=creating a fixed priority unabortable task signal=] given
|priorityOption|.
1. Otherwise if |abortOption| is not null and [=implements=] the {{TaskSignal}} interface, then
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should this attempt to handle the case where |abortOption| is "inherit", and the interited abort source is a TaskSignal?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

In this algorithm, |abortOption| only applies to determining the abort component, in which case it's irrelevant.

If original signal should also be used for priority, then the caller will set the |priorityOption| to "inherit" (step 5 in the algo above). If the original signal was a TaskSignal, it gets set as both the abort and priority sources in the |inheritedState|, and it funnels into this check.

</div>

<div algorithm>
To <dfn>compute the scheduling state from options</dfn> given a {{Scheduler}} object |scheduler|,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Overall, I find this algorithm hard to follow; I've written and deleted at least three comments where I thought there were errors already :)

Something structured like this might be clearer:

  1. Let result be...
  2. Let inheritedState be...
  3. If abortOption is "inherit":
    • if inheritedState is not null, then set result’s abort source to inheritedState’s abort source.
  4. Otherwise, set result’s abort source to abortOption.
  5. If priorityOption is "inherit":
    • if inheritedState is not null, then set result’s priority source to inheritedState’s priority source.
  6. Otherwise, if priorityOption is not null, then set result’s priority source to the result of creating a fixed priority unabortable task signal given priorityOption.
  7. Otherwise, if abortOption is not null and implements the TaskSignal interface, then set result’s priority source to abortOption. (although I would have expected this to reference result's abort source, rather than abortOption here)
  8. If result’s priority source is null, then set result’s priority source to the result of creating a fixed priority unabortable task signal given "user-visible".
  9. Return result.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Heh, I want back and forth on this several times when drafting this. IIUC this was mostly rewriting 3 & 5? Just making sure I didn't miss anything (changed those in latest version).

Re: using abortOption in step 7 -- this handles the case where the arguments are of the form {signal: someSignal}, where there's no inheritance and the signal could be either a TaskSignal or plain AbortSignal. If it's a TaskSignal, then that priority gets used; otherwise, it defaults to "user-visible" in step 8.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Thanks -- I missed that possibility

spec/controlling-tasks.md Show resolved Hide resolved
spec/introduction.md Outdated Show resolved Hide resolved
spec/patches.md Show resolved Hide resolved
spec/scheduling-tasks.md Show resolved Hide resolved
spec/scheduling-tasks.md Outdated Show resolved Hide resolved
spec/scheduling-tasks.md Show resolved Hide resolved
spec/scheduling-tasks.md Show resolved Hide resolved
spec/scheduling-tasks.md Show resolved Hide resolved
spec/scheduling-tasks.md Show resolved Hide resolved
spec/scheduling-tasks.md Show resolved Hide resolved
Copy link
Collaborator Author

@shaseley shaseley left a comment

Choose a reason for hiding this comment

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

Thanks for the reviews! Really helpful feedback. I think I've addressed everything; PTAnL.

spec/controlling-tasks.md Show resolved Hide resolved
spec/introduction.md Outdated Show resolved Hide resolved
spec/introduction.md Outdated Show resolved Hide resolved

The priority and abort properties of the continuation are determined in a similar way as
{{Scheduler/postTask()}}, but can additionally be inherited from the originating task. If
neither the {{SchedulerYieldOptions/signal}} nor the {{SchedulerYieldOptions/priority}}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's neither. signal can also include priority (if it's a TaskSignal), in which case you'd be overriding that with the inherited priority and would have no way to specify to use the signal's priority.

Making both required wouldn't work either -- the priority option (which is there for convenience vs. always requiring a TaskSignal) overrides the signal's priority, which isn't typically what you'd want if you provide a TaskSignal. (Note: this is how postTask() works as well % inheritance).

spec/scheduling-tasks.md Outdated Show resolved Hide resolved
spec/scheduling-tasks.md Outdated Show resolved Hide resolved
1. Otherwise if |priorityOption| is not null, then set |result|'s [=scheduling state/priority
source=] to the result of [=creating a fixed priority unabortable task signal=] given
|priorityOption|.
1. Otherwise if |abortOption| is not null and [=implements=] the {{TaskSignal}} interface, then
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

In this algorithm, |abortOption| only applies to determining the abort component, in which case it's irrelevant.

If original signal should also be used for priority, then the caller will set the |priorityOption| to "inherit" (step 5 in the algo above). If the original signal was a TaskSignal, it gets set as both the abort and priority sources in the |inheritedState|, and it funnels into this check.

</div>

<div algorithm>
To <dfn>compute the scheduling state from options</dfn> given a {{Scheduler}} object |scheduler|,
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Heh, I want back and forth on this several times when drafting this. IIUC this was mostly rewriting 3 & 5? Just making sure I didn't miss anything (changed those in latest version).

Re: using abortOption in step 7 -- this handles the case where the arguments are of the form {signal: someSignal}, where there's no inheritance and the signal could be either a TaskSignal or plain AbortSignal. If it's a TaskSignal, then that priority gets used; otherwise, it defaults to "user-visible" in step 8.

spec/scheduling-tasks.md Show resolved Hide resolved
spec/scheduling-tasks.md Show resolved Hide resolved
@shaseley shaseley requested review from clelland and mmocny May 15, 2024 23:52
@shaseley shaseley marked this pull request as ready for review May 28, 2024 20:01
@shaseley shaseley merged commit e6c47f5 into WICG:main May 28, 2024
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants