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 killable future combinator #696
Conversation
If you think it's worth including a non-sync version as well, let me know and I'll add that. IMO the tiny perf difference is probably not worth the additional complexity. |
This seems pretty slick to me! This form of cancellation feels like it's common enough in other libraries that it seems worthwhile to include here as well. I do think though yeah if we have a |
At a high level.
Going in more detail, I'm not sure about either the implementation or the API. The implication of I would say either the implementation should change or the API should not allow constructing a |
@carllerche I need to be able to construct a I do agree, though, that it's misleading and user might think they could use one R.E. Naming: bikeshed away. I think I still personally prefer |
How would you support other types? The cancellation path only knows how to return |
An enum.
Should this API be tied to the executor then? It could be an additional fn on the I don't have strong opinions either way. I'm mostly just brainstorming here. |
|
To be clear, I don't necessarily think my idea to support more than |
Yeah it seems like a reasonable idea. |
Another question is whether future has to be canceled when KillHandle/CancelHandle is dropped. This is useful if you keep a handle in some structure just to cancel the future when not needed anymore. And that structure may occasionally drop unexpectedly (e.g. on panic). Currently, I use often use |
@tailhook I'd personally be opposed to cancelling on on drop of the |
Well, you have to be careful both ways. You're likely to have a memory leak if CancelHandle is dropped without cancelation. Can you describe your real life use case? |
I just use this to kill futures early or to kill a future from another future deep inside without plumbing through some kind of shared flag. In the first case, the future would still be expected to complete normally, and in the second case, it doesn't make any sense why an inner future dropping should kill the outer one. |
@cramertj I wonder if all this is evidence that we may want to let this bake externally first? That way we could iterate on it elsewhere and bring it in once it's stabilized? |
@alexcrichton I'd be happy to throw it up in its own repo somewhere, but I'm not sure we'll get more useful feedback from having it there than we would by continuing to discuss here (or on IRC or something). |
IMO we should fix the "out of crate" discover ability problem. I would suggest taking the following two steps:
|
@cramertj it's true yeah, seems like a good idea though to have a piece of documentation somewhere listing extensions for anyone interested. |
src/future/killable.rs
Outdated
/// When `kill` is called on `handle`, this future will complete | ||
/// immediately. If `kill` has already been called, the future | ||
/// will complete immediately without making any further progress. | ||
pub fn new(future: T, handle: &KillHandle) -> Self { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
docs here are tabs
src/future/killable.rs
Outdated
/// Creates a new `KillHandle` which can be used to kill a running future. | ||
/// | ||
/// This function is usually paired with one or more calls to `Killable::new`. | ||
pub fn new() -> Self { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and here
Updated in response to comments. |
src/future/killable.rs
Outdated
} | ||
|
||
impl<T> Future for Killable<T> where T: Future<Item = ()> { | ||
type Item = (); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Item
could be Option<T::Item>
where when canceled returns None
. A separate type to convey more semantic meaning makes sense to me as well, but would be missing basically all the useful stuff from Option
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of my use cases for this have been with futures that were side-effect only, usually being given to a spawn
or similar. However, it seems that both you and @carllerche have other use cases in mind, so I'll change to use Result<T::Item, Killed>
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't think of something in particular but there isn't much reason not to provide the generality
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose at that point we could also provide a Stream
impl, but I'm going to omit it for the time being.
The reason to avoid Future<Item = Option/Result<T>>
was due to additional complexity for the user, who now will have to map(|_| ())
etc. which may or may not be obvious.
@cramertj would you still be looking to merge in this repo? Or would you be ok having it out of repo for a bit? |
@alexcrichton Up to you-- my preference would be to try and get in good shape to be merged into |
@cramertj well, it seems like discoverability is a general problem. Do you not think that my suggestion above would aid in that regards? |
@carllerche I disagree that discoverability is the main problem here. I think the main problem is that we need a place to incubate standard, well-motivated combinators without tying them to futures/tokio stability guarantees. I think a better solution to this would be to have a dedicated futures/tokio combinator library that can frequently release breaking changes. Breaking releases to combinators won't result in the interoperability problems that frequent breaking changes to the FWIW, I think this applies as well to methods like |
@cramertj I think you hit the nail on the head there with the problem of tying combinators to the stability of the This sounds a lot like |
@alexcrichton Yup, +1 for |
Yes, I'm definitely for creating another crate and moving stuff there. IMO the core traits already have too many fns on them, which causes problems for iteration. I didn't know moving stuff to another crate was on the table, so this makes me happy. |
@cramertj would you be willing to maintain such a crate? I'm not sure we have a great place for it right now... |
Note for folks reading: futures-tools has been created. |
Is there a reason the crate isn't included in this git repository? The cargo workspace feature would make it easy. It seems that if the plan is to migrate code between the two repos (move a bunch of stuff from here into -tools in futures 0.2 then incrementally "promote" as APIs stabilize) it would be easiest to do so in one repository. |
@carllerche That approach sounds good to me. Side question: do we ever want to promote? For that matter, we could make One downside of this approach would be that it might not be clear to end users when a breaking change to |
Perhaps discussion for a futures-util crate should be promoted to a dedicated issue at this point :) |
@carllerche @alexcrichton @aturon I've opened #711 for discussion. |
@cramertj want to retarget at 0.2 to merge? |
@cramertj what's the status here? |
Is |
Any chance to get this in for 0.3? |
Closing, opened #1079 targeting 0.3. |
Closes #693