yoshuawuyts / futures-concurrency

Structured concurrency operations for async Rust

Home Page:https://docs.rs/futures-concurrency

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Stream `merge` function calls `poll_next` again after returning `Poll::Ready(None)`

phil-opp opened this issue · comments

The Stream::poll_next should not be called again after it returned Ready(None), otherwise it "may panic, block forever, or cause other kinds of problems". The merge implementation does not seem to respect this.

For example, the following code will panic:

use futures_concurrency::stream::Merge;
use futures::StreamExt;

#[tokio::main(flavor = "current_thread")]
async fn main() {
    let create_stream = |count| futures::stream::unfold(count, |n| async move {
        if n > 1 {
            Some ((n, n - 1))
        } else {
            None
        }
    });
    let stream_1 = create_stream(10);
    let stream_2 = create_stream(10);
    
    let merged = (stream_1, stream_2).merge();
    println!("{:?}", merged.collect::<Vec<_>>().await);
}

The panic message is:

thread 'main' panicked at 'Unfold must not be polled after it returned Poll::Ready(None)'

Fusing the streams (i.e. create_stream(10).fuse()) fixes this, but this should not be needed, or enforced by the API.

commented

Oops, that's not good. I believe you already reported this a while back, so I apologize for the regression. Let's fix this ASAP.

commented

Okay, so I'm trying to repro it on the main branch, and the following test seems to pass without any problem:

    #[test]
    fn main() {
        block_on(async {
            let create_stream = |count| {
                futures::stream::unfold(count, |n| async move {
                    if n > 1 {
                        Some((n, n - 1))
                    } else {
                        None
                    }
                })
            };
            let stream_1 = create_stream(10);
            let stream_2 = create_stream(10);

            let merged = (stream_1, stream_2).merge();
            futures::StreamExt::collect::<Vec<_>>(merged).await;
            // merged.collect::<Vec<_>>().await;
        })
    }

I tried replacing future_lite::StreamExt with futures::StreamExt, and both seem to work. What version of futures-concurrency do you have in your Cargo.lock file?

Ah, I used the latest crates.io release, v7.0.0. I just retried with the latest master commit and it looks like the issue is fixed there.

I tried to pinpoint the commit and it looks like #96 fixed the issue.

commented

Okay! -- Seems like the right thing to do is then to publish a new release. On it!

Perfect, thanks a lot!