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

BiLock assertion failure and segfault in --release mode (single-threaded!) #296

Closed
dwrensha opened this issue Dec 17, 2016 · 5 comments
Closed

Comments

@dwrensha
Copy link
Contributor

Consider the following program (which has been minimized from a much more complex situation):

# Cargo.toml
[package]
name = "crash"
version = "0.0.1"

[[bin]]
name = "crash"
path = "crash.rs"

[dependencies]
futures = "=0.1.6"
tokio-core = "=0.1.1"
mio-uds = "=0.6.0"
// crash.rs
extern crate futures;
extern crate tokio_core;
extern crate mio_uds;

use futures::{Async, Future, Poll};
use futures::sync::oneshot;
use tokio_core::io::Io;

enum State<W> {
    Writing(W, oneshot::Sender<()>),
    BetweenWrites(W),
    Empty,
}

pub struct WriteQueue<W>  {
    pending: Option<oneshot::Sender<()>>,
    state: State<W>,
}

impl <W> WriteQueue<W> {
    pub fn new(writer: W) -> WriteQueue<W>
    {
        let (complete, _) = futures::oneshot();
        WriteQueue {
            pending: Some(complete),
            state: State::BetweenWrites(writer),
        }
    }
}

enum IntermediateState<W> {
    WriteDone(W),
    StartWrite(oneshot::Sender<()>),
    Resolve,
}

impl <W> Future for WriteQueue<W> {
    type Item = ();
    type Error = ();

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        loop {
            let next: IntermediateState<W> = match self.state {
                State::Writing(_, _) => return Err(()),
                State::BetweenWrites(ref mut _writer) => {
                    let front = self.pending.take();
                    match front {
                        Some(complete) => {
                            IntermediateState::StartWrite(complete)
                        }
                        None => {
                            IntermediateState::Resolve
                        }
                    }
                }
                State::Empty => unimplemented!(),
            };

            match next {
                IntermediateState::WriteDone(_w) => unimplemented!(),
                IntermediateState::StartWrite(c) => {
                    let new_state = match ::std::mem::replace(&mut self.state, State::Empty) {
                        State::BetweenWrites(w) => {
                            State::Writing(w, c)
                        }
                        _ => unreachable!(),
                    };
                    self.state = new_state;
                }
                IntermediateState::Resolve => {
                    match ::std::mem::replace(&mut self.state, State::Empty) {
                        State::BetweenWrites(_w) => {
                            return Ok(Async::Ready(()))
                        }
                        _ => unreachable!(),
                    }
                }
            }
        }
    }
}

pub fn main() {
    let mut core = ::tokio_core::reactor::Core::new().unwrap();
    let (_, server_stream) = ::mio_uds::UnixStream::pair().unwrap();
    let handle = core.handle();
    let (_, server_writer) = ::tokio_core::reactor::PollEvented::new(server_stream, &handle).unwrap().split();
    let _ = core.run(WriteQueue::new(server_writer));
}

If I run it in debug mode, it exits without error, as I would expect. If I run in in release mode, I see, to my surprise:

$ RUST_BACKTRACE=1 ./target/release/crash 
thread 'main' panicked at 'assertion failed: `(left == right)` (left: `1`, right: `0`)', /Users/dwrensha/.multirust/toolchains/stable/cargo/registry/src/github.com-1ecc6299db9ec823/futures-0.1.6/src/lib.rs:195
stack backtrace:
   1:        0x100960408 - std::sys::backtrace::tracing::imp::write::h6f1d53a70916b90d
   2:        0x10096256f - std::panicking::default_hook::{{closure}}::h137e876f7d3b5850
   3:        0x1009616c5 - std::panicking::default_hook::h0ac3811ec7cee78c
   4:        0x100961c16 - std::panicking::rust_panic_with_hook::hc303199e04562edf
   5:        0x100961ab4 - std::panicking::begin_panic::h6ed03353807cf54d
   6:        0x1009619d2 - std::panicking::begin_panic_fmt::hc321cece241bb2f5
   7:        0x10095261f - <alloc::arc::Arc<T>>::drop_slow::h94a0b56e89cad006
   8:        0x100953dfb - crash::main::hddfbd08facd330e5
   9:        0x100962b2a - __rust_maybe_catch_panic
  10:        0x100961206 - std::rt::lang_start::h538f8960e7644c80
Segmentation fault: 11

The assertion being tripped is in BiLock::drop()

@alexcrichton
Copy link
Member

Minimized to not use mio-uds or tokio-core, still working on it.

@alexcrichton
Copy link
Member

Looks like this may be a compiler bug :(

@dwrensha
Copy link
Contributor Author

Thanks for looking into this. In rust-lang/rust#38437 you seem to be suggesting that the problem only exists on nightly. Note, however, that the program that I pasted in my original comment above also behaves weirdly on stable and beta.

@alexcrichton
Copy link
Member

Yeah that's why I want to leave this open. I think that they're all related to the same bug, and my guess is that codegen changed between stable/beta/nightly to hide the bug of the reduced test case on stable/beta but leave it exposed on nightly.

Put another way, my hope (and assumption) is that fixing rust-lang/rust#38437 will fix this test case on all three channels. If that isn't the case though I'll keep digging into the stable/beta versions.

@alexcrichton
Copy link
Member

Ok I just confirmed that rust-lang/rust#38600 fixed this, so closing now that it's landed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants