Skip to content
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

BufReader<impl AsyncWrite + AsyncSeek> panics when calling write_all and immediately seek #4875

Closed
hack3ric opened this issue Jul 30, 2022 · 8 comments
Labels
A-tokio Area: The main tokio crate C-bug Category: This is a bug. E-easy Call for participation: Experience needed to fix: Easy / not much E-help-wanted Call for participation: Help is requested to fix this issue. M-fs Module: tokio/fs M-io Module: tokio/io

Comments

@hack3ric
Copy link

hack3ric commented Jul 30, 2022

Version
tokio v1.19.2 and v1.20.1
tokio-util v0.7.3

Platform
macOS 12.3 x86_64

Description
The following code panics:

use std::io::SeekFrom;
use tokio::fs::File;
use tokio::io::{self, AsyncSeekExt, AsyncWriteExt, BufReader};

#[tokio::main]
async fn main() -> io::Result<()> {
    let mut x = BufReader::new(File::create("test").await?);
    x.write_all(b"test").await?;
    x.seek(SeekFrom::Start(0)).await?;
    Ok(())
}
thread 'main' panicked at 'must wait for poll_complete before calling start_seek', /Users/hacker/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.20.0/src/fs/file.rs:570:28
stack backtrace:
   0: std::panicking::begin_panic
             at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/panicking.rs:616:12
   1: <tokio::fs::file::File as tokio::io::async_seek::AsyncSeek>::start_seek
             at /Users/hacker/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.20.0/src/fs/file.rs:570:28
   2: <tokio::io::util::buf_reader::BufReader<R> as tokio::io::async_seek::AsyncSeek>::poll_complete
             at /Users/hacker/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.20.0/src/io/util/buf_reader.rs:242:17
   3: <&mut T as tokio::io::async_seek::AsyncSeek>::poll_complete
             at /Users/hacker/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.20.0/src/io/async_seek.rs:56:13
   4: <tokio::io::seek::Seek<S> as core::future::future::Future>::poll
             at /Users/hacker/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.20.0/src/io/seek.rs:49:25
   5: test_mlua::main::{{closure}}
             at ./src/main.rs:9:31
   6: <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll
             at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/core/src/future/mod.rs:91:19
   7: tokio::park::thread::CachedParkThread::block_on::{{closure}}
             at /Users/hacker/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.20.0/src/park/thread.rs:263:54
   8: tokio::coop::with_budget::{{closure}}
             at /Users/hacker/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.20.0/src/coop.rs:102:9
   9: std::thread::local::LocalKey<T>::try_with
             at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/thread/local.rs:445:16
  10: std::thread::local::LocalKey<T>::with
             at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/std/src/thread/local.rs:421:9
  11: tokio::coop::with_budget
             at /Users/hacker/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.20.0/src/coop.rs:95:5
  12: tokio::coop::budget
             at /Users/hacker/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.20.0/src/coop.rs:72:5
  13: tokio::park::thread::CachedParkThread::block_on
             at /Users/hacker/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.20.0/src/park/thread.rs:263:31
  14: tokio::runtime::enter::Enter::block_on
             at /Users/hacker/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.20.0/src/runtime/enter.rs:152:13
  15: tokio::runtime::thread_pool::ThreadPool::block_on
             at /Users/hacker/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.20.0/src/runtime/thread_pool/mod.rs:90:9
  16: tokio::runtime::Runtime::block_on
             at /Users/hacker/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.20.0/src/runtime/mod.rs:484:43
  17: test_tokio::main
             at ./src/main.rs:10:5
  18: core::ops::function::FnOnce::call_once
             at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library/core/src/ops/function.rs:248:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
@hack3ric hack3ric added A-tokio Area: The main tokio crate C-bug Category: This is a bug. labels Jul 30, 2022
@hack3ric hack3ric changed the title BufReader<impl AsyncWrite> panics when calling write_all and immediately seek BufReader<impl AsyncWrite + AsyncSeek> panics when calling write_all and immediately seek Jul 30, 2022
@Darksonn
Copy link
Contributor

One way to fix this is to have BufReader flush the IO resource before attempting to seek.

@Darksonn
Copy link
Contributor

The documentation for AsyncSeek specifies that an error should be returned if the seek cannot be started. It is incorrect for File to panic in this situation.

@Darksonn Darksonn added E-help-wanted Call for participation: Help is requested to fix this issue. E-easy Call for participation: Experience needed to fix: Easy / not much M-fs Module: tokio/fs M-io Module: tokio/io labels Aug 10, 2022
@sebpuetz
Copy link
Contributor

I was looking into this issue and opened a PR at #4897 to change the panic to an error.

Although the File impl seems incorrect, the behavior between BufReader and BufWriter's poll_complete impl is not consistent, i.e. BufWriter flushes its buffer before seeking:

// Flush the internal buffer before seeking.
ready!(self.as_mut().flush_buf(cx))?;

Do you think it makes sense to also change BufReader to flush the underlying writer before attempting to seek? It would probably make the error in the snippet provided by @hack3ric less surprising

@Darksonn
Copy link
Contributor

Actually, I realize now that BufReader will not be able to flush before seeking. We don't require AsyncWrite for AsyncSeek to be implemented on BufReader. If we make any further changes, it should probably be making File::start_seek work even if there is an active operation (other than a seek, that is).

@sebpuetz
Copy link
Contributor

Looking into File::start_seek I don't see an obvious change that would make the function work on a File busy with read / write. Conceptually, the seek operation needs to be queued but File's state only has a single slot that's already occupied by the pending operation.

Even if the JoinHandle in State::Busy was tagged with the type of pending operation (i.e. make it available outside the running blocking task), we wouldn't be able to start the seek and appropriately communicate that to the caller - we'd still have to return an error that we were unable to submit the operation

@Darksonn
Copy link
Contributor

If we were to do that, then we would need to add an additional field to Busy to track that a seek should be started whenever the currently busy operation finishes. The poll_complete method (and probably poll_flush too) should then wait for the busy operation to complete, then submit the seek operation, then wait for the seek operation to complete.

@jacobsimpson
Copy link

It looks like the PR above (#4897) has been merged. When I run the sample code from the initial post against version 1.21.0, it now comes back as an error result from the seek line, instead of a panic. Is this issue considered fixed?

@Darksonn
Copy link
Contributor

Darksonn commented Oct 4, 2022

Yes, thanks for pointing that out.

@Darksonn Darksonn closed this as completed Oct 4, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-tokio Area: The main tokio crate C-bug Category: This is a bug. E-easy Call for participation: Experience needed to fix: Easy / not much E-help-wanted Call for participation: Help is requested to fix this issue. M-fs Module: tokio/fs M-io Module: tokio/io
Projects
None yet
Development

No branches or pull requests

4 participants