-
Notifications
You must be signed in to change notification settings - Fork 13k
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 TimeoutReceiver to allow recv()ing with a timeout #13862
Conversation
I would have expected this to be done through selection with a timer channel itself, rather than encapsulating it in a |
Hmm ideally yes it should select either the timer channel or the other channel, like in Go you can select multiple and the first to send something will trigger that part. I have no idea how to achieve this in rust though so I resorted to this loop which seems kinda ugly and not very performant but does the job. If you have some pointers I am happy to try and improve this though. |
I believe the |
I updated the code to use the stuff from the select macro, this way it now uses 0% cpu while waiting as opposed to slamming one core fully like the previous commit ;) Now regarding the usefulness of this PR, you could say that |
In general, we favor composability over specific use cases in libraries, one of the best examples of this being iterators. This seems like it falls into the bucket of a convenience function rather than serving a core purpose, so I would be inclined to have better documentation about timers and select. Currently the So, all in all, I would be more in favor of documenting how to receive with a timeout, which would introduce both timers and selection, instead of adding a new primitive. While this primitive is useful for some cases, it unfortunately requires many allocations for each |
With this patch:
Without: let (tx, rx) = channel();
let timer = Timer::new().unwrap();
let timeout = timer.oneshot(10000);
select!(
val = rx.recv() => println!("Received {}", val),
() = timeout.recv() => println!("timed out, no message received in 10 seconds"),
) On one hand I think it's a bit nicer with this TimeoutReceiver, it feels more rustic to me with the Option than with select!. But I agree it's not a massive difference, once you know how to do it :) So I guess I'll update the PR to just add a doc example then if that's ok, because I think that would have solved my problem as well. Just for my knowledge's sake however @alexcrichton would you mind expanding on what you said with regard to my patch allocating too much? AFAIK it doesn't allocate more than my example above, and I don't see how it could do less per recv(), maybe using a periodic timer vs a oneshot one, but that's not exactly the same behavior. |
Ah, sorry, by more allocate-y I meant in terms of continual receiving. The let (tx, rx) = channel();
let timer = Timer::new().unwrap();
let timeout = timer.period(10000);
loop {
select! {
val = rx.recv() => println!("Received {}", val),
() = timeout.recv() => println!("timed out, no message received in 10 seconds"),
}
} (in this case no allocations are done on the inner loop). That seems like a good idea to add a new example. I think at the top of |
I will send another PR with an example yes, but your code there is not really correct in that if you receive a message after 5seconds, the next message will only have 5seconds before a timeout occurs, since it doesn't seem possible to "reset" a periodic timer. With my version it really was 10seconds per message. This usually matters quite a lot IMO since if you build in a timeout you probably want to abort when it fires. |
Closing due to inactivity, but feel free to reopen with the example we talked about! |
Finally what I promised to do in #13862 /cc @alexcrichton
comment out disabled code
…ang#13863) Fixes rust-lang#13862 `missing_headers::check` is sometimes called from outside of a body (specifically, from `check_attributes`, where the LateContext's ParamEnv is not yet properly initialized for that item). Using that empty ParamEnv for trait solving things from within the body can then lead to various ICEs, like the linked issue where we have a const generic parameter `DMA_INST` without a `ConstArgHasType` bound in the ParamEnv so the const parameter has no type, which is normally not supposed to happen. We have the item's DefId so we can just get its ParamEnv/TypingEnv from there, and using that one for trait solving should be safe. changelog: none
Not sure if this is the best way to do this, or if there is already a way that I missed, but it comes in very handy when you want to avoid blocking forever.