WICG / scheduling-apis

APIs for scheduling and controlling prioritized tasks.
https://wicg.github.io/scheduling-apis/
Other
907 stars 45 forks source link

Proposal should address priority inversion #98

Open rniwa opened 1 month ago

rniwa commented 1 month ago

I have mentioned this numerous times during TPAC, etc... but if we add a priority to each task, people will encounter priority inversion. It's not a question of if, it's a question of when. So this proposal really needs to address priority inversion or else it's not really viable in my view.

At minimum, there needs to be a way to boost the priority of a task. A better approach will be automatically boosting the priority of a low priority task as needed.

shaseley commented 1 month ago

I have mentioned this numerous times during TPAC, etc... but if we add a priority to each task, people will encounter priority inversion. It's not a question of if, it's a question of when. So this proposal really needs to address priority inversion or else it's not really viable in my view.

In my view, the (main thread) web scheduling model (no preemption, single thread) is significantly different than scenarios where priority inversion typically arises (threading, synchronization, preemption). I wrote in detail about this in https://github.com/WICG/scheduling-apis/blob/main/misc/priority-inversion.md, which I had linked in the original request for position. It would be extremely helpful if you could provide examples of situations you're concerned about, which I've asked for before, as well as understanding if/how your understanding differs from what I've written there.

Also, just to clarify "each task", the proposal only adds a priority to tasks scheduled with this API. The intent is for this priority to influence the event loop priority, but UAs have flexibility to choose between scheduler tasks and other task sources.

At minimum, there needs to be a way to boost the priority of a task. A better approach will be automatically boosting the priority of a low priority task as needed.

Boost the priority of which tasks? The priority of tasks scheduled with these APIs can be changed in JavaScript with TaskController.setPriority().

mmocny commented 4 weeks ago

I enjoyed reading that document, Scott!

It's been a long time since I studied the cs theory here so I may be rusty :). Some questions below:


Synchronous userspace tasks that use scheduler.postTask() are not at risk of priority inversion in the same way because of the lack of preemption

Where there is a greater risk of priority inversions of this form is with yieldy asyncrounous tasks, which resemble threads

I think this distinction makes sense, but it feels somewhat equivalent to developers, in practice.


Other Forms of Priority Inversion

I think I have a simple example which is similar but not exactly like your Example 1?

Example 3: A higher priority task await's a promise which is created (and later fulfilled) by a lower priority task. The lower priority task might need to wait for all higher tasks to finish before being scheduled (and resolving the promise) meaning the higher priority task awaiting effectively becomes lower priority.

Example:

const {promise: userDataPromise, resolve, reject} = Promise.withResolvers();

// Will get scheduled at low priority
// Even when the fetch completes, it might take time to schedule continuation and resolve
// (Assumes priority inheritance... but would apply also to default priority)
async function loadLazyUserData() {
  const userData = await fetch(...);
  resolve(userData);
}

function onLoad() {
  scheduler.postTask(setupUI, { priority: 'user-blocking' });
  scheduler.postTask(loadLazyUserData, { priority: 'background' });
}

// This task is high priority.
// This task awaits a promise that is fulfilled by a lower priority task.
// When the fetch response becomes available, promise will still remain unfulfilled until that lower priority task gets scheduled.
// This high priority event handler effectively becomes lower priority.
userButton.onclick = async () => {
  const userData = await userDataPromise;
}

There may be an opportunity for a UA to detect and mitigate priority inversions

Some ideas: