-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
A running subscription without a yield never gets killed if using iced_native::subscription::unfold. #1348
Comments
What do you mean by yielding? If by "without a yield" you mean never awaiting and returning control to the async runtime, then that would be against the contract of a
|
Sorry yes, by yield I do mean awaiting. However I also never block inside my async function, I can even use awaits inside of the main core of my function, but if I neglect to do it when transition to the "finished" state of my stream, now I'm stuck busy looping, when I would have expected returning a command to pull me out of that state. Probably not useful to discuss without a concrete example, so here's pretty much exactly what I've implemented: (Sorry, I don't have the code in front of me, and went from memory, there might be some errors, but the control flow is the important bit here anyways) enum UploadState {
Creating,
Uploading(f32),
Finished,
}
// assume this is in my app impl or something
fn subscription(&mut self) -> Subscriotion<Message> {
// assume the impl sets this when it gets the Message::Finished message
if self.finished {
return Subscription::none();
}
subscription::unfold(UploadState::Creating, async |state| {
match state {
UploadState::Creating => (Some(Message::Uploading(0.)), UploadState::Uploading(0.)),
UploadState::Downloading(progress) => {
if (progress < 100.) {
progress += 1.;
tokio::sleep(Duration::from_millis(100);
(Some(Message::Uploading(progress)), UploadState::Uploading(progress))
} else {
(Some(Message::Uploading(100.)), UploadState::Finished)
}
}
// This will now busy loop forever, unless I put an await in this match arm
UploadState::Finished => (Some(Message::Finished), UploadState::Finished)
}
})
} A naive expectation would be that if a message is returned that would return control to the runtime somehow, after all a message should get sent out, but it doesn't, and the message gets queued until I put an await inside the final match arm. I don't think this is good behaviour because it means you have to yield arbitrarily to get a message out, even if your closure does no sync blocking. |
A In other words, when a Therefore, your However, the application should eventually process the But keep in mind that your |
Perfect! Thank you for looking into this. |
Is there an existing issue for this?
Is this issue related to iced?
What happened?
(maybe should be a discussion, maybe should be an issue, I consider this a bug, even if just a documentation issue)
I created a subscription with
iced_native::subscription::unfold
, and found it to run forever, even when the message indicating it finished got updated and the subscription function of theApplication
was called. After lengthy investigation, I found that the async function needs to yield at least once after the state is updated to get killed.What is the expected behavior?
I would have expected that the subscription would be killed once the main application state got updated. There was nothing in the documentation to point me to suspect that I needed to yield to release the subscription and let it be killed.
IMO this definitely should be somewhere in the documentation, it's a very easy issue to hit, and not necessarily obvious. Ideally there should be a yield somewhere in the system (maybe after every iteration or when the returned Message is None?), but am not familiar enough with future streams to offer a good solution.
Version
0.4
Operative System
Linux
Do you have any log output?
No response
The text was updated successfully, but these errors were encountered: