2013-02-03 20:15:43 -06:00
|
|
|
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
|
|
use option::*;
|
|
|
|
use result::*;
|
|
|
|
use ops::Drop;
|
2013-05-12 19:34:15 -05:00
|
|
|
use old_iter::CopyableIter;
|
2013-02-03 20:15:43 -06:00
|
|
|
use cell::{Cell, empty_cell};
|
|
|
|
use cast::transmute;
|
2013-05-14 17:30:01 -05:00
|
|
|
use clone::Clone;
|
2013-04-26 20:59:59 -05:00
|
|
|
use rt::io::IoError;
|
|
|
|
use rt::io::net::ip::IpAddr;
|
|
|
|
use rt::uv::*;
|
2013-04-27 21:36:08 -05:00
|
|
|
use rt::uv::idle::IdleWatcher;
|
2013-04-26 20:59:59 -05:00
|
|
|
use rt::rtio::*;
|
|
|
|
use rt::sched::{Scheduler, local_sched};
|
|
|
|
use rt::io::{standard_error, OtherIoError};
|
2013-05-06 16:28:16 -05:00
|
|
|
use rt::tube::Tube;
|
2013-02-03 20:15:43 -06:00
|
|
|
|
2013-05-12 19:34:15 -05:00
|
|
|
#[cfg(test)] use container::Container;
|
2013-02-03 20:15:43 -06:00
|
|
|
#[cfg(test)] use uint;
|
2013-04-20 03:16:06 -05:00
|
|
|
#[cfg(test)] use unstable::run_in_bare_thread;
|
2013-04-26 20:59:59 -05:00
|
|
|
#[cfg(test)] use rt::test::*;
|
2013-02-03 20:15:43 -06:00
|
|
|
|
|
|
|
pub struct UvEventLoop {
|
|
|
|
uvio: UvIoFactory
|
|
|
|
}
|
|
|
|
|
|
|
|
pub impl UvEventLoop {
|
2013-03-21 21:07:54 -05:00
|
|
|
fn new() -> UvEventLoop {
|
2013-02-03 20:15:43 -06:00
|
|
|
UvEventLoop {
|
|
|
|
uvio: UvIoFactory(Loop::new())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A convenience constructor
|
2013-03-21 21:07:54 -05:00
|
|
|
fn new_scheduler() -> Scheduler {
|
2013-02-03 20:15:43 -06:00
|
|
|
Scheduler::new(~UvEventLoop::new())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for UvEventLoop {
|
|
|
|
fn finalize(&self) {
|
|
|
|
// XXX: Need mutable finalizer
|
2013-05-10 17:15:06 -05:00
|
|
|
let this = unsafe {
|
2013-02-03 20:15:43 -06:00
|
|
|
transmute::<&UvEventLoop, &mut UvEventLoop>(self)
|
|
|
|
};
|
2013-05-10 17:15:06 -05:00
|
|
|
this.uvio.uv_loop().close();
|
2013-02-03 20:15:43 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl EventLoop for UvEventLoop {
|
|
|
|
|
|
|
|
fn run(&mut self) {
|
|
|
|
self.uvio.uv_loop().run();
|
|
|
|
}
|
|
|
|
|
|
|
|
fn callback(&mut self, f: ~fn()) {
|
|
|
|
let mut idle_watcher = IdleWatcher::new(self.uvio.uv_loop());
|
|
|
|
do idle_watcher.start |idle_watcher, status| {
|
2013-03-28 20:39:09 -05:00
|
|
|
assert!(status.is_none());
|
2013-02-03 20:15:43 -06:00
|
|
|
let mut idle_watcher = idle_watcher;
|
|
|
|
idle_watcher.stop();
|
2013-05-12 16:35:52 -05:00
|
|
|
idle_watcher.close(||());
|
2013-02-03 20:15:43 -06:00
|
|
|
f();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-10 15:11:35 -05:00
|
|
|
fn io<'a>(&'a mut self) -> Option<&'a mut IoFactoryObject> {
|
|
|
|
Some(&mut self.uvio)
|
|
|
|
}
|
2013-02-03 20:15:43 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_callback_run_once() {
|
|
|
|
do run_in_bare_thread {
|
|
|
|
let mut event_loop = UvEventLoop::new();
|
|
|
|
let mut count = 0;
|
|
|
|
let count_ptr: *mut int = &mut count;
|
|
|
|
do event_loop.callback {
|
|
|
|
unsafe { *count_ptr += 1 }
|
|
|
|
}
|
|
|
|
event_loop.run();
|
2013-03-28 20:39:09 -05:00
|
|
|
assert!(count == 1);
|
2013-02-03 20:15:43 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct UvIoFactory(Loop);
|
|
|
|
|
|
|
|
pub impl UvIoFactory {
|
2013-04-10 15:11:35 -05:00
|
|
|
fn uv_loop<'a>(&'a mut self) -> &'a mut Loop {
|
|
|
|
match self { &UvIoFactory(ref mut ptr) => ptr }
|
|
|
|
}
|
2013-02-03 20:15:43 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
impl IoFactory for UvIoFactory {
|
|
|
|
// Connect to an address and return a new stream
|
|
|
|
// NB: This blocks the task waiting on the connection.
|
|
|
|
// It would probably be better to return a future
|
2013-04-26 20:59:59 -05:00
|
|
|
fn tcp_connect(&mut self, addr: IpAddr) -> Result<~RtioTcpStreamObject, IoError> {
|
2013-02-03 20:15:43 -06:00
|
|
|
// Create a cell in the task to hold the result. We will fill
|
|
|
|
// the cell before resuming the task.
|
|
|
|
let result_cell = empty_cell();
|
2013-04-26 20:59:59 -05:00
|
|
|
let result_cell_ptr: *Cell<Result<~RtioTcpStreamObject, IoError>> = &result_cell;
|
2013-02-03 20:15:43 -06:00
|
|
|
|
2013-04-18 21:32:32 -05:00
|
|
|
let scheduler = local_sched::take();
|
2013-04-15 18:00:15 -05:00
|
|
|
assert!(scheduler.in_task_context());
|
2013-02-03 20:15:43 -06:00
|
|
|
|
2013-04-15 18:00:15 -05:00
|
|
|
// Block this task and take ownership, switch to scheduler context
|
2013-04-15 18:19:01 -05:00
|
|
|
do scheduler.deschedule_running_task_and_then |task| {
|
2013-02-03 20:15:43 -06:00
|
|
|
|
2013-04-15 18:00:15 -05:00
|
|
|
rtdebug!("connect: entered scheduler context");
|
2013-04-18 21:32:32 -05:00
|
|
|
do local_sched::borrow |scheduler| {
|
2013-03-28 20:39:09 -05:00
|
|
|
assert!(!scheduler.in_task_context());
|
2013-04-15 18:19:01 -05:00
|
|
|
}
|
2013-04-15 18:00:15 -05:00
|
|
|
let mut tcp_watcher = TcpWatcher::new(self.uv_loop());
|
|
|
|
let task_cell = Cell(task);
|
|
|
|
|
|
|
|
// Wait for a connection
|
|
|
|
do tcp_watcher.connect(addr) |stream_watcher, status| {
|
|
|
|
rtdebug!("connect: in connect callback");
|
2013-05-12 16:35:52 -05:00
|
|
|
if status.is_none() {
|
2013-04-15 18:00:15 -05:00
|
|
|
rtdebug!("status is none");
|
2013-05-12 16:35:52 -05:00
|
|
|
let res = Ok(~UvTcpStream { watcher: stream_watcher });
|
|
|
|
|
|
|
|
// Store the stream in the task's stack
|
|
|
|
unsafe { (*result_cell_ptr).put_back(res); }
|
|
|
|
|
|
|
|
// Context switch
|
|
|
|
let scheduler = local_sched::take();
|
|
|
|
scheduler.resume_task_immediately(task_cell.take());
|
2013-04-15 18:00:15 -05:00
|
|
|
} else {
|
|
|
|
rtdebug!("status is some");
|
2013-05-12 16:35:52 -05:00
|
|
|
let task_cell = Cell(task_cell.take());
|
|
|
|
do stream_watcher.close {
|
|
|
|
let res = Err(uv_error_to_io_error(status.get()));
|
|
|
|
unsafe { (*result_cell_ptr).put_back(res); }
|
|
|
|
let scheduler = local_sched::take();
|
|
|
|
scheduler.resume_task_immediately(task_cell.take());
|
|
|
|
}
|
2013-04-15 18:00:15 -05:00
|
|
|
};
|
2013-02-03 20:15:43 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-28 20:39:09 -05:00
|
|
|
assert!(!result_cell.is_empty());
|
2013-02-03 20:15:43 -06:00
|
|
|
return result_cell.take();
|
|
|
|
}
|
|
|
|
|
2013-04-26 20:59:59 -05:00
|
|
|
fn tcp_bind(&mut self, addr: IpAddr) -> Result<~RtioTcpListenerObject, IoError> {
|
2013-02-03 20:15:43 -06:00
|
|
|
let mut watcher = TcpWatcher::new(self.uv_loop());
|
2013-04-24 22:20:03 -05:00
|
|
|
match watcher.bind(addr) {
|
2013-05-06 16:28:16 -05:00
|
|
|
Ok(_) => Ok(~UvTcpListener::new(watcher)),
|
2013-04-24 22:20:03 -05:00
|
|
|
Err(uverr) => {
|
2013-05-12 16:35:52 -05:00
|
|
|
let scheduler = local_sched::take();
|
|
|
|
do scheduler.deschedule_running_task_and_then |task| {
|
|
|
|
let task_cell = Cell(task);
|
|
|
|
do watcher.as_stream().close {
|
|
|
|
let scheduler = local_sched::take();
|
|
|
|
scheduler.resume_task_immediately(task_cell.take());
|
|
|
|
}
|
|
|
|
}
|
2013-04-24 22:20:03 -05:00
|
|
|
Err(uv_error_to_io_error(uverr))
|
|
|
|
}
|
|
|
|
}
|
2013-02-03 20:15:43 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-27 17:21:03 -05:00
|
|
|
// FIXME #6090: Prefer newtype structs but Drop doesn't work
|
|
|
|
pub struct UvTcpListener {
|
2013-05-06 16:28:16 -05:00
|
|
|
watcher: TcpWatcher,
|
|
|
|
listening: bool,
|
|
|
|
incoming_streams: Tube<Result<~RtioTcpStreamObject, IoError>>
|
2013-04-27 17:21:03 -05:00
|
|
|
}
|
2013-02-03 20:15:43 -06:00
|
|
|
|
|
|
|
impl UvTcpListener {
|
2013-05-06 16:28:16 -05:00
|
|
|
fn new(watcher: TcpWatcher) -> UvTcpListener {
|
|
|
|
UvTcpListener {
|
|
|
|
watcher: watcher,
|
|
|
|
listening: false,
|
|
|
|
incoming_streams: Tube::new()
|
|
|
|
}
|
2013-02-03 20:15:43 -06:00
|
|
|
}
|
|
|
|
|
2013-04-27 17:21:03 -05:00
|
|
|
fn watcher(&self) -> TcpWatcher { self.watcher }
|
2013-02-03 20:15:43 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for UvTcpListener {
|
|
|
|
fn finalize(&self) {
|
2013-05-12 16:35:52 -05:00
|
|
|
let watcher = self.watcher();
|
|
|
|
let scheduler = local_sched::take();
|
|
|
|
do scheduler.deschedule_running_task_and_then |task| {
|
|
|
|
let task_cell = Cell(task);
|
|
|
|
do watcher.as_stream().close {
|
|
|
|
let scheduler = local_sched::take();
|
|
|
|
scheduler.resume_task_immediately(task_cell.take());
|
|
|
|
}
|
|
|
|
}
|
2013-02-03 20:15:43 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-26 20:59:59 -05:00
|
|
|
impl RtioTcpListener for UvTcpListener {
|
2013-02-03 20:15:43 -06:00
|
|
|
|
2013-04-26 20:59:59 -05:00
|
|
|
fn accept(&mut self) -> Result<~RtioTcpStreamObject, IoError> {
|
2013-02-03 20:15:43 -06:00
|
|
|
rtdebug!("entering listen");
|
|
|
|
|
2013-05-06 16:28:16 -05:00
|
|
|
if self.listening {
|
|
|
|
return self.incoming_streams.recv();
|
|
|
|
}
|
2013-04-15 18:00:15 -05:00
|
|
|
|
2013-05-06 16:28:16 -05:00
|
|
|
self.listening = true;
|
2013-04-15 18:00:15 -05:00
|
|
|
|
2013-05-06 16:28:16 -05:00
|
|
|
let server_tcp_watcher = self.watcher();
|
|
|
|
let incoming_streams_cell = Cell(self.incoming_streams.clone());
|
|
|
|
|
|
|
|
let incoming_streams_cell = Cell(incoming_streams_cell.take());
|
|
|
|
let mut server_tcp_watcher = server_tcp_watcher;
|
|
|
|
do server_tcp_watcher.listen |server_stream_watcher, status| {
|
|
|
|
let maybe_stream = if status.is_none() {
|
|
|
|
let mut server_stream_watcher = server_stream_watcher;
|
|
|
|
let mut loop_ = server_stream_watcher.event_loop();
|
|
|
|
let mut client_tcp_watcher = TcpWatcher::new(&mut loop_);
|
|
|
|
let client_tcp_watcher = client_tcp_watcher.as_stream();
|
|
|
|
// XXX: Need's to be surfaced in interface
|
|
|
|
server_stream_watcher.accept(client_tcp_watcher);
|
|
|
|
Ok(~UvTcpStream { watcher: client_tcp_watcher })
|
|
|
|
} else {
|
|
|
|
Err(standard_error(OtherIoError))
|
|
|
|
};
|
2013-04-15 18:00:15 -05:00
|
|
|
|
2013-05-06 16:28:16 -05:00
|
|
|
let mut incoming_streams = incoming_streams_cell.take();
|
|
|
|
incoming_streams.send(maybe_stream);
|
|
|
|
incoming_streams_cell.put_back(incoming_streams);
|
2013-02-03 20:15:43 -06:00
|
|
|
}
|
|
|
|
|
2013-05-06 16:28:16 -05:00
|
|
|
return self.incoming_streams.recv();
|
2013-02-03 20:15:43 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-27 17:21:03 -05:00
|
|
|
// FIXME #6090: Prefer newtype structs but Drop doesn't work
|
|
|
|
pub struct UvTcpStream {
|
|
|
|
watcher: StreamWatcher
|
|
|
|
}
|
2013-02-03 20:15:43 -06:00
|
|
|
|
2013-04-26 20:59:59 -05:00
|
|
|
impl UvTcpStream {
|
2013-04-27 17:21:03 -05:00
|
|
|
fn watcher(&self) -> StreamWatcher { self.watcher }
|
2013-02-03 20:15:43 -06:00
|
|
|
}
|
|
|
|
|
2013-04-26 20:59:59 -05:00
|
|
|
impl Drop for UvTcpStream {
|
2013-02-03 20:15:43 -06:00
|
|
|
fn finalize(&self) {
|
2013-05-12 16:35:52 -05:00
|
|
|
rtdebug!("closing tcp stream");
|
|
|
|
let watcher = self.watcher();
|
|
|
|
let scheduler = local_sched::take();
|
|
|
|
do scheduler.deschedule_running_task_and_then |task| {
|
|
|
|
let task_cell = Cell(task);
|
|
|
|
do watcher.close {
|
|
|
|
let scheduler = local_sched::take();
|
|
|
|
scheduler.resume_task_immediately(task_cell.take());
|
|
|
|
}
|
|
|
|
}
|
2013-02-03 20:15:43 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-26 20:59:59 -05:00
|
|
|
impl RtioTcpStream for UvTcpStream {
|
|
|
|
fn read(&mut self, buf: &mut [u8]) -> Result<uint, IoError> {
|
2013-02-03 20:15:43 -06:00
|
|
|
let result_cell = empty_cell();
|
2013-04-26 20:59:59 -05:00
|
|
|
let result_cell_ptr: *Cell<Result<uint, IoError>> = &result_cell;
|
2013-02-03 20:15:43 -06:00
|
|
|
|
2013-04-18 21:32:32 -05:00
|
|
|
let scheduler = local_sched::take();
|
2013-04-15 18:00:15 -05:00
|
|
|
assert!(scheduler.in_task_context());
|
|
|
|
let watcher = self.watcher();
|
|
|
|
let buf_ptr: *&mut [u8] = &buf;
|
2013-04-15 18:19:01 -05:00
|
|
|
do scheduler.deschedule_running_task_and_then |task| {
|
2013-04-15 18:00:15 -05:00
|
|
|
rtdebug!("read: entered scheduler context");
|
2013-04-18 21:32:32 -05:00
|
|
|
do local_sched::borrow |scheduler| {
|
2013-03-28 20:39:09 -05:00
|
|
|
assert!(!scheduler.in_task_context());
|
2013-04-15 18:19:01 -05:00
|
|
|
}
|
2013-04-15 18:00:15 -05:00
|
|
|
let mut watcher = watcher;
|
|
|
|
let task_cell = Cell(task);
|
|
|
|
// XXX: We shouldn't reallocate these callbacks every
|
|
|
|
// call to read
|
|
|
|
let alloc: AllocCallback = |_| unsafe {
|
|
|
|
slice_to_uv_buf(*buf_ptr)
|
|
|
|
};
|
|
|
|
do watcher.read_start(alloc) |watcher, nread, _buf, status| {
|
|
|
|
|
|
|
|
// Stop reading so that no read callbacks are
|
|
|
|
// triggered before the user calls `read` again.
|
|
|
|
// XXX: Is there a performance impact to calling
|
|
|
|
// stop here?
|
2013-02-03 20:15:43 -06:00
|
|
|
let mut watcher = watcher;
|
2013-04-15 18:00:15 -05:00
|
|
|
watcher.read_stop();
|
|
|
|
|
|
|
|
let result = if status.is_none() {
|
|
|
|
assert!(nread >= 0);
|
|
|
|
Ok(nread as uint)
|
|
|
|
} else {
|
2013-05-08 21:28:53 -05:00
|
|
|
Err(uv_error_to_io_error(status.unwrap()))
|
2013-02-03 20:15:43 -06:00
|
|
|
};
|
2013-04-15 18:00:15 -05:00
|
|
|
|
|
|
|
unsafe { (*result_cell_ptr).put_back(result); }
|
|
|
|
|
2013-04-18 21:32:32 -05:00
|
|
|
let scheduler = local_sched::take();
|
2013-04-15 18:00:15 -05:00
|
|
|
scheduler.resume_task_immediately(task_cell.take());
|
2013-02-03 20:15:43 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-28 20:39:09 -05:00
|
|
|
assert!(!result_cell.is_empty());
|
2013-02-03 20:15:43 -06:00
|
|
|
return result_cell.take();
|
|
|
|
}
|
|
|
|
|
2013-04-26 20:59:59 -05:00
|
|
|
fn write(&mut self, buf: &[u8]) -> Result<(), IoError> {
|
2013-02-03 20:15:43 -06:00
|
|
|
let result_cell = empty_cell();
|
2013-04-26 20:59:59 -05:00
|
|
|
let result_cell_ptr: *Cell<Result<(), IoError>> = &result_cell;
|
2013-04-18 21:32:32 -05:00
|
|
|
let scheduler = local_sched::take();
|
2013-04-15 18:00:15 -05:00
|
|
|
assert!(scheduler.in_task_context());
|
|
|
|
let watcher = self.watcher();
|
|
|
|
let buf_ptr: *&[u8] = &buf;
|
2013-04-15 18:19:01 -05:00
|
|
|
do scheduler.deschedule_running_task_and_then |task| {
|
2013-04-15 18:00:15 -05:00
|
|
|
let mut watcher = watcher;
|
|
|
|
let task_cell = Cell(task);
|
2013-04-27 19:26:35 -05:00
|
|
|
let buf = unsafe { slice_to_uv_buf(*buf_ptr) };
|
2013-04-15 18:00:15 -05:00
|
|
|
do watcher.write(buf) |_watcher, status| {
|
|
|
|
let result = if status.is_none() {
|
|
|
|
Ok(())
|
|
|
|
} else {
|
2013-05-08 21:28:53 -05:00
|
|
|
Err(uv_error_to_io_error(status.unwrap()))
|
2013-04-15 18:00:15 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
unsafe { (*result_cell_ptr).put_back(result); }
|
|
|
|
|
2013-04-18 21:32:32 -05:00
|
|
|
let scheduler = local_sched::take();
|
2013-04-15 18:00:15 -05:00
|
|
|
scheduler.resume_task_immediately(task_cell.take());
|
2013-02-03 20:15:43 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-28 20:39:09 -05:00
|
|
|
assert!(!result_cell.is_empty());
|
2013-02-03 20:15:43 -06:00
|
|
|
return result_cell.take();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_simple_io_no_connect() {
|
2013-04-20 03:55:10 -05:00
|
|
|
do run_in_newsched_task {
|
2013-04-27 04:07:32 -05:00
|
|
|
unsafe {
|
|
|
|
let io = local_sched::unsafe_borrow_io();
|
|
|
|
let addr = next_test_ip4();
|
|
|
|
let maybe_chan = (*io).tcp_connect(addr);
|
|
|
|
assert!(maybe_chan.is_err());
|
|
|
|
}
|
2013-02-03 20:15:43 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_simple_tcp_server_and_client() {
|
2013-04-20 03:55:10 -05:00
|
|
|
do run_in_newsched_task {
|
2013-04-20 04:41:30 -05:00
|
|
|
let addr = next_test_ip4();
|
2013-02-03 20:15:43 -06:00
|
|
|
|
2013-04-20 03:55:10 -05:00
|
|
|
// Start the server first so it's listening when we connect
|
2013-04-23 17:11:28 -05:00
|
|
|
do spawntask_immediately {
|
2013-04-18 21:32:32 -05:00
|
|
|
unsafe {
|
|
|
|
let io = local_sched::unsafe_borrow_io();
|
2013-04-27 04:07:32 -05:00
|
|
|
let mut listener = (*io).tcp_bind(addr).unwrap();
|
2013-04-26 20:59:59 -05:00
|
|
|
let mut stream = listener.accept().unwrap();
|
2013-04-18 21:32:32 -05:00
|
|
|
let mut buf = [0, .. 2048];
|
|
|
|
let nread = stream.read(buf).unwrap();
|
|
|
|
assert!(nread == 8);
|
|
|
|
for uint::range(0, nread) |i| {
|
|
|
|
rtdebug!("%u", buf[i] as uint);
|
|
|
|
assert!(buf[i] == i as u8);
|
|
|
|
}
|
2013-02-03 20:15:43 -06:00
|
|
|
}
|
2013-04-20 03:55:10 -05:00
|
|
|
}
|
2013-02-03 20:15:43 -06:00
|
|
|
|
2013-04-23 17:11:28 -05:00
|
|
|
do spawntask_immediately {
|
2013-04-20 03:55:10 -05:00
|
|
|
unsafe {
|
|
|
|
let io = local_sched::unsafe_borrow_io();
|
2013-04-27 04:07:32 -05:00
|
|
|
let mut stream = (*io).tcp_connect(addr).unwrap();
|
2013-04-20 03:55:10 -05:00
|
|
|
stream.write([0, 1, 2, 3, 4, 5, 6, 7]);
|
|
|
|
}
|
|
|
|
}
|
2013-02-03 20:15:43 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test] #[ignore(reason = "busted")]
|
|
|
|
fn test_read_and_block() {
|
2013-04-20 03:55:10 -05:00
|
|
|
do run_in_newsched_task {
|
2013-04-20 04:41:30 -05:00
|
|
|
let addr = next_test_ip4();
|
2013-02-03 20:15:43 -06:00
|
|
|
|
2013-04-23 17:11:28 -05:00
|
|
|
do spawntask_immediately {
|
2013-04-18 21:32:32 -05:00
|
|
|
let io = unsafe { local_sched::unsafe_borrow_io() };
|
2013-04-27 04:07:32 -05:00
|
|
|
let mut listener = unsafe { (*io).tcp_bind(addr).unwrap() };
|
2013-04-26 20:59:59 -05:00
|
|
|
let mut stream = listener.accept().unwrap();
|
2013-04-15 18:00:15 -05:00
|
|
|
let mut buf = [0, .. 2048];
|
2013-02-03 20:15:43 -06:00
|
|
|
|
2013-04-15 18:00:15 -05:00
|
|
|
let expected = 32;
|
|
|
|
let mut current = 0;
|
|
|
|
let mut reads = 0;
|
2013-02-03 20:15:43 -06:00
|
|
|
|
2013-04-15 18:00:15 -05:00
|
|
|
while current < expected {
|
|
|
|
let nread = stream.read(buf).unwrap();
|
|
|
|
for uint::range(0, nread) |i| {
|
|
|
|
let val = buf[i] as uint;
|
|
|
|
assert!(val == current % 8);
|
|
|
|
current += 1;
|
|
|
|
}
|
|
|
|
reads += 1;
|
|
|
|
|
2013-04-18 21:32:32 -05:00
|
|
|
let scheduler = local_sched::take();
|
2013-04-15 18:00:15 -05:00
|
|
|
// Yield to the other task in hopes that it
|
|
|
|
// will trigger a read callback while we are
|
|
|
|
// not ready for it
|
2013-04-15 18:19:01 -05:00
|
|
|
do scheduler.deschedule_running_task_and_then |task| {
|
|
|
|
let task = Cell(task);
|
2013-04-18 21:32:32 -05:00
|
|
|
do local_sched::borrow |scheduler| {
|
2013-04-15 18:19:01 -05:00
|
|
|
scheduler.task_queue.push_back(task.take());
|
2013-02-03 20:15:43 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-15 18:00:15 -05:00
|
|
|
// Make sure we had multiple reads
|
|
|
|
assert!(reads > 1);
|
2013-04-20 03:55:10 -05:00
|
|
|
}
|
|
|
|
|
2013-04-23 17:11:28 -05:00
|
|
|
do spawntask_immediately {
|
2013-04-27 04:07:32 -05:00
|
|
|
unsafe {
|
|
|
|
let io = local_sched::unsafe_borrow_io();
|
|
|
|
let mut stream = (*io).tcp_connect(addr).unwrap();
|
|
|
|
stream.write([0, 1, 2, 3, 4, 5, 6, 7]);
|
|
|
|
stream.write([0, 1, 2, 3, 4, 5, 6, 7]);
|
|
|
|
stream.write([0, 1, 2, 3, 4, 5, 6, 7]);
|
|
|
|
stream.write([0, 1, 2, 3, 4, 5, 6, 7]);
|
|
|
|
}
|
2013-04-20 03:55:10 -05:00
|
|
|
}
|
2013-02-03 20:15:43 -06:00
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-20 04:16:21 -05:00
|
|
|
#[test]
|
2013-02-03 20:15:43 -06:00
|
|
|
fn test_read_read_read() {
|
2013-04-20 03:55:10 -05:00
|
|
|
do run_in_newsched_task {
|
2013-04-20 04:41:30 -05:00
|
|
|
let addr = next_test_ip4();
|
2013-04-20 18:15:32 -05:00
|
|
|
static MAX: uint = 500000;
|
2013-04-23 22:41:00 -05:00
|
|
|
|
2013-04-23 17:11:28 -05:00
|
|
|
do spawntask_immediately {
|
2013-04-20 04:16:21 -05:00
|
|
|
unsafe {
|
|
|
|
let io = local_sched::unsafe_borrow_io();
|
2013-04-27 04:07:32 -05:00
|
|
|
let mut listener = (*io).tcp_bind(addr).unwrap();
|
2013-04-26 20:59:59 -05:00
|
|
|
let mut stream = listener.accept().unwrap();
|
2013-05-12 19:34:15 -05:00
|
|
|
let buf = [1, .. 2048];
|
2013-04-20 04:16:21 -05:00
|
|
|
let mut total_bytes_written = 0;
|
|
|
|
while total_bytes_written < MAX {
|
|
|
|
stream.write(buf);
|
|
|
|
total_bytes_written += buf.len();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-02-03 20:15:43 -06:00
|
|
|
|
2013-04-23 17:11:28 -05:00
|
|
|
do spawntask_immediately {
|
2013-04-27 04:07:32 -05:00
|
|
|
unsafe {
|
|
|
|
let io = local_sched::unsafe_borrow_io();
|
|
|
|
let mut stream = (*io).tcp_connect(addr).unwrap();
|
|
|
|
let mut buf = [0, .. 2048];
|
|
|
|
let mut total_bytes_read = 0;
|
|
|
|
while total_bytes_read < MAX {
|
|
|
|
let nread = stream.read(buf).unwrap();
|
|
|
|
rtdebug!("read %u bytes", nread as uint);
|
|
|
|
total_bytes_read += nread;
|
|
|
|
for uint::range(0, nread) |i| {
|
|
|
|
assert!(buf[i] == 1);
|
|
|
|
}
|
2013-04-22 21:20:31 -05:00
|
|
|
}
|
2013-04-27 04:07:32 -05:00
|
|
|
rtdebug!("read %u bytes total", total_bytes_read as uint);
|
2013-02-03 20:15:43 -06:00
|
|
|
}
|
2013-04-20 03:55:10 -05:00
|
|
|
}
|
2013-02-03 20:15:43 -06:00
|
|
|
}
|
|
|
|
}
|