dpc / mioco.pre-0.9

Scalable, coroutine-based, asynchronous IO handling library for Rust programming language. (aka MIO COroutines).
Mozilla Public License 2.0
457 stars 30 forks source link

BufRead::read_line() panicked in the master branch #124

Closed redtankd closed 8 years ago

redtankd commented 8 years ago

thread 'mioco_thread_1' panicked at 'assertion failed: coroutine.last_event.id().as_usize() == self.shared().0.borrow().common.id.unwrap().as_usize()', /Users/redtank/dev/git/3rd/mioco/src/evented.rs:58 stack backtrace: 1: 0x1056d7b38 - std::sys::backtrace::tracing::imp::write::h9fb600083204ae7f 2: 0x1056d9ed5 - std::panicking::defaulthook::$u7b$$u7b$closure$u7d$$u7d$::hca543c34f11229ac 3: 0x1056d9b0e - std::panicking::default_hook::hc2c969e7453d080c 4: 0x1056ce3f6 - std::sys_common::unwind::begin_unwind_inner::h30e12d15ce2b2e25 5: 0x10554dd97 - std::sys_common::unwind::begin_unwind::h65390c63b501d6ed 6: 0x10559bac3 - mioco::src::evented::EventedImpl::block_onprv::h7a8d8cb88f24e91a 7: 0x10559b23b - ::blockon::h1e279a08fb83196e 8: 0x1055a4acb - <mioco..MioAdapter as std..io..Read>::read::ha82f454170b1ba97 9: 0x1055a478e - _<std..io..BufReader as std..io..BufRead>::fill_buf::hc811fdaa004ac97b 10: 0x1055a4339 - std::io::read_until::h425b7f19190b8c10 11: 0x1055a3fd1 - std::io::BufRead::readline::$u7b$$u7b$closure$u7d$$u7d$::h7d5b831c6618ee34 12: 0x1055a3d7c - std::io::append_to_string::h5b65085600ba97b7 13: 0x1055a3d06 - std::io::BufRead::readline::h94ef60f891ce5cf3 14: 0x1055a0778 - test::ConnectionHandler::command::h72cbdb2c583004d5 15: 0x105598e57 - test::ConnectionHandler::run::h6f84bbaabdd71b7b 16: 0x105597440 - test::main::$u7b$$u7b$closure$u7d$$u7d$::$u7b$$u7b$closure$u7d$$u7d$::h1755119d9da0b0e6 17: 0x105597391 - <std..panic..AssertRecoverSafe as std..ops..FnOnce<()>>::callonce::h3ab9d376da01b702 18: 0x10559733a - mioco::src::coroutine::Coroutine::spawn::$u7b$$u7b$closure$u7d$$u7d$::_$u7b$$u7b$closure$u7d$$u7d$::h1689735ffe5030b0 19: 0x105596e48 - std::panic::catchunwind::$u7b$$u7b$closure$u7d$$u7d$::ha0addfb34ed677d9 20: 0x105596df9 - std::sys_common::unwind::try::try_fn::hfb21ff1bd9094e8a 21: 0x1056d6d9b - __rust_try 22: 0x1056d6d23 - std::sys_common::unwind::inner_try::h47a4d9cd4a369dcd 23: 0x105596d63 - std::sys_common::unwind::try::h929f127de1c2b766 24: 0x105596b45 - std::panic::catchunwind::hfd1424f66b3c2541 25: 0x105596a88 - std::panic::recover::hf41fcb160de33757 26: 0x105595a25 - mioco::src::coroutine::Coroutine::spawn::$u7b$$u7b$closure$u7d$$u7d$::h04a20bee2d4bfd7b 27: 0x105598154 - _<F as std..boxed..FnBox>::call_box::hab9acd65724e9105 28: 0x1055cc9f2 - mioco::src::coroutine::Coroutine::spawn::coroutine_context_start_fn::h114954b77f9d76e5

#[macro_use]
extern crate mioco;
extern crate env_logger;

use std::io::prelude::*;
use std::io::BufReader;
use std::io::BufWriter;
use std::io::Error;
use std::io::ErrorKind;
use std::net::SocketAddr;

use std::sync::Arc;
use std::sync::RwLock;
use std::collections::HashMap;
use std::str::FromStr;

use mioco::tcp::TcpListener;
use mioco::tcp::TcpStream;

type Registry = Arc<RwLock<HashMap<String, String>>>;

const DEFAULT_LISTEN_ADDR: &'static str = "127.0.0.1:5555";

fn listend_addr() -> SocketAddr {
    FromStr::from_str(DEFAULT_LISTEN_ADDR).unwrap()
}

fn main() {
    env_logger::init().unwrap();

    mioco::start(|| {
        let addr = listend_addr();
        let listener = TcpListener::bind(&addr).unwrap();
        println!("Starting tcp echo server on {:?}",
                 listener.local_addr().unwrap());

        let registry = Arc::new(RwLock::new(HashMap::<String, String>::new()));

        loop {
            let stream = listener.accept().unwrap();
            let registry = registry.clone();

            mioco::spawn(move || {
                ConnectionHandler::new(stream, registry).run();
            });
        }
    })
        .unwrap();
}

struct ConnectionHandler {
    stream: TcpStream,
    reader: BufReader<TcpStream>,
    writer: BufWriter<TcpStream>,

    status: ConnectionStatus,
    user: String,

    user_registry: Registry,
}

#[derive(PartialEq)]
enum ConnectionStatus {
    Connected,
    Login,
}

impl ConnectionHandler {
    fn new(stream: TcpStream, registry: Registry) -> ConnectionHandler {
        let reader = BufReader::new(stream.try_clone().unwrap());
        let writer = BufWriter::new(stream.try_clone().unwrap());

        ConnectionHandler {
            stream: stream,
            reader: reader,
            writer: writer,
            status: ConnectionStatus::Connected,
            user: "".to_string(),
            user_registry: registry,
        }
    }

    fn write(&mut self, msg: &[u8]) {
        self.writer.write(msg).unwrap();
        self.writer.flush().unwrap();
    }

    fn run(mut self) {
        if self.status == ConnectionStatus::Connected {
            self.login();
        }
        if self.status == ConnectionStatus::Login {
            self.command();
        }

        println!("it's over");
    }

    fn login(&mut self) {
        let mut timer = mioco::timer::Timer::new();
        timer.set_timeout(5000);

        select!(
            r:self.stream => {
                let mut line = String::new();
                self.reader
                    .read_line(&mut line)
                    // the format is "login $username $password"
                    .and_then( |_| {
                        if line.starts_with("login ") {
                            if let Some(user) = line.split_whitespace().nth(1) {
                                self.user = user.to_string();
                                self.user_registry
                                    .write()
                                    .unwrap()
                                    .insert(user.to_string(), "login1".to_string());
                                self.status = ConnectionStatus::Login;
                                self.write(b"login successfully\n");
                                println!("user \"{}\" login", user);
                                return Ok(());
                            }
                        }
                        Err(Error::new(ErrorKind::PermissionDenied, 
                            "user or password is invaild!"))
                    })
                    .or_else(|e| {
                        let msg = format!("login fail because of {:?}: {}\n", 
                            e.kind(), e);
                        print!("{}", msg);
                        self.write(msg.as_bytes());
                        Err(e)
                    })
                    .unwrap_or(());
            },
            r:timer => {
                println!("login timeout");
                self.write(b"login timeout\n");
            },
        )
    }

    fn command(mut self) {
        loop {
            println!("----sfasfasasf");
            let mut line = String::new();
            let result = self.reader.read_line(&mut line);

            match result {
                Ok(_) => {
                    match line.trim() {
                        "heartbreak" => self.write(b"got\n"),
                        "whoami" => {
                            let str = self.user.clone() + "\n";
                            self.write(str.as_bytes());
                        }
                        "quit" => {
                            self.write(b"bye\n");
                            break;
                        }
                        line => {
                            println!("{}", line);
                        }
                    }
                }
                Err(e) => println!("{:?}", e.kind()),
            }
        }
    }
}
lhallam commented 8 years ago

edit: On second though, these issues probably aren't related, I'll make a new issue.

In writing a test case for the strange behaviour of timers with select, I also came across this error:

extern crate mioco;

fn main(){
    mioco::start(move ||{
        let mut timer_a = mioco::timer::Timer::new();
        let mut timer_b = mioco::timer::Timer::new();
        timer_a.set_timeout(1000);
        timer_b.set_timeout(10);
        loop{
            use mioco::Evented;
            unsafe{
                timer_a.select_add(mioco::RW::read());
                timer_b.select_add(mioco::RW::read());
            }
            let ret = mioco::select_wait();
            if ret.id() == timer_a.id(){
                println!("tick.. {}",timer_a.read());
                timer_a.set_timeout(1000);
            }
            if ret.id() == timer_b.id(){
                println!("tock.. {}",timer_b.read());
                timer_b.set_timeout(1000);
            }
        }
    }).unwrap();
}
     Running `target/debug/mioco_timer`
tock.. SteadyTime(SteadyTime { tv_sec: 18885, tv_nsec: 515231715 })
tock.. SteadyTime(SteadyTime { tv_sec: 18886, tv_nsec: 515267592 })
tick.. SteadyTime(SteadyTime { tv_sec: 18886, tv_nsec: 515342030 })
tick.. SteadyTime(SteadyTime { tv_sec: 18887, tv_nsec: 515534829 })
thread '<main>' panicked at 'assertion failed: coroutine.last_event.id().as_usize() ==
    self.shared().0.borrow().common.id.unwrap().as_usize()', /home/lewis/.multirust/toolchains/nightly/cargo/registry/src/github.com-88ac128001ac3a9a/mioco-0.4.1/src/evented.rs:58
note: Run with `RUST_BACKTRACE=1` for a backtrace.
thread '<main>' panicked at 'called `Result::unwrap()` on an `Err` value: Any', ../src/libcore/result.rs:785
error: Process didn't exit successfully: `target/debug/mioco_timer` (exit code: 101)
dpc commented 8 years ago

Could you try if the fix for #125 did not fixed this one as well? It seems to me it does, but I might be wrong.

redtankd commented 8 years ago

yes, it is fixed. thank u very much.