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

A recent change to beta & nightly channels broke the tokio-core build #40551

Closed
carllerche opened this issue Mar 15, 2017 · 8 comments
Closed
Labels
regression-from-stable-to-beta Performance or correctness regression from stable to beta. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@carllerche
Copy link
Member

I just noticed that tokio-core builds started to fail on Travis when using the beta and nightly channels. We are going to change our code to fix it, but I wanted to report the regression:

The build: https://travis-ci.org/tokio-rs/tokio-core/jobs/211426672

The function:

    fn read_buf<B: BufMut>(&mut self, buf: &mut B) -> Poll<usize, io::Error> {
        if let Async::NotReady = <TcpStream>::poll_read(self) {
            return Err(::would_block())
        }
        let mut bufs: [_; 16] = Default::default();
        unsafe {
            let n = buf.bytes_vec_mut(&mut bufs);
            match self.io.get_ref().read_bufs(&mut bufs[..n]) {
                Ok(n) => {
                    buf.advance_mut(n);
                    Ok(Async::Ready(n))
                }
                Err(e) => {
                    if e.kind() == io::ErrorKind::WouldBlock {
                        self.io.need_write();
                    }
                    Err(e)
                }
            }
        }
    }

The error:

error[E0499]: cannot borrow `*buf` as mutable more than once at a time
   --> src/net/tcp.rs:521:21
    |
518 |             let n = buf.bytes_vec_mut(&mut bufs);
    |                     --- first mutable borrow occurs here
...
521 |                     buf.advance_mut(n);
    |                     ^^^ second mutable borrow occurs here
...
532 |     }
    |     - first borrow ends here
@alexcrichton
Copy link
Member

My guess is that this is fallout from #40319 in order to fix the soundness hole exposed by #40288.

This was not detected in the crater run on #40319 because this code wasn't published on crates.io yet (it was just in git).

@rust-lang/compiler can you confirm though? If it's something else we may wish to track this regression.

@alexcrichton alexcrichton added regression-from-stable-to-beta Performance or correctness regression from stable to beta. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Mar 15, 2017
@nikomatsakis
Copy link
Contributor

nikomatsakis commented Mar 15, 2017

@alexcrichton I agree with your diagnosis that this the most likely cause, though it's hard to say for sure (without trying the builds)

@nikomatsakis
Copy link
Contributor

What is the signature of bytes_vec_mut?

@carllerche
Copy link
Member Author

This is the bytes_vec_mut fn:

    unsafe fn bytes_vec_mut<'a>(&'a mut self, dst: &mut [&'a mut IoVec]) -> usize {
        if dst.is_empty() {
            return 0;
        }

        if self.has_remaining_mut() {
            dst[0] = self.bytes_mut().into();
            1
        } else {
            0
        }
    }

From here: https://github.com/carllerche/bytes/blob/master/src/buf/buf_mut.rs#L189

@arielb1
Copy link
Contributor

arielb1 commented Mar 15, 2017

Looks like #40319. Now let me see whether the regression is correct.

@arielb1
Copy link
Contributor

arielb1 commented Mar 15, 2017

Looks like an NLL problem:

    fn read_buf<B: BufMut>(&mut self, buf: &mut B) -> Poll<usize, io::Error> {
        if let Async::NotReady = <TcpStream>::poll_read(self) {
            return Err(::would_block())
        }
        let mut bufs: [_; 16] = Default::default();
        unsafe {
            let n = buf.bytes_vec_mut(&mut bufs); // `buf` is borrowed here
            match
                self.io.get_ref().read_bufs(&mut bufs[..n]) // `buf` needs to stay valid until here
            {
                Ok(n) => {
                    buf.advance_mut(n); // ... but because regions are lexical, is also borrowed here
                    Ok(Async::Ready(n))
                }
                Err(e) => {
                    if e.kind() == io::ErrorKind::WouldBlock {
                        self.io.need_write();
                    }
                    Err(e)
                }
            }
        }
    }

If you invalidated buf between the 2 lines, that would have caused trouble.

You should be able to fix it by placing the borrow within its own scope:

    fn read_buf<B: BufMut>(&mut self, buf: &mut B) -> Poll<usize, io::Error> {
        if let Async::NotReady = <TcpStream>::poll_read(self) {
            return Err(::would_block())
        }
        let mut bufs: [_; 16] = Default::default();
        unsafe {
            let result /* : io::Result<usize> - no borrowed content */ = {
                let n = buf.bytes_vec_mut(&mut bufs);
                self.io.get_ref().read_bufs(&mut bufs[..n])
                // `buf` is only borrowed within ^
            };
            match result {
                Ok(n) => {
                    buf.advance_mut(n);
                    Ok(Async::Ready(n))
                }
                Err(e) => {
                    if e.kind() == io::ErrorKind::WouldBlock {
                        self.io.need_write();
                    }
                    Err(e)
                }
            }
        }
    }

@alexcrichton
Copy link
Member

Ah yes and to be clear this has been fixed and published in the style @arielb1 mentioned.

If this is classified as an acceptable regression (e.g. just a soundness fix) please feel free to close!

@nikomatsakis
Copy link
Contributor

Looks correct to me, thanks @arielb1 for looking in depth.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
regression-from-stable-to-beta Performance or correctness regression from stable to beta. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

4 participants