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

Problem: run_cooperatively is rather limiting #16

Merged
merged 10 commits into from
Feb 13, 2021
Merged

Conversation

yrashk
Copy link
Contributor

@yrashk yrashk commented Feb 13, 2021

It only allows to run tasks cooperatively under setTimeout and will
always execute just one iteration before yielding back to the browser,
therefore taking control of the behaviour. It would have been nice to
have more control over yielding in the tasks themselves.

Solution: introduce manual yielding primitives
single_threaded::yield_timeout, single_threaded::yield_async and
single_threaded::yield_animation_frame

These functions return futures that first yield to the environment using
setTimemout or requestAnimationFrame, respectively. These futures
will be ready and will produce the output of the enclosed future (if there's one) once
yielded and those future produced the output.

This allows tasks to control the yielding to a (hopefully) sufficient
degree.

Also, this change saves until argument to run at the
yield/resume threshold, so the executor will stop if until is
specified to be a certain task when that task is done.


This PR obsoletes #14

It only allows to run tasks cooperatively under `setTimeout` and will
always execute just one iteration before yielding back to the browser,
therefore taking control of the behaviour. It would have been nice to
have more control over yielding in the tasks themselves.

Solution: introduce manual yielding primitives
`single_threaded::yield_timeout` and
`single_threaded::yield_animation_frame`

These functions return futures that first yield to the environment using
`setTimemout` or `requestAnimationFrame`, respectively. These futures
will be ready and will produce the output of the enclosed future once
yielded and those future produced the output.

This allows tasks to control the yielding to a (hopefully) sufficient
degree.

Also, this change saves `until` argument to `run` at the
yield/resume threshold, so the executor will stop if `until` is
specified to be a certain task when that task is done.
This is because cooperative mode gives control back to the browser and
values are no longer on the stack.

Solution: ensure given future is always static
when compiled with `cooperative`
It's really confusing why should it have one. It was done to re-use the
infrastucture that supported `yield_timeout`, which supports this to
effectively allow running async APIs to completion before returning into
the executor.

Solution: separate it from `yield_timeout` and remove the parameter

This future will now return the high-resolution timer, "indicating the
point in time when requestAnimationFrame() starts to execute callback
functions."
This is rather non-obvious.

Solution: introduce `yield_async` for better ergonomics
It's a bit unclear how it behaves in cooperative mode. Will it not exit
until everything is done?

Solution: clarify that it will exit if control is yielded
to the environment.
When `cooperative` feature is enabled, all futures have to have a static
lifetime even if no yielding to the environment is expected.

Solution: disable yielding to the environment during `block_on`
instead of imposing a static lifetime requirement.
Why can't we simply await for the timeout and then schedule a future
using `yield_async` to be executed with the environment?

Solution: remove the enclosed future from it
and make `duration` non-optional
This doesn't really make a lot of practical sense. Why should a task
have an ability to delay the whole executor? Can't really think of much
pratical use.

Solution: change it to delay only the task it's in
by making it ready when the timeout has passed.
When executor exited because `until` condition has been satisfied, if
there has been a cooperative future (like `YieldTimeout` or
`YieldAnimationFrame`), it will be started again by one of those
futures, should they have their promises resolved.

This is not the expected behaviour.

Solution: maintain state of satisfaction the exit condition
and don't run the executor if restarted when it has been established
that the exit condition has been met.
For some tasks would be most appropriate to wait for the browser to be
idle before doing some background activity.

Solution: add support for experimental `requestIdleCallback` API

It's under `requestIdleCallback` feature gate.

Can be used like this:

```rust
async move {
  loop {
     executor::yield_until_idle(None).await;
     // Do something
  }
}
```
@yrashk yrashk merged commit 3254d89 into wasm-rs:master Feb 13, 2021
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.

None yet

1 participant