Skip to content

Sequential async/await calls to AsyncWriteExt::write_all result in reordered futures #2978

Open
@alexmaco

Description

Version
all versions from 0.2.20 to 0.3.0 inclusive. Older versions not tested

Platform
Linux $HOSTNAME 4.15.0-118-generic #119-Ubuntu SMP Tue Sep 8 12:30:01 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
Linux $HOSTNAME 4.4.132+ #8 SMP Thu Aug 13 08:51:08 UTC 2020 armv7l GNU/Linux

Description

In a function that performs and awaits 2 calls to AsyncWriteExt::write_all in sequence to stdout, I would expect the data to show up in the same order on stdout.

However, the contents of data and \n in the write_out function below are sometimes reordered.

I tried this code:

use tokio::io::{self, AsyncBufReadExt, AsyncWriteExt};
use tokio::runtime;

fn main() -> Result<(), String> {
    let rt = runtime::Builder::new_current_thread()
        .build()
        .map_err(|e| format!("Failed to create runtime: {:?}", e))?;

    rt.block_on(async {
        let stdin = io::stdin();
        let buf_read = io::BufReader::new(stdin);
        let mut lines = buf_read.lines();
        while let Some(line) = lines
            .next_line()
            .await
            .map_err(|e| format!("Error reading from stdin: {:?}", e))?
        {
            match write_out(line.as_bytes()).await {
                Ok(()) => {}
                Err(e) => return Err(format!("Error printing output: {}", e)),
            };
        }

        Ok(())
    })
}

// The problem seems to be in this function
async fn write_out(data: &[u8]) -> io::Result<()> {
    let mut out = io::stdout();
    out.write_all(data).await?;
    out.write_all(b"\n").await?;
    Ok(())
}

Enabled features features = ["io-std", "io-util", "rt"]
I've also tried enabling the "process" feature and calling enable_io on the Builder, but the reorder issue is the same.

I invoke this as:

$ seq 1 10000 > test_lines
$ cat test_lines | target/release/test_bin > output_lines
$ diff -u test_lines output_lines

I would expect test_lines and output_lines to be identical. However the diff produces multiple deltas like this:

...
@@ -8801,8 +8801,8 @@
 8801
 8802
 8803
-8804
-8805
+88048805
+
 8806
 8807
 8808
@@ -8815,8 +8815,8 @@
 8815
 8816
 8817
-8818
-8819
+88188819
+
 8820
 8821
 8822
...

Running a debug version produces few or no differences, but running a release version usually produces tens of reorders over the 10k line sequence.

I've run into this issue while working on https://github.com/alexmaco/fpipe/ , and this bug is making that tool unreliable.

Please let me know how I can assist in debugging this, since the io-related tokio source I've read seems correct on first glance.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    A-tokioArea: The main tokio crateC-bugCategory: This is a bug.E-help-wantedCall for participation: Help is requested to fix this issue.M-ioModule: tokio/io

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions