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

Consider adding spawn_unchecked for non-static futures #2385

Closed
rnarubin opened this issue Apr 8, 2020 · 6 comments
Closed

Consider adding spawn_unchecked for non-static futures #2385

rnarubin opened this issue Apr 8, 2020 · 6 comments
Labels
A-tokio Area: The main tokio crate C-feature-request Category: A feature request. M-task Module: tokio/task

Comments

@rnarubin
Copy link

rnarubin commented Apr 8, 2020

Currently the various spawn functions require the provided future to have a 'static lifetime, in much the same way that std::thread::spawn requires the passed closure to have a 'static bound. This is because there's nothing constraining the future from outliving its originating scope, so for soundness it should not have any shorter borrows.

In theory, however, there are ways to constrain the lifespan of a future (e.g. closure scopes and forceful cancellation) such that it would be safe to borrow from a non-'static scope. For libraries exploring this design space, the only current workaround to spawn such a future is to box it as a trait object and then transmute to the static lifetime:

unsafe fn make_static(f: impl Future<Output=()> + Send) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
  std::mem::transmute::<
    Pin<Box<dyn Future<Output=()> + Send>>,
    Pin<Box<dyn Future<Output=()> + Send + 'static>>
  >(Box::pin(f))
}

Although this works, it requires both an allocation and dynamic dispatch (where the underlying scheduler will also add similar indirection). This is even more complicated when the Output type is non-'static, which is a can of worms I won't open here for brevity.

I propose adding an unsafe spawn_unchecked function to the Handle type with a signature like this:

pub unsafe fn spawn_unchecked<'a, F>(&self, f: F) -> JoinHandle<F::Output>
where
    F: Future + Send + 'a,
    F::Output: Send + 'a; 

This is similar to the proposal for adding such a function to std::thread with exactly the same motivation: allowing libraries to build scoped-spawn functionality without the overhead of lifetime workarounds.

@Darksonn
Copy link
Contributor

Darksonn commented Apr 8, 2020

Note that because of the fact that futures can be cancelled at any time, doing this while correctly handling cancellation becomes much more difficult compared to ordinary scoped threads. See more details in #1879.

@Darksonn Darksonn added A-tokio Area: The main tokio crate C-feature-request Category: A feature request. M-task Module: tokio/task labels Apr 29, 2020
@LucioFranco
Copy link
Member

This sounds like something dangerous but could probably be implemented externally? You can write an extension trait that provides this.

@carllerche
Copy link
Member

This would be an equivalent to: https://doc.rust-lang.org/std/thread/struct.Builder.html#method.spawn_unchecked

@carllerche
Copy link
Member

@rnarubin as a word of warning, Drop cannot be relied on for safety. So, this would probably work when spawning from a sync context, but not from an async context.

@alecmocatta
Copy link

@LucioFranco Implementing this functionality externally necessitates the allocations and dynamic dispatch mentioned in the OP, as that's the only way to convert a non-'static type to 'static. The only way to enable this functionality "zero-cost" is by adding something like spawn_unchecked that does not enforce the 'static bound – see rust-lang/rust#55043 for more details.

I think this is worth having for the same reason as in std: to be used as a building block for libraries that can experiment with building safe abstractions around it, such as scoped tasks (for instance https://docs.rs/async-scoped/0.4/async_scoped/fn.scope_and_block.html). Currently such libraries have to rely on inefficient subversions of the type system/borrow checker (e.g. here).

Would you be open to a PR that adds _unchecked versions of spawn functions?

@carllerche
Copy link
Member

Thanks for the suggestion. We discussed this again recently.

We are opting to not add this function, at least for the time being.

  • The equivalent spawn_unchecked function in std is still flagged as unstable.
  • It is possible to work around the 'static bound using unsafe code.

Once spawn_unchecked lands on the stable Rust channel, we can revisit this question.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-tokio Area: The main tokio crate C-feature-request Category: A feature request. M-task Module: tokio/task
Projects
None yet
Development

No branches or pull requests

5 participants