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 providing a Spawn trait #313

Closed
carllerche opened this issue Dec 29, 2016 · 11 comments
Closed

Consider providing a Spawn trait #313

carllerche opened this issue Dec 29, 2016 · 11 comments

Comments

@carllerche
Copy link
Member

Given the number of different types of executors for driving futures, it seems worth providing a trait representing the ability to spawn a task with a future.

One option:

/// Value that can spawn a future
///
/// On spawn, the executor takes ownership of the future and becomes responsible
/// to call `Future::poll()` whenever a readiness notification is raised.
pub trait Spawn<T: Future<Item = (), Error = ()>> {

    /// Spawns a future to run on this `Spawn`.
    ///
    /// This function will return immediately, and schedule the future `f` to
    /// run on `self`. The details of scheduling and execution are left to the
    /// implementations of `Spawn`.
    fn spawn_detached(&self, f: T);
}

Implementation

The reason the function is spawn_detached is that there also are variants built on top of this primitive that return a future representing the completion of the spawned future:https://github.com/carllerche/futures-spawn/blob/inference/src/lib.rs#L21-L73

SpawnHelper exists to make rustc happy in some cases. There could be better ways of implementing this entire abstraction so it is up for discussion.

I also updated CPU pool to work with this Spawn trait: https://github.com/carllerche/futures-threadpool

Relates to: tokio-rs/tokio-core#57, tokio-rs/tokio-core#77

@Diggsey
Copy link
Contributor

Diggsey commented Dec 31, 2016

This also allows implementing some more concurrency primitives, like a futures-based Mutex: https://github.com/Diggsey/futures-spawn/tree/sync

(Allocations could be reduced a little, but it's using purely safe code)

@alexcrichton
Copy link
Member

Some notes from a discussion with @carllerche today:

  • The Spawn trait seems good as-is
  • We may wish to add Future::background(self, &S: Spawn<Self>) to ergonomically run in the background
  • Methods could be added to oneshot modules for "spawn with a handle", e.g. futures::{sync, unsync}::oneshot::{spawn, spawn_fn}.
  • Everything but Spawn::spawn_detached may wish to panic if the S: Spawn refuses the acceptance of the future.

alexcrichton added a commit that referenced this issue Apr 21, 2017
This commit adds a new trait to the `future` module, `Spawn`. This trait has
only one method:

    trait Spawn<F: Future<Item = (), Error = ()>> {
        fn spawn(&self, f: F);
    }

The purpose of this trait is to unify the various executors found throughout the
ecosystem. Crates which require the ability to spawn futures will now have the
option ot operate generically over all `Spawn` instances instead of a particular
executor.

A `Future::background` method was also added to ergonomically pass a future to
an executor and run it in the background. The two `oneshot` modules (sync and
unsync) also grew `spawn` and `spawn_fn` functions which will spawn a future
onto an executor, returning a handle to the resulting future. This can be used
to spawn work onto a specific executor and still get notified when the work
itself is completed.

Finally, an implementation of the `Spawn` trait was added to `CpuPool`. Due to
the differences in unwinding/panic semantics, though, the `CpuPool::spawn`
method which previously existed was not deprecated.

Closes #313
@alexcrichton
Copy link
Member

I've opened a PR for this change: #455

@alexcrichton
Copy link
Member

also cc @antoyo, this is what we were talking about in Mexico a few weeks ago.

@antoyo
Copy link

antoyo commented Apr 21, 2017

Thanks for your great work.
I'm keen to use this new feature.

@antoyo
Copy link

antoyo commented Apr 22, 2017

I played a bit with this new Spawn trait and futures-glib and I'm wondering how it will work with the networking code.
For instance, TcpStream needs a Handle in tokio-core while TcpStream in futures-glib requires a MainContext that is attached to a Source
I don't understand how we can make that generic with the new Spawn trait because, in the futures-glib case, we actually need a MainContext to call Source::attach(MainContext) so a Spawn would not be sufficient.
If we take hyper as an example, the TcpStream can either come from futures-glib or tokio.
Can you tell me how this should work?
(I can show you some code if needed.)
Thanks for your help.

alexcrichton added a commit that referenced this issue Apr 24, 2017
This commit adds a new trait to the `future` module, `Spawn`. This trait has
only one method:

    trait Spawn<F: Future<Item = (), Error = ()>> {
        fn spawn(&self, f: F);
    }

The purpose of this trait is to unify the various executors found throughout the
ecosystem. Crates which require the ability to spawn futures will now have the
option ot operate generically over all `Spawn` instances instead of a particular
executor.

A `Future::background` method was also added to ergonomically pass a future to
an executor and run it in the background. The two `oneshot` modules (sync and
unsync) also grew `spawn` and `spawn_fn` functions which will spawn a future
onto an executor, returning a handle to the resulting future. This can be used
to spawn work onto a specific executor and still get notified when the work
itself is completed.

Finally, an implementation of the `Spawn` trait was added to `CpuPool`. Due to
the differences in unwinding/panic semantics, though, the `CpuPool::spawn`
method which previously existed was not deprecated.

Closes #313
@carllerche
Copy link
Member Author

@antoyo I'm not really understanding the problem. Code would help (me at least)

@alexcrichton
Copy link
Member

@antoyo oh so in futures-glib you'd have:

impl<F> Spawn<F> for Executor
    where F: Future<Item = (), Error = ()> + 'static,
{ 
    // ...
}

That'd just abstract the spawning of futures, but wouldn't allow abstracting over I/O. For I/O the tokio-io crate would be used with AsyncRead and AsyncWrite, similar to what hyper does today. That is, if the crate doesn't require a concrete I/O object, it'd operate generically over those two traits. For futures-glib this just means that those traits are implemented for the relevant I/O objects.

Now the tricky piece comes in when you actually want to do something concrete! For example issue a TCP connection in Hyper. For this Hyper has a trait where the default implementation uses a Handle from tokio-core. There's unfortunately no way to abstract that generically, but the idea is that local crates would follow a similar pattern as to hyper. Providing a default implementation with a specific runtime (e.g. futures-glib or tokio-core) but a trait to slot in new implementations.

Does that make sense?

@antoyo
Copy link

antoyo commented Apr 25, 2017

Yeah, thanks.
I'll play with that in the next weeks.

alexcrichton added a commit that referenced this issue May 3, 2017
This commit adds a new trait to the `future` module, `Spawn`. This trait has
only one method:

    trait Spawn<F: Future<Item = (), Error = ()>> {
        fn spawn(&self, f: F);
    }

The purpose of this trait is to unify the various executors found throughout the
ecosystem. Crates which require the ability to spawn futures will now have the
option ot operate generically over all `Spawn` instances instead of a particular
executor.

A `Future::background` method was also added to ergonomically pass a future to
an executor and run it in the background. The two `oneshot` modules (sync and
unsync) also grew `spawn` and `spawn_fn` functions which will spawn a future
onto an executor, returning a handle to the resulting future. This can be used
to spawn work onto a specific executor and still get notified when the work
itself is completed.

Finally, an implementation of the `Spawn` trait was added to `CpuPool`. Due to
the differences in unwinding/panic semantics, though, the `CpuPool::spawn`
method which previously existed was not deprecated.

Closes #313
@antoyo
Copy link

antoyo commented May 10, 2017

I started to look at this, but I have not managed to understand how I could do that.
For instance, in hyper, the handle is sent to bind_client() here.
What to do about that?
In tokio-proto, bind_client needs a Handle.

I also have a related question.
I was looking into tokio-uds to see how to do the same but it seems tied to mio.
So is it the kind of crate that needs to be completely rewritten for futures-glib?
Thanks for your help.

P.S.: do you think we should continue this discussion elsewhere? If yes, where do you suggest?

@alexcrichton
Copy link
Member

@antoyo ah yes so tokio-proto will need to be updated first here, as the only reason it needs a Handle is actually to spawn something. Once that's implemented then Hyper would also be generic over a Spawn implementation to be usable with futures-glib.

Also yeah unfortunately for now tokio-uds will need to be duplicated for futures-glib, I don't believe it's possible for it to share code with the glib-based version.

Also no worries continuing discussion here! Fine by me :)

alexcrichton added a commit that referenced this issue May 24, 2017
This commit adds a new trait to the `future` module, `Executor`. This trait has
only one method:

    trait Execute<F: Future<Item = (), Error = ()>> {
        fn execute(&self, f: F) -> Result<(), ExecuteError<F>>;
    }

The purpose of this trait is to unify the various executors found throughout the
ecosystem. Crates which require the ability to spawn futures will now have the
option ot operate generically over all `Executor` instances instead of a
particular executor.

A `Future::background` method was also added to ergonomically pass a future to
an executor and run it in the background. The two `oneshot` modules (sync and
unsync) also grew `spawn` and `spawn_fn` functions which will spawn a future
onto an executor, returning a handle to the resulting future. This can be used
to spawn work onto a specific executor and still get notified when the work
itself is completed.

Finally, an implementation of the `Executor` trait was added to `CpuPool`. Due
to the differences in unwinding/panic semantics, though, the `CpuPool::spawn`
method which previously existed was not deprecated.

Closes #313
alexcrichton added a commit that referenced this issue May 25, 2017
This commit adds a new trait to the `future` module, `Executor`. This trait has
only one method:

    trait Execute<F: Future<Item = (), Error = ()>> {
        fn execute(&self, f: F) -> Result<(), ExecuteError<F>>;
    }

The purpose of this trait is to unify the various executors found throughout the
ecosystem. Crates which require the ability to spawn futures will now have the
option ot operate generically over all `Executor` instances instead of a
particular executor.

The two `oneshot` modules (sync and unsync) also grew `spawn` and `spawn_fn`
functions which will spawn a future onto an executor, returning a handle to the
resulting future. This can be used to spawn work onto a specific executor and
still get notified when the work itself is completed.

Finally, an implementation of the `Executor` trait was added to `CpuPool`. Due
to the differences in unwinding/panic semantics, though, the `CpuPool::spawn`
method which previously existed was not deprecated.

Closes #313
alexcrichton added a commit that referenced this issue May 25, 2017
This commit adds a new trait to the `future` module, `Executor`. This trait has
only one method:

    trait Execute<F: Future<Item = (), Error = ()>> {
        fn execute(&self, f: F) -> Result<(), ExecuteError<F>>;
    }

The purpose of this trait is to unify the various executors found throughout the
ecosystem. Crates which require the ability to spawn futures will now have the
option ot operate generically over all `Executor` instances instead of a
particular executor.

The two `oneshot` modules (sync and unsync) also grew `spawn` and `spawn_fn`
functions which will spawn a future onto an executor, returning a handle to the
resulting future. This can be used to spawn work onto a specific executor and
still get notified when the work itself is completed.

Finally, an implementation of the `Executor` trait was added to `CpuPool`. Due
to the differences in unwinding/panic semantics, though, the `CpuPool::spawn`
method which previously existed was not deprecated.

Closes #313
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

No branches or pull requests

4 participants