Skip to content

Clean up abort algorithms and task queue map entries #84

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

Merged
merged 2 commits into from
Apr 29, 2024

Conversation

shaseley
Copy link
Collaborator

@shaseley shaseley commented Apr 4, 2024

This introduces removal of abort algorithms and task queue map entries on abort and task completion:

  • Add a helper struct (task handle) to store state and algorithms needed to clean up on abort/task complete. It was a bit cleaner to keep this all together.
  • On abort and task complete, remove the task queue map entry if the queue became empty
  • On task complete, remove the abort algorithm
  • On abort, remove the pending task

Notes:

  1. This also fixes a spec bug where it was possible for delayed tasks not to be aborted if the abort happened while waiting to queue the task. This is fixed here by moving up the point where we add the abort algorithm and conditionally and adjusting the algorithm to handle the case where the task hasn't been queued yet (during delay).

  2. For delayed tasks, getting the destination task queue is deferred until actually queuing the task (after the delay expires). It's important to wait to ensure the map entry exists when queuing the task.

  3. Chromium removes both abort algorithms and task queues lazily during GC, which is another valid approach (these are not web observable).


Preview | Diff

@shaseley shaseley marked this pull request as ready for review April 8, 2024 22:43
@shaseley shaseley requested review from clelland and mmocny April 11, 2024 02:46
@shaseley
Copy link
Collaborator Author

Friendly ping @clelland @mmocny. Not sure if adding as reviewers sent emails or not.

Copy link
Collaborator

@mmocny mmocny left a comment

Choose a reason for hiding this comment

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

One change and one question, otherwise LGTM!

queue/removal steps=].
1. Set |handle|'s [=task handle/task complete steps=] to the following steps:
1. If |signal| is not null, then [=AbortSignal/remove=] |handle|'s [=task handle/abort steps=]
from |signal|.
Copy link
Collaborator

Choose a reason for hiding this comment

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

from |handle|?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Nevermind, I see now, once we complete this handle we don't need the signal to call the abort steps any more.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That's right. I need to fix this in some other specs too, but abort algorithms can accumulate, which can be leaky.


1. Let |queue| be a new [=scheduler task queue=].
1. Set |queue|'s [=scheduler task queue/priority=] to |priority|.
1. Set |queue|'s [=scheduler task queue/tasks=] to a new empty [=set=].
1. Set |queue|'s [=scheduler task queue/removal steps=] to |removalSteps|.
Copy link
Collaborator

Choose a reason for hiding this comment

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

A new task queue starts empty and has removal steps attached.

After the last task is rejected/completed, we will call the removal steps, and those steps I think make this queue no longer valid without re-creating.

I don't think it can happen per the changes in this change, but it seems like a task-queue must never stay empty after creation, nor ever return back from non-empty?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think this comment refers to the risk of having a reference to the TQ but the queue having been removed from the map? That would indeed be a problem -- and I did have to rewrite things to make sure that can't happen (specifically in the delayed task section, which previously got the TQ when postTask called, but didn't actually queue the task until the delay expired).

A task queue could go from empty to non-empty in the same task, e.g. the last task is dequeued, runs, queues more tasks to the queue within that task. But in that case it's okay because it would still be in the map.

Implementations might want to do the cleanup differently, e.g. in Chrome we use a weak map from signal --> task queue and let GC take care of cleanup (similar for abort algorithms -- we have a GCed handle). But this isn't observable since the task queues themselves aren't web-exposed. Adding the cleanup explicitly here at least makes it so the spec isn't leaky, and indicates when resources could be released.

This introduces removal of abort algorithms and task queue map entries
on abort and task completion:
 - Add a helper struct (task handle) to store state and algorithms
   needed to clean up on abort/task complete. It was a bit cleaner to
   keep this all together.
 - On abort and task complete, remove the task queue map entry if the
   queue became empty
 - On task complete, remove the abort algorithm
 - On abort, remove the pending task

Notes:
 1. This also fixes a spec bug where it was possible for delayed tasks
    not to be aborted if the abort happened while waiting to queue the
    task. This is fixed here by moving up the point where we add the
    abort algorithm and conditionally and adjusting the algorithm to
    handle the case where the task hasn't been queued yet (during
    delay).

 2. For delayed tasks, getting the destination task queue is deferred
    until actually queuing the task (after the delay expires). It's
    important to wait to ensure the map entry exists when queuing the
    task.

 3. Chromium removes both abort algorithms and task queues lazily
    during GC, which is another valid approach (these are not web
    observable).
@shaseley shaseley merged commit 239d8c2 into WICG:main Apr 29, 2024
2 checks passed
@shaseley shaseley deleted the clean-up-state branch April 29, 2024 21:25
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.

Need to remove the entry from the task queue maps when the queue is empty
2 participants