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

Add a generic Executor trait #455

Merged
merged 1 commit into from
May 25, 2017
Merged

Add a generic Executor trait #455

merged 1 commit into from
May 25, 2017

Conversation

alexcrichton
Copy link
Member

@alexcrichton alexcrichton commented Apr 21, 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
Copy link
Member Author

I was pretty happy with everything until I ended up hitting CpuPool and the differences in panicking semantics. Overall though I'm pretty happy with how this turned out. I'd still like to play around with this in tokio-core though before landing.

r? @carllerche

Copy link
Member

@carllerche carllerche left a comment

Choose a reason for hiding this comment

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

Looks good at first glance. Just the Result return value comment.

/// This function will return immediately, and schedule the future `future`
/// to run on `self`. The details of scheduling and execution are left to
/// the implementations of `Spawn`.
fn spawn(&self, future: F);
Copy link
Member

Choose a reason for hiding this comment

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

This function should return Result<(), Error<F>> to allow the executor to return the future if it fails to spawn it.

The docs should say that returning the future on error is "best effort" and returning Ok is not a guarantee that the future will be executed.

Copy link
Member Author

Choose a reason for hiding this comment

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

Oops forgot that! I've updated with the error

@carllerche
Copy link
Member

Originally experimentation: https://github.com/carllerche/futures-spawn, though the latest is on a branch https://github.com/carllerche/futures-spawn/tree/restructure.

Once this PR is merged, I need to remember to deprecate that crate (leaving this comment to remind me!)

@bluetech
Copy link

bluetech commented May 3, 2017

I saw this PR, and wanted to see if I understand the goal correctly (I hope you don't mind).

Consider a Hyper tokio server for example. Currently, hyper depends on tokio-core and tokio-proto -> tokio-core. If I understand this trait correctly it can help with removing this dependency -- instead of taking Core/Handle, it will take this trait. Then it should be possible to run a Hyper server with e.g.

  • A tokio-core/mio event loop
  • A cached thread pool (CpuPool)
  • Any other Spawn implementation

by just changing a few lines in the application code (the library is agnostic).

To test this assumption and check if it's viable, I tried to do the following. First in Hyper:

  • Removed all client/ code (irrelevant for this discussion)
  • Removed the helper Server trait, leaving only Http to use directly (Server uses tokio-core so I'm just removing it from consideration for now).
  • Replaced all uses of Handle with an appropriate Spawn bound

and then in tokio-proto:

  • Removed tcp_client/tcp_server - not used by hyper so ignoring that for now.
  • Removed all of mentions of old_io (backward compatibility support for tokio_core::io, replaced by tokio_io).
  • Replaced all uses of Handle with an appropriate Spawn bound

In the last bullet it got messy and I ran out of time to play with it, so I didn't reach a conclusion. But I think that it can be done - am I correct?

If it would be possible to statically parametrize a Hyper server (as an example) over a Spawn trait, and it would all work flawlessly when they are switched, I think it would really show the strength of the abstractions here - it would be quite a feat :)

@alexcrichton
Copy link
Member Author

@bluetech you are indeed spot on! That's the intended use case, decoupling crates like Hyper from tokio-core specifically and allowing them to interoprate more gracefully with other crates like futures-glib. The Spawn trait isn't sufficient for everything tokio-proto and hyper use, but everything minus the conveniences are sufficient to use Spawn (or at least that's the intention).

@leoyvens
Copy link
Contributor

Name conflict with the Spawn struct seems bad. Executor seems like the right name here, we should steal that name from the current Executor trait and rename that (to Dispatch perhaps?). It is an unfortunate name anyway since when the docs say executor they mean something that spawns futures rather than something that actually implements the current Executor trait.

Going maybe out of scope from this PR, the current Executor starts looking like an odd duck since it is an executor but doesn't implement Spawn. Maybe we should make the current Executor into a struct that implements Spawn trait, and deprecate the execute method from the Spawn struct. It dosen't fit well anyways since task.execute(executor) reads backwards.

The renames would go like:
Executor trait -> Dispatch trait
Spawn trait -> Executor trait

Refactoring Dispatch:
Create a Dispatcher struct that wraps Arc<Dispatch>.
Dispatcher implements new Executor trait (the trait proposed here as Spawn).
Deprecate Spawn::execute from the Spawn struct.

@alexcrichton
Copy link
Member Author

@leodasvacas that sounds pretty reasonable to me! I definitely think the current Executor trait is basically deprecated and should likely be replaced by this.

@carllerche what do you think about the naming here?

@carllerche
Copy link
Member

Renaming seems fine. I would rename the trait fn to match the trait name.

Executor is deprecated, so reusing the name seems fine.

Other names:

  • Schedule
  • Run.

I don't have a huge preference when it comes to the actual name. I do think avoiding the Spawn conflict seems good.

@alexcrichton alexcrichton changed the title Add a generic Spawn trait Add a generic Executor trait May 24, 2017
@alexcrichton
Copy link
Member Author

Ok I've updated to Executor. r? @carllerche?

@leoyvens
Copy link
Contributor

Nice! The fate of "OldExecutor" will be decided later? background is ergonomic and I'm not against it, but there are some points to be discussed:

  • It's offering both a panicking and non-panicking version of the same API, std generally avoids this with a preference towards non-panicking.
  • Naming: I think we should just call it execute_on for consistency and neutrality (the executor may block for all we know).

Copy link
Member

@carllerche carllerche left a comment

Choose a reason for hiding this comment

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

Seems mostly good, I had one comment re: panicking (aka, I also think it should be avoided). I personally am leaning towards just dropping the error in the "convenience" function.

where E: Executor<Self>,
Self: Future<Item = (), Error = ()> + Sized,
{
executor.execute(self).expect("failed to execute future in background")
Copy link
Member

Choose a reason for hiding this comment

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

I agree that we should avoid panicking. An executor being unable to execute a task is a very plausible situation to get into.

The two options are for background to return a Result OR for it to silently drop the error.

I'm leaning towards just dropping the error because:

a) background is a convenience API
b) There is no guarantee that an executor returns an error on execution failure. It's completely acceptable for the executor to silently drop the task as well.

@alexcrichton
Copy link
Member Author

Ok I've updated to just delete background for now.

@leodasvacas I'd like to deprecate the old Executor trait as part of this. I meant to investigate your suggestion a bit more but I'll go do that now.

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
Copy link
Member Author

Ok I've commented that we're likely to deprecate executor::Executor and Spawn::execute but I haven't done so just yet. I don't think we have tons of users of that trait so I don't want to agonize too much over names just yet. in that case I'm going to merge!

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

4 participants