From 34be071353adf441a9d324528dafadc7c4aba82c Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Wed, 24 Apr 2013 18:26:49 -0700 Subject: [PATCH 01/55] core::rt: Remove Close trait We will just use RAII for now. --- src/libcore/rt/io/file.rs | 6 +----- src/libcore/rt/io/mod.rs | 11 +---------- src/libcore/rt/io/native/file.rs | 8 -------- src/libcore/rt/io/net/tcp.rs | 4 ---- src/libcore/rt/io/net/udp.rs | 4 ---- src/libcore/rt/io/net/unix.rs | 4 ---- src/libcore/rt/io/stdio.rs | 9 +-------- 7 files changed, 3 insertions(+), 43 deletions(-) diff --git a/src/libcore/rt/io/file.rs b/src/libcore/rt/io/file.rs index 85dc180452ffc..1f61cf25fbdd4 100644 --- a/src/libcore/rt/io/file.rs +++ b/src/libcore/rt/io/file.rs @@ -10,7 +10,7 @@ use prelude::*; use super::support::PathLike; -use super::{Reader, Writer, Seek, Close}; +use super::{Reader, Writer, Seek}; use super::SeekStyle; /// # XXX @@ -69,10 +69,6 @@ impl Seek for FileStream { fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail!() } } -impl Close for FileStream { - fn close(&mut self) { fail!() } -} - #[test] #[ignore] fn super_simple_smoke_test_lets_go_read_some_files_and_have_a_good_time() { diff --git a/src/libcore/rt/io/mod.rs b/src/libcore/rt/io/mod.rs index fea32bc5b7509..d2249aad95ed2 100644 --- a/src/libcore/rt/io/mod.rs +++ b/src/libcore/rt/io/mod.rs @@ -383,16 +383,7 @@ pub trait Writer { fn flush(&mut self); } -/// I/O types that may be closed -/// -/// Any further operations performed on a closed resource will raise -/// on `io_error` -pub trait Close { - /// Close the I/O resource - fn close(&mut self); -} - -pub trait Stream: Reader + Writer + Close { } +pub trait Stream: Reader + Writer { } pub enum SeekStyle { /// Seek from the beginning of the stream diff --git a/src/libcore/rt/io/native/file.rs b/src/libcore/rt/io/native/file.rs index e203df815f2f4..31c90336a24c2 100644 --- a/src/libcore/rt/io/native/file.rs +++ b/src/libcore/rt/io/native/file.rs @@ -40,10 +40,6 @@ impl Writer for FileDesc { fn flush(&mut self) { fail!() } } -impl Close for FileDesc { - fn close(&mut self) { fail!() } -} - impl Seek for FileDesc { fn tell(&self) -> u64 { fail!() } @@ -72,10 +68,6 @@ impl Writer for CFile { fn flush(&mut self) { fail!() } } -impl Close for CFile { - fn close(&mut self) { fail!() } -} - impl Seek for CFile { fn tell(&self) -> u64 { fail!() } fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail!() } diff --git a/src/libcore/rt/io/net/tcp.rs b/src/libcore/rt/io/net/tcp.rs index c95b4344fe75d..00b48738d0bb7 100644 --- a/src/libcore/rt/io/net/tcp.rs +++ b/src/libcore/rt/io/net/tcp.rs @@ -32,10 +32,6 @@ impl Writer for TcpStream { fn flush(&mut self) { fail!() } } -impl Close for TcpStream { - fn close(&mut self) { fail!() } -} - pub struct TcpListener; impl TcpListener { diff --git a/src/libcore/rt/io/net/udp.rs b/src/libcore/rt/io/net/udp.rs index 1f1254a7029f0..bb5457e334dda 100644 --- a/src/libcore/rt/io/net/udp.rs +++ b/src/libcore/rt/io/net/udp.rs @@ -32,10 +32,6 @@ impl Writer for UdpStream { fn flush(&mut self) { fail!() } } -impl Close for UdpStream { - fn close(&mut self) { fail!() } -} - pub struct UdpListener; impl UdpListener { diff --git a/src/libcore/rt/io/net/unix.rs b/src/libcore/rt/io/net/unix.rs index f449a857467cc..b85b7dd059d82 100644 --- a/src/libcore/rt/io/net/unix.rs +++ b/src/libcore/rt/io/net/unix.rs @@ -32,10 +32,6 @@ impl Writer for UnixStream { fn flush(&mut self) { fail!() } } -impl Close for UnixStream { - fn close(&mut self) { fail!() } -} - pub struct UnixListener; impl UnixListener { diff --git a/src/libcore/rt/io/stdio.rs b/src/libcore/rt/io/stdio.rs index 26950986f7a09..247fe9544088b 100644 --- a/src/libcore/rt/io/stdio.rs +++ b/src/libcore/rt/io/stdio.rs @@ -9,7 +9,7 @@ // except according to those terms. use prelude::*; -use super::{Reader, Writer, Close}; +use super::{Reader, Writer}; pub fn stdin() -> StdReader { fail!() } @@ -39,10 +39,6 @@ impl Reader for StdReader { fn eof(&mut self) -> bool { fail!() } } -impl Close for StdReader { - fn close(&mut self) { fail!() } -} - pub struct StdWriter; impl StdWriter { @@ -55,6 +51,3 @@ impl Writer for StdWriter { fn flush(&mut self) { fail!() } } -impl Close for StdWriter { - fn close(&mut self) { fail!() } -} From 0b4d4edf8bc6a90c0bcbf06599ddf92fea1ed58f Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Wed, 24 Apr 2013 18:27:30 -0700 Subject: [PATCH 02/55] core::rt: Fix a warning about unnecessary mutable variable --- src/libcore/rt/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/rt/test.rs b/src/libcore/rt/test.rs index 63db705408800..0c6843c605d15 100644 --- a/src/libcore/rt/test.rs +++ b/src/libcore/rt/test.rs @@ -77,7 +77,7 @@ pub fn spawntask_try(f: ~fn()) -> Result<(), ()> { // Switch to the scheduler let f = Cell(Cell(f)); - let mut sched = local_sched::take(); + let sched = local_sched::take(); do sched.deschedule_running_task_and_then() |old_task| { let old_task = Cell(old_task); let f = f.take(); From b2fbd34603c5e209ab7a61a09ca943bd5b15f1a3 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Wed, 24 Apr 2013 20:20:03 -0700 Subject: [PATCH 03/55] core::rt: Begin implementing TcpStream This ended up touching a lot of code related to error handling. --- src/libcore/macros.rs | 8 ++ src/libcore/rt/io/mod.rs | 11 +- src/libcore/rt/io/net/tcp.rs | 164 ++++++++++++++++++++++++---- src/libcore/rt/local_services.rs | 3 +- src/libcore/rt/mod.rs | 12 +- src/libcore/rt/rtio.rs | 5 +- src/libcore/rt/sched/local_sched.rs | 31 ++++-- src/libcore/rt/uv/mod.rs | 60 +++++++++- src/libcore/rt/uv/net.rs | 14 ++- src/libcore/rt/uvio.rs | 24 ++-- src/libcore/rt/uvll.rs | 7 ++ 11 files changed, 282 insertions(+), 57 deletions(-) diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs index b19a753b71577..b2e94f327c86e 100644 --- a/src/libcore/macros.rs +++ b/src/libcore/macros.rs @@ -30,6 +30,14 @@ macro_rules! rtdebug ( ($( $arg:expr),+) => ( $(let _ = $arg)*; ) ) +macro_rules! rtassert ( + ( $arg:expr ) => ( { + if !$arg { + abort!("assertion failed: %s", stringify!($arg)); + } + } ) +) + macro_rules! abort( ($( $msg:expr),+) => ( { rtdebug!($($msg),+); diff --git a/src/libcore/rt/io/mod.rs b/src/libcore/rt/io/mod.rs index d2249aad95ed2..93daa36dd60a7 100644 --- a/src/libcore/rt/io/mod.rs +++ b/src/libcore/rt/io/mod.rs @@ -252,7 +252,9 @@ pub use self::stdio::println; pub use self::file::FileStream; pub use self::net::ip::IpAddr; +#[cfg(not(stage0))] pub use self::net::tcp::TcpListener; +#[cfg(not(stage0))] pub use self::net::tcp::TcpStream; pub use self::net::udp::UdpStream; @@ -266,6 +268,7 @@ pub mod file; /// Synchronous, non-blocking network I/O. pub mod net { + #[cfg(not(stage0))] pub mod tcp; pub mod udp; pub mod ip; @@ -326,12 +329,14 @@ pub struct IoError { #[deriving(Eq)] pub enum IoErrorKind { + PreviousIoError, + OtherIoError, + EndOfFile, FileNotFound, - FilePermission, + PermissionDenied, ConnectionFailed, Closed, - OtherIoError, - PreviousIoError + ConnectionRefused, } // XXX: Can't put doc comments on macros diff --git a/src/libcore/rt/io/net/tcp.rs b/src/libcore/rt/io/net/tcp.rs index 00b48738d0bb7..2ac2ffb60a8cf 100644 --- a/src/libcore/rt/io/net/tcp.rs +++ b/src/libcore/rt/io/net/tcp.rs @@ -8,63 +8,179 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use prelude::*; -use super::super::*; -use super::ip::IpAddr; +use option::{Option, Some, None}; +use result::{Result, Ok, Err}; +use ops::Drop; +use rt::sched::local_sched::unsafe_borrow_io; +use rt::io::net::ip::IpAddr; +use rt::io::{Reader, Writer, Listener}; +use rt::io::io_error; +use rt::rtio; +use rt::rtio::{IoFactory, TcpListener, Stream}; -pub struct TcpStream; +pub struct TcpStream { + rtstream: ~rtio::StreamObject +} impl TcpStream { - pub fn connect(_addr: IpAddr) -> Option { - fail!() + fn new(s: ~rtio::StreamObject) -> TcpStream { + TcpStream { + rtstream: s + } + } + + pub fn connect(addr: IpAddr) -> Option { + let stream = unsafe { + rtdebug!("borrowing io to connect"); + let io = unsafe_borrow_io(); + rtdebug!("about to connect"); + io.connect(addr) + }; + + match stream { + Ok(s) => { + Some(TcpStream::new(s)) + } + Err(ioerr) => { + rtdebug!("failed to connect: %?", ioerr); + io_error::cond.raise(ioerr); + return None; + } + } } } impl Reader for TcpStream { - fn read(&mut self, _buf: &mut [u8]) -> Option { fail!() } + fn read(&mut self, buf: &mut [u8]) -> Option { + let bytes_read = self.rtstream.read(buf); + match bytes_read { + Ok(read) => Some(read), + Err(_) => { + abort!("TODO"); + } + } + } fn eof(&mut self) -> bool { fail!() } } impl Writer for TcpStream { - fn write(&mut self, _buf: &[u8]) { fail!() } + fn write(&mut self, buf: &[u8]) { + let res = self.rtstream.write(buf); + match res { + Ok(_) => (), + Err(_) => { + abort!("TODO"); + } + } + } fn flush(&mut self) { fail!() } } -pub struct TcpListener; +impl Drop for TcpStream { + fn finalize(&self) { + self.rtstream.close(); + } +} + +pub struct TcpListener { + rtlistener: ~rtio::TcpListenerObject +} impl TcpListener { - pub fn bind(_addr: IpAddr) -> Option { - fail!() + pub fn bind(addr: IpAddr) -> Option { + let listener = unsafe { unsafe_borrow_io().bind(addr) }; + match listener { + Ok(l) => { + Some(TcpListener { + rtlistener: l + }) + } + Err(ioerr) => { + io_error::cond.raise(ioerr); + return None; + } + } } } impl Listener for TcpListener { - fn accept(&mut self) -> Option { fail!() } + fn accept(&mut self) -> Option { + let rtstream = self.rtlistener.listen(); + match rtstream { + Some(s) => { + Some(TcpStream::new(s)) + } + None => { + abort!("TODO"); + } + } + } +} + +impl Drop for TcpListener { + fn finalize(&self) { + self.rtlistener.close(); + } } #[cfg(test)] mod test { + use super::*; + use rt::test::*; + use rt::io::net::ip::Ipv4; + use rt::io::*; + + #[test] + fn bind_error() { + do run_in_newsched_task { + let mut called = false; + do io_error::cond.trap(|e| { + assert!(e.kind == PermissionDenied); + called = true; + }).in { + let addr = Ipv4(0, 0, 0, 0, 1); + let listener = TcpListener::bind(addr); + assert!(listener.is_none()); + } + assert!(called); + } + } + + #[test] + fn connect_error() { + do run_in_newsched_task { + let mut called = false; + do io_error::cond.trap(|e| { + assert!(e.kind == ConnectionRefused); + called = true; + }).in { + let addr = Ipv4(0, 0, 0, 0, 1); + let stream = TcpStream::connect(addr); + assert!(stream.is_none()); + } + assert!(called); + } + } - #[test] #[ignore] + #[test] fn smoke_test() { - /*do run_in_newsched_task { + do run_in_newsched_task { let addr = next_test_ip4(); - do spawn_immediately { - let listener = TcpListener::bind(addr); - do listener.accept() { - let mut buf = [0]; - listener.read(buf); - assert!(buf[0] == 99); - } + do spawntask_immediately { + let mut listener = TcpListener::bind(addr); + let mut stream = listener.accept(); + let mut buf = [0]; + stream.read(buf); + assert!(buf[0] == 99); } - do spawn_immediately { - let stream = TcpStream::connect(addr); + do spawntask_immediately { + let mut stream = TcpStream::connect(addr); stream.write([99]); } - }*/ + } } } diff --git a/src/libcore/rt/local_services.rs b/src/libcore/rt/local_services.rs index 01bef5e245888..47e8669b54692 100644 --- a/src/libcore/rt/local_services.rs +++ b/src/libcore/rt/local_services.rs @@ -177,7 +177,8 @@ pub unsafe fn unsafe_borrow_local_services() -> &mut LocalServices { transmute_mut_region(&mut task.local_services) } None => { - fail!(~"no local services for schedulers yet") + // Don't fail. Infinite recursion + abort!("no local services for schedulers yet") } } } diff --git a/src/libcore/rt/mod.rs b/src/libcore/rt/mod.rs index 25f6c870654a6..72715ea9b2823 100644 --- a/src/libcore/rt/mod.rs +++ b/src/libcore/rt/mod.rs @@ -8,7 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -/*! The Rust runtime, including the scheduler and I/O interface */ +/*! The Rust runtime, including the scheduler and I/O interface + +# XXX + +* Unsafe uses of borrowed pointers should just use unsafe pointers +* Unwinding is not wired up correctly + +*/ + #[doc(hidden)]; @@ -16,7 +24,7 @@ use libc::c_char; #[path = "sched/mod.rs"] mod sched; -mod rtio; +pub mod rtio; pub mod uvll; mod uvio; #[path = "uv/mod.rs"] diff --git a/src/libcore/rt/rtio.rs b/src/libcore/rt/rtio.rs index fd64438c61b46..961a032607eb3 100644 --- a/src/libcore/rt/rtio.rs +++ b/src/libcore/rt/rtio.rs @@ -11,6 +11,7 @@ use option::*; use result::*; +use rt::io::IoError; use super::io::net::ip::IpAddr; // XXX: ~object doesn't work currently so these are some placeholder @@ -28,8 +29,8 @@ pub trait EventLoop { } pub trait IoFactory { - fn connect(&mut self, addr: IpAddr) -> Option<~StreamObject>; - fn bind(&mut self, addr: IpAddr) -> Option<~TcpListenerObject>; + fn connect(&mut self, addr: IpAddr) -> Result<~StreamObject, IoError>; + fn bind(&mut self, addr: IpAddr) -> Result<~TcpListenerObject, IoError>; } pub trait TcpListener { diff --git a/src/libcore/rt/sched/local_sched.rs b/src/libcore/rt/sched/local_sched.rs index a7e02f30e0167..c4153381d91aa 100644 --- a/src/libcore/rt/sched/local_sched.rs +++ b/src/libcore/rt/sched/local_sched.rs @@ -13,18 +13,21 @@ use prelude::*; use ptr::mut_null; use libc::c_void; -use cast::transmute; +use cast; +use cell::Cell; use super::Scheduler; use super::super::rtio::IoFactoryObject; use tls = super::super::thread_local_storage; +use unstable::finally::Finally; + #[cfg(test)] use super::super::uvio::UvEventLoop; /// Give the Scheduler to thread-local storage pub fn put(sched: ~Scheduler) { unsafe { let key = tls_key(); - let void_sched: *mut c_void = transmute::<~Scheduler, *mut c_void>(sched); + let void_sched: *mut c_void = cast::transmute(sched); tls::set(key, void_sched); } } @@ -34,8 +37,8 @@ pub fn take() -> ~Scheduler { unsafe { let key = tls_key(); let void_sched: *mut c_void = tls::get(key); - assert!(void_sched.is_not_null()); - let sched = transmute::<*mut c_void, ~Scheduler>(void_sched); + rtassert!(void_sched.is_not_null()); + let sched: ~Scheduler = cast::transmute(void_sched); tls::set(key, mut_null()); return sched; } @@ -55,8 +58,18 @@ pub fn exists() -> bool { /// While the scheduler is borrowed it is not available in TLS. pub fn borrow(f: &fn(&mut Scheduler)) { let mut sched = take(); - f(sched); - put(sched); + + // XXX: Need a different abstraction from 'finally' here to avoid unsafety + unsafe { + let unsafe_sched = cast::transmute_mut_region(&mut *sched); + let sched = Cell(sched); + + do (|| { + f(unsafe_sched); + }).finally { + put(sched.take()); + } + } } /// Borrow a mutable reference to the thread-local Scheduler @@ -68,11 +81,11 @@ pub fn borrow(f: &fn(&mut Scheduler)) { pub unsafe fn unsafe_borrow() -> &mut Scheduler { let key = tls_key(); let mut void_sched: *mut c_void = tls::get(key); - assert!(void_sched.is_not_null()); + rtassert!(void_sched.is_not_null()); { let void_sched_ptr = &mut void_sched; let sched: &mut ~Scheduler = { - transmute::<&mut *mut c_void, &mut ~Scheduler>(void_sched_ptr) + cast::transmute::<&mut *mut c_void, &mut ~Scheduler>(void_sched_ptr) }; let sched: &mut Scheduler = &mut **sched; return sched; @@ -91,7 +104,7 @@ fn tls_key() -> tls::Key { fn maybe_tls_key() -> Option { unsafe { let key: *mut c_void = rust_get_sched_tls_key(); - let key: &mut tls::Key = transmute(key); + let key: &mut tls::Key = cast::transmute(key); let key = *key; // Check that the key has been initialized. diff --git a/src/libcore/rt/uv/mod.rs b/src/libcore/rt/uv/mod.rs index 013a28abf2813..87aa7524ed610 100644 --- a/src/libcore/rt/uv/mod.rs +++ b/src/libcore/rt/uv/mod.rs @@ -34,17 +34,22 @@ via `close` and `delete` methods. */ +use libc; +use vec; +use ptr; +use cast; +use str; use option::*; use str::raw::from_c_str; use to_str::ToStr; -use vec; -use ptr; use libc::{c_void, c_int, size_t, malloc, free}; use cast::transmute; use ptr::null; -use super::uvll; use unstable::finally::Finally; +use rt::uvll; +use rt::io::{IoError, FileNotFound}; + #[cfg(test)] use unstable::run_in_bare_thread; pub use self::file::{FsRequest, FsCallback}; @@ -211,6 +216,55 @@ fn error_smoke_test() { assert!(err.to_str() == ~"EOF: end of file"); } +pub fn last_uv_error>(watcher: &W) -> UvError { + unsafe { + let loop_ = loop_from_watcher(watcher); + UvError(uvll::last_error(loop_.native_handle())) + } +} + +pub fn uv_error_to_io_error(uverr: UvError) -> IoError { + + // XXX: Could go in str::raw + unsafe fn c_str_to_static_slice(s: *libc::c_char) -> &'static str { + let s = s as *u8; + let mut curr = s, len = 0u; + while *curr != 0u8 { + len += 1u; + curr = ptr::offset(s, len); + } + + str::raw::buf_as_slice(s, len, |d| cast::transmute(d)) + } + + + unsafe { + // Importing error constants + use rt::uvll::*; + use rt::io::*; + + // uv error descriptions are static + let c_desc = uvll::strerror(&*uverr); + let desc = c_str_to_static_slice(c_desc); + + let kind = match uverr.code { + UNKNOWN => OtherIoError, + OK => OtherIoError, + EOF => EndOfFile, + EACCES => PermissionDenied, + ECONNREFUSED => ConnectionRefused, + e => { + abort!("unknown uv error code: %u", e as uint); + } + }; + + IoError { + kind: kind, + desc: desc, + detail: None + } + } +} /// Given a uv handle, convert a callback status to a UvError // XXX: Follow the pattern below by parameterizing over T: Watcher, not T diff --git a/src/libcore/rt/uv/net.rs b/src/libcore/rt/uv/net.rs index 376231e3b27c5..6d8979e04d68e 100644 --- a/src/libcore/rt/uv/net.rs +++ b/src/libcore/rt/uv/net.rs @@ -18,13 +18,14 @@ use super::{Loop, Watcher, Request, UvError, Buf, Callback, NativeHandle, NullCa install_watcher_data, get_watcher_data, drop_watcher_data, vec_to_uv_buf, vec_from_uv_buf}; use super::super::io::net::ip::{IpAddr, Ipv4, Ipv6}; +use rt::uv::last_uv_error; #[cfg(test)] use cell::Cell; #[cfg(test)] use unstable::run_in_bare_thread; #[cfg(test)] use super::super::thread::Thread; #[cfg(test)] use super::super::test::*; -fn ip4_as_uv_ip4(addr: IpAddr, f: &fn(*sockaddr_in)) { +fn ip4_as_uv_ip4(addr: IpAddr, f: &fn(*sockaddr_in) -> T) -> T { match addr { Ipv4(a, b, c, d, p) => { unsafe { @@ -34,7 +35,7 @@ fn ip4_as_uv_ip4(addr: IpAddr, f: &fn(*sockaddr_in)) { c as uint, d as uint), p as int); do (|| { - f(addr); + f(addr) }).finally { free_ip4_addr(addr); } @@ -193,15 +194,18 @@ pub impl TcpWatcher { } } - fn bind(&mut self, address: IpAddr) { + fn bind(&mut self, address: IpAddr) -> Result<(), UvError> { match address { Ipv4(*) => { do ip4_as_uv_ip4(address) |addr| { let result = unsafe { uvll::tcp_bind(self.native_handle(), addr) }; - // XXX: bind is likely to fail. need real error handling - assert!(result == 0); + if result == 0 { + Ok(()) + } else { + Err(last_uv_error(self)) + } } } _ => fail!() diff --git a/src/libcore/rt/uvio.rs b/src/libcore/rt/uvio.rs index 8f1a6ea0d34c1..2c4ff37e4be45 100644 --- a/src/libcore/rt/uvio.rs +++ b/src/libcore/rt/uvio.rs @@ -11,6 +11,7 @@ use option::*; use result::*; +use rt::io::IoError; use super::io::net::ip::IpAddr; use super::uv::*; use super::rtio::*; @@ -98,11 +99,11 @@ 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 - fn connect(&mut self, addr: IpAddr) -> Option<~StreamObject> { + fn connect(&mut self, addr: IpAddr) -> Result<~StreamObject, IoError> { // Create a cell in the task to hold the result. We will fill // the cell before resuming the task. let result_cell = empty_cell(); - let result_cell_ptr: *Cell> = &result_cell; + let result_cell_ptr: *Cell> = &result_cell; let scheduler = local_sched::take(); assert!(scheduler.in_task_context()); @@ -122,11 +123,12 @@ impl IoFactory for UvIoFactory { rtdebug!("connect: in connect callback"); let maybe_stream = if status.is_none() { rtdebug!("status is none"); - Some(~UvStream(stream_watcher)) + Ok(~UvStream(stream_watcher)) } else { rtdebug!("status is some"); + // XXX: Wait for close stream_watcher.close(||()); - None + Err(uv_error_to_io_error(status.get())) }; // Store the stream in the task's stack @@ -142,10 +144,16 @@ impl IoFactory for UvIoFactory { return result_cell.take(); } - fn bind(&mut self, addr: IpAddr) -> Option<~TcpListenerObject> { + fn bind(&mut self, addr: IpAddr) -> Result<~TcpListenerObject, IoError> { let mut watcher = TcpWatcher::new(self.uv_loop()); - watcher.bind(addr); - return Some(~UvTcpListener(watcher)); + match watcher.bind(addr) { + Ok(_) => Ok(~UvTcpListener(watcher)), + Err(uverr) => { + // XXX: Should we wait until close completes? + watcher.as_stream().close(||()); + Err(uv_error_to_io_error(uverr)) + } + } } } @@ -321,7 +329,7 @@ fn test_simple_io_no_connect() { let io = unsafe { local_sched::unsafe_borrow_io() }; let addr = next_test_ip4(); let maybe_chan = io.connect(addr); - assert!(maybe_chan.is_none()); + assert!(maybe_chan.is_err()); } } diff --git a/src/libcore/rt/uvll.rs b/src/libcore/rt/uvll.rs index 4bff3bff7d3ae..2a2812c671847 100644 --- a/src/libcore/rt/uvll.rs +++ b/src/libcore/rt/uvll.rs @@ -33,6 +33,13 @@ use libc::{size_t, c_int, c_uint, c_void, c_char, uintptr_t}; use libc::{malloc, free}; use prelude::*; +pub static UNKNOWN: c_int = -1; +pub static OK: c_int = 0; +pub static EOF: c_int = 1; +pub static EADDRINFO: c_int = 2; +pub static EACCES: c_int = 3; +pub static ECONNREFUSED: c_int = 12; + pub struct uv_err_t { code: c_int, sys_errno_: c_int From 93ca5ebccb4ff6761fc61b31f7a9e1e6ffc866df Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 26 Apr 2013 18:59:59 -0700 Subject: [PATCH 04/55] core::rt: Clean up the interface to rtio Make names that better match rt::io. Return error types. --- src/libcore/rt/io/mod.rs | 1 + src/libcore/rt/io/net/tcp.rs | 23 ++++++----- src/libcore/rt/rtio.rs | 18 ++++---- src/libcore/rt/uv/mod.rs | 2 +- src/libcore/rt/uvio.rs | 79 ++++++++++++++++++------------------ 5 files changed, 63 insertions(+), 60 deletions(-) diff --git a/src/libcore/rt/io/mod.rs b/src/libcore/rt/io/mod.rs index 93daa36dd60a7..8f56005d0a4f6 100644 --- a/src/libcore/rt/io/mod.rs +++ b/src/libcore/rt/io/mod.rs @@ -238,6 +238,7 @@ Out of scope * How does I/O relate to the Iterator trait? * std::base64 filters * Using conditions is a big unknown since we don't have much experience with them +* Too many uses of OtherIoError */ diff --git a/src/libcore/rt/io/net/tcp.rs b/src/libcore/rt/io/net/tcp.rs index 2ac2ffb60a8cf..95f43b259ce52 100644 --- a/src/libcore/rt/io/net/tcp.rs +++ b/src/libcore/rt/io/net/tcp.rs @@ -9,21 +9,22 @@ // except according to those terms. use option::{Option, Some, None}; -use result::{Result, Ok, Err}; +use result::{Ok, Err}; use ops::Drop; use rt::sched::local_sched::unsafe_borrow_io; use rt::io::net::ip::IpAddr; use rt::io::{Reader, Writer, Listener}; use rt::io::io_error; -use rt::rtio; -use rt::rtio::{IoFactory, TcpListener, Stream}; +use rt::rtio::{IoFactory, + RtioTcpListener, RtioTcpListenerObject, + RtioTcpStream, RtioTcpStreamObject}; pub struct TcpStream { - rtstream: ~rtio::StreamObject + rtstream: ~RtioTcpStreamObject } impl TcpStream { - fn new(s: ~rtio::StreamObject) -> TcpStream { + fn new(s: ~RtioTcpStreamObject) -> TcpStream { TcpStream { rtstream: s } @@ -34,7 +35,7 @@ impl TcpStream { rtdebug!("borrowing io to connect"); let io = unsafe_borrow_io(); rtdebug!("about to connect"); - io.connect(addr) + io.tcp_connect(addr) }; match stream { @@ -85,12 +86,12 @@ impl Drop for TcpStream { } pub struct TcpListener { - rtlistener: ~rtio::TcpListenerObject + rtlistener: ~RtioTcpListenerObject } impl TcpListener { pub fn bind(addr: IpAddr) -> Option { - let listener = unsafe { unsafe_borrow_io().bind(addr) }; + let listener = unsafe { unsafe_borrow_io().tcp_bind(addr) }; match listener { Ok(l) => { Some(TcpListener { @@ -107,12 +108,12 @@ impl TcpListener { impl Listener for TcpListener { fn accept(&mut self) -> Option { - let rtstream = self.rtlistener.listen(); + let rtstream = self.rtlistener.accept(); match rtstream { - Some(s) => { + Ok(s) => { Some(TcpStream::new(s)) } - None => { + Err(_) => { abort!("TODO"); } } diff --git a/src/libcore/rt/rtio.rs b/src/libcore/rt/rtio.rs index 961a032607eb3..1d8604bc3fd6c 100644 --- a/src/libcore/rt/rtio.rs +++ b/src/libcore/rt/rtio.rs @@ -18,8 +18,8 @@ use super::io::net::ip::IpAddr; // types to use instead pub type EventLoopObject = super::uvio::UvEventLoop; pub type IoFactoryObject = super::uvio::UvIoFactory; -pub type StreamObject = super::uvio::UvStream; -pub type TcpListenerObject = super::uvio::UvTcpListener; +pub type RtioTcpStreamObject = super::uvio::UvTcpStream; +pub type RtioTcpListenerObject = super::uvio::UvTcpListener; pub trait EventLoop { fn run(&mut self); @@ -29,15 +29,15 @@ pub trait EventLoop { } pub trait IoFactory { - fn connect(&mut self, addr: IpAddr) -> Result<~StreamObject, IoError>; - fn bind(&mut self, addr: IpAddr) -> Result<~TcpListenerObject, IoError>; + fn tcp_connect(&mut self, addr: IpAddr) -> Result<~RtioTcpStreamObject, IoError>; + fn tcp_bind(&mut self, addr: IpAddr) -> Result<~RtioTcpListenerObject, IoError>; } -pub trait TcpListener { - fn listen(&mut self) -> Option<~StreamObject>; +pub trait RtioTcpListener { + fn accept(&mut self) -> Result<~RtioTcpStreamObject, IoError>; } -pub trait Stream { - fn read(&mut self, buf: &mut [u8]) -> Result; - fn write(&mut self, buf: &[u8]) -> Result<(), ()>; +pub trait RtioTcpStream { + fn read(&mut self, buf: &mut [u8]) -> Result; + fn write(&mut self, buf: &[u8]) -> Result<(), IoError>; } diff --git a/src/libcore/rt/uv/mod.rs b/src/libcore/rt/uv/mod.rs index 87aa7524ed610..24b6c353cced0 100644 --- a/src/libcore/rt/uv/mod.rs +++ b/src/libcore/rt/uv/mod.rs @@ -48,7 +48,7 @@ use ptr::null; use unstable::finally::Finally; use rt::uvll; -use rt::io::{IoError, FileNotFound}; +use rt::io::IoError; #[cfg(test)] use unstable::run_in_bare_thread; diff --git a/src/libcore/rt/uvio.rs b/src/libcore/rt/uvio.rs index 2c4ff37e4be45..70f233a29d340 100644 --- a/src/libcore/rt/uvio.rs +++ b/src/libcore/rt/uvio.rs @@ -10,19 +10,20 @@ use option::*; use result::*; - -use rt::io::IoError; -use super::io::net::ip::IpAddr; -use super::uv::*; -use super::rtio::*; use ops::Drop; use cell::{Cell, empty_cell}; use cast::transmute; -use super::sched::{Scheduler, local_sched}; + +use rt::io::IoError; +use rt::io::net::ip::IpAddr; +use rt::uv::*; +use rt::rtio::*; +use rt::sched::{Scheduler, local_sched}; +use rt::io::{standard_error, OtherIoError}; #[cfg(test)] use uint; #[cfg(test)] use unstable::run_in_bare_thread; -#[cfg(test)] use super::test::*; +#[cfg(test)] use rt::test::*; pub struct UvEventLoop { uvio: UvIoFactory @@ -99,11 +100,11 @@ 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 - fn connect(&mut self, addr: IpAddr) -> Result<~StreamObject, IoError> { + fn tcp_connect(&mut self, addr: IpAddr) -> Result<~RtioTcpStreamObject, IoError> { // Create a cell in the task to hold the result. We will fill // the cell before resuming the task. let result_cell = empty_cell(); - let result_cell_ptr: *Cell> = &result_cell; + let result_cell_ptr: *Cell> = &result_cell; let scheduler = local_sched::take(); assert!(scheduler.in_task_context()); @@ -123,7 +124,7 @@ impl IoFactory for UvIoFactory { rtdebug!("connect: in connect callback"); let maybe_stream = if status.is_none() { rtdebug!("status is none"); - Ok(~UvStream(stream_watcher)) + Ok(~UvTcpStream(stream_watcher)) } else { rtdebug!("status is some"); // XXX: Wait for close @@ -144,7 +145,7 @@ impl IoFactory for UvIoFactory { return result_cell.take(); } - fn bind(&mut self, addr: IpAddr) -> Result<~TcpListenerObject, IoError> { + fn tcp_bind(&mut self, addr: IpAddr) -> Result<~RtioTcpListenerObject, IoError> { let mut watcher = TcpWatcher::new(self.uv_loop()); match watcher.bind(addr) { Ok(_) => Ok(~UvTcpListener(watcher)), @@ -177,12 +178,12 @@ impl Drop for UvTcpListener { } } -impl TcpListener for UvTcpListener { +impl RtioTcpListener for UvTcpListener { - fn listen(&mut self) -> Option<~StreamObject> { + fn accept(&mut self) -> Result<~RtioTcpStreamObject, IoError> { rtdebug!("entering listen"); let result_cell = empty_cell(); - let result_cell_ptr: *Cell> = &result_cell; + let result_cell_ptr: *Cell> = &result_cell; let server_tcp_watcher = self.watcher(); @@ -199,9 +200,9 @@ impl TcpListener for UvTcpListener { let client_tcp_watcher = TcpWatcher::new(&mut loop_).as_stream(); // XXX: Needs to be surfaced in interface server_stream_watcher.accept(client_tcp_watcher); - Some(~UvStream::new(client_tcp_watcher)) + Ok(~UvTcpStream::new(client_tcp_watcher)) } else { - None + Err(standard_error(OtherIoError)) }; unsafe { (*result_cell_ptr).put_back(maybe_stream); } @@ -218,15 +219,15 @@ impl TcpListener for UvTcpListener { } } -pub struct UvStream(StreamWatcher); +pub struct UvTcpStream(StreamWatcher); -impl UvStream { - fn new(watcher: StreamWatcher) -> UvStream { - UvStream(watcher) +impl UvTcpStream { + fn new(watcher: StreamWatcher) -> UvTcpStream { + UvTcpStream(watcher) } fn watcher(&self) -> StreamWatcher { - match self { &UvStream(w) => w } + match self { &UvTcpStream(w) => w } } // XXX: finalize isn't working for ~UvStream??? @@ -236,17 +237,17 @@ impl UvStream { } } -impl Drop for UvStream { +impl Drop for UvTcpStream { fn finalize(&self) { rtdebug!("closing stream"); //self.watcher().close(||()); } } -impl Stream for UvStream { - fn read(&mut self, buf: &mut [u8]) -> Result { +impl RtioTcpStream for UvTcpStream { + fn read(&mut self, buf: &mut [u8]) -> Result { let result_cell = empty_cell(); - let result_cell_ptr: *Cell> = &result_cell; + let result_cell_ptr: *Cell> = &result_cell; let scheduler = local_sched::take(); assert!(scheduler.in_task_context()); @@ -277,7 +278,7 @@ impl Stream for UvStream { assert!(nread >= 0); Ok(nread as uint) } else { - Err(()) + Err(standard_error(OtherIoError)) }; unsafe { (*result_cell_ptr).put_back(result); } @@ -291,9 +292,9 @@ impl Stream for UvStream { return result_cell.take(); } - fn write(&mut self, buf: &[u8]) -> Result<(), ()> { + fn write(&mut self, buf: &[u8]) -> Result<(), IoError> { let result_cell = empty_cell(); - let result_cell_ptr: *Cell> = &result_cell; + let result_cell_ptr: *Cell> = &result_cell; let scheduler = local_sched::take(); assert!(scheduler.in_task_context()); let watcher = self.watcher(); @@ -308,7 +309,7 @@ impl Stream for UvStream { let result = if status.is_none() { Ok(()) } else { - Err(()) + Err(standard_error(OtherIoError)) }; unsafe { (*result_cell_ptr).put_back(result); } @@ -328,7 +329,7 @@ fn test_simple_io_no_connect() { do run_in_newsched_task { let io = unsafe { local_sched::unsafe_borrow_io() }; let addr = next_test_ip4(); - let maybe_chan = io.connect(addr); + let maybe_chan = io.tcp_connect(addr); assert!(maybe_chan.is_err()); } } @@ -342,8 +343,8 @@ fn test_simple_tcp_server_and_client() { do spawntask_immediately { unsafe { let io = local_sched::unsafe_borrow_io(); - let mut listener = io.bind(addr).unwrap(); - let mut stream = listener.listen().unwrap(); + let mut listener = io.tcp_bind(addr).unwrap(); + let mut stream = listener.accept().unwrap(); let mut buf = [0, .. 2048]; let nread = stream.read(buf).unwrap(); assert!(nread == 8); @@ -359,7 +360,7 @@ fn test_simple_tcp_server_and_client() { do spawntask_immediately { unsafe { let io = local_sched::unsafe_borrow_io(); - let mut stream = io.connect(addr).unwrap(); + let mut stream = io.tcp_connect(addr).unwrap(); stream.write([0, 1, 2, 3, 4, 5, 6, 7]); stream.close(); } @@ -374,8 +375,8 @@ fn test_read_and_block() { do spawntask_immediately { let io = unsafe { local_sched::unsafe_borrow_io() }; - let mut listener = io.bind(addr).unwrap(); - let mut stream = listener.listen().unwrap(); + let mut listener = io.tcp_bind(addr).unwrap(); + let mut stream = listener.accept().unwrap(); let mut buf = [0, .. 2048]; let expected = 32; @@ -412,7 +413,7 @@ fn test_read_and_block() { do spawntask_immediately { let io = unsafe { local_sched::unsafe_borrow_io() }; - let mut stream = io.connect(addr).unwrap(); + 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]); @@ -432,8 +433,8 @@ fn test_read_read_read() { do spawntask_immediately { unsafe { let io = local_sched::unsafe_borrow_io(); - let mut listener = io.bind(addr).unwrap(); - let mut stream = listener.listen().unwrap(); + let mut listener = io.tcp_bind(addr).unwrap(); + let mut stream = listener.accept().unwrap(); let mut buf = [1, .. 2048]; let mut total_bytes_written = 0; while total_bytes_written < MAX { @@ -447,7 +448,7 @@ fn test_read_read_read() { do spawntask_immediately { let io = unsafe { local_sched::unsafe_borrow_io() }; - let mut stream = io.connect(addr).unwrap(); + let mut stream = io.tcp_connect(addr).unwrap(); let mut buf = [0, .. 2048]; let mut total_bytes_read = 0; while total_bytes_read < MAX { From 23bf892ae5f185146d170af621c4da17e559dfa4 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 26 Apr 2013 23:21:58 -0700 Subject: [PATCH 05/55] core::rt: Improve docs --- src/libcore/rt/mod.rs | 65 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 8 deletions(-) diff --git a/src/libcore/rt/mod.rs b/src/libcore/rt/mod.rs index 72715ea9b2823..c332e48489a72 100644 --- a/src/libcore/rt/mod.rs +++ b/src/libcore/rt/mod.rs @@ -8,12 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -/*! The Rust runtime, including the scheduler and I/O interface +/*! Rust runtime services, including the task scheduler and I/O interface # XXX * Unsafe uses of borrowed pointers should just use unsafe pointers -* Unwinding is not wired up correctly */ @@ -22,29 +21,76 @@ use libc::c_char; +/// The Scheduler and Task types, and thread-local access thereof #[path = "sched/mod.rs"] mod sched; -pub mod rtio; -pub mod uvll; -mod uvio; + +/// Synchronous I/O +#[path = "io/mod.rs"] +pub mod io; + +/// Thread-local implementations of language-critical runtime features like @ +pub mod local_services; + +/// The EventLoop and internal synchronous I/O interface, dynamically +/// overridable so that it's primary implementation on libuv can +/// live outside of core. +mod rtio; + +/// libuv #[path = "uv/mod.rs"] mod uv; -#[path = "io/mod.rs"] -mod io; + +/// The implementation of `rtio` for libuv +mod uvio; + +/// C bindings to libuv +pub mod uvll; + + // FIXME #5248: The import in `sched` doesn't resolve unless this is pub! +/// Bindings to pthread/windows thread-local storage pub mod thread_local_storage; + +/// A parallel work-stealing queue mod work_queue; + +/// Stack segments and their cacheing mod stack; + +/// CPU context swapping mod context; + +/// Bindings to system threading libraries mod thread; + +/// The runtime configuration, read from environment variables pub mod env; -pub mod local_services; + +/// The local, managed heap mod local_heap; /// Tools for testing the runtime #[cfg(test)] pub mod test; +/// Set up a default runtime configuration, given compiler-supplied arguments. +/// +/// This is invoked by the `start` _language item_ (unstable::lang) to +/// run a Rust executable. +/// +/// # Arguments +/// +/// * `main` - A C-abi function that takes no arguments and returns `c_void`. +/// It is a wrapper around the user-defined `main` function, and will be run +/// in a task. +/// * `argc` & `argv` - The argument vector. On Unix this information is used +/// by os::args. +/// * `crate_map` - Runtime information about the executing crate, mostly for logging +/// +/// # Return value +/// +/// The return value is used as the process return code. 0 on success, 101 on error. pub fn start(main: *u8, _argc: int, _argv: **c_char, _crate_map: *u8) -> int { use self::sched::{Scheduler, Task}; @@ -79,6 +125,8 @@ pub fn start(main: *u8, _argc: int, _argv: **c_char, _crate_map: *u8) -> int { /// Possible contexts in which Rust code may be executing. /// Different runtime services are available depending on context. +/// Mostly used for determining if we're using the new scheduler +/// or the old scheduler. #[deriving(Eq)] pub enum RuntimeContext { // Only the exchange heap is available @@ -91,6 +139,7 @@ pub enum RuntimeContext { OldTaskContext } +/// Determine the current RuntimeContext pub fn context() -> RuntimeContext { use task::rt::rust_task; From ab284d44d8d181b12783d82755ff83a014027e4a Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 27 Apr 2013 00:09:27 -0700 Subject: [PATCH 06/55] core::rt Restructure some modules Put all uv code under rt::uv, as if it were in its own crate. Pull local_sched out of rt::sched. --- src/libcore/rt/{sched => }/local_sched.rs | 8 ++++---- src/libcore/rt/mod.rs | 19 +++++++------------ src/libcore/rt/rtio.rs | 9 +++++---- src/libcore/rt/{sched/mod.rs => sched.rs} | 2 +- src/libcore/rt/test.rs | 2 +- src/libcore/rt/uv/file.rs | 4 ++-- src/libcore/rt/uv/mod.rs | 12 +++++++++--- src/libcore/rt/uv/net.rs | 4 ++-- src/libcore/rt/{ => uv}/uvio.rs | 0 src/libcore/rt/{ => uv}/uvll.rs | 0 10 files changed, 31 insertions(+), 29 deletions(-) rename src/libcore/rt/{sched => }/local_sched.rs (96%) rename src/libcore/rt/{sched/mod.rs => sched.rs} (99%) rename src/libcore/rt/{ => uv}/uvio.rs (100%) rename src/libcore/rt/{ => uv}/uvll.rs (100%) diff --git a/src/libcore/rt/sched/local_sched.rs b/src/libcore/rt/local_sched.rs similarity index 96% rename from src/libcore/rt/sched/local_sched.rs rename to src/libcore/rt/local_sched.rs index c4153381d91aa..6ef3d0bad5e0b 100644 --- a/src/libcore/rt/sched/local_sched.rs +++ b/src/libcore/rt/local_sched.rs @@ -16,12 +16,12 @@ use libc::c_void; use cast; use cell::Cell; -use super::Scheduler; -use super::super::rtio::IoFactoryObject; -use tls = super::super::thread_local_storage; +use rt::sched::Scheduler; +use rt::rtio::{EventLoop, IoFactoryObject}; +use tls = rt::thread_local_storage; use unstable::finally::Finally; -#[cfg(test)] use super::super::uvio::UvEventLoop; +#[cfg(test)] use rt::uv::uvio::UvEventLoop; /// Give the Scheduler to thread-local storage pub fn put(sched: ~Scheduler) { diff --git a/src/libcore/rt/mod.rs b/src/libcore/rt/mod.rs index c332e48489a72..8501e36111f86 100644 --- a/src/libcore/rt/mod.rs +++ b/src/libcore/rt/mod.rs @@ -21,10 +21,12 @@ use libc::c_char; -/// The Scheduler and Task types, and thread-local access thereof -#[path = "sched/mod.rs"] +/// The Scheduler and Task types mod sched; +/// Thread-local access to the current Scheduler +mod local_sched; + /// Synchronous I/O #[path = "io/mod.rs"] pub mod io; @@ -39,14 +41,7 @@ mod rtio; /// libuv #[path = "uv/mod.rs"] -mod uv; - -/// The implementation of `rtio` for libuv -mod uvio; - -/// C bindings to libuv -pub mod uvll; - +pub mod uv; // FIXME #5248: The import in `sched` doesn't resolve unless this is pub! /// Bindings to pthread/windows thread-local storage @@ -94,7 +89,7 @@ pub mod test; pub fn start(main: *u8, _argc: int, _argv: **c_char, _crate_map: *u8) -> int { use self::sched::{Scheduler, Task}; - use self::uvio::UvEventLoop; + use self::uv::uvio::UvEventLoop; use sys::Closure; use ptr; use cast; @@ -175,7 +170,7 @@ pub fn context() -> RuntimeContext { fn test_context() { use unstable::run_in_bare_thread; use self::sched::{local_sched, Task}; - use self::uvio::UvEventLoop; + use rt::uv::uvio::UvEventLoop; use cell::Cell; assert!(context() == OldTaskContext); diff --git a/src/libcore/rt/rtio.rs b/src/libcore/rt/rtio.rs index 1d8604bc3fd6c..497ff8841b6bd 100644 --- a/src/libcore/rt/rtio.rs +++ b/src/libcore/rt/rtio.rs @@ -13,13 +13,14 @@ use result::*; use rt::io::IoError; use super::io::net::ip::IpAddr; +use rt::uv::uvio; // XXX: ~object doesn't work currently so these are some placeholder // types to use instead -pub type EventLoopObject = super::uvio::UvEventLoop; -pub type IoFactoryObject = super::uvio::UvIoFactory; -pub type RtioTcpStreamObject = super::uvio::UvTcpStream; -pub type RtioTcpListenerObject = super::uvio::UvTcpListener; +pub type EventLoopObject = uvio::UvEventLoop; +pub type IoFactoryObject = uvio::UvIoFactory; +pub type RtioTcpStreamObject = uvio::UvTcpStream; +pub type RtioTcpListenerObject = uvio::UvTcpListener; pub trait EventLoop { fn run(&mut self); diff --git a/src/libcore/rt/sched/mod.rs b/src/libcore/rt/sched.rs similarity index 99% rename from src/libcore/rt/sched/mod.rs rename to src/libcore/rt/sched.rs index ba057254583b0..1ab4b5debd380 100644 --- a/src/libcore/rt/sched/mod.rs +++ b/src/libcore/rt/sched.rs @@ -19,7 +19,7 @@ use super::context::Context; use super::local_services::LocalServices; use cell::Cell; -#[cfg(test)] use super::uvio::UvEventLoop; +#[cfg(test)] use rt::uv::uvio::UvEventLoop; #[cfg(test)] use unstable::run_in_bare_thread; #[cfg(test)] use int; diff --git a/src/libcore/rt/test.rs b/src/libcore/rt/test.rs index 0c6843c605d15..185443563fc9b 100644 --- a/src/libcore/rt/test.rs +++ b/src/libcore/rt/test.rs @@ -19,7 +19,7 @@ use rt::local_services::LocalServices; pub fn run_in_newsched_task(f: ~fn()) { use unstable::run_in_bare_thread; use super::sched::Task; - use super::uvio::UvEventLoop; + use rt::uv::uvio::UvEventLoop; let f = Cell(f); diff --git a/src/libcore/rt/uv/file.rs b/src/libcore/rt/uv/file.rs index a4aef7485d737..816a3a10a9016 100644 --- a/src/libcore/rt/uv/file.rs +++ b/src/libcore/rt/uv/file.rs @@ -12,8 +12,8 @@ use prelude::*; use ptr::null; use libc::c_void; use super::{UvError, Callback, Request, NativeHandle, Loop}; -use super::super::uvll; -use super::super::uvll::*; +use rt::uv::uvll; +use rt::uv::uvll::*; pub type FsCallback = ~fn(FsRequest, Option); impl Callback for FsCallback { } diff --git a/src/libcore/rt/uv/mod.rs b/src/libcore/rt/uv/mod.rs index 24b6c353cced0..5d31f39411da0 100644 --- a/src/libcore/rt/uv/mod.rs +++ b/src/libcore/rt/uv/mod.rs @@ -10,7 +10,7 @@ /*! -Bindings to libuv. +Bindings to libuv, along with the default implementation of `core::rt::rtio`. UV types consist of the event loop (Loop), Watchers, Requests and Callbacks. @@ -47,7 +47,6 @@ use cast::transmute; use ptr::null; use unstable::finally::Finally; -use rt::uvll; use rt::io::IoError; #[cfg(test)] use unstable::run_in_bare_thread; @@ -56,6 +55,13 @@ pub use self::file::{FsRequest, FsCallback}; pub use self::net::{StreamWatcher, TcpWatcher}; pub use self::net::{ReadCallback, AllocCallback, ConnectionCallback, ConnectCallback}; + +/// The implementation of `rtio` for libuv +pub mod uvio; + +/// C bindings to libuv +pub mod uvll; + pub mod file; pub mod net; @@ -240,7 +246,7 @@ pub fn uv_error_to_io_error(uverr: UvError) -> IoError { unsafe { // Importing error constants - use rt::uvll::*; + use rt::uv::uvll::*; use rt::io::*; // uv error descriptions are static diff --git a/src/libcore/rt/uv/net.rs b/src/libcore/rt/uv/net.rs index 6d8979e04d68e..ff47e2caec8fe 100644 --- a/src/libcore/rt/uv/net.rs +++ b/src/libcore/rt/uv/net.rs @@ -11,8 +11,8 @@ use prelude::*; use libc::{size_t, ssize_t, c_int, c_void}; use cast::transmute_mut_region; -use super::super::uvll; -use super::super::uvll::*; +use rt::uv::uvll; +use rt::uv::uvll::*; use super::{Loop, Watcher, Request, UvError, Buf, Callback, NativeHandle, NullCallback, loop_from_watcher, status_to_maybe_uv_error, install_watcher_data, get_watcher_data, drop_watcher_data, diff --git a/src/libcore/rt/uvio.rs b/src/libcore/rt/uv/uvio.rs similarity index 100% rename from src/libcore/rt/uvio.rs rename to src/libcore/rt/uv/uvio.rs diff --git a/src/libcore/rt/uvll.rs b/src/libcore/rt/uv/uvll.rs similarity index 100% rename from src/libcore/rt/uvll.rs rename to src/libcore/rt/uv/uvll.rs From 01b7b7d5a0feeb88b5dba1e295925a7564473685 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 27 Apr 2013 02:07:32 -0700 Subject: [PATCH 07/55] core::rt: Use unsafe pointers instead of transmuted regions --- src/libcore/rt/context.rs | 6 +-- src/libcore/rt/io/net/tcp.rs | 4 +- src/libcore/rt/local_sched.rs | 19 ++++----- src/libcore/rt/local_services.rs | 21 +++++----- src/libcore/rt/mod.rs | 9 +---- src/libcore/rt/sched.rs | 41 +++++++++++--------- src/libcore/rt/uv/uvio.rs | 60 ++++++++++++++++------------- src/libcore/sys.rs | 2 +- src/libcore/task/local_data_priv.rs | 2 +- src/libuv | 2 +- 10 files changed, 86 insertions(+), 80 deletions(-) diff --git a/src/libcore/rt/context.rs b/src/libcore/rt/context.rs index 9c1e566f218f6..9c1612884f044 100644 --- a/src/libcore/rt/context.rs +++ b/src/libcore/rt/context.rs @@ -111,9 +111,9 @@ fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void, sp: let sp = align_down(sp); let sp = mut_offset(sp, -4); - unsafe { *sp = arg as uint; } + unsafe { *sp = arg as uint }; let sp = mut_offset(sp, -1); - unsafe { *sp = 0; } // The final return address + unsafe { *sp = 0 }; // The final return address regs.esp = sp as u32; regs.eip = fptr as u32; @@ -195,7 +195,7 @@ fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void, sp: fn align_down(sp: *mut uint) -> *mut uint { unsafe { - let sp = transmute::<*mut uint, uint>(sp); + let sp: uint = transmute(sp); let sp = sp & !(16 - 1); transmute::(sp) } diff --git a/src/libcore/rt/io/net/tcp.rs b/src/libcore/rt/io/net/tcp.rs index 95f43b259ce52..0c7e0cf129d7f 100644 --- a/src/libcore/rt/io/net/tcp.rs +++ b/src/libcore/rt/io/net/tcp.rs @@ -35,7 +35,7 @@ impl TcpStream { rtdebug!("borrowing io to connect"); let io = unsafe_borrow_io(); rtdebug!("about to connect"); - io.tcp_connect(addr) + (*io).tcp_connect(addr) }; match stream { @@ -91,7 +91,7 @@ pub struct TcpListener { impl TcpListener { pub fn bind(addr: IpAddr) -> Option { - let listener = unsafe { unsafe_borrow_io().tcp_bind(addr) }; + let listener = unsafe { (*unsafe_borrow_io()).tcp_bind(addr) }; match listener { Ok(l) => { Some(TcpListener { diff --git a/src/libcore/rt/local_sched.rs b/src/libcore/rt/local_sched.rs index 6ef3d0bad5e0b..ef159d9fe5dde 100644 --- a/src/libcore/rt/local_sched.rs +++ b/src/libcore/rt/local_sched.rs @@ -78,23 +78,24 @@ pub fn borrow(f: &fn(&mut Scheduler)) { /// /// Because this leaves the Scheduler in thread-local storage it is possible /// For the Scheduler pointer to be aliased -pub unsafe fn unsafe_borrow() -> &mut Scheduler { +pub unsafe fn unsafe_borrow() -> *mut Scheduler { let key = tls_key(); let mut void_sched: *mut c_void = tls::get(key); rtassert!(void_sched.is_not_null()); { - let void_sched_ptr = &mut void_sched; - let sched: &mut ~Scheduler = { - cast::transmute::<&mut *mut c_void, &mut ~Scheduler>(void_sched_ptr) - }; - let sched: &mut Scheduler = &mut **sched; + let sched: *mut *mut c_void = &mut void_sched; + let sched: *mut ~Scheduler = sched as *mut ~Scheduler; + let sched: *mut Scheduler = &mut **sched; return sched; } } -pub unsafe fn unsafe_borrow_io() -> &mut IoFactoryObject { - let sched = unsafe_borrow(); - return sched.event_loop.io().unwrap(); +pub unsafe fn unsafe_borrow_io() -> *mut IoFactoryObject { + unsafe { + let sched = unsafe_borrow(); + let io: *mut IoFactoryObject = (*sched).event_loop.io().unwrap(); + return io; + } } fn tls_key() -> tls::Key { diff --git a/src/libcore/rt/local_services.rs b/src/libcore/rt/local_services.rs index 47e8669b54692..94840d7b5d584 100644 --- a/src/libcore/rt/local_services.rs +++ b/src/libcore/rt/local_services.rs @@ -169,16 +169,17 @@ pub fn borrow_local_services(f: &fn(&mut LocalServices)) { } } -pub unsafe fn unsafe_borrow_local_services() -> &mut LocalServices { - use cast::transmute_mut_region; - - match local_sched::unsafe_borrow().current_task { - Some(~ref mut task) => { - transmute_mut_region(&mut task.local_services) - } - None => { - // Don't fail. Infinite recursion - abort!("no local services for schedulers yet") +pub unsafe fn unsafe_borrow_local_services() -> *mut LocalServices { + unsafe { + match (*local_sched::unsafe_borrow()).current_task { + Some(~ref mut task) => { + let s: *mut LocalServices = &mut task.local_services; + return s; + } + None => { + // Don't fail. Infinite recursion + abort!("no local services for schedulers yet") + } } } } diff --git a/src/libcore/rt/mod.rs b/src/libcore/rt/mod.rs index 8501e36111f86..b5fba51ca7f4d 100644 --- a/src/libcore/rt/mod.rs +++ b/src/libcore/rt/mod.rs @@ -8,14 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -/*! Rust runtime services, including the task scheduler and I/O interface - -# XXX - -* Unsafe uses of borrowed pointers should just use unsafe pointers - -*/ - +//! Rust runtime services, including the task scheduler and I/O interface #[doc(hidden)]; diff --git a/src/libcore/rt/sched.rs b/src/libcore/rt/sched.rs index 1ab4b5debd380..546272474edb6 100644 --- a/src/libcore/rt/sched.rs +++ b/src/libcore/rt/sched.rs @@ -106,6 +106,7 @@ pub impl Scheduler { } } + let scheduler = &mut *scheduler; scheduler.event_loop.callback(run_scheduler_once); scheduler.event_loop.run(); } @@ -179,7 +180,7 @@ pub impl Scheduler { // Take pointers to both the task and scheduler's saved registers. unsafe { let sched = local_sched::unsafe_borrow(); - let (sched_context, _, next_task_context) = sched.get_contexts(); + let (sched_context, _, next_task_context) = (*sched).get_contexts(); let next_task_context = next_task_context.unwrap(); // Context switch to the task, restoring it's registers // and saving the scheduler's @@ -187,10 +188,10 @@ pub impl Scheduler { let sched = local_sched::unsafe_borrow(); // The running task should have passed ownership elsewhere - assert!(sched.current_task.is_none()); + assert!((*sched).current_task.is_none()); // Running tasks may have asked us to do some cleanup - sched.run_cleanup_job(); + (*sched).run_cleanup_job(); } } @@ -208,21 +209,25 @@ pub impl Scheduler { rtdebug!("blocking task"); - let blocked_task = this.current_task.swap_unwrap(); - let f_fake_region = unsafe { transmute::<&fn(~Task), &fn(~Task)>(f) }; - let f_opaque = ClosureConverter::from_fn(f_fake_region); - this.enqueue_cleanup_job(GiveTask(blocked_task, f_opaque)); + unsafe { + let blocked_task = this.current_task.swap_unwrap(); + let f_fake_region = transmute::<&fn(~Task), &fn(~Task)>(f); + let f_opaque = ClosureConverter::from_fn(f_fake_region); + this.enqueue_cleanup_job(GiveTask(blocked_task, f_opaque)); + } local_sched::put(this); - let sched = unsafe { local_sched::unsafe_borrow() }; - let (sched_context, last_task_context, _) = sched.get_contexts(); - let last_task_context = last_task_context.unwrap(); - Context::swap(last_task_context, sched_context); + unsafe { + let sched = local_sched::unsafe_borrow(); + let (sched_context, last_task_context, _) = (*sched).get_contexts(); + let last_task_context = last_task_context.unwrap(); + Context::swap(last_task_context, sched_context); - // We could be executing in a different thread now - let sched = unsafe { local_sched::unsafe_borrow() }; - sched.run_cleanup_job(); + // We could be executing in a different thread now + let sched = local_sched::unsafe_borrow(); + (*sched).run_cleanup_job(); + } } /// Switch directly to another task, without going through the scheduler. @@ -244,14 +249,14 @@ pub impl Scheduler { unsafe { let sched = local_sched::unsafe_borrow(); - let (_, last_task_context, next_task_context) = sched.get_contexts(); + let (_, last_task_context, next_task_context) = (*sched).get_contexts(); let last_task_context = last_task_context.unwrap(); let next_task_context = next_task_context.unwrap(); Context::swap(last_task_context, next_task_context); // We could be executing in a different thread now let sched = local_sched::unsafe_borrow(); - sched.run_cleanup_job(); + (*sched).run_cleanup_job(); } } @@ -356,10 +361,10 @@ pub impl Task { // have asked us to do some cleanup. unsafe { let sched = local_sched::unsafe_borrow(); - sched.run_cleanup_job(); + (*sched).run_cleanup_job(); let sched = local_sched::unsafe_borrow(); - let task = sched.current_task.get_mut_ref(); + let task = (*sched).current_task.get_mut_ref(); // FIXME #6141: shouldn't neet to put `start()` in another closure task.local_services.run(||start()); } diff --git a/src/libcore/rt/uv/uvio.rs b/src/libcore/rt/uv/uvio.rs index 70f233a29d340..704294102ae98 100644 --- a/src/libcore/rt/uv/uvio.rs +++ b/src/libcore/rt/uv/uvio.rs @@ -327,10 +327,12 @@ impl RtioTcpStream for UvTcpStream { #[test] fn test_simple_io_no_connect() { do run_in_newsched_task { - let io = unsafe { local_sched::unsafe_borrow_io() }; - let addr = next_test_ip4(); - let maybe_chan = io.tcp_connect(addr); - assert!(maybe_chan.is_err()); + 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()); + } } } @@ -343,7 +345,7 @@ fn test_simple_tcp_server_and_client() { do spawntask_immediately { unsafe { let io = local_sched::unsafe_borrow_io(); - let mut listener = io.tcp_bind(addr).unwrap(); + let mut listener = (*io).tcp_bind(addr).unwrap(); let mut stream = listener.accept().unwrap(); let mut buf = [0, .. 2048]; let nread = stream.read(buf).unwrap(); @@ -360,7 +362,7 @@ fn test_simple_tcp_server_and_client() { do spawntask_immediately { unsafe { let io = local_sched::unsafe_borrow_io(); - let mut stream = io.tcp_connect(addr).unwrap(); + let mut stream = (*io).tcp_connect(addr).unwrap(); stream.write([0, 1, 2, 3, 4, 5, 6, 7]); stream.close(); } @@ -375,7 +377,7 @@ fn test_read_and_block() { do spawntask_immediately { let io = unsafe { local_sched::unsafe_borrow_io() }; - let mut listener = io.tcp_bind(addr).unwrap(); + let mut listener = unsafe { (*io).tcp_bind(addr).unwrap() }; let mut stream = listener.accept().unwrap(); let mut buf = [0, .. 2048]; @@ -412,13 +414,15 @@ fn test_read_and_block() { } do spawntask_immediately { - let io = unsafe { 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]); - stream.close(); + 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]); + stream.close(); + } } } @@ -433,7 +437,7 @@ fn test_read_read_read() { do spawntask_immediately { unsafe { let io = local_sched::unsafe_borrow_io(); - let mut listener = io.tcp_bind(addr).unwrap(); + let mut listener = (*io).tcp_bind(addr).unwrap(); let mut stream = listener.accept().unwrap(); let mut buf = [1, .. 2048]; let mut total_bytes_written = 0; @@ -447,20 +451,22 @@ fn test_read_read_read() { } do spawntask_immediately { - let io = unsafe { 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); + 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); + } } + rtdebug!("read %u bytes total", total_bytes_read as uint); + stream.close(); } - rtdebug!("read %u bytes total", total_bytes_read as uint); - stream.close(); } } } diff --git a/src/libcore/sys.rs b/src/libcore/sys.rs index 4eca7ebbb371e..a27b6fe615f33 100644 --- a/src/libcore/sys.rs +++ b/src/libcore/sys.rs @@ -218,7 +218,7 @@ pub fn begin_unwind_(msg: *c_char, file: *c_char, line: size_t) -> ! { gc::cleanup_stack_for_failure(); unsafe { let local_services = unsafe_borrow_local_services(); - match local_services.unwinder { + match (*local_services).unwinder { Some(ref mut unwinder) => unwinder.begin_unwind(), None => abort!("failure without unwinder. aborting process") } diff --git a/src/libcore/task/local_data_priv.rs b/src/libcore/task/local_data_priv.rs index a30db039f30d7..27f58057e2ff5 100644 --- a/src/libcore/task/local_data_priv.rs +++ b/src/libcore/task/local_data_priv.rs @@ -36,7 +36,7 @@ impl Handle { } _ => { let local_services = unsafe_borrow_local_services(); - NewHandle(&mut local_services.storage) + NewHandle(&mut (*local_services).storage) } } } diff --git a/src/libuv b/src/libuv index 218ab86721eef..97ac7c087a0ca 160000 --- a/src/libuv +++ b/src/libuv @@ -1 +1 @@ -Subproject commit 218ab86721eefd7b7e97fa6d9f95a80a1fa8686c +Subproject commit 97ac7c087a0caf6b0f611b80e14f7fe3cb18bb27 From b771c993044d87d80c4ebc740e86b1b744770c57 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 27 Apr 2013 15:21:03 -0700 Subject: [PATCH 08/55] core::rt: Fix the finalizer on UvTcpStream and UvTcpListener Eliminates a lot of calls to `close` --- src/libcore/rt/io/net/tcp.rs | 13 --------- src/libcore/rt/uv/uvio.rs | 55 +++++++++++------------------------- 2 files changed, 16 insertions(+), 52 deletions(-) diff --git a/src/libcore/rt/io/net/tcp.rs b/src/libcore/rt/io/net/tcp.rs index 0c7e0cf129d7f..ae4a7a2039717 100644 --- a/src/libcore/rt/io/net/tcp.rs +++ b/src/libcore/rt/io/net/tcp.rs @@ -10,7 +10,6 @@ use option::{Option, Some, None}; use result::{Ok, Err}; -use ops::Drop; use rt::sched::local_sched::unsafe_borrow_io; use rt::io::net::ip::IpAddr; use rt::io::{Reader, Writer, Listener}; @@ -79,12 +78,6 @@ impl Writer for TcpStream { fn flush(&mut self) { fail!() } } -impl Drop for TcpStream { - fn finalize(&self) { - self.rtstream.close(); - } -} - pub struct TcpListener { rtlistener: ~RtioTcpListenerObject } @@ -120,12 +113,6 @@ impl Listener for TcpListener { } } -impl Drop for TcpListener { - fn finalize(&self) { - self.rtlistener.close(); - } -} - #[cfg(test)] mod test { use super::*; diff --git a/src/libcore/rt/uv/uvio.rs b/src/libcore/rt/uv/uvio.rs index 704294102ae98..49fbf1429630d 100644 --- a/src/libcore/rt/uv/uvio.rs +++ b/src/libcore/rt/uv/uvio.rs @@ -124,7 +124,7 @@ impl IoFactory for UvIoFactory { rtdebug!("connect: in connect callback"); let maybe_stream = if status.is_none() { rtdebug!("status is none"); - Ok(~UvTcpStream(stream_watcher)) + Ok(~UvTcpStream { watcher: stream_watcher }) } else { rtdebug!("status is some"); // XXX: Wait for close @@ -148,7 +148,7 @@ impl IoFactory for UvIoFactory { fn tcp_bind(&mut self, addr: IpAddr) -> Result<~RtioTcpListenerObject, IoError> { let mut watcher = TcpWatcher::new(self.uv_loop()); match watcher.bind(addr) { - Ok(_) => Ok(~UvTcpListener(watcher)), + Ok(_) => Ok(~UvTcpListener { watcher: watcher }), Err(uverr) => { // XXX: Should we wait until close completes? watcher.as_stream().close(||()); @@ -158,23 +158,19 @@ impl IoFactory for UvIoFactory { } } -pub struct UvTcpListener(TcpWatcher); +// FIXME #6090: Prefer newtype structs but Drop doesn't work +pub struct UvTcpListener { + watcher: TcpWatcher +} impl UvTcpListener { - fn watcher(&self) -> TcpWatcher { - match self { &UvTcpListener(w) => w } - } - - fn close(&self) { - // XXX: Need to wait until close finishes before returning - self.watcher().as_stream().close(||()); - } + fn watcher(&self) -> TcpWatcher { self.watcher } } impl Drop for UvTcpListener { fn finalize(&self) { - // XXX: Again, this never gets called. Use .close() instead - //self.watcher().as_stream().close(||()); + // XXX: Need to wait until close finishes before returning + self.watcher().as_stream().close(||()); } } @@ -200,7 +196,7 @@ impl RtioTcpListener for UvTcpListener { let client_tcp_watcher = TcpWatcher::new(&mut loop_).as_stream(); // XXX: Needs to be surfaced in interface server_stream_watcher.accept(client_tcp_watcher); - Ok(~UvTcpStream::new(client_tcp_watcher)) + Ok(~UvTcpStream { watcher: client_tcp_watcher }) } else { Err(standard_error(OtherIoError)) }; @@ -219,28 +215,19 @@ impl RtioTcpListener for UvTcpListener { } } -pub struct UvTcpStream(StreamWatcher); +// FIXME #6090: Prefer newtype structs but Drop doesn't work +pub struct UvTcpStream { + watcher: StreamWatcher +} impl UvTcpStream { - fn new(watcher: StreamWatcher) -> UvTcpStream { - UvTcpStream(watcher) - } - - fn watcher(&self) -> StreamWatcher { - match self { &UvTcpStream(w) => w } - } - - // XXX: finalize isn't working for ~UvStream??? - fn close(&self) { - // XXX: Need to wait until this finishes before returning - self.watcher().close(||()); - } + fn watcher(&self) -> StreamWatcher { self.watcher } } impl Drop for UvTcpStream { fn finalize(&self) { rtdebug!("closing stream"); - //self.watcher().close(||()); + self.watcher().close(||()); } } @@ -354,8 +341,6 @@ fn test_simple_tcp_server_and_client() { rtdebug!("%u", buf[i] as uint); assert!(buf[i] == i as u8); } - stream.close(); - listener.close(); } } @@ -364,7 +349,6 @@ fn test_simple_tcp_server_and_client() { 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.close(); } } } @@ -408,9 +392,6 @@ fn test_read_and_block() { // Make sure we had multiple reads assert!(reads > 1); - - stream.close(); - listener.close(); } do spawntask_immediately { @@ -421,7 +402,6 @@ fn test_read_and_block() { 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.close(); } } @@ -445,8 +425,6 @@ fn test_read_read_read() { stream.write(buf); total_bytes_written += buf.len(); } - stream.close(); - listener.close(); } } @@ -465,7 +443,6 @@ fn test_read_read_read() { } } rtdebug!("read %u bytes total", total_bytes_read as uint); - stream.close(); } } } From cfd183db15af50f06b5e2803b03f87061adad9f5 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 27 Apr 2013 17:26:35 -0700 Subject: [PATCH 09/55] core::rt: Fix some copies in uv --- src/libcore/rt/uv/mod.rs | 2 -- src/libcore/rt/uv/net.rs | 17 ++++++++--------- src/libcore/rt/uv/uvio.rs | 4 +--- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/libcore/rt/uv/mod.rs b/src/libcore/rt/uv/mod.rs index 5d31f39411da0..22ed82fbed391 100644 --- a/src/libcore/rt/uv/mod.rs +++ b/src/libcore/rt/uv/mod.rs @@ -362,7 +362,6 @@ struct WatcherData { connect_cb: Option, close_cb: Option, alloc_cb: Option, - buf: Option } pub fn install_watcher_data>(watcher: &mut W) { @@ -373,7 +372,6 @@ pub fn install_watcher_data>(watcher: &mut W) { connect_cb: None, close_cb: None, alloc_cb: None, - buf: None }; let data = transmute::<~WatcherData, *c_void>(data); uvll::set_data_for_uv_handle(watcher.native_handle(), data); diff --git a/src/libcore/rt/uv/net.rs b/src/libcore/rt/uv/net.rs index ff47e2caec8fe..49c42fa3587ac 100644 --- a/src/libcore/rt/uv/net.rs +++ b/src/libcore/rt/uv/net.rs @@ -11,12 +11,13 @@ use prelude::*; use libc::{size_t, ssize_t, c_int, c_void}; use cast::transmute_mut_region; +use util::ignore; use rt::uv::uvll; use rt::uv::uvll::*; use super::{Loop, Watcher, Request, UvError, Buf, Callback, NativeHandle, NullCallback, loop_from_watcher, status_to_maybe_uv_error, install_watcher_data, get_watcher_data, drop_watcher_data, - vec_to_uv_buf, vec_from_uv_buf}; + vec_to_uv_buf, vec_from_uv_buf, slice_to_uv_buf}; use super::super::io::net::ip::{IpAddr, Ipv4, Ipv6}; use rt::uv::last_uv_error; @@ -99,17 +100,13 @@ pub impl StreamWatcher { unsafe { uvll::read_stop(handle); } } - // XXX: Needs to take &[u8], not ~[u8] - fn write(&mut self, msg: ~[u8], cb: ConnectionCallback) { + fn write(&mut self, buf: Buf, cb: ConnectionCallback) { // XXX: Borrowck let data = get_watcher_data(unsafe { transmute_mut_region(self) }); assert!(data.write_cb.is_none()); data.write_cb = Some(cb); let req = WriteRequest::new(); - let buf = vec_to_uv_buf(msg); - assert!(data.buf.is_none()); - data.buf = Some(buf); let bufs = [buf]; unsafe { assert!(0 == uvll::write(req.native_handle(), @@ -123,7 +120,6 @@ pub impl StreamWatcher { write_request.delete(); let cb = { let data = get_watcher_data(&mut stream_watcher); - let _vec = vec_from_uv_buf(data.buf.swap_unwrap()); let cb = data.write_cb.swap_unwrap(); cb }; @@ -434,10 +430,13 @@ fn listen() { assert!(status.is_none()); let mut stream_watcher = stream_watcher; let msg = ~[0, 1, 2, 3, 4, 5, 6 ,7 ,8, 9]; - do stream_watcher.write(msg) |stream_watcher, status| { + let buf = slice_to_uv_buf(msg); + let msg_cell = Cell(msg); + do stream_watcher.write(buf) |stream_watcher, status| { rtdebug!("writing"); assert!(status.is_none()); - stream_watcher.close(||()); + let msg_cell = Cell(msg_cell.take()); + stream_watcher.close(||ignore(msg_cell.take())); } } loop_.run(); diff --git a/src/libcore/rt/uv/uvio.rs b/src/libcore/rt/uv/uvio.rs index 49fbf1429630d..d8858717f6f39 100644 --- a/src/libcore/rt/uv/uvio.rs +++ b/src/libcore/rt/uv/uvio.rs @@ -289,9 +289,7 @@ impl RtioTcpStream for UvTcpStream { do scheduler.deschedule_running_task_and_then |task| { let mut watcher = watcher; let task_cell = Cell(task); - let buf = unsafe { &*buf_ptr }; - // XXX: OMGCOPIES - let buf = buf.to_vec(); + let buf = unsafe { slice_to_uv_buf(*buf_ptr) }; do watcher.write(buf) |_watcher, status| { let result = if status.is_none() { Ok(()) From 6ab02c03da1e610f9c2f4c9a185a74f37f6195f2 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 27 Apr 2013 17:47:33 -0700 Subject: [PATCH 10/55] core::rt: Convert some uv functions to extension methods --- src/libcore/rt/uv/mod.rs | 193 ++++++++++++++++++-------------------- src/libcore/rt/uv/net.rs | 75 +++++++-------- src/libcore/rt/uv/uvio.rs | 7 +- 3 files changed, 127 insertions(+), 148 deletions(-) diff --git a/src/libcore/rt/uv/mod.rs b/src/libcore/rt/uv/mod.rs index 22ed82fbed391..7d5a4f00204d8 100644 --- a/src/libcore/rt/uv/mod.rs +++ b/src/libcore/rt/uv/mod.rs @@ -77,9 +77,7 @@ pub trait Request { } /// handle. Watchers are generally created, then `start`ed, `stop`ed /// and `close`ed, but due to their complex life cycle may not be /// entirely memory safe if used in unanticipated patterns. -pub trait Watcher { - fn event_loop(&self) -> Loop; -} +pub trait Watcher { } pub type NullCallback = ~fn(); impl Callback for NullCallback { } @@ -123,12 +121,7 @@ impl NativeHandle<*uvll::uv_loop_t> for Loop { } pub struct IdleWatcher(*uvll::uv_idle_t); - -impl Watcher for IdleWatcher { - fn event_loop(&self) -> Loop { - loop_from_watcher(self) - } -} +impl Watcher for IdleWatcher { } pub type IdleCallback = ~fn(IdleWatcher, Option); impl Callback for IdleCallback { } @@ -146,14 +139,14 @@ pub impl IdleWatcher { fn start(&mut self, cb: IdleCallback) { - set_watcher_callback(self, cb); + self.set_callback(cb); unsafe { assert!(0 == uvll::idle_start(self.native_handle(), idle_cb)) }; extern fn idle_cb(handle: *uvll::uv_idle_t, status: c_int) { let idle_watcher: IdleWatcher = NativeHandle::from_native_handle(handle); - let cb: &IdleCallback = borrow_callback_from_watcher(&idle_watcher); + let cb: &IdleCallback = idle_watcher.borrow_callback(); let status = status_to_maybe_uv_error(handle, status); (*cb)(idle_watcher, status); } @@ -167,9 +160,11 @@ pub impl IdleWatcher { unsafe { uvll::close(self.native_handle(), close_cb) }; extern fn close_cb(handle: *uvll::uv_idle_t) { - let mut idle_watcher = NativeHandle::from_native_handle(handle); - drop_watcher_callback::(&mut idle_watcher); - unsafe { uvll::idle_delete(handle) }; + unsafe { + let mut idle_watcher: IdleWatcher = NativeHandle::from_native_handle(handle); + idle_watcher.drop_callback::(); + uvll::idle_delete(handle); + } } } } @@ -224,7 +219,7 @@ fn error_smoke_test() { pub fn last_uv_error>(watcher: &W) -> UvError { unsafe { - let loop_ = loop_from_watcher(watcher); + let loop_ = watcher.event_loop(); UvError(uvll::last_error(loop_.native_handle())) } } @@ -288,111 +283,103 @@ pub fn status_to_maybe_uv_error(handle: *T, status: c_int) -> Option } } -/// Get the uv event loop from a Watcher -pub fn loop_from_watcher>( - watcher: &W) -> Loop { - - let handle = watcher.native_handle(); - let loop_ = unsafe { uvll::get_loop_for_uv_handle(handle) }; - NativeHandle::from_native_handle(loop_) +/// Callbacks used by StreamWatchers, set as custom data on the foreign handle +struct WatcherData { + read_cb: Option, + write_cb: Option, + connect_cb: Option, + close_cb: Option, + alloc_cb: Option, } -/// Set the custom data on a handle to a callback Note: This is only -/// suitable for watchers that make just one type of callback. For -/// others use WatcherData -pub fn set_watcher_callback, CB: Callback>( - watcher: &mut W, cb: CB) { - - drop_watcher_callback::(watcher); - // XXX: Boxing the callback so it fits into a - // pointer. Unfortunate extra allocation - let boxed_cb = ~cb; - let data = unsafe { transmute::<~CB, *c_void>(boxed_cb) }; - unsafe { uvll::set_data_for_uv_handle(watcher.native_handle(), data) }; +pub trait WatcherInterop { + fn event_loop(&self) -> Loop; + fn set_callback(&mut self, cb: CB); + fn drop_callback(&mut self); + fn borrow_callback(&self) -> &CB; + fn install_watcher_data(&mut self); + fn get_watcher_data<'r>(&'r mut self) -> &'r mut WatcherData; + fn drop_watcher_data(&mut self); } -/// Delete a callback from a handle's custom data -pub fn drop_watcher_callback, CB: Callback>( - watcher: &mut W) { - - unsafe { - let handle = watcher.native_handle(); - let handle_data: *c_void = uvll::get_data_for_uv_handle(handle); - if handle_data.is_not_null() { - // Take ownership of the callback and drop it - let _cb = transmute::<*c_void, ~CB>(handle_data); - // Make sure the pointer is zeroed - uvll::set_data_for_uv_handle(watcher.native_handle(), null::<()>()); +impl> WatcherInterop for W { + /// Get the uv event loop from a Watcher + pub fn event_loop(&self) -> Loop { + unsafe { + let handle = self.native_handle(); + let loop_ = uvll::get_loop_for_uv_handle(handle); + NativeHandle::from_native_handle(loop_) } } -} -/// Take a pointer to the callback installed as custom data -pub fn borrow_callback_from_watcher, - CB: Callback>(watcher: &W) -> &CB { + /// Set the custom data on a handle to a callback Note: This is only + /// suitable for watchers that make just one type of callback. For + /// others use WatcherData + pub fn set_callback(&mut self, cb: CB) { + unsafe { + self.drop_callback::(); - unsafe { - let handle = watcher.native_handle(); - let handle_data: *c_void = uvll::get_data_for_uv_handle(handle); - assert!(handle_data.is_not_null()); - let cb = transmute::<&*c_void, &~CB>(&handle_data); - return &**cb; + // XXX: Boxing the callback so it fits into a + // pointer. Unfortunate extra allocation + let boxed_cb = ~cb; + let data = transmute::<~CB, *c_void>(boxed_cb); + uvll::set_data_for_uv_handle(self.native_handle(), data); + } } -} -/// Take ownership of the callback installed as custom data -pub fn take_callback_from_watcher, CB: Callback>( - watcher: &mut W) -> CB { - - unsafe { - let handle = watcher.native_handle(); - let handle_data: *c_void = uvll::get_data_for_uv_handle(handle); - assert!(handle_data.is_not_null()); - uvll::set_data_for_uv_handle(handle, null::<()>()); - let cb: ~CB = transmute::<*c_void, ~CB>(handle_data); - let cb = match cb { ~cb => cb }; - return cb; + /// Delete a callback from a handle's custom data + pub fn drop_callback(&mut self) { + unsafe { + let handle = self.native_handle(); + let handle_data: *c_void = uvll::get_data_for_uv_handle(handle); + if handle_data.is_not_null() { + // Take ownership of the callback and drop it + let _cb = transmute::<*c_void, ~CB>(handle_data); + // Make sure the pointer is zeroed + uvll::set_data_for_uv_handle(self.native_handle(), null::<()>()); + } + } } -} - -/// Callbacks used by StreamWatchers, set as custom data on the foreign handle -struct WatcherData { - read_cb: Option, - write_cb: Option, - connect_cb: Option, - close_cb: Option, - alloc_cb: Option, -} -pub fn install_watcher_data>(watcher: &mut W) { - unsafe { - let data = ~WatcherData { - read_cb: None, - write_cb: None, - connect_cb: None, - close_cb: None, - alloc_cb: None, - }; - let data = transmute::<~WatcherData, *c_void>(data); - uvll::set_data_for_uv_handle(watcher.native_handle(), data); + /// Take a pointer to the callback installed as custom data + pub fn borrow_callback(&self) -> &CB { + unsafe { + let handle = self.native_handle(); + let handle_data: *c_void = uvll::get_data_for_uv_handle(handle); + assert!(handle_data.is_not_null()); + let cb = transmute::<&*c_void, &~CB>(&handle_data); + return &**cb; + } } -} -pub fn get_watcher_data<'r, H, W: Watcher + NativeHandle<*H>>( - watcher: &'r mut W) -> &'r mut WatcherData { + pub fn install_watcher_data(&mut self) { + unsafe { + let data = ~WatcherData { + read_cb: None, + write_cb: None, + connect_cb: None, + close_cb: None, + alloc_cb: None, + }; + let data = transmute::<~WatcherData, *c_void>(data); + uvll::set_data_for_uv_handle(self.native_handle(), data); + } + } - unsafe { - let data = uvll::get_data_for_uv_handle(watcher.native_handle()); - let data = transmute::<&*c_void, &mut ~WatcherData>(&data); - return &mut **data; + pub fn get_watcher_data<'r>(&'r mut self) -> &'r mut WatcherData { + unsafe { + let data = uvll::get_data_for_uv_handle(self.native_handle()); + let data = transmute::<&*c_void, &mut ~WatcherData>(&data); + return &mut **data; + } } -} -pub fn drop_watcher_data>(watcher: &mut W) { - unsafe { - let data = uvll::get_data_for_uv_handle(watcher.native_handle()); - let _data = transmute::<*c_void, ~WatcherData>(data); - uvll::set_data_for_uv_handle(watcher.native_handle(), null::<()>()); + pub fn drop_watcher_data(&mut self) { + unsafe { + let data = uvll::get_data_for_uv_handle(self.native_handle()); + let _data = transmute::<*c_void, ~WatcherData>(data); + uvll::set_data_for_uv_handle(self.native_handle(), null::<()>()); + } } } diff --git a/src/libcore/rt/uv/net.rs b/src/libcore/rt/uv/net.rs index 49c42fa3587ac..1209609347aec 100644 --- a/src/libcore/rt/uv/net.rs +++ b/src/libcore/rt/uv/net.rs @@ -10,14 +10,11 @@ use prelude::*; use libc::{size_t, ssize_t, c_int, c_void}; -use cast::transmute_mut_region; use util::ignore; use rt::uv::uvll; use rt::uv::uvll::*; use super::{Loop, Watcher, Request, UvError, Buf, Callback, NativeHandle, NullCallback, - loop_from_watcher, status_to_maybe_uv_error, - install_watcher_data, get_watcher_data, drop_watcher_data, - vec_to_uv_buf, vec_from_uv_buf, slice_to_uv_buf}; + status_to_maybe_uv_error, vec_to_uv_buf, vec_from_uv_buf, slice_to_uv_buf}; use super::super::io::net::ip::{IpAddr, Ipv4, Ipv6}; use rt::uv::last_uv_error; @@ -49,12 +46,7 @@ fn ip4_as_uv_ip4(addr: IpAddr, f: &fn(*sockaddr_in) -> T) -> T { // uv_stream t is the parent class of uv_tcp_t, uv_pipe_t, uv_tty_t // and uv_file_t pub struct StreamWatcher(*uvll::uv_stream_t); - -impl Watcher for StreamWatcher { - fn event_loop(&self) -> Loop { - loop_from_watcher(self) - } -} +impl Watcher for StreamWatcher { } pub type ReadCallback = ~fn(StreamWatcher, int, Buf, Option); impl Callback for ReadCallback { } @@ -66,17 +58,18 @@ impl Callback for AllocCallback { } pub impl StreamWatcher { fn read_start(&mut self, alloc: AllocCallback, cb: ReadCallback) { - // XXX: Borrowchk problems - let data = get_watcher_data(unsafe { transmute_mut_region(self) }); - data.alloc_cb = Some(alloc); - data.read_cb = Some(cb); + { + let data = self.get_watcher_data(); + data.alloc_cb = Some(alloc); + data.read_cb = Some(cb); + } let handle = self.native_handle(); unsafe { uvll::read_start(handle, alloc_cb, read_cb); } extern fn alloc_cb(stream: *uvll::uv_stream_t, suggested_size: size_t) -> Buf { let mut stream_watcher: StreamWatcher = NativeHandle::from_native_handle(stream); - let data = get_watcher_data(&mut stream_watcher); + let data = stream_watcher.get_watcher_data(); let alloc_cb = data.alloc_cb.get_ref(); return (*alloc_cb)(suggested_size as uint); } @@ -85,7 +78,7 @@ pub impl StreamWatcher { rtdebug!("buf addr: %x", buf.base as uint); rtdebug!("buf len: %d", buf.len as int); let mut stream_watcher: StreamWatcher = NativeHandle::from_native_handle(stream); - let data = get_watcher_data(&mut stream_watcher); + let data = stream_watcher.get_watcher_data(); let cb = data.read_cb.get_ref(); let status = status_to_maybe_uv_error(stream, nread as c_int); (*cb)(stream_watcher, nread as int, buf, status); @@ -101,17 +94,18 @@ pub impl StreamWatcher { } fn write(&mut self, buf: Buf, cb: ConnectionCallback) { - // XXX: Borrowck - let data = get_watcher_data(unsafe { transmute_mut_region(self) }); - assert!(data.write_cb.is_none()); - data.write_cb = Some(cb); + { + let data = self.get_watcher_data(); + assert!(data.write_cb.is_none()); + data.write_cb = Some(cb); + } let req = WriteRequest::new(); let bufs = [buf]; unsafe { assert!(0 == uvll::write(req.native_handle(), - self.native_handle(), - bufs, write_cb)); + self.native_handle(), + bufs, write_cb)); } extern fn write_cb(req: *uvll::uv_write_t, status: c_int) { @@ -119,7 +113,7 @@ pub impl StreamWatcher { let mut stream_watcher = write_request.stream(); write_request.delete(); let cb = { - let data = get_watcher_data(&mut stream_watcher); + let data = stream_watcher.get_watcher_data(); let cb = data.write_cb.swap_unwrap(); cb }; @@ -139,7 +133,7 @@ pub impl StreamWatcher { fn close(self, cb: NullCallback) { { let mut this = self; - let data = get_watcher_data(&mut this); + let data = this.get_watcher_data(); assert!(data.close_cb.is_none()); data.close_cb = Some(cb); } @@ -149,9 +143,10 @@ pub impl StreamWatcher { extern fn close_cb(handle: *uvll::uv_stream_t) { let mut stream_watcher: StreamWatcher = NativeHandle::from_native_handle(handle); { - get_watcher_data(&mut stream_watcher).close_cb.swap_unwrap()(); + let mut data = stream_watcher.get_watcher_data(); + data.close_cb.swap_unwrap()(); } - drop_watcher_data(&mut stream_watcher); + stream_watcher.drop_watcher_data(); unsafe { free_handle(handle as *c_void) } } } @@ -168,12 +163,7 @@ impl NativeHandle<*uvll::uv_stream_t> for StreamWatcher { } pub struct TcpWatcher(*uvll::uv_tcp_t); - -impl Watcher for TcpWatcher { - fn event_loop(&self) -> Loop { - loop_from_watcher(self) - } -} +impl Watcher for TcpWatcher { } pub type ConnectionCallback = ~fn(StreamWatcher, Option); impl Callback for ConnectionCallback { } @@ -184,8 +174,8 @@ pub impl TcpWatcher { let handle = malloc_handle(UV_TCP); assert!(handle.is_not_null()); assert!(0 == uvll::tcp_init(loop_.native_handle(), handle)); - let mut watcher = NativeHandle::from_native_handle(handle); - install_watcher_data(&mut watcher); + let mut watcher: TcpWatcher = NativeHandle::from_native_handle(handle); + watcher.install_watcher_data(); return watcher; } } @@ -210,8 +200,8 @@ pub impl TcpWatcher { fn connect(&mut self, address: IpAddr, cb: ConnectionCallback) { unsafe { - assert!(get_watcher_data(self).connect_cb.is_none()); - get_watcher_data(self).connect_cb = Some(cb); + assert!(self.get_watcher_data().connect_cb.is_none()); + self.get_watcher_data().connect_cb = Some(cb); let connect_handle = ConnectRequest::new().native_handle(); match address { @@ -232,7 +222,7 @@ pub impl TcpWatcher { let mut stream_watcher = connect_request.stream(); connect_request.delete(); let cb: ConnectionCallback = { - let data = get_watcher_data(&mut stream_watcher); + let data = stream_watcher.get_watcher_data(); data.connect_cb.swap_unwrap() }; let status = status_to_maybe_uv_error(stream_watcher.native_handle(), status); @@ -242,10 +232,11 @@ pub impl TcpWatcher { } fn listen(&mut self, cb: ConnectionCallback) { - // XXX: Borrowck - let data = get_watcher_data(unsafe { transmute_mut_region(self) }); - assert!(data.connect_cb.is_none()); - data.connect_cb = Some(cb); + { + let data = self.get_watcher_data(); + assert!(data.connect_cb.is_none()); + data.connect_cb = Some(cb); + } unsafe { static BACKLOG: c_int = 128; // XXX should be configurable @@ -257,7 +248,7 @@ pub impl TcpWatcher { extern fn connection_cb(handle: *uvll::uv_stream_t, status: c_int) { rtdebug!("connection_cb"); let mut stream_watcher: StreamWatcher = NativeHandle::from_native_handle(handle); - let cb = get_watcher_data(&mut stream_watcher).connect_cb.swap_unwrap(); + let cb = stream_watcher.get_watcher_data().connect_cb.swap_unwrap(); let status = status_to_maybe_uv_error(stream_watcher.native_handle(), status); cb(stream_watcher, status); } diff --git a/src/libcore/rt/uv/uvio.rs b/src/libcore/rt/uv/uvio.rs index d8858717f6f39..af3bac5b65430 100644 --- a/src/libcore/rt/uv/uvio.rs +++ b/src/libcore/rt/uv/uvio.rs @@ -192,9 +192,10 @@ impl RtioTcpListener for UvTcpListener { 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_ = loop_from_watcher(&server_stream_watcher); - let client_tcp_watcher = TcpWatcher::new(&mut loop_).as_stream(); - // XXX: Needs to be surfaced in interface + 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 { From 91ca3a9b295404e6dc36677d415573935a9989db Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 27 Apr 2013 18:50:35 -0700 Subject: [PATCH 11/55] core::rt: Reording code --- src/libcore/rt/uv/mod.rs | 229 ++++++++++++++++++++------------------- 1 file changed, 115 insertions(+), 114 deletions(-) diff --git a/src/libcore/rt/uv/mod.rs b/src/libcore/rt/uv/mod.rs index 7d5a4f00204d8..79afaf69ad35a 100644 --- a/src/libcore/rt/uv/mod.rs +++ b/src/libcore/rt/uv/mod.rs @@ -178,111 +178,6 @@ impl NativeHandle<*uvll::uv_idle_t> for IdleWatcher { } } -// XXX: Need to define the error constants like EOF so they can be -// compared to the UvError type - -pub struct UvError(uvll::uv_err_t); - -pub impl UvError { - - fn name(&self) -> ~str { - unsafe { - let inner = match self { &UvError(ref a) => a }; - let name_str = uvll::err_name(inner); - assert!(name_str.is_not_null()); - from_c_str(name_str) - } - } - - fn desc(&self) -> ~str { - unsafe { - let inner = match self { &UvError(ref a) => a }; - let desc_str = uvll::strerror(inner); - assert!(desc_str.is_not_null()); - from_c_str(desc_str) - } - } -} - -impl ToStr for UvError { - fn to_str(&self) -> ~str { - fmt!("%s: %s", self.name(), self.desc()) - } -} - -#[test] -fn error_smoke_test() { - let err = uvll::uv_err_t { code: 1, sys_errno_: 1 }; - let err: UvError = UvError(err); - assert!(err.to_str() == ~"EOF: end of file"); -} - -pub fn last_uv_error>(watcher: &W) -> UvError { - unsafe { - let loop_ = watcher.event_loop(); - UvError(uvll::last_error(loop_.native_handle())) - } -} - -pub fn uv_error_to_io_error(uverr: UvError) -> IoError { - - // XXX: Could go in str::raw - unsafe fn c_str_to_static_slice(s: *libc::c_char) -> &'static str { - let s = s as *u8; - let mut curr = s, len = 0u; - while *curr != 0u8 { - len += 1u; - curr = ptr::offset(s, len); - } - - str::raw::buf_as_slice(s, len, |d| cast::transmute(d)) - } - - - unsafe { - // Importing error constants - use rt::uv::uvll::*; - use rt::io::*; - - // uv error descriptions are static - let c_desc = uvll::strerror(&*uverr); - let desc = c_str_to_static_slice(c_desc); - - let kind = match uverr.code { - UNKNOWN => OtherIoError, - OK => OtherIoError, - EOF => EndOfFile, - EACCES => PermissionDenied, - ECONNREFUSED => ConnectionRefused, - e => { - abort!("unknown uv error code: %u", e as uint); - } - }; - - IoError { - kind: kind, - desc: desc, - detail: None - } - } -} - -/// Given a uv handle, convert a callback status to a UvError -// XXX: Follow the pattern below by parameterizing over T: Watcher, not T -pub fn status_to_maybe_uv_error(handle: *T, status: c_int) -> Option { - if status != -1 { - None - } else { - unsafe { - rtdebug!("handle: %x", handle as uint); - let loop_ = uvll::get_loop_for_uv_handle(handle); - rtdebug!("loop: %x", loop_ as uint); - let err = uvll::last_error(loop_); - Some(UvError(err)) - } - } -} - /// Callbacks used by StreamWatchers, set as custom data on the foreign handle struct WatcherData { read_cb: Option, @@ -383,21 +278,109 @@ impl> WatcherInterop for W { } } +// XXX: Need to define the error constants like EOF so they can be +// compared to the UvError type + +pub struct UvError(uvll::uv_err_t); + +pub impl UvError { + + fn name(&self) -> ~str { + unsafe { + let inner = match self { &UvError(ref a) => a }; + let name_str = uvll::err_name(inner); + assert!(name_str.is_not_null()); + from_c_str(name_str) + } + } + + fn desc(&self) -> ~str { + unsafe { + let inner = match self { &UvError(ref a) => a }; + let desc_str = uvll::strerror(inner); + assert!(desc_str.is_not_null()); + from_c_str(desc_str) + } + } +} + +impl ToStr for UvError { + fn to_str(&self) -> ~str { + fmt!("%s: %s", self.name(), self.desc()) + } +} + #[test] -fn test_slice_to_uv_buf() { - let slice = [0, .. 20]; - let buf = slice_to_uv_buf(slice); +fn error_smoke_test() { + let err = uvll::uv_err_t { code: 1, sys_errno_: 1 }; + let err: UvError = UvError(err); + assert!(err.to_str() == ~"EOF: end of file"); +} + +pub fn last_uv_error>(watcher: &W) -> UvError { + unsafe { + let loop_ = watcher.event_loop(); + UvError(uvll::last_error(loop_.native_handle())) + } +} + +pub fn uv_error_to_io_error(uverr: UvError) -> IoError { + + // XXX: Could go in str::raw + unsafe fn c_str_to_static_slice(s: *libc::c_char) -> &'static str { + let s = s as *u8; + let mut curr = s, len = 0u; + while *curr != 0u8 { + len += 1u; + curr = ptr::offset(s, len); + } + + str::raw::buf_as_slice(s, len, |d| cast::transmute(d)) + } - assert!(buf.len == 20); unsafe { - let base = transmute::<*u8, *mut u8>(buf.base); - (*base) = 1; - (*ptr::mut_offset(base, 1)) = 2; + // Importing error constants + use rt::uv::uvll::*; + use rt::io::*; + + // uv error descriptions are static + let c_desc = uvll::strerror(&*uverr); + let desc = c_str_to_static_slice(c_desc); + + let kind = match uverr.code { + UNKNOWN => OtherIoError, + OK => OtherIoError, + EOF => EndOfFile, + EACCES => PermissionDenied, + ECONNREFUSED => ConnectionRefused, + e => { + abort!("unknown uv error code: %u", e as uint); + } + }; + + IoError { + kind: kind, + desc: desc, + detail: None + } } +} - assert!(slice[0] == 1); - assert!(slice[1] == 2); +/// Given a uv handle, convert a callback status to a UvError +// XXX: Follow the pattern below by parameterizing over T: Watcher, not T +pub fn status_to_maybe_uv_error(handle: *T, status: c_int) -> Option { + if status != -1 { + None + } else { + unsafe { + rtdebug!("handle: %x", handle as uint); + let loop_ = uvll::get_loop_for_uv_handle(handle); + rtdebug!("loop: %x", loop_ as uint); + let err = uvll::last_error(loop_); + Some(UvError(err)) + } + } } /// The uv buffer type @@ -437,6 +420,24 @@ pub fn vec_from_uv_buf(buf: Buf) -> Option<~[u8]> { } } +#[test] +fn test_slice_to_uv_buf() { + let slice = [0, .. 20]; + let buf = slice_to_uv_buf(slice); + + assert!(buf.len == 20); + + unsafe { + let base = transmute::<*u8, *mut u8>(buf.base); + (*base) = 1; + (*ptr::mut_offset(base, 1)) = 2; + } + + assert!(slice[0] == 1); + assert!(slice[1] == 2); +} + + #[test] fn loop_smoke_test() { do run_in_bare_thread { From 9138feab1509e930a811d5b727e5cad61e369852 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 27 Apr 2013 19:28:40 -0700 Subject: [PATCH 12/55] core::rt: Only use one mechanism for attaching custom data to uv handles --- src/libcore/rt/uv/mod.rs | 69 +++++++++++----------------------------- 1 file changed, 19 insertions(+), 50 deletions(-) diff --git a/src/libcore/rt/uv/mod.rs b/src/libcore/rt/uv/mod.rs index 79afaf69ad35a..801a6173b1ed2 100644 --- a/src/libcore/rt/uv/mod.rs +++ b/src/libcore/rt/uv/mod.rs @@ -132,28 +132,38 @@ pub impl IdleWatcher { let handle = uvll::idle_new(); assert!(handle.is_not_null()); assert!(0 == uvll::idle_init(loop_.native_handle(), handle)); - uvll::set_data_for_uv_handle(handle, null::<()>()); - NativeHandle::from_native_handle(handle) + let mut watcher: IdleWatcher = NativeHandle::from_native_handle(handle); + watcher.install_watcher_data(); + return watcher } } fn start(&mut self, cb: IdleCallback) { + { + let data = self.get_watcher_data(); + data.idle_cb = Some(cb); + } - self.set_callback(cb); unsafe { assert!(0 == uvll::idle_start(self.native_handle(), idle_cb)) }; extern fn idle_cb(handle: *uvll::uv_idle_t, status: c_int) { - let idle_watcher: IdleWatcher = NativeHandle::from_native_handle(handle); - let cb: &IdleCallback = idle_watcher.borrow_callback(); + let mut idle_watcher: IdleWatcher = NativeHandle::from_native_handle(handle); + let data = idle_watcher.get_watcher_data(); + let cb: &IdleCallback = data.idle_cb.get_ref(); let status = status_to_maybe_uv_error(handle, status); (*cb)(idle_watcher, status); } } fn stop(&mut self) { - unsafe { assert!(0 == uvll::idle_stop(self.native_handle())); } + // NB: Not resetting the Rust idl_cb to None here because `stop` is likely + // called from *within* the idle callback, which would cause a use after free + + unsafe { + assert!(0 == uvll::idle_stop(self.native_handle())); + } } fn close(self) { @@ -162,7 +172,7 @@ pub impl IdleWatcher { extern fn close_cb(handle: *uvll::uv_idle_t) { unsafe { let mut idle_watcher: IdleWatcher = NativeHandle::from_native_handle(handle); - idle_watcher.drop_callback::(); + idle_watcher.drop_watcher_data(); uvll::idle_delete(handle); } } @@ -185,13 +195,11 @@ struct WatcherData { connect_cb: Option, close_cb: Option, alloc_cb: Option, + idle_cb: Option } pub trait WatcherInterop { fn event_loop(&self) -> Loop; - fn set_callback(&mut self, cb: CB); - fn drop_callback(&mut self); - fn borrow_callback(&self) -> &CB; fn install_watcher_data(&mut self); fn get_watcher_data<'r>(&'r mut self) -> &'r mut WatcherData; fn drop_watcher_data(&mut self); @@ -207,46 +215,6 @@ impl> WatcherInterop for W { } } - /// Set the custom data on a handle to a callback Note: This is only - /// suitable for watchers that make just one type of callback. For - /// others use WatcherData - pub fn set_callback(&mut self, cb: CB) { - unsafe { - self.drop_callback::(); - - // XXX: Boxing the callback so it fits into a - // pointer. Unfortunate extra allocation - let boxed_cb = ~cb; - let data = transmute::<~CB, *c_void>(boxed_cb); - uvll::set_data_for_uv_handle(self.native_handle(), data); - } - } - - /// Delete a callback from a handle's custom data - pub fn drop_callback(&mut self) { - unsafe { - let handle = self.native_handle(); - let handle_data: *c_void = uvll::get_data_for_uv_handle(handle); - if handle_data.is_not_null() { - // Take ownership of the callback and drop it - let _cb = transmute::<*c_void, ~CB>(handle_data); - // Make sure the pointer is zeroed - uvll::set_data_for_uv_handle(self.native_handle(), null::<()>()); - } - } - } - - /// Take a pointer to the callback installed as custom data - pub fn borrow_callback(&self) -> &CB { - unsafe { - let handle = self.native_handle(); - let handle_data: *c_void = uvll::get_data_for_uv_handle(handle); - assert!(handle_data.is_not_null()); - let cb = transmute::<&*c_void, &~CB>(&handle_data); - return &**cb; - } - } - pub fn install_watcher_data(&mut self) { unsafe { let data = ~WatcherData { @@ -255,6 +223,7 @@ impl> WatcherInterop for W { connect_cb: None, close_cb: None, alloc_cb: None, + idle_cb: None }; let data = transmute::<~WatcherData, *c_void>(data); uvll::set_data_for_uv_handle(self.native_handle(), data); From dbf89664aa9f5414aa02b4c1ab2b481581de17b9 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 27 Apr 2013 19:36:08 -0700 Subject: [PATCH 13/55] core::rt: Move the implementation of IdleWatcher to its own file --- src/libcore/rt/uv/idle.rs | 83 +++++++++++++++++++++++++++++++++++++++ src/libcore/rt/uv/mod.rs | 71 +-------------------------------- src/libcore/rt/uv/uvio.rs | 1 + 3 files changed, 86 insertions(+), 69 deletions(-) create mode 100644 src/libcore/rt/uv/idle.rs diff --git a/src/libcore/rt/uv/idle.rs b/src/libcore/rt/uv/idle.rs new file mode 100644 index 0000000000000..aba5b3df9370e --- /dev/null +++ b/src/libcore/rt/uv/idle.rs @@ -0,0 +1,83 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use libc::c_int; +use option::{Option, Some, None}; +use rt::uv::uvll; +use rt::uv::{Watcher, Callback, Loop, UvError, NativeHandle}; +use rt::uv::status_to_maybe_uv_error; + +pub struct IdleWatcher(*uvll::uv_idle_t); +impl Watcher for IdleWatcher { } + +pub type IdleCallback = ~fn(IdleWatcher, Option); +impl Callback for IdleCallback { } + +pub impl IdleWatcher { + fn new(loop_: &mut Loop) -> IdleWatcher { + unsafe { + let handle = uvll::idle_new(); + assert!(handle.is_not_null()); + assert!(0 == uvll::idle_init(loop_.native_handle(), handle)); + let mut watcher: IdleWatcher = NativeHandle::from_native_handle(handle); + watcher.install_watcher_data(); + return watcher + } + } + + fn start(&mut self, cb: IdleCallback) { + { + let data = self.get_watcher_data(); + data.idle_cb = Some(cb); + } + + unsafe { + assert!(0 == uvll::idle_start(self.native_handle(), idle_cb)) + }; + + extern fn idle_cb(handle: *uvll::uv_idle_t, status: c_int) { + let mut idle_watcher: IdleWatcher = NativeHandle::from_native_handle(handle); + let data = idle_watcher.get_watcher_data(); + let cb: &IdleCallback = data.idle_cb.get_ref(); + let status = status_to_maybe_uv_error(handle, status); + (*cb)(idle_watcher, status); + } + } + + fn stop(&mut self) { + // NB: Not resetting the Rust idle_cb to None here because `stop` is likely + // called from *within* the idle callback, causing a use after free + + unsafe { + assert!(0 == uvll::idle_stop(self.native_handle())); + } + } + + fn close(self) { + unsafe { uvll::close(self.native_handle(), close_cb) }; + + extern fn close_cb(handle: *uvll::uv_idle_t) { + unsafe { + let mut idle_watcher: IdleWatcher = NativeHandle::from_native_handle(handle); + idle_watcher.drop_watcher_data(); + uvll::idle_delete(handle); + } + } + } +} + +impl NativeHandle<*uvll::uv_idle_t> for IdleWatcher { + fn from_native_handle(handle: *uvll::uv_idle_t) -> IdleWatcher { + IdleWatcher(handle) + } + fn native_handle(&self) -> *uvll::uv_idle_t { + match self { &IdleWatcher(ptr) => ptr } + } +} diff --git a/src/libcore/rt/uv/mod.rs b/src/libcore/rt/uv/mod.rs index 801a6173b1ed2..79eda96a25fe9 100644 --- a/src/libcore/rt/uv/mod.rs +++ b/src/libcore/rt/uv/mod.rs @@ -54,7 +54,7 @@ use rt::io::IoError; pub use self::file::{FsRequest, FsCallback}; pub use self::net::{StreamWatcher, TcpWatcher}; pub use self::net::{ReadCallback, AllocCallback, ConnectionCallback, ConnectCallback}; - +pub use self::idle::{IdleWatcher, IdleCallback}; /// The implementation of `rtio` for libuv pub mod uvio; @@ -64,6 +64,7 @@ pub mod uvll; pub mod file; pub mod net; +pub mod idle; /// A trait for callbacks to implement. Provides a little extra type safety /// for generic, unsafe interop functions like `set_watcher_callback`. @@ -120,74 +121,6 @@ impl NativeHandle<*uvll::uv_loop_t> for Loop { } } -pub struct IdleWatcher(*uvll::uv_idle_t); -impl Watcher for IdleWatcher { } - -pub type IdleCallback = ~fn(IdleWatcher, Option); -impl Callback for IdleCallback { } - -pub impl IdleWatcher { - fn new(loop_: &mut Loop) -> IdleWatcher { - unsafe { - let handle = uvll::idle_new(); - assert!(handle.is_not_null()); - assert!(0 == uvll::idle_init(loop_.native_handle(), handle)); - let mut watcher: IdleWatcher = NativeHandle::from_native_handle(handle); - watcher.install_watcher_data(); - return watcher - } - } - - fn start(&mut self, cb: IdleCallback) { - { - let data = self.get_watcher_data(); - data.idle_cb = Some(cb); - } - - unsafe { - assert!(0 == uvll::idle_start(self.native_handle(), idle_cb)) - }; - - extern fn idle_cb(handle: *uvll::uv_idle_t, status: c_int) { - let mut idle_watcher: IdleWatcher = NativeHandle::from_native_handle(handle); - let data = idle_watcher.get_watcher_data(); - let cb: &IdleCallback = data.idle_cb.get_ref(); - let status = status_to_maybe_uv_error(handle, status); - (*cb)(idle_watcher, status); - } - } - - fn stop(&mut self) { - // NB: Not resetting the Rust idl_cb to None here because `stop` is likely - // called from *within* the idle callback, which would cause a use after free - - unsafe { - assert!(0 == uvll::idle_stop(self.native_handle())); - } - } - - fn close(self) { - unsafe { uvll::close(self.native_handle(), close_cb) }; - - extern fn close_cb(handle: *uvll::uv_idle_t) { - unsafe { - let mut idle_watcher: IdleWatcher = NativeHandle::from_native_handle(handle); - idle_watcher.drop_watcher_data(); - uvll::idle_delete(handle); - } - } - } -} - -impl NativeHandle<*uvll::uv_idle_t> for IdleWatcher { - fn from_native_handle(handle: *uvll::uv_idle_t) -> IdleWatcher { - IdleWatcher(handle) - } - fn native_handle(&self) -> *uvll::uv_idle_t { - match self { &IdleWatcher(ptr) => ptr } - } -} - /// Callbacks used by StreamWatchers, set as custom data on the foreign handle struct WatcherData { read_cb: Option, diff --git a/src/libcore/rt/uv/uvio.rs b/src/libcore/rt/uv/uvio.rs index af3bac5b65430..0c52bca0dc83a 100644 --- a/src/libcore/rt/uv/uvio.rs +++ b/src/libcore/rt/uv/uvio.rs @@ -17,6 +17,7 @@ use cast::transmute; use rt::io::IoError; use rt::io::net::ip::IpAddr; use rt::uv::*; +use rt::uv::idle::IdleWatcher; use rt::rtio::*; use rt::sched::{Scheduler, local_sched}; use rt::io::{standard_error, OtherIoError}; From a134503d74814b40a07c31133a72638dc29ba6fc Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 27 Apr 2013 19:53:42 -0700 Subject: [PATCH 14/55] core::rt: Move all the uv callback definitions to one place --- src/libcore/rt/uv/file.rs | 6 +----- src/libcore/rt/uv/idle.rs | 7 ++----- src/libcore/rt/uv/mod.rs | 35 ++++++++++++++++++----------------- src/libcore/rt/uv/net.rs | 17 ++--------------- 4 files changed, 23 insertions(+), 42 deletions(-) diff --git a/src/libcore/rt/uv/file.rs b/src/libcore/rt/uv/file.rs index 816a3a10a9016..2d14505509759 100644 --- a/src/libcore/rt/uv/file.rs +++ b/src/libcore/rt/uv/file.rs @@ -11,15 +11,11 @@ use prelude::*; use ptr::null; use libc::c_void; -use super::{UvError, Callback, Request, NativeHandle, Loop}; +use rt::uv::{Request, NativeHandle, Loop, FsCallback}; use rt::uv::uvll; use rt::uv::uvll::*; -pub type FsCallback = ~fn(FsRequest, Option); -impl Callback for FsCallback { } - pub struct FsRequest(*uvll::uv_fs_t); - impl Request for FsRequest; impl FsRequest { diff --git a/src/libcore/rt/uv/idle.rs b/src/libcore/rt/uv/idle.rs index aba5b3df9370e..fe1ce8697bf9e 100644 --- a/src/libcore/rt/uv/idle.rs +++ b/src/libcore/rt/uv/idle.rs @@ -9,17 +9,14 @@ // except according to those terms. use libc::c_int; -use option::{Option, Some, None}; +use option::{Some, None}; use rt::uv::uvll; -use rt::uv::{Watcher, Callback, Loop, UvError, NativeHandle}; +use rt::uv::{Watcher, Loop, NativeHandle, IdleCallback}; use rt::uv::status_to_maybe_uv_error; pub struct IdleWatcher(*uvll::uv_idle_t); impl Watcher for IdleWatcher { } -pub type IdleCallback = ~fn(IdleWatcher, Option); -impl Callback for IdleCallback { } - pub impl IdleWatcher { fn new(loop_: &mut Loop) -> IdleWatcher { unsafe { diff --git a/src/libcore/rt/uv/mod.rs b/src/libcore/rt/uv/mod.rs index 79eda96a25fe9..2c83873359a8f 100644 --- a/src/libcore/rt/uv/mod.rs +++ b/src/libcore/rt/uv/mod.rs @@ -51,10 +51,9 @@ use rt::io::IoError; #[cfg(test)] use unstable::run_in_bare_thread; -pub use self::file::{FsRequest, FsCallback}; +pub use self::file::FsRequest; pub use self::net::{StreamWatcher, TcpWatcher}; -pub use self::net::{ReadCallback, AllocCallback, ConnectionCallback, ConnectCallback}; -pub use self::idle::{IdleWatcher, IdleCallback}; +pub use self::idle::IdleWatcher; /// The implementation of `rtio` for libuv pub mod uvio; @@ -66,11 +65,12 @@ pub mod file; pub mod net; pub mod idle; -/// A trait for callbacks to implement. Provides a little extra type safety -/// for generic, unsafe interop functions like `set_watcher_callback`. -pub trait Callback { } - -pub trait Request { } +/// XXX: Loop(*handle) is buggy with destructors. Normal structs +/// with dtors may not be destructured, but tuple structs can, +/// but the results are not correct. +pub struct Loop { + handle: *uvll::uv_loop_t +} /// The trait implemented by uv 'watchers' (handles). Watchers are /// non-owning wrappers around the uv handles and are not completely @@ -80,8 +80,7 @@ pub trait Request { } /// entirely memory safe if used in unanticipated patterns. pub trait Watcher { } -pub type NullCallback = ~fn(); -impl Callback for NullCallback { } +pub trait Request { } /// A type that wraps a native handle pub trait NativeHandle { @@ -89,13 +88,6 @@ pub trait NativeHandle { pub fn native_handle(&self) -> T; } -/// XXX: Loop(*handle) is buggy with destructors. Normal structs -/// with dtors may not be destructured, but tuple structs can, -/// but the results are not correct. -pub struct Loop { - handle: *uvll::uv_loop_t -} - pub impl Loop { fn new() -> Loop { let handle = unsafe { uvll::loop_new() }; @@ -121,6 +113,15 @@ impl NativeHandle<*uvll::uv_loop_t> for Loop { } } +// XXX: The uv alloc callback also has a *uv_handle_t arg +pub type AllocCallback = ~fn(uint) -> Buf; +pub type ReadCallback = ~fn(StreamWatcher, int, Buf, Option); +pub type NullCallback = ~fn(); +pub type IdleCallback = ~fn(IdleWatcher, Option); +pub type ConnectionCallback = ~fn(StreamWatcher, Option); +pub type FsCallback = ~fn(FsRequest, Option); + + /// Callbacks used by StreamWatchers, set as custom data on the foreign handle struct WatcherData { read_cb: Option, diff --git a/src/libcore/rt/uv/net.rs b/src/libcore/rt/uv/net.rs index 1209609347aec..6261996a8b650 100644 --- a/src/libcore/rt/uv/net.rs +++ b/src/libcore/rt/uv/net.rs @@ -13,7 +13,8 @@ use libc::{size_t, ssize_t, c_int, c_void}; use util::ignore; use rt::uv::uvll; use rt::uv::uvll::*; -use super::{Loop, Watcher, Request, UvError, Buf, Callback, NativeHandle, NullCallback, +use rt::uv::{AllocCallback, ConnectionCallback, ReadCallback}; +use super::{Loop, Watcher, Request, UvError, Buf, NativeHandle, NullCallback, status_to_maybe_uv_error, vec_to_uv_buf, vec_from_uv_buf, slice_to_uv_buf}; use super::super::io::net::ip::{IpAddr, Ipv4, Ipv6}; use rt::uv::last_uv_error; @@ -48,13 +49,6 @@ fn ip4_as_uv_ip4(addr: IpAddr, f: &fn(*sockaddr_in) -> T) -> T { pub struct StreamWatcher(*uvll::uv_stream_t); impl Watcher for StreamWatcher { } -pub type ReadCallback = ~fn(StreamWatcher, int, Buf, Option); -impl Callback for ReadCallback { } - -// XXX: The uv alloc callback also has a *uv_handle_t arg -pub type AllocCallback = ~fn(uint) -> Buf; -impl Callback for AllocCallback { } - pub impl StreamWatcher { fn read_start(&mut self, alloc: AllocCallback, cb: ReadCallback) { @@ -165,9 +159,6 @@ impl NativeHandle<*uvll::uv_stream_t> for StreamWatcher { pub struct TcpWatcher(*uvll::uv_tcp_t); impl Watcher for TcpWatcher { } -pub type ConnectionCallback = ~fn(StreamWatcher, Option); -impl Callback for ConnectionCallback { } - pub impl TcpWatcher { fn new(loop_: &mut Loop) -> TcpWatcher { unsafe { @@ -268,12 +259,8 @@ impl NativeHandle<*uvll::uv_tcp_t> for TcpWatcher { } } -pub type ConnectCallback = ~fn(ConnectRequest, Option); -impl Callback for ConnectCallback { } - // uv_connect_t is a subclass of uv_req_t struct ConnectRequest(*uvll::uv_connect_t); - impl Request for ConnectRequest { } impl ConnectRequest { From ad6719ee0b4977c949a34b7a2250ed7274cf442e Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 2 May 2013 22:44:20 -0700 Subject: [PATCH 15/55] core::rt: Just a small fix to TcpStream --- src/libcore/rt/io/net/tcp.rs | 26 ++++++++++++++++++++++++++ src/libcore/rt/uv/net.rs | 7 ++++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/libcore/rt/io/net/tcp.rs b/src/libcore/rt/io/net/tcp.rs index ae4a7a2039717..a669509a48a35 100644 --- a/src/libcore/rt/io/net/tcp.rs +++ b/src/libcore/rt/io/net/tcp.rs @@ -171,4 +171,30 @@ mod test { } } } + + #[test] #[ignore] + fn multiple_connect_serial() { + do run_in_newsched_task { + let addr = next_test_ip4(); + let max = 100; + + do spawntask_immediately { + let mut listener = TcpListener::bind(addr); + for max.times { + let mut stream = listener.accept(); + let mut buf = [0]; + stream.read(buf); + assert!(buf[0] == 99); + } + } + + do spawntask_immediately { + for max.times { + let mut stream = TcpStream::connect(addr); + stream.write([99]); + } + } + } + } + } diff --git a/src/libcore/rt/uv/net.rs b/src/libcore/rt/uv/net.rs index 6261996a8b650..b22adafdf2d9e 100644 --- a/src/libcore/rt/uv/net.rs +++ b/src/libcore/rt/uv/net.rs @@ -239,9 +239,10 @@ pub impl TcpWatcher { extern fn connection_cb(handle: *uvll::uv_stream_t, status: c_int) { rtdebug!("connection_cb"); let mut stream_watcher: StreamWatcher = NativeHandle::from_native_handle(handle); - let cb = stream_watcher.get_watcher_data().connect_cb.swap_unwrap(); - let status = status_to_maybe_uv_error(stream_watcher.native_handle(), status); - cb(stream_watcher, status); + let data = stream_watcher.get_watcher_data(); + let cb = data.connect_cb.get_ref(); + let status = status_to_maybe_uv_error(handle, status); + (*cb)(stream_watcher, status); } } From 10355d7a7d1ba87d49149e5fcdc5d3955932afe2 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 27 Apr 2013 18:57:15 -0700 Subject: [PATCH 16/55] core::rt Wire up logging to newsched tasks --- src/libcore/core.rc | 5 ++- src/libcore/logging.rs | 68 +++++++++++++++++++++++--------- src/libcore/macros.rs | 4 +- src/libcore/rt/local_services.rs | 24 +++++++++-- src/libcore/rt/logging.rs | 38 ++++++++++++++++++ src/libcore/rt/mod.rs | 3 ++ 6 files changed, 117 insertions(+), 25 deletions(-) create mode 100644 src/libcore/rt/logging.rs diff --git a/src/libcore/core.rc b/src/libcore/core.rc index d029fbc07f6fb..073a6dcf4d259 100644 --- a/src/libcore/core.rc +++ b/src/libcore/core.rc @@ -246,8 +246,11 @@ mod unicode; #[path = "num/cmath.rs"] mod cmath; mod stackwalk; + +// XXX: This shouldn't be pub, and it should be reexported under 'unstable' +// but name resolution doesn't work without it being pub. #[path = "rt/mod.rs"] -mod rt; +pub mod rt; // A curious inner-module that's not exported that contains the binding // 'core' so that macro-expanded references to core::error and such diff --git a/src/libcore/logging.rs b/src/libcore/logging.rs index 69ecad56a8f5c..4ecd72ebd0c00 100644 --- a/src/libcore/logging.rs +++ b/src/libcore/logging.rs @@ -10,17 +10,15 @@ //! Logging -pub mod rustrt { - use libc; - - pub extern { - unsafe fn rust_log_console_on(); - unsafe fn rust_log_console_off(); - unsafe fn rust_log_str(level: u32, - string: *libc::c_char, - size: libc::size_t); - } -} +use option::*; +use either::*; +use rt; +use rt::logging::{Logger, StdErrLogger}; +use io; +use libc; +use repr; +use vec; +use cast; /// Turns on logging to stdout globally pub fn console_on() { @@ -45,17 +43,51 @@ pub fn console_off() { #[cfg(not(test))] #[lang="log_type"] pub fn log_type(level: u32, object: &T) { - use cast::transmute; - use io; - use libc; - use repr; - use vec; let bytes = do io::with_bytes_writer |writer| { repr::write_repr(writer, object); }; + + match rt::context() { + rt::OldTaskContext => { + unsafe { + let len = bytes.len() as libc::size_t; + rustrt::rust_log_str(level, cast::transmute(vec::raw::to_ptr(bytes)), len); + } + } + _ => { + // XXX: Bad allocation + let msg = bytes.to_str(); + newsched_log_str(msg); + } + } +} + +fn newsched_log_str(msg: ~str) { + unsafe { - let len = bytes.len() as libc::size_t; - rustrt::rust_log_str(level, transmute(vec::raw::to_ptr(bytes)), len); + match rt::local_services::unsafe_try_borrow_local_services() { + Some(local) => { + // Use the available logger + (*local).logger.log(Left(msg)); + } + None => { + // There is no logger anywhere, just write to stderr + let mut logger = StdErrLogger; + logger.log(Left(msg)); + } + } + } +} + +pub mod rustrt { + use libc; + + pub extern { + unsafe fn rust_log_console_on(); + unsafe fn rust_log_console_off(); + unsafe fn rust_log_str(level: u32, + string: *libc::c_char, + size: libc::size_t); } } diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs index b2e94f327c86e..c4f0384f71ed6 100644 --- a/src/libcore/macros.rs +++ b/src/libcore/macros.rs @@ -11,7 +11,7 @@ #[macro_escape]; // Some basic logging -macro_rules! rtdebug_ ( +macro_rules! rtdebug ( ($( $arg:expr),+) => ( { dumb_println(fmt!( $($arg),+ )); @@ -26,7 +26,7 @@ macro_rules! rtdebug_ ( ) // An alternate version with no output, for turning off logging -macro_rules! rtdebug ( +macro_rules! rtdebug_ ( ($( $arg:expr),+) => ( $(let _ = $arg)*; ) ) diff --git a/src/libcore/rt/local_services.rs b/src/libcore/rt/local_services.rs index 94840d7b5d584..b8bf6e067801f 100644 --- a/src/libcore/rt/local_services.rs +++ b/src/libcore/rt/local_services.rs @@ -23,19 +23,19 @@ use libc::{c_void, uintptr_t}; use cast::transmute; use super::sched::local_sched; use super::local_heap::LocalHeap; +use rt::logging::{Logger, StdErrLogger}; pub struct LocalServices { heap: LocalHeap, gc: GarbageCollector, storage: LocalStorage, - logger: Logger, + logger: StdErrLogger, unwinder: Option, destroyed: bool } pub struct GarbageCollector; pub struct LocalStorage(*c_void, Option<~fn(*c_void)>); -pub struct Logger; pub struct Unwinder { unwinding: bool, @@ -47,7 +47,7 @@ impl LocalServices { heap: LocalHeap::new(), gc: GarbageCollector, storage: LocalStorage(ptr::null(), None), - logger: Logger, + logger: StdErrLogger, unwinder: Some(Unwinder { unwinding: false }), destroyed: false } @@ -58,7 +58,7 @@ impl LocalServices { heap: LocalHeap::new(), gc: GarbageCollector, storage: LocalStorage(ptr::null(), None), - logger: Logger, + logger: StdErrLogger, unwinder: None, destroyed: false } @@ -184,6 +184,14 @@ pub unsafe fn unsafe_borrow_local_services() -> *mut LocalServices { } } +pub unsafe fn unsafe_try_borrow_local_services() -> Option<*mut LocalServices> { + if local_sched::exists() { + Some(unsafe_borrow_local_services()) + } else { + None + } +} + #[cfg(test)] mod test { use rt::test::*; @@ -231,4 +239,12 @@ mod test { let _ = r.next(); } } + + #[test] + fn logging() { + do run_in_newsched_task() { + info!("here i am. logging in a newsched task"); + } + } } + diff --git a/src/libcore/rt/logging.rs b/src/libcore/rt/logging.rs new file mode 100644 index 0000000000000..4ed09fd829f22 --- /dev/null +++ b/src/libcore/rt/logging.rs @@ -0,0 +1,38 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use either::*; + +pub trait Logger { + fn log(&mut self, msg: Either<~str, &'static str>); +} + +pub struct StdErrLogger; + +impl Logger for StdErrLogger { + fn log(&mut self, msg: Either<~str, &'static str>) { + use io::{Writer, WriterUtil}; + + let s: &str = match msg { + Left(ref s) => { + let s: &str = *s; + s + } + Right(ref s) => { + let s: &str = *s; + s + } + }; + let dbg = ::libc::STDERR_FILENO as ::io::fd_t; + dbg.write_str(s); + dbg.write_str("\n"); + dbg.flush(); + } +} \ No newline at end of file diff --git a/src/libcore/rt/mod.rs b/src/libcore/rt/mod.rs index b5fba51ca7f4d..c7cdb277247cb 100644 --- a/src/libcore/rt/mod.rs +++ b/src/libcore/rt/mod.rs @@ -58,6 +58,9 @@ pub mod env; /// The local, managed heap mod local_heap; +/// The Logger trait and implementations +pub mod logging; + /// Tools for testing the runtime #[cfg(test)] pub mod test; From 272c3c2cfb1008ffc4437599ce94aad59e0b72b7 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 3 May 2013 13:26:41 -0700 Subject: [PATCH 17/55] Tidy --- src/libcore/logging.rs | 1 - src/libcore/rt/io/net/tcp.rs | 6 +++--- src/libcore/rt/local_sched.rs | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/libcore/logging.rs b/src/libcore/logging.rs index 4ecd72ebd0c00..70195afb20a84 100644 --- a/src/libcore/logging.rs +++ b/src/libcore/logging.rs @@ -64,7 +64,6 @@ pub fn log_type(level: u32, object: &T) { } fn newsched_log_str(msg: ~str) { - unsafe { match rt::local_services::unsafe_try_borrow_local_services() { Some(local) => { diff --git a/src/libcore/rt/io/net/tcp.rs b/src/libcore/rt/io/net/tcp.rs index a669509a48a35..49acc53f4f125 100644 --- a/src/libcore/rt/io/net/tcp.rs +++ b/src/libcore/rt/io/net/tcp.rs @@ -56,7 +56,7 @@ impl Reader for TcpStream { match bytes_read { Ok(read) => Some(read), Err(_) => { - abort!("TODO"); + abort!("XXX"); } } } @@ -70,7 +70,7 @@ impl Writer for TcpStream { match res { Ok(_) => (), Err(_) => { - abort!("TODO"); + abort!("XXX"); } } } @@ -107,7 +107,7 @@ impl Listener for TcpListener { Some(TcpStream::new(s)) } Err(_) => { - abort!("TODO"); + abort!("XXX"); } } } diff --git a/src/libcore/rt/local_sched.rs b/src/libcore/rt/local_sched.rs index ef159d9fe5dde..44dbd55373fc9 100644 --- a/src/libcore/rt/local_sched.rs +++ b/src/libcore/rt/local_sched.rs @@ -63,7 +63,7 @@ pub fn borrow(f: &fn(&mut Scheduler)) { unsafe { let unsafe_sched = cast::transmute_mut_region(&mut *sched); let sched = Cell(sched); - + do (|| { f(unsafe_sched); }).finally { From 936fce551587fd5584722022252f489c88be292c Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 3 May 2013 15:48:18 -0700 Subject: [PATCH 18/55] Warnings --- src/libcore/rt/uv/net.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/rt/uv/net.rs b/src/libcore/rt/uv/net.rs index b22adafdf2d9e..550acd8aee400 100644 --- a/src/libcore/rt/uv/net.rs +++ b/src/libcore/rt/uv/net.rs @@ -10,7 +10,6 @@ use prelude::*; use libc::{size_t, ssize_t, c_int, c_void}; -use util::ignore; use rt::uv::uvll; use rt::uv::uvll::*; use rt::uv::{AllocCallback, ConnectionCallback, ReadCallback}; @@ -19,6 +18,7 @@ use super::{Loop, Watcher, Request, UvError, Buf, NativeHandle, NullCallback, use super::super::io::net::ip::{IpAddr, Ipv4, Ipv6}; use rt::uv::last_uv_error; +#[cfg(test)] use util::ignore; #[cfg(test)] use cell::Cell; #[cfg(test)] use unstable::run_in_bare_thread; #[cfg(test)] use super::super::thread::Thread; From 40a9de5ebc8bea4d0a42a89e657a35a5b07d4042 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 3 May 2013 16:58:20 -0700 Subject: [PATCH 19/55] core::rt: Add a very simple ref counted pointer --- src/libcore/rt/mod.rs | 3 + src/libcore/rt/rc.rs | 143 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+) create mode 100644 src/libcore/rt/rc.rs diff --git a/src/libcore/rt/mod.rs b/src/libcore/rt/mod.rs index c7cdb277247cb..ce3fb71ef2c4e 100644 --- a/src/libcore/rt/mod.rs +++ b/src/libcore/rt/mod.rs @@ -65,6 +65,9 @@ pub mod logging; #[cfg(test)] pub mod test; +/// Reference counting +pub mod rc; + /// Set up a default runtime configuration, given compiler-supplied arguments. /// /// This is invoked by the `start` _language item_ (unstable::lang) to diff --git a/src/libcore/rt/rc.rs b/src/libcore/rt/rc.rs new file mode 100644 index 0000000000000..2ee254466e4b0 --- /dev/null +++ b/src/libcore/rt/rc.rs @@ -0,0 +1,143 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! An owned, task-local, reference counted type +//! +//! # Safety note +//! +//! XXX There is currently no type-system mechanism for enforcing that +//! reference counted types are both allocated on the exchange heap +//! and also non-sendable +//! +//! This doesn't prevent borrowing multiple aliasable mutable pointers + +use ops::Drop; +use clone::Clone; +use libc::c_void; +use sys; +use cast; + +pub struct RC { + p: *c_void // ~(uint, T) +} + +impl RC { + pub fn new(val: T) -> RC { + unsafe { + let v = ~(1, val); + let p: *c_void = cast::transmute(v); + RC { p: p } + } + } + + fn get_mut_state(&mut self) -> *mut (uint, T) { + unsafe { + let p: &mut ~(uint, T) = cast::transmute(&mut self.p); + let p: *mut (uint, T) = &mut **p; + return p; + } + } + + fn get_state(&self) -> *(uint, T) { + unsafe { + let p: &~(uint, T) = cast::transmute(&self.p); + let p: *(uint, T) = &**p; + return p; + } + } + + pub fn unsafe_borrow_mut(&mut self) -> *mut T { + unsafe { + match *self.get_mut_state() { + (_, ref mut p) => { + let p: *mut T = p; + return p; + } + } + } + } + + pub fn refcount(&self) -> uint { + unsafe { + match *self.get_state() { + (count, _) => count + } + } + } +} + +#[unsafe_destructor] +impl Drop for RC { + fn finalize(&self) { + assert!(self.refcount() > 0); + + unsafe { + // XXX: Mutable finalizer + let this: &mut RC = cast::transmute_mut(self); + + match *this.get_mut_state() { + (ref mut count, _) => { + *count = *count - 1 + } + } + + if this.refcount() == 0 { + let _: ~(uint, T) = cast::transmute(this.p); + } + } + } +} + +impl Clone for RC { + fn clone(&self) -> RC { + unsafe { + // XXX: Mutable clone + let this: &mut RC = cast::transmute_mut(self); + + match *this.get_mut_state() { + (ref mut count, _) => { + *count = *count + 1; + } + } + } + + RC { p: self.p } + } +} + +#[cfg(test)] +mod test { + use super::RC; + + #[test] + fn smoke_test() { + unsafe { + let mut v1 = RC::new(100); + assert!(*v1.unsafe_borrow_mut() == 100); + assert!(v1.refcount() == 1); + + let mut v2 = v1.clone(); + assert!(*v2.unsafe_borrow_mut() == 100); + assert!(v2.refcount() == 2); + + *v2.unsafe_borrow_mut() = 200; + assert!(*v2.unsafe_borrow_mut() == 200); + assert!(*v1.unsafe_borrow_mut() == 200); + + let v3 = v2.clone(); + assert!(v3.refcount() == 3); + { + let _v1 = v1; + let _v2 = v2; + } + assert!(v3.refcount() == 1); + } + } +} From 414f3c7d252fcd54562c0c8a85499d7d07f5e612 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 4 May 2013 17:30:31 -0700 Subject: [PATCH 20/55] core::rt: Add a simple channel type for passing buffered messages between Scheduler and Task Called 'Tube' for lack of anything better. --- src/libcore/logging.rs | 3 +- src/libcore/rt/mod.rs | 6 +- src/libcore/rt/tube.rs | 182 +++++++++++++++++++++++++++++++++++++++++ src/libcore/sys.rs | 29 +++++-- 4 files changed, 212 insertions(+), 8 deletions(-) create mode 100644 src/libcore/rt/tube.rs diff --git a/src/libcore/logging.rs b/src/libcore/logging.rs index 70195afb20a84..4308d22548f43 100644 --- a/src/libcore/logging.rs +++ b/src/libcore/logging.rs @@ -19,6 +19,7 @@ use libc; use repr; use vec; use cast; +use str; /// Turns on logging to stdout globally pub fn console_on() { @@ -57,7 +58,7 @@ pub fn log_type(level: u32, object: &T) { } _ => { // XXX: Bad allocation - let msg = bytes.to_str(); + let msg = str::from_bytes(bytes); newsched_log_str(msg); } } diff --git a/src/libcore/rt/mod.rs b/src/libcore/rt/mod.rs index ce3fb71ef2c4e..b2ba6d7d3c407 100644 --- a/src/libcore/rt/mod.rs +++ b/src/libcore/rt/mod.rs @@ -18,7 +18,7 @@ use libc::c_char; mod sched; /// Thread-local access to the current Scheduler -mod local_sched; +pub mod local_sched; /// Synchronous I/O #[path = "io/mod.rs"] @@ -68,6 +68,10 @@ pub mod test; /// Reference counting pub mod rc; +/// A simple single-threaded channel type for passing buffered data between +/// scheduler and task context +pub mod tube; + /// Set up a default runtime configuration, given compiler-supplied arguments. /// /// This is invoked by the `start` _language item_ (unstable::lang) to diff --git a/src/libcore/rt/tube.rs b/src/libcore/rt/tube.rs new file mode 100644 index 0000000000000..ef376199fcbdc --- /dev/null +++ b/src/libcore/rt/tube.rs @@ -0,0 +1,182 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A very simple unsynchronized channel type for sending buffered data from +//! scheduler context to task context. +//! +//! XXX: This would be safer to use if split into two types like Port/Chan + +use option::*; +use clone::Clone; +use super::rc::RC; +use rt::sched::Task; +use rt::{context, TaskContext, SchedulerContext}; +use rt::local_sched; + +struct TubeState { + blocked_task: Option<~Task>, + buf: ~[T] +} + +pub struct Tube { + p: RC> +} + +impl Tube { + pub fn new() -> Tube { + Tube { + p: RC::new(TubeState { + blocked_task: None, + buf: ~[] + }) + } + } + + pub fn send(&mut self, val: T) { + rtdebug!("tube send"); + assert!(context() == SchedulerContext); + + unsafe { + let state = self.p.unsafe_borrow_mut(); + (*state).buf.push(val); + + if (*state).blocked_task.is_some() { + // There's a waiting task. Wake it up + rtdebug!("waking blocked tube"); + let task = (*state).blocked_task.swap_unwrap(); + let sched = local_sched::take(); + sched.resume_task_immediately(task); + } + } + } + + pub fn recv(&mut self) -> T { + assert!(context() == TaskContext); + + unsafe { + let state = self.p.unsafe_borrow_mut(); + if !(*state).buf.is_empty() { + return (*state).buf.shift(); + } else { + // Block and wait for the next message + rtdebug!("blocking on tube recv"); + assert!(self.p.refcount() > 1); // There better be somebody to wake us up + assert!((*state).blocked_task.is_none()); + let sched = local_sched::take(); + do sched.deschedule_running_task_and_then |task| { + (*state).blocked_task = Some(task); + } + rtdebug!("waking after tube recv"); + let buf = &mut (*state).buf; + assert!(!buf.is_empty()); + return buf.shift(); + } + } + } +} + +impl Clone for Tube { + fn clone(&self) -> Tube { + Tube { p: self.p.clone() } + } +} + +#[cfg(test)] +mod test { + use int; + use cell::Cell; + use rt::local_sched; + use rt::test::*; + use rt::rtio::EventLoop; + use super::*; + + #[test] + fn simple_test() { + do run_in_newsched_task { + let mut tube: Tube = Tube::new(); + let tube_clone = tube.clone(); + let tube_clone_cell = Cell(tube_clone); + let sched = local_sched::take(); + do sched.deschedule_running_task_and_then |task| { + let mut tube_clone = tube_clone_cell.take(); + tube_clone.send(1); + let sched = local_sched::take(); + sched.resume_task_immediately(task); + } + + assert!(tube.recv() == 1); + } + } + + #[test] + fn blocking_test() { + do run_in_newsched_task { + let mut tube: Tube = Tube::new(); + let tube_clone = tube.clone(); + let tube_clone = Cell(Cell(Cell(tube_clone))); + let sched = local_sched::take(); + do sched.deschedule_running_task_and_then |task| { + let tube_clone = tube_clone.take(); + do local_sched::borrow |sched| { + let tube_clone = tube_clone.take(); + do sched.event_loop.callback { + let mut tube_clone = tube_clone.take(); + // The task should be blocked on this now and + // sending will wake it up. + tube_clone.send(1); + } + } + let sched = local_sched::take(); + sched.resume_task_immediately(task); + } + + assert!(tube.recv() == 1); + } + } + + #[test] + fn many_blocking_test() { + static MAX: int = 100; + + do run_in_newsched_task { + let mut tube: Tube = Tube::new(); + let tube_clone = tube.clone(); + let tube_clone = Cell(tube_clone); + let sched = local_sched::take(); + do sched.deschedule_running_task_and_then |task| { + callback_send(tube_clone.take(), 0); + + fn callback_send(tube: Tube, i: int) { + if i == 100 { return; } + + let tube = Cell(Cell(tube)); + do local_sched::borrow |sched| { + let tube = tube.take(); + do sched.event_loop.callback { + let mut tube = tube.take(); + // The task should be blocked on this now and + // sending will wake it up. + tube.send(i); + callback_send(tube, i + 1); + } + } + } + + let sched = local_sched::take(); + sched.resume_task_immediately(task); + } + + for int::range(0, MAX) |i| { + let j = tube.recv(); + assert!(j == i); + } + } + } +} diff --git a/src/libcore/sys.rs b/src/libcore/sys.rs index a27b6fe615f33..50a739ec67df7 100644 --- a/src/libcore/sys.rs +++ b/src/libcore/sys.rs @@ -202,10 +202,12 @@ impl FailWithCause for &'static str { // FIXME #4427: Temporary until rt::rt_fail_ goes away pub fn begin_unwind_(msg: *c_char, file: *c_char, line: size_t) -> ! { - use rt::{context, OldTaskContext}; - use rt::local_services::unsafe_borrow_local_services; + use option::Option; + use rt::{context, OldTaskContext, TaskContext}; + use rt::local_services::{unsafe_borrow_local_services, Unwinder}; - match context() { + let context = context(); + match context { OldTaskContext => { unsafe { gc::cleanup_stack_for_failure(); @@ -214,11 +216,26 @@ pub fn begin_unwind_(msg: *c_char, file: *c_char, line: size_t) -> ! { } } _ => { - // XXX: Need to print the failure message - gc::cleanup_stack_for_failure(); unsafe { + // XXX: Bad re-allocations. fail! needs some refactoring + let msg = str::raw::from_c_str(msg); + let file = str::raw::from_c_str(file); + + let outmsg = fmt!("%s at line %i of file %s", msg, line as int, file); + + // XXX: Logging doesn't work correctly in non-task context because it + // invokes the local heap + if context == TaskContext { + error!(outmsg); + } else { + rtdebug!("%s", outmsg); + } + + gc::cleanup_stack_for_failure(); + let local_services = unsafe_borrow_local_services(); - match (*local_services).unwinder { + let unwinder: &mut Option = &mut (*local_services).unwinder; + match *unwinder { Some(ref mut unwinder) => unwinder.begin_unwind(), None => abort!("failure without unwinder. aborting process") } From d234cf7e440db0abbf867997c45d993a542ee640 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 6 May 2013 14:28:16 -0700 Subject: [PATCH 21/55] core::rt: Make TCP servers work --- src/libcore/rt/io/net/tcp.rs | 86 ++++++++++++++++++++++++++++++++++-- src/libcore/rt/test.rs | 40 +++++++++++++++++ src/libcore/rt/uv/uvio.rs | 69 ++++++++++++++++------------- 3 files changed, 161 insertions(+), 34 deletions(-) diff --git a/src/libcore/rt/io/net/tcp.rs b/src/libcore/rt/io/net/tcp.rs index 49acc53f4f125..90f99f8df8b87 100644 --- a/src/libcore/rt/io/net/tcp.rs +++ b/src/libcore/rt/io/net/tcp.rs @@ -79,7 +79,7 @@ impl Writer for TcpStream { } pub struct TcpListener { - rtlistener: ~RtioTcpListenerObject + rtlistener: ~RtioTcpListenerObject, } impl TcpListener { @@ -116,6 +116,8 @@ impl Listener for TcpListener { #[cfg(test)] mod test { use super::*; + use int; + use cell::Cell; use rt::test::*; use rt::io::net::ip::Ipv4; use rt::io::*; @@ -172,11 +174,11 @@ mod test { } } - #[test] #[ignore] + #[test] fn multiple_connect_serial() { do run_in_newsched_task { let addr = next_test_ip4(); - let max = 100; + let max = 10; do spawntask_immediately { let mut listener = TcpListener::bind(addr); @@ -197,4 +199,82 @@ mod test { } } + #[test] + fn multiple_connect_interleaved_greedy_schedule() { + do run_in_newsched_task { + let addr = next_test_ip4(); + static MAX: int = 10; + + do spawntask_immediately { + let mut listener = TcpListener::bind(addr); + for int::range(0, MAX) |i| { + let stream = Cell(listener.accept()); + rtdebug!("accepted"); + // Start another task to handle the connection + do spawntask_immediately { + let mut stream = stream.take(); + let mut buf = [0]; + stream.read(buf); + assert!(buf[0] == i as u8); + rtdebug!("read"); + } + } + } + + connect(0, addr); + + fn connect(i: int, addr: IpAddr) { + if i == MAX { return } + + do spawntask_immediately { + rtdebug!("connecting"); + let mut stream = TcpStream::connect(addr); + // Connect again before writing + connect(i + 1, addr); + rtdebug!("writing"); + stream.write([i as u8]); + } + } + } + } + + #[test] + fn multiple_connect_interleaved_lazy_schedule() { + do run_in_newsched_task { + let addr = next_test_ip4(); + static MAX: int = 10; + + do spawntask_immediately { + let mut listener = TcpListener::bind(addr); + for int::range(0, MAX) |_| { + let stream = Cell(listener.accept()); + rtdebug!("accepted"); + // Start another task to handle the connection + do spawntask_later { + let mut stream = stream.take(); + let mut buf = [0]; + stream.read(buf); + assert!(buf[0] == 99); + rtdebug!("read"); + } + } + } + + connect(0, addr); + + fn connect(i: int, addr: IpAddr) { + if i == MAX { return } + + do spawntask_later { + rtdebug!("connecting"); + let mut stream = TcpStream::connect(addr); + // Connect again before writing + connect(i + 1, addr); + rtdebug!("writing"); + stream.write([99]); + } + } + } + } + } diff --git a/src/libcore/rt/test.rs b/src/libcore/rt/test.rs index 185443563fc9b..8d0ae0caf4d62 100644 --- a/src/libcore/rt/test.rs +++ b/src/libcore/rt/test.rs @@ -64,6 +64,46 @@ pub fn spawntask_immediately(f: ~fn()) { } } +/// Create a new task and run it right now. Aborts on failure +pub fn spawntask_later(f: ~fn()) { + use super::sched::*; + + let mut sched = local_sched::take(); + let task = ~Task::with_local(&mut sched.stack_pool, + LocalServices::without_unwinding(), + f); + + sched.task_queue.push_front(task); + local_sched::put(sched); +} + +/// Spawn a task and either run it immediately or run it later +pub fn spawntask_random(f: ~fn()) { + use super::sched::*; + use rand::{Rand, rng}; + + let mut rng = rng(); + let run_now: bool = Rand::rand(&mut rng); + + let mut sched = local_sched::take(); + let task = ~Task::with_local(&mut sched.stack_pool, + LocalServices::without_unwinding(), + f); + + if run_now { + do sched.switch_running_tasks_and_then(task) |task| { + let task = Cell(task); + do local_sched::borrow |sched| { + sched.task_queue.push_front(task.take()); + } + } + } else { + sched.task_queue.push_front(task); + local_sched::put(sched); + } +} + + /// Spawn a task and wait for it to finish, returning whether it completed successfully or failed pub fn spawntask_try(f: ~fn()) -> Result<(), ()> { use cell::Cell; diff --git a/src/libcore/rt/uv/uvio.rs b/src/libcore/rt/uv/uvio.rs index 0c52bca0dc83a..2218c0734fbfb 100644 --- a/src/libcore/rt/uv/uvio.rs +++ b/src/libcore/rt/uv/uvio.rs @@ -21,6 +21,7 @@ use rt::uv::idle::IdleWatcher; use rt::rtio::*; use rt::sched::{Scheduler, local_sched}; use rt::io::{standard_error, OtherIoError}; +use rt::tube::Tube; #[cfg(test)] use uint; #[cfg(test)] use unstable::run_in_bare_thread; @@ -149,7 +150,7 @@ impl IoFactory for UvIoFactory { fn tcp_bind(&mut self, addr: IpAddr) -> Result<~RtioTcpListenerObject, IoError> { let mut watcher = TcpWatcher::new(self.uv_loop()); match watcher.bind(addr) { - Ok(_) => Ok(~UvTcpListener { watcher: watcher }), + Ok(_) => Ok(~UvTcpListener::new(watcher)), Err(uverr) => { // XXX: Should we wait until close completes? watcher.as_stream().close(||()); @@ -161,10 +162,20 @@ impl IoFactory for UvIoFactory { // FIXME #6090: Prefer newtype structs but Drop doesn't work pub struct UvTcpListener { - watcher: TcpWatcher + watcher: TcpWatcher, + listening: bool, + incoming_streams: Tube> } impl UvTcpListener { + fn new(watcher: TcpWatcher) -> UvTcpListener { + UvTcpListener { + watcher: watcher, + listening: false, + incoming_streams: Tube::new() + } + } + fn watcher(&self) -> TcpWatcher { self.watcher } } @@ -179,41 +190,37 @@ impl RtioTcpListener for UvTcpListener { fn accept(&mut self) -> Result<~RtioTcpStreamObject, IoError> { rtdebug!("entering listen"); - let result_cell = empty_cell(); - let result_cell_ptr: *Cell> = &result_cell; - let server_tcp_watcher = self.watcher(); + if self.listening { + return self.incoming_streams.recv(); + } - let scheduler = local_sched::take(); - assert!(scheduler.in_task_context()); + self.listening = true; - do scheduler.deschedule_running_task_and_then |task| { - let task_cell = Cell(task); - 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)) - }; - - unsafe { (*result_cell_ptr).put_back(maybe_stream); } + 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)) + }; - rtdebug!("resuming task from listen"); - // Context switch - let scheduler = local_sched::take(); - scheduler.resume_task_immediately(task_cell.take()); - } + let mut incoming_streams = incoming_streams_cell.take(); + incoming_streams.send(maybe_stream); + incoming_streams_cell.put_back(incoming_streams); } - assert!(!result_cell.is_empty()); - return result_cell.take(); + return self.incoming_streams.recv(); } } From 101aaa38616411b7fcef0599a3e514cea7df3a87 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 6 May 2013 18:53:45 -0700 Subject: [PATCH 22/55] core::rt: 0 is a valid TLS key --- src/libcore/rt/local_sched.rs | 2 +- src/rt/rust_builtin.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libcore/rt/local_sched.rs b/src/libcore/rt/local_sched.rs index 44dbd55373fc9..e96a2e8de2d65 100644 --- a/src/libcore/rt/local_sched.rs +++ b/src/libcore/rt/local_sched.rs @@ -119,7 +119,7 @@ fn maybe_tls_key() -> Option { // another thread. I think this is fine since the only action // they could take if it was initialized would be to check the // thread-local value and see that it's not set. - if key != 0 { + if key != -1 { return Some(key); } else { return None; diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp index 903289281222b..39a6f5bfd1b7b 100644 --- a/src/rt/rust_builtin.cpp +++ b/src/rt/rust_builtin.cpp @@ -830,9 +830,9 @@ rust_get_rt_env() { } #ifndef _WIN32 -pthread_key_t sched_key; +pthread_key_t sched_key = -1; #else -DWORD sched_key; +DWORD sched_key = -1; #endif extern "C" void* From 4472a50ceb5b6f0337371226d3ef7cc3c30ee04c Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 6 May 2013 23:10:59 -0700 Subject: [PATCH 23/55] rtdebug off --- src/libcore/macros.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs index c4f0384f71ed6..b2e94f327c86e 100644 --- a/src/libcore/macros.rs +++ b/src/libcore/macros.rs @@ -11,7 +11,7 @@ #[macro_escape]; // Some basic logging -macro_rules! rtdebug ( +macro_rules! rtdebug_ ( ($( $arg:expr),+) => ( { dumb_println(fmt!( $($arg),+ )); @@ -26,7 +26,7 @@ macro_rules! rtdebug ( ) // An alternate version with no output, for turning off logging -macro_rules! rtdebug_ ( +macro_rules! rtdebug ( ($( $arg:expr),+) => ( $(let _ = $arg)*; ) ) From 52f015acebd7b1fa4fb040aa40d94416f053de24 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Tue, 7 May 2013 15:57:15 -0700 Subject: [PATCH 24/55] core: Cleanup warnings --- src/libcore/macros.rs | 8 +- src/libcore/rt/local_sched.rs | 8 +- src/libcore/rt/local_services.rs | 20 ++-- src/libcore/rt/rc.rs | 1 - src/libcore/rt/uv/idle.rs | 2 +- src/libcore/rt/uv/net.rs | 187 ++++++++++++++++--------------- 6 files changed, 117 insertions(+), 109 deletions(-) diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs index b2e94f327c86e..fda48b6ffb7d9 100644 --- a/src/libcore/macros.rs +++ b/src/libcore/macros.rs @@ -42,6 +42,12 @@ macro_rules! abort( ($( $msg:expr),+) => ( { rtdebug!($($msg),+); - unsafe { ::libc::abort(); } + do_abort(); + + // NB: This is in a fn to avoid putting the `unsafe` block in a macro, + // which causes spurious 'unnecessary unsafe block' warnings. + fn do_abort() -> ! { + unsafe { ::libc::abort(); } + } } ) ) diff --git a/src/libcore/rt/local_sched.rs b/src/libcore/rt/local_sched.rs index e96a2e8de2d65..eb35eb7881d39 100644 --- a/src/libcore/rt/local_sched.rs +++ b/src/libcore/rt/local_sched.rs @@ -91,11 +91,9 @@ pub unsafe fn unsafe_borrow() -> *mut Scheduler { } pub unsafe fn unsafe_borrow_io() -> *mut IoFactoryObject { - unsafe { - let sched = unsafe_borrow(); - let io: *mut IoFactoryObject = (*sched).event_loop.io().unwrap(); - return io; - } + let sched = unsafe_borrow(); + let io: *mut IoFactoryObject = (*sched).event_loop.io().unwrap(); + return io; } fn tls_key() -> tls::Key { diff --git a/src/libcore/rt/local_services.rs b/src/libcore/rt/local_services.rs index b8bf6e067801f..673b2a6c2bc41 100644 --- a/src/libcore/rt/local_services.rs +++ b/src/libcore/rt/local_services.rs @@ -23,7 +23,7 @@ use libc::{c_void, uintptr_t}; use cast::transmute; use super::sched::local_sched; use super::local_heap::LocalHeap; -use rt::logging::{Logger, StdErrLogger}; +use rt::logging::StdErrLogger; pub struct LocalServices { heap: LocalHeap, @@ -170,16 +170,14 @@ pub fn borrow_local_services(f: &fn(&mut LocalServices)) { } pub unsafe fn unsafe_borrow_local_services() -> *mut LocalServices { - unsafe { - match (*local_sched::unsafe_borrow()).current_task { - Some(~ref mut task) => { - let s: *mut LocalServices = &mut task.local_services; - return s; - } - None => { - // Don't fail. Infinite recursion - abort!("no local services for schedulers yet") - } + match (*local_sched::unsafe_borrow()).current_task { + Some(~ref mut task) => { + let s: *mut LocalServices = &mut task.local_services; + return s; + } + None => { + // Don't fail. Infinite recursion + abort!("no local services for schedulers yet") } } } diff --git a/src/libcore/rt/rc.rs b/src/libcore/rt/rc.rs index 2ee254466e4b0..1c0c8c14fdfa6 100644 --- a/src/libcore/rt/rc.rs +++ b/src/libcore/rt/rc.rs @@ -21,7 +21,6 @@ use ops::Drop; use clone::Clone; use libc::c_void; -use sys; use cast; pub struct RC { diff --git a/src/libcore/rt/uv/idle.rs b/src/libcore/rt/uv/idle.rs index fe1ce8697bf9e..518429eeaff05 100644 --- a/src/libcore/rt/uv/idle.rs +++ b/src/libcore/rt/uv/idle.rs @@ -9,7 +9,7 @@ // except according to those terms. use libc::c_int; -use option::{Some, None}; +use option::Some; use rt::uv::uvll; use rt::uv::{Watcher, Loop, NativeHandle, IdleCallback}; use rt::uv::status_to_maybe_uv_error; diff --git a/src/libcore/rt/uv/net.rs b/src/libcore/rt/uv/net.rs index 550acd8aee400..fede71ec67931 100644 --- a/src/libcore/rt/uv/net.rs +++ b/src/libcore/rt/uv/net.rs @@ -13,17 +13,11 @@ use libc::{size_t, ssize_t, c_int, c_void}; use rt::uv::uvll; use rt::uv::uvll::*; use rt::uv::{AllocCallback, ConnectionCallback, ReadCallback}; -use super::{Loop, Watcher, Request, UvError, Buf, NativeHandle, NullCallback, - status_to_maybe_uv_error, vec_to_uv_buf, vec_from_uv_buf, slice_to_uv_buf}; -use super::super::io::net::ip::{IpAddr, Ipv4, Ipv6}; +use rt::uv::{Loop, Watcher, Request, UvError, Buf, NativeHandle, NullCallback, + status_to_maybe_uv_error}; +use rt::io::net::ip::{IpAddr, Ipv4, Ipv6}; use rt::uv::last_uv_error; -#[cfg(test)] use util::ignore; -#[cfg(test)] use cell::Cell; -#[cfg(test)] use unstable::run_in_bare_thread; -#[cfg(test)] use super::super::thread::Thread; -#[cfg(test)] use super::super::test::*; - fn ip4_as_uv_ip4(addr: IpAddr, f: &fn(*sockaddr_in) -> T) -> T { match addr { Ipv4(a, b, c, d, p) => { @@ -334,96 +328,109 @@ impl NativeHandle<*uvll::uv_write_t> for WriteRequest { } -#[test] -fn connect_close() { - do run_in_bare_thread() { - let mut loop_ = Loop::new(); - let mut tcp_watcher = { TcpWatcher::new(&mut loop_) }; - // Connect to a port where nobody is listening - let addr = next_test_ip4(); - do tcp_watcher.connect(addr) |stream_watcher, status| { - rtdebug!("tcp_watcher.connect!"); - assert!(status.is_some()); - assert!(status.get().name() == ~"ECONNREFUSED"); - stream_watcher.close(||()); +#[cfg(test)] +mod test { + use super::*; + use util::ignore; + use cell::Cell; + use vec; + use unstable::run_in_bare_thread; + use rt::thread::Thread; + use rt::test::*; + use rt::uv::{Loop, AllocCallback}; + use rt::uv::{vec_from_uv_buf, vec_to_uv_buf, slice_to_uv_buf}; + + #[test] + fn connect_close() { + do run_in_bare_thread() { + let mut loop_ = Loop::new(); + let mut tcp_watcher = { TcpWatcher::new(&mut loop_) }; + // Connect to a port where nobody is listening + let addr = next_test_ip4(); + do tcp_watcher.connect(addr) |stream_watcher, status| { + rtdebug!("tcp_watcher.connect!"); + assert!(status.is_some()); + assert!(status.get().name() == ~"ECONNREFUSED"); + stream_watcher.close(||()); + } + loop_.run(); + loop_.close(); } - loop_.run(); - loop_.close(); } -} -#[test] -fn listen() { - do run_in_bare_thread() { - static MAX: int = 10; - let mut loop_ = Loop::new(); - let mut server_tcp_watcher = { TcpWatcher::new(&mut loop_) }; - let addr = next_test_ip4(); - server_tcp_watcher.bind(addr); - let loop_ = loop_; - rtdebug!("listening"); - do server_tcp_watcher.listen |server_stream_watcher, status| { - rtdebug!("listened!"); - assert!(status.is_none()); - let mut server_stream_watcher = server_stream_watcher; - let mut loop_ = loop_; - let mut client_tcp_watcher = TcpWatcher::new(&mut loop_); - let mut client_tcp_watcher = client_tcp_watcher.as_stream(); - server_stream_watcher.accept(client_tcp_watcher); - let count_cell = Cell(0); - let server_stream_watcher = server_stream_watcher; - rtdebug!("starting read"); - let alloc: AllocCallback = |size| { - vec_to_uv_buf(vec::from_elem(size, 0)) - }; - do client_tcp_watcher.read_start(alloc) - |stream_watcher, nread, buf, status| { - - rtdebug!("i'm reading!"); - let buf = vec_from_uv_buf(buf); - let mut count = count_cell.take(); - if status.is_none() { - rtdebug!("got %d bytes", nread); - let buf = buf.unwrap(); - for buf.slice(0, nread as uint).each |byte| { - assert!(*byte == count as u8); - rtdebug!("%u", *byte as uint); - count += 1; - } - } else { - assert!(count == MAX); - do stream_watcher.close { - server_stream_watcher.close(||()); + #[test] + fn listen() { + do run_in_bare_thread() { + static MAX: int = 10; + let mut loop_ = Loop::new(); + let mut server_tcp_watcher = { TcpWatcher::new(&mut loop_) }; + let addr = next_test_ip4(); + server_tcp_watcher.bind(addr); + let loop_ = loop_; + rtdebug!("listening"); + do server_tcp_watcher.listen |server_stream_watcher, status| { + rtdebug!("listened!"); + assert!(status.is_none()); + let mut server_stream_watcher = server_stream_watcher; + let mut loop_ = loop_; + let mut client_tcp_watcher = TcpWatcher::new(&mut loop_); + let mut client_tcp_watcher = client_tcp_watcher.as_stream(); + server_stream_watcher.accept(client_tcp_watcher); + let count_cell = Cell(0); + let server_stream_watcher = server_stream_watcher; + rtdebug!("starting read"); + let alloc: AllocCallback = |size| { + vec_to_uv_buf(vec::from_elem(size, 0)) + }; + do client_tcp_watcher.read_start(alloc) + |stream_watcher, nread, buf, status| { + + rtdebug!("i'm reading!"); + let buf = vec_from_uv_buf(buf); + let mut count = count_cell.take(); + if status.is_none() { + rtdebug!("got %d bytes", nread); + let buf = buf.unwrap(); + for buf.slice(0, nread as uint).each |byte| { + assert!(*byte == count as u8); + rtdebug!("%u", *byte as uint); + count += 1; + } + } else { + assert!(count == MAX); + do stream_watcher.close { + server_stream_watcher.close(||()); + } } + count_cell.put_back(count); } - count_cell.put_back(count); } - } - let _client_thread = do Thread::start { - rtdebug!("starting client thread"); - let mut loop_ = Loop::new(); - let mut tcp_watcher = { TcpWatcher::new(&mut loop_) }; - do tcp_watcher.connect(addr) |stream_watcher, status| { - rtdebug!("connecting"); - assert!(status.is_none()); - let mut stream_watcher = stream_watcher; - let msg = ~[0, 1, 2, 3, 4, 5, 6 ,7 ,8, 9]; - let buf = slice_to_uv_buf(msg); - let msg_cell = Cell(msg); - do stream_watcher.write(buf) |stream_watcher, status| { - rtdebug!("writing"); + let _client_thread = do Thread::start { + rtdebug!("starting client thread"); + let mut loop_ = Loop::new(); + let mut tcp_watcher = { TcpWatcher::new(&mut loop_) }; + do tcp_watcher.connect(addr) |stream_watcher, status| { + rtdebug!("connecting"); assert!(status.is_none()); - let msg_cell = Cell(msg_cell.take()); - stream_watcher.close(||ignore(msg_cell.take())); + let mut stream_watcher = stream_watcher; + let msg = ~[0, 1, 2, 3, 4, 5, 6 ,7 ,8, 9]; + let buf = slice_to_uv_buf(msg); + let msg_cell = Cell(msg); + do stream_watcher.write(buf) |stream_watcher, status| { + rtdebug!("writing"); + assert!(status.is_none()); + let msg_cell = Cell(msg_cell.take()); + stream_watcher.close(||ignore(msg_cell.take())); + } } - } + loop_.run(); + loop_.close(); + }; + + let mut loop_ = loop_; loop_.run(); loop_.close(); - }; - - let mut loop_ = loop_; - loop_.run(); - loop_.close(); + } } -} +} \ No newline at end of file From 329dfcaba0af7af736ac7e853be45783ad7ac4b0 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Tue, 7 May 2013 15:54:06 -0700 Subject: [PATCH 25/55] core: Move unstable::exchange_alloc to rt::global_heap --- src/libcore/os.rs | 2 +- .../{unstable/exchange_alloc.rs => rt/global_heap.rs} | 0 src/libcore/rt/mod.rs | 5 ++++- src/libcore/unstable.rs | 2 -- src/libcore/unstable/lang.rs | 6 +++--- 5 files changed, 8 insertions(+), 7 deletions(-) rename src/libcore/{unstable/exchange_alloc.rs => rt/global_heap.rs} (100%) diff --git a/src/libcore/os.rs b/src/libcore/os.rs index d983377245c4e..022204fa69b62 100644 --- a/src/libcore/os.rs +++ b/src/libcore/os.rs @@ -724,7 +724,7 @@ pub fn list_dir(p: &Path) -> ~[~str] { use os::win32::{ as_utf16_p }; - use unstable::exchange_alloc::{malloc_raw, free_raw}; + use rt::global_heap::{malloc_raw, free_raw}; #[nolink] extern { unsafe fn rust_list_dir_wfd_size() -> libc::size_t; diff --git a/src/libcore/unstable/exchange_alloc.rs b/src/libcore/rt/global_heap.rs similarity index 100% rename from src/libcore/unstable/exchange_alloc.rs rename to src/libcore/rt/global_heap.rs diff --git a/src/libcore/rt/mod.rs b/src/libcore/rt/mod.rs index b2ba6d7d3c407..55f777ebf9ac7 100644 --- a/src/libcore/rt/mod.rs +++ b/src/libcore/rt/mod.rs @@ -14,7 +14,10 @@ use libc::c_char; -/// The Scheduler and Task types +/// The global (exchange) heap. +pub mod global_heap; + +/// The Scheduler and Task types. mod sched; /// Thread-local access to the current Scheduler diff --git a/src/libcore/unstable.rs b/src/libcore/unstable.rs index 25e4d07b01da1..9530d7ab50d64 100644 --- a/src/libcore/unstable.rs +++ b/src/libcore/unstable.rs @@ -26,8 +26,6 @@ pub mod global; pub mod finally; #[path = "unstable/weak_task.rs"] pub mod weak_task; -#[path = "unstable/exchange_alloc.rs"] -pub mod exchange_alloc; #[path = "unstable/intrinsics.rs"] pub mod intrinsics; #[path = "unstable/simd.rs"] diff --git a/src/libcore/unstable/lang.rs b/src/libcore/unstable/lang.rs index 8153c2d43d998..e521fb59fbe5f 100644 --- a/src/libcore/unstable/lang.rs +++ b/src/libcore/unstable/lang.rs @@ -16,12 +16,12 @@ use libc::{c_char, c_uchar, c_void, size_t, uintptr_t, c_int, STDERR_FILENO}; use managed::raw::BoxRepr; use str; use sys; -use unstable::exchange_alloc; use cast::transmute; use rt::{context, OldTaskContext}; use rt::local_services::borrow_local_services; use option::{Option, Some, None}; use io; +use rt::global_heap; #[allow(non_camel_case_types)] pub type rust_task = c_void; @@ -153,7 +153,7 @@ unsafe fn fail_borrowed(box: *mut BoxRepr, file: *c_char, line: size_t) { #[lang="exchange_malloc"] #[inline(always)] pub unsafe fn exchange_malloc(td: *c_char, size: uintptr_t) -> *c_char { - transmute(exchange_alloc::malloc(transmute(td), transmute(size))) + transmute(global_heap::malloc(transmute(td), transmute(size))) } /// Because this code is so perf. sensitive, use a static constant so that @@ -233,7 +233,7 @@ impl DebugPrints for io::fd_t { #[lang="exchange_free"] #[inline(always)] pub unsafe fn exchange_free(ptr: *c_char) { - exchange_alloc::free(transmute(ptr)) + global_heap::free(transmute(ptr)) } #[lang="malloc"] From f934fa73aca85cded967420ef2ab0dd9f14a6638 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 6 May 2013 23:11:02 -0700 Subject: [PATCH 26/55] core::rt: Docs --- src/libcore/rt/mod.rs | 74 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 13 deletions(-) diff --git a/src/libcore/rt/mod.rs b/src/libcore/rt/mod.rs index 55f777ebf9ac7..5a6a6e4c7d88d 100644 --- a/src/libcore/rt/mod.rs +++ b/src/libcore/rt/mod.rs @@ -8,7 +8,57 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Rust runtime services, including the task scheduler and I/O interface +/*! The Rust Runtime, including the task scheduler and I/O + +The `rt` module provides the private runtime infrastructure necessary +to support core language features like the exchange and local heap, +the garbage collector, logging, local data and unwinding. It also +implements the default task scheduler and task model. Initialization +routines are provided for setting up runtime resources in common +configurations, including that used by `rustc` when generating +executables. + +It is intended that the features provided by `rt` can be factored in a +way such that the core library can be built with different 'profiles' +for different use cases, e.g. excluding the task scheduler. A number +of runtime features though are critical to the functioning of the +language and an implementation must be provided regardless of the +execution environment. + +Of foremost importance is the global exchange heap, in the module +`global_heap`. Very little practical Rust code can be written without +access to the global heap. Unlike most of `rt` the global heap is +truly a global resource and generally operates independently of the +rest of the runtime. + +All other runtime features are 'local', either thread-local or +task-local. Those critical to the functioning of the language are +defined in the module `local_services`. Local services are those which +are expected to be available to Rust code generally but rely on +thread- or task-local state. These currently include the local heap, +the garbage collector, local storage, logging and the stack unwinder. +Local services are primarily implemented for tasks, but may also +be implemented for use outside of tasks. + +The relationship between `rt` and the rest of the core library is +not entirely clear yet and some modules will be moving into or +out of `rt` as development proceeds. + +Several modules in `core` are clients of `rt`: + +* `core::task` - The user-facing interface to the Rust task model. +* `core::task::local_data` - The interface to local data. +* `core::gc` - The garbage collector. +* `core::unstable::lang` - Miscellaneous lang items, some of which rely on `core::rt`. +* `core::condition` - Uses local data. +* `core::cleanup` - Local heap destruction. +* `core::io` - In the future `core::io` will use an `rt` implementation. +* `core::logging` +* `core::pipes` +* `core::comm` +* `core::stackwalk` + +*/ #[doc(hidden)]; @@ -20,39 +70,37 @@ pub mod global_heap; /// The Scheduler and Task types. mod sched; -/// Thread-local access to the current Scheduler +/// Thread-local access to the current Scheduler. pub mod local_sched; -/// Synchronous I/O +/// Synchronous I/O. #[path = "io/mod.rs"] pub mod io; -/// Thread-local implementations of language-critical runtime features like @ +/// Thread-local implementations of language-critical runtime features like @. pub mod local_services; -/// The EventLoop and internal synchronous I/O interface, dynamically -/// overridable so that it's primary implementation on libuv can -/// live outside of core. +/// The EventLoop and internal synchronous I/O interface. mod rtio; -/// libuv +/// libuv and default rtio implementation. #[path = "uv/mod.rs"] pub mod uv; // FIXME #5248: The import in `sched` doesn't resolve unless this is pub! -/// Bindings to pthread/windows thread-local storage +/// Bindings to pthread/windows thread-local storage. pub mod thread_local_storage; -/// A parallel work-stealing queue +/// A parallel work-stealing dequeue. mod work_queue; -/// Stack segments and their cacheing +/// Stack segments and caching. mod stack; -/// CPU context swapping +/// CPU context swapping. mod context; -/// Bindings to system threading libraries +/// Bindings to system threading libraries. mod thread; /// The runtime configuration, read from environment variables From 204e3d82ccf5015e39f847aafea148d5180ab951 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 11 May 2013 18:59:28 -0700 Subject: [PATCH 27/55] core::rt: Register stacks with valgrind. #6428 --- src/libcore/rt/io/net/tcp.rs | 2 +- src/libcore/rt/stack.rs | 39 +++++++++++++++++++++++++++++++----- src/rt/rust_stack.cpp | 11 ++++++++++ src/rt/rustrt.def.in | 2 ++ 4 files changed, 48 insertions(+), 6 deletions(-) diff --git a/src/libcore/rt/io/net/tcp.rs b/src/libcore/rt/io/net/tcp.rs index 90f99f8df8b87..b4c021ed28ffe 100644 --- a/src/libcore/rt/io/net/tcp.rs +++ b/src/libcore/rt/io/net/tcp.rs @@ -122,7 +122,7 @@ mod test { use rt::io::net::ip::Ipv4; use rt::io::*; - #[test] + #[test] #[ignore] fn bind_error() { do run_in_newsched_task { let mut called = false; diff --git a/src/libcore/rt/stack.rs b/src/libcore/rt/stack.rs index 9eca3bda0473c..068bc834ce6a3 100644 --- a/src/libcore/rt/stack.rs +++ b/src/libcore/rt/stack.rs @@ -9,21 +9,36 @@ // except according to those terms. use vec; +use ops::Drop; +use libc::{c_uint, uintptr_t}; pub struct StackSegment { - buf: ~[u8] + buf: ~[u8], + valgrind_id: c_uint } pub impl StackSegment { fn new(size: uint) -> StackSegment { - // Crate a block of uninitialized values - let mut stack = vec::with_capacity(size); unsafe { + // Crate a block of uninitialized values + let mut stack = vec::with_capacity(size); vec::raw::set_len(&mut stack, size); + + let mut stk = StackSegment { + buf: stack, + valgrind_id: 0 + }; + + // XXX: Using the FFI to call a C macro. Slow + stk.valgrind_id = rust_valgrind_stack_register(stk.start(), stk.end()); + return stk; } + } - StackSegment { - buf: stack + /// Point to the low end of the allocated stack + fn start(&self) -> *uint { + unsafe { + vec::raw::to_ptr(self.buf) as *uint } } @@ -35,6 +50,15 @@ pub impl StackSegment { } } +impl Drop for StackSegment { + fn finalize(&self) { + unsafe { + // XXX: Using the FFI to call a C macro. Slow + rust_valgrind_stack_deregister(self.valgrind_id); + } + } +} + pub struct StackPool(()); impl StackPool { @@ -47,3 +71,8 @@ impl StackPool { fn give_segment(&self, _stack: StackSegment) { } } + +extern { + fn rust_valgrind_stack_register(start: *uintptr_t, end: *uintptr_t) -> c_uint; + fn rust_valgrind_stack_deregister(id: c_uint); +} \ No newline at end of file diff --git a/src/rt/rust_stack.cpp b/src/rt/rust_stack.cpp index f07690a955ea2..a609ac573245d 100644 --- a/src/rt/rust_stack.cpp +++ b/src/rt/rust_stack.cpp @@ -92,3 +92,14 @@ destroy_exchange_stack(rust_exchange_alloc *exchange, stk_seg *stk) { deregister_valgrind_stack(stk); exchange->free(stk); } + + +extern "C" CDECL unsigned int +rust_valgrind_stack_register(void *start, void *end) { + return VALGRIND_STACK_REGISTER(start, end); +} + +extern "C" CDECL void +rust_valgrind_stack_deregister(unsigned int id) { + VALGRIND_STACK_DEREGISTER(id); +} diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index 6be41251f1bd9..75a5a069605c7 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -234,3 +234,5 @@ rust_try rust_begin_unwind rust_take_task_borrow_list rust_set_task_borrow_list +rust_valgrind_stack_register +rust_valgrind_stack_deregister \ No newline at end of file From ee0ce64d9db10aebc491454b6595d6edf69fe513 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sun, 12 May 2013 14:35:52 -0700 Subject: [PATCH 28/55] core::rt: Wait for handles to close --- src/libcore/rt/uv/idle.rs | 15 ++++++++-- src/libcore/rt/uv/mod.rs | 6 ++-- src/libcore/rt/uv/uvio.rs | 63 +++++++++++++++++++++++++++------------ 3 files changed, 60 insertions(+), 24 deletions(-) diff --git a/src/libcore/rt/uv/idle.rs b/src/libcore/rt/uv/idle.rs index 518429eeaff05..fecb9391caa54 100644 --- a/src/libcore/rt/uv/idle.rs +++ b/src/libcore/rt/uv/idle.rs @@ -11,7 +11,7 @@ use libc::c_int; use option::Some; use rt::uv::uvll; -use rt::uv::{Watcher, Loop, NativeHandle, IdleCallback}; +use rt::uv::{Watcher, Loop, NativeHandle, IdleCallback, NullCallback}; use rt::uv::status_to_maybe_uv_error; pub struct IdleWatcher(*uvll::uv_idle_t); @@ -57,12 +57,23 @@ pub impl IdleWatcher { } } - fn close(self) { + fn close(self, cb: NullCallback) { + { + let mut this = self; + let data = this.get_watcher_data(); + assert!(data.close_cb.is_none()); + data.close_cb = Some(cb); + } + unsafe { uvll::close(self.native_handle(), close_cb) }; extern fn close_cb(handle: *uvll::uv_idle_t) { unsafe { let mut idle_watcher: IdleWatcher = NativeHandle::from_native_handle(handle); + { + let mut data = idle_watcher.get_watcher_data(); + data.close_cb.swap_unwrap()(); + } idle_watcher.drop_watcher_data(); uvll::idle_delete(handle); } diff --git a/src/libcore/rt/uv/mod.rs b/src/libcore/rt/uv/mod.rs index 2c83873359a8f..684099d7fd11f 100644 --- a/src/libcore/rt/uv/mod.rs +++ b/src/libcore/rt/uv/mod.rs @@ -356,7 +356,7 @@ fn idle_new_then_close() { do run_in_bare_thread { let mut loop_ = Loop::new(); let idle_watcher = { IdleWatcher::new(&mut loop_) }; - idle_watcher.close(); + idle_watcher.close(||()); } } @@ -372,7 +372,7 @@ fn idle_smoke_test() { assert!(status.is_none()); if unsafe { *count_ptr == 10 } { idle_watcher.stop(); - idle_watcher.close(); + idle_watcher.close(||()); } else { unsafe { *count_ptr = *count_ptr + 1; } } @@ -396,7 +396,7 @@ fn idle_start_stop_start() { assert!(status.is_none()); let mut idle_watcher = idle_watcher; idle_watcher.stop(); - idle_watcher.close(); + idle_watcher.close(||()); } } loop_.run(); diff --git a/src/libcore/rt/uv/uvio.rs b/src/libcore/rt/uv/uvio.rs index 2218c0734fbfb..c031d7a1a6961 100644 --- a/src/libcore/rt/uv/uvio.rs +++ b/src/libcore/rt/uv/uvio.rs @@ -66,7 +66,7 @@ impl EventLoop for UvEventLoop { assert!(status.is_none()); let mut idle_watcher = idle_watcher; idle_watcher.stop(); - idle_watcher.close(); + idle_watcher.close(||()); f(); } } @@ -124,22 +124,26 @@ impl IoFactory for UvIoFactory { // Wait for a connection do tcp_watcher.connect(addr) |stream_watcher, status| { rtdebug!("connect: in connect callback"); - let maybe_stream = if status.is_none() { + if status.is_none() { rtdebug!("status is none"); - Ok(~UvTcpStream { watcher: stream_watcher }) + 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()); } else { rtdebug!("status is some"); - // XXX: Wait for close - stream_watcher.close(||()); - Err(uv_error_to_io_error(status.get())) + 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()); + } }; - - // Store the stream in the task's stack - unsafe { (*result_cell_ptr).put_back(maybe_stream); } - - // Context switch - let scheduler = local_sched::take(); - scheduler.resume_task_immediately(task_cell.take()); } } @@ -152,8 +156,14 @@ impl IoFactory for UvIoFactory { match watcher.bind(addr) { Ok(_) => Ok(~UvTcpListener::new(watcher)), Err(uverr) => { - // XXX: Should we wait until close completes? - watcher.as_stream().close(||()); + 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()); + } + } Err(uv_error_to_io_error(uverr)) } } @@ -181,8 +191,15 @@ impl UvTcpListener { impl Drop for UvTcpListener { fn finalize(&self) { - // XXX: Need to wait until close finishes before returning - self.watcher().as_stream().close(||()); + 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()); + } + } } } @@ -235,8 +252,16 @@ impl UvTcpStream { impl Drop for UvTcpStream { fn finalize(&self) { - rtdebug!("closing stream"); - self.watcher().close(||()); + 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()); + } + } } } From 6a6076ae810d470dfb511712c303a4ee7ffedf00 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Tue, 14 May 2013 19:06:20 -0700 Subject: [PATCH 29/55] core::rt: Ignore tcp test multiple_connect_interleaved_lazy_schedule Hangs on mac. --- src/libcore/rt/io/net/tcp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/rt/io/net/tcp.rs b/src/libcore/rt/io/net/tcp.rs index b4c021ed28ffe..addba40b31ebc 100644 --- a/src/libcore/rt/io/net/tcp.rs +++ b/src/libcore/rt/io/net/tcp.rs @@ -238,7 +238,7 @@ mod test { } } - #[test] + #[test] #[ignore(reason = "hangs on mac")] fn multiple_connect_interleaved_lazy_schedule() { do run_in_newsched_task { let addr = next_test_ip4(); From bfd9aa9755149725e39d8024d693ed76f92a30df Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Tue, 7 May 2013 18:13:15 -0700 Subject: [PATCH 30/55] core:rt: A few micro-opts --- src/libcore/rt/context.rs | 1 + src/libcore/rt/global_heap.rs | 14 +++++++++----- src/libcore/rt/local_sched.rs | 1 + src/libcore/rt/thread_local_storage.rs | 3 +++ src/rt/rust_exchange_alloc.cpp | 16 ++++++---------- src/rt/rustrt.def.in | 2 +- 6 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/libcore/rt/context.rs b/src/libcore/rt/context.rs index 9c1612884f044..2add314fd1196 100644 --- a/src/libcore/rt/context.rs +++ b/src/libcore/rt/context.rs @@ -84,6 +84,7 @@ pub impl Context { } extern { + #[rust_stack] fn swap_registers(out_regs: *mut Registers, in_regs: *Registers); } diff --git a/src/libcore/rt/global_heap.rs b/src/libcore/rt/global_heap.rs index 3b35c2fb8047f..ce7ff87b44580 100644 --- a/src/libcore/rt/global_heap.rs +++ b/src/libcore/rt/global_heap.rs @@ -9,7 +9,7 @@ // except according to those terms. use sys::{TypeDesc, size_of}; -use libc::{c_void, size_t}; +use libc::{c_void, size_t, uintptr_t}; use c_malloc = libc::malloc; use c_free = libc::free; use managed::raw::{BoxHeaderRepr, BoxRepr}; @@ -34,7 +34,7 @@ pub unsafe fn malloc(td: *TypeDesc, size: uint) -> *c_void { box.header.prev = null(); box.header.next = null(); - let exchange_count = &mut *rust_get_exchange_count_ptr(); + let exchange_count = &mut *exchange_count_ptr(); atomic_xadd(exchange_count, 1); return transmute(box); @@ -52,7 +52,7 @@ pub unsafe fn malloc_raw(size: uint) -> *c_void { } pub unsafe fn free(ptr: *c_void) { - let exchange_count = &mut *rust_get_exchange_count_ptr(); + let exchange_count = &mut *exchange_count_ptr(); atomic_xsub(exchange_count, 1); assert!(ptr.is_not_null()); @@ -77,7 +77,11 @@ fn align_to(size: uint, align: uint) -> uint { (size + align - 1) & !(align - 1) } +fn exchange_count_ptr() -> *mut int { + // XXX: Need mutable globals + unsafe { transmute(&rust_exchange_count) } +} + extern { - #[rust_stack] - fn rust_get_exchange_count_ptr() -> *mut int; + static rust_exchange_count: uintptr_t; } diff --git a/src/libcore/rt/local_sched.rs b/src/libcore/rt/local_sched.rs index eb35eb7881d39..1ef1fd33a83a5 100644 --- a/src/libcore/rt/local_sched.rs +++ b/src/libcore/rt/local_sched.rs @@ -126,6 +126,7 @@ fn maybe_tls_key() -> Option { } extern { + #[fast_ffi] fn rust_get_sched_tls_key() -> *mut c_void; } diff --git a/src/libcore/rt/thread_local_storage.rs b/src/libcore/rt/thread_local_storage.rs index 366996fb93560..6a08c0f59b183 100644 --- a/src/libcore/rt/thread_local_storage.rs +++ b/src/libcore/rt/thread_local_storage.rs @@ -46,8 +46,11 @@ type pthread_key_t = ::libc::c_uint; #[cfg(unix)] extern { + #[fast_ffi] fn pthread_key_create(key: *mut pthread_key_t, dtor: *u8) -> c_int; + #[fast_ffi] fn pthread_setspecific(key: pthread_key_t, value: *mut c_void) -> c_int; + #[fast_ffi] fn pthread_getspecific(key: pthread_key_t) -> *mut c_void; } diff --git a/src/rt/rust_exchange_alloc.cpp b/src/rt/rust_exchange_alloc.cpp index 5958c68f3e7d1..89257dc9f6e43 100644 --- a/src/rt/rust_exchange_alloc.cpp +++ b/src/rt/rust_exchange_alloc.cpp @@ -15,14 +15,15 @@ #include #include -uintptr_t exchange_count = 0; +extern uintptr_t rust_exchange_count; +uintptr_t rust_exchange_count = 0; void * rust_exchange_alloc::malloc(size_t size) { void *value = ::malloc(size); assert(value); - sync::increment(exchange_count); + sync::increment(rust_exchange_count); return value; } @@ -36,20 +37,15 @@ rust_exchange_alloc::realloc(void *ptr, size_t size) { void rust_exchange_alloc::free(void *ptr) { - sync::decrement(exchange_count); + sync::decrement(rust_exchange_count); ::free(ptr); } -extern "C" uintptr_t * -rust_get_exchange_count_ptr() { - return &exchange_count; -} - void rust_check_exchange_count_on_exit() { - if (exchange_count != 0) { + if (rust_exchange_count != 0) { printf("exchange heap not empty on exit\n"); - printf("%d dangling allocations\n", (int)exchange_count); + printf("%d dangling allocations\n", (int)rust_exchange_count); abort(); } } diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index 75a5a069605c7..a62d7991d4953 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -195,7 +195,7 @@ rust_register_exit_function rust_get_global_data_ptr rust_inc_kernel_live_count rust_dec_kernel_live_count -rust_get_exchange_count_ptr +rust_exchange_count rust_get_sched_tls_key swap_registers rust_readdir From 36ad366519137122871b04b407370dab4a97c645 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Wed, 8 May 2013 14:52:30 -0700 Subject: [PATCH 31/55] core::rt: Add a test of standalone use of the runtime --- src/libcore/rt/mod.rs | 24 ++---------------------- src/libcore/unstable/lang.rs | 19 +++++++++++++++++-- src/test/run-pass/core-rt-smoke.rs | 18 ++++++++++++++++++ 3 files changed, 37 insertions(+), 24 deletions(-) create mode 100644 src/test/run-pass/core-rt-smoke.rs diff --git a/src/libcore/rt/mod.rs b/src/libcore/rt/mod.rs index f04c38f79e800..5dee9a7773155 100644 --- a/src/libcore/rt/mod.rs +++ b/src/libcore/rt/mod.rs @@ -131,9 +131,6 @@ pub mod tube; /// /// # Arguments /// -/// * `main` - A C-abi function that takes no arguments and returns `c_void`. -/// It is a wrapper around the user-defined `main` function, and will be run -/// in a task. /// * `argc` & `argv` - The argument vector. On Unix this information is used /// by os::args. /// * `crate_map` - Runtime information about the executing crate, mostly for logging @@ -141,31 +138,14 @@ pub mod tube; /// # Return value /// /// The return value is used as the process return code. 0 on success, 101 on error. -pub fn start(main: *u8, _argc: int, _argv: **c_char, _crate_map: *u8) -> int { +pub fn start(_argc: int, _argv: **c_char, _crate_map: *u8, main: ~fn()) -> int { use self::sched::{Scheduler, Task}; use self::uv::uvio::UvEventLoop; - use sys::Closure; - use ptr; - use cast; let loop_ = ~UvEventLoop::new(); let mut sched = ~Scheduler::new(loop_); - - let main_task = ~do Task::new(&mut sched.stack_pool) { - - unsafe { - // `main` is an `fn() -> ()` that doesn't take an environment - // XXX: Could also call this as an `extern "Rust" fn` once they work - let main = Closure { - code: main as *(), - env: ptr::null(), - }; - let mainfn: &fn() = cast::transmute(main); - - mainfn(); - } - }; + let main_task = ~Task::new(&mut sched.stack_pool, main); sched.task_queue.push_back(main_task); sched.run(); diff --git a/src/libcore/unstable/lang.rs b/src/libcore/unstable/lang.rs index e521fb59fbe5f..ce32cb4c282f2 100644 --- a/src/libcore/unstable/lang.rs +++ b/src/libcore/unstable/lang.rs @@ -424,7 +424,10 @@ pub unsafe fn strdup_uniq(ptr: *c_uchar, len: uint) -> ~str { pub fn start(main: *u8, argc: int, argv: **c_char, crate_map: *u8) -> int { use libc::getenv; - use rt::start; + use rt; + use sys::Closure; + use ptr; + use cast; unsafe { let use_old_rt = do str::as_c_str("RUST_NEWRT") |s| { @@ -434,7 +437,19 @@ pub fn start(main: *u8, argc: int, argv: **c_char, return rust_start(main as *c_void, argc as c_int, argv, crate_map as *c_void) as int; } else { - return start(main, argc, argv, crate_map); + return do rt::start(argc, argv, crate_map) { + unsafe { + // `main` is an `fn() -> ()` that doesn't take an environment + // XXX: Could also call this as an `extern "Rust" fn` once they work + let main = Closure { + code: main as *(), + env: ptr::null(), + }; + let mainfn: &fn() = cast::transmute(main); + + mainfn(); + } + }; } } diff --git a/src/test/run-pass/core-rt-smoke.rs b/src/test/run-pass/core-rt-smoke.rs new file mode 100644 index 0000000000000..fb08cda3b2577 --- /dev/null +++ b/src/test/run-pass/core-rt-smoke.rs @@ -0,0 +1,18 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// A simple test of starting the runtime manually + +#[start] +fn start(argc: int, argv: **u8, crate_map: *u8) -> int { + do core::rt::start(argc, argv, crate_map) { + debug!("creating my own runtime is joy"); + } +} \ No newline at end of file From f6401bad24d2fb1e1f959595c2f57cb4964e7082 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Wed, 8 May 2013 15:26:07 -0700 Subject: [PATCH 32/55] core: Use a global lock instead of runtime lock for os::getenv, etc. #4726 --- src/libcore/os.rs | 26 ++++++++++++++------------ src/libuv | 2 +- src/rt/rust_env.cpp | 16 ++++++++++++++++ src/rt/rustrt.def.in | 5 ++++- 4 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/libcore/os.rs b/src/libcore/os.rs index 93319efa3b761..a198b495127e7 100644 --- a/src/libcore/os.rs +++ b/src/libcore/os.rs @@ -147,23 +147,25 @@ pub mod win32 { /* Accessing environment variables is not generally threadsafe. -This uses a per-runtime lock to serialize access. -FIXME #4726: It would probably be appropriate to make this a real global +Serialize access through a global lock. */ fn with_env_lock(f: &fn() -> T) -> T { - use unstable::global::global_data_clone_create; - use unstable::sync::{Exclusive, exclusive}; - - struct SharedValue(()); - type ValueMutex = Exclusive; - fn key(_: ValueMutex) { } + use unstable::finally::Finally; unsafe { - let lock: ValueMutex = global_data_clone_create(key, || { - ~exclusive(SharedValue(())) - }); + return do (|| { + rust_take_env_lock(); + f() + }).finally { + rust_drop_env_lock(); + }; + } - lock.with_imm(|_| f() ) + extern { + #[fast_ffi] + fn rust_take_env_lock(); + #[fast_ffi] + fn rust_drop_env_lock(); } } diff --git a/src/libuv b/src/libuv index 97ac7c087a0ca..218ab86721eef 160000 --- a/src/libuv +++ b/src/libuv @@ -1 +1 @@ -Subproject commit 97ac7c087a0caf6b0f611b80e14f7fe3cb18bb27 +Subproject commit 218ab86721eefd7b7e97fa6d9f95a80a1fa8686c diff --git a/src/rt/rust_env.cpp b/src/rt/rust_env.cpp index 360d611492853..ed38be3550f74 100644 --- a/src/rt/rust_env.cpp +++ b/src/rt/rust_env.cpp @@ -13,6 +13,7 @@ // that might come from the environment is loaded here, once, during // init. +#include "sync/lock_and_signal.h" #include "rust_env.h" // The environment variables that the runtime knows about @@ -26,6 +27,18 @@ #define RUST_DEBUG_MEM "RUST_DEBUG_MEM" #define RUST_DEBUG_BORROW "RUST_DEBUG_BORROW" +static lock_and_signal env_lock; + +extern "C" CDECL void +rust_take_env_lock() { + env_lock.lock(); +} + +extern "C" CDECL void +rust_drop_env_lock() { + env_lock.unlock(); +} + #if defined(__WIN32__) static int get_num_cpus() { @@ -119,6 +132,8 @@ copyenv(const char* name) { rust_env* load_env(int argc, char **argv) { + scoped_lock with(env_lock); + rust_env *env = (rust_env*)malloc(sizeof(rust_env)); env->num_sched_threads = (size_t)get_num_threads(); @@ -141,3 +156,4 @@ free_env(rust_env *env) { free(env->rust_seed); free(env); } + diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index a62d7991d4953..958b31eb3429b 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -235,4 +235,7 @@ rust_begin_unwind rust_take_task_borrow_list rust_set_task_borrow_list rust_valgrind_stack_register -rust_valgrind_stack_deregister \ No newline at end of file +rust_valgrind_stack_deregister +rust_take_env_lock +rust_drop_env_lock + From cc2897d559742c5f7630557975aa72c12a0eff01 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Wed, 8 May 2013 15:28:30 -0700 Subject: [PATCH 33/55] core: Replace use of libc::getenv with os::getenv --- src/libcore/unstable/lang.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/libcore/unstable/lang.rs b/src/libcore/unstable/lang.rs index ce32cb4c282f2..071d21ae8a807 100644 --- a/src/libcore/unstable/lang.rs +++ b/src/libcore/unstable/lang.rs @@ -423,16 +423,14 @@ pub unsafe fn strdup_uniq(ptr: *c_uchar, len: uint) -> ~str { #[lang="start"] pub fn start(main: *u8, argc: int, argv: **c_char, crate_map: *u8) -> int { - use libc::getenv; use rt; use sys::Closure; use ptr; use cast; + use os; unsafe { - let use_old_rt = do str::as_c_str("RUST_NEWRT") |s| { - getenv(s).is_null() - }; + let use_old_rt = os::getenv("RUST_NEWRT").is_none(); if use_old_rt { return rust_start(main as *c_void, argc as c_int, argv, crate_map as *c_void) as int; From 0a54bad3d1d306845b04f146c2c31d4a70e9ede3 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Wed, 8 May 2013 16:53:40 -0700 Subject: [PATCH 34/55] core::rt: Initialize logging --- src/libcore/rt/logging.rs | 32 +++++++++++++++++++++++++++++++- src/libcore/rt/mod.rs | 11 +++++++++-- src/libcore/unstable/lang.rs | 2 +- src/rt/rust_log.cpp | 4 ++++ src/rt/rustrt.def.in | 2 +- 5 files changed, 46 insertions(+), 5 deletions(-) diff --git a/src/libcore/rt/logging.rs b/src/libcore/rt/logging.rs index 4ed09fd829f22..a0d0539768912 100644 --- a/src/libcore/rt/logging.rs +++ b/src/libcore/rt/logging.rs @@ -35,4 +35,34 @@ impl Logger for StdErrLogger { dbg.write_str("\n"); dbg.flush(); } -} \ No newline at end of file +} + +/// Configure logging by traversing the crate map and setting the +/// per-module global logging flags based on the logging spec +pub fn init(crate_map: *u8) { + use os; + use str; + use ptr; + use option::{Some, None}; + use libc::c_char; + + let log_spec = os::getenv("RUST_LOG"); + match log_spec { + Some(spec) => { + do str::as_c_str(spec) |s| { + unsafe { + rust_update_log_settings(crate_map, s); + } + } + } + None => { + unsafe { + rust_update_log_settings(crate_map, ptr::null()); + } + } + } + + extern { + fn rust_update_log_settings(crate_map: *u8, settings: *c_char); + } +} diff --git a/src/libcore/rt/mod.rs b/src/libcore/rt/mod.rs index 5dee9a7773155..cebc87f8c2352 100644 --- a/src/libcore/rt/mod.rs +++ b/src/libcore/rt/mod.rs @@ -62,7 +62,6 @@ Several modules in `core` are clients of `rt`: #[doc(hidden)]; -use libc::c_char; use ptr::Ptr; /// The global (exchange) heap. @@ -138,11 +137,13 @@ pub mod tube; /// # Return value /// /// The return value is used as the process return code. 0 on success, 101 on error. -pub fn start(_argc: int, _argv: **c_char, _crate_map: *u8, main: ~fn()) -> int { +pub fn start(_argc: int, _argv: **u8, crate_map: *u8, main: ~fn()) -> int { use self::sched::{Scheduler, Task}; use self::uv::uvio::UvEventLoop; + init(crate_map); + let loop_ = ~UvEventLoop::new(); let mut sched = ~Scheduler::new(loop_); let main_task = ~Task::new(&mut sched.stack_pool, main); @@ -153,6 +154,12 @@ pub fn start(_argc: int, _argv: **c_char, _crate_map: *u8, main: ~fn()) -> int { return 0; } +/// One-time runtime initialization. Currently all this does is set up logging +/// based on the RUST_LOG environment variable. +pub fn init(crate_map: *u8) { + logging::init(crate_map); +} + /// Possible contexts in which Rust code may be executing. /// Different runtime services are available depending on context. /// Mostly used for determining if we're using the new scheduler diff --git a/src/libcore/unstable/lang.rs b/src/libcore/unstable/lang.rs index 071d21ae8a807..1249392484d23 100644 --- a/src/libcore/unstable/lang.rs +++ b/src/libcore/unstable/lang.rs @@ -435,7 +435,7 @@ pub fn start(main: *u8, argc: int, argv: **c_char, return rust_start(main as *c_void, argc as c_int, argv, crate_map as *c_void) as int; } else { - return do rt::start(argc, argv, crate_map) { + return do rt::start(argc, argv as **u8, crate_map) { unsafe { // `main` is an `fn() -> ()` that doesn't take an environment // XXX: Could also call this as an `extern "Rust" fn` once they work diff --git a/src/rt/rust_log.cpp b/src/rt/rust_log.cpp index c2b58c9fda732..df24f569495b4 100644 --- a/src/rt/rust_log.cpp +++ b/src/rt/rust_log.cpp @@ -324,6 +324,10 @@ void update_log_settings(void* crate_map, char* settings) { free(buffer); } +extern "C" CDECL void +rust_update_log_settings(void* crate_map, char* settings) { + update_log_settings(crate_map, settings); +} // // Local Variables: diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index 958b31eb3429b..f1ddb17c499a1 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -238,4 +238,4 @@ rust_valgrind_stack_register rust_valgrind_stack_deregister rust_take_env_lock rust_drop_env_lock - +rust_update_log_settings From 174ec1e42252c46cc6722296379968ba7a6c6fbd Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Wed, 8 May 2013 19:28:53 -0700 Subject: [PATCH 35/55] core::rt: Error handling for TcpStream.read --- src/libcore/macros.rs | 4 +- src/libcore/rt/io/mod.rs | 1 + src/libcore/rt/io/net/tcp.rs | 89 +++++++++++++++++++++++++++++++++--- src/libcore/rt/uv/mod.rs | 5 ++ src/libcore/rt/uv/uvio.rs | 4 +- src/libcore/rt/uv/uvll.rs | 1 + 6 files changed, 93 insertions(+), 11 deletions(-) diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs index fda48b6ffb7d9..04ee086b9ae4b 100644 --- a/src/libcore/macros.rs +++ b/src/libcore/macros.rs @@ -11,7 +11,7 @@ #[macro_escape]; // Some basic logging -macro_rules! rtdebug_ ( +macro_rules! rtdebug ( ($( $arg:expr),+) => ( { dumb_println(fmt!( $($arg),+ )); @@ -26,7 +26,7 @@ macro_rules! rtdebug_ ( ) // An alternate version with no output, for turning off logging -macro_rules! rtdebug ( +macro_rules! rtdebug_ ( ($( $arg:expr),+) => ( $(let _ = $arg)*; ) ) diff --git a/src/libcore/rt/io/mod.rs b/src/libcore/rt/io/mod.rs index 8f56005d0a4f6..ab4b83f7cf0ea 100644 --- a/src/libcore/rt/io/mod.rs +++ b/src/libcore/rt/io/mod.rs @@ -338,6 +338,7 @@ pub enum IoErrorKind { ConnectionFailed, Closed, ConnectionRefused, + ConnectionReset } // XXX: Can't put doc comments on macros diff --git a/src/libcore/rt/io/net/tcp.rs b/src/libcore/rt/io/net/tcp.rs index addba40b31ebc..4a25334a2fcbc 100644 --- a/src/libcore/rt/io/net/tcp.rs +++ b/src/libcore/rt/io/net/tcp.rs @@ -13,7 +13,7 @@ use result::{Ok, Err}; use rt::sched::local_sched::unsafe_borrow_io; use rt::io::net::ip::IpAddr; use rt::io::{Reader, Writer, Listener}; -use rt::io::io_error; +use rt::io::{io_error, EndOfFile}; use rt::rtio::{IoFactory, RtioTcpListener, RtioTcpListenerObject, RtioTcpStream, RtioTcpStreamObject}; @@ -55,8 +55,12 @@ impl Reader for TcpStream { let bytes_read = self.rtstream.read(buf); match bytes_read { Ok(read) => Some(read), - Err(_) => { - abort!("XXX"); + Err(ioerr) => { + // EOF is indicated by returning None + if ioerr.kind != EndOfFile { + io_error::cond.raise(ioerr); + } + return None; } } } @@ -69,8 +73,8 @@ impl Writer for TcpStream { let res = self.rtstream.write(buf); match res { Ok(_) => (), - Err(_) => { - abort!("XXX"); + Err(ioerr) => { + io_error::cond.raise(ioerr); } } } @@ -106,8 +110,9 @@ impl Listener for TcpListener { Ok(s) => { Some(TcpStream::new(s)) } - Err(_) => { - abort!("XXX"); + Err(ioerr) => { + io_error::cond.raise(ioerr); + return None; } } } @@ -174,6 +179,76 @@ mod test { } } + #[test] + fn read_eof() { + do run_in_newsched_task { + let addr = next_test_ip4(); + + do spawntask_immediately { + let mut listener = TcpListener::bind(addr); + let mut stream = listener.accept(); + let mut buf = [0]; + let nread = stream.read(buf); + assert!(nread.is_none()); + } + + do spawntask_immediately { + let _stream = TcpStream::connect(addr); + // Close + } + } + } + + #[test] + fn read_eof_twice() { + do run_in_newsched_task { + let addr = next_test_ip4(); + + do spawntask_immediately { + let mut listener = TcpListener::bind(addr); + let mut stream = listener.accept(); + let mut buf = [0]; + let nread = stream.read(buf); + assert!(nread.is_none()); + let nread = stream.read(buf); + assert!(nread.is_none()); + } + + do spawntask_immediately { + let _stream = TcpStream::connect(addr); + // Close + } + } + } + + #[test] + fn write_close() { + do run_in_newsched_task { + let addr = next_test_ip4(); + + do spawntask_immediately { + let mut listener = TcpListener::bind(addr); + let mut stream = listener.accept(); + let buf = [0]; + loop { + let mut stop = false; + do io_error::cond.trap(|e| { + assert!(e.kind == ConnectionReset); + stop = true; + }).in { + stream.write(buf); + } + if stop { break } + } + } + + do spawntask_immediately { + let stream = TcpStream::connect(addr); + // Close + } + } + } + #[test] fn multiple_connect_serial() { do run_in_newsched_task { diff --git a/src/libcore/rt/uv/mod.rs b/src/libcore/rt/uv/mod.rs index e719449139758..3d9aef6cb61d4 100644 --- a/src/libcore/rt/uv/mod.rs +++ b/src/libcore/rt/uv/mod.rs @@ -210,6 +210,10 @@ pub impl UvError { from_c_str(desc_str) } } + + fn is_eof(&self) -> bool { + self.code == uvll::EOF + } } impl ToStr for UvError { @@ -262,6 +266,7 @@ pub fn uv_error_to_io_error(uverr: UvError) -> IoError { EOF => EndOfFile, EACCES => PermissionDenied, ECONNREFUSED => ConnectionRefused, + ECONNRESET => ConnectionReset, e => { abort!("unknown uv error code: %u", e as uint); } diff --git a/src/libcore/rt/uv/uvio.rs b/src/libcore/rt/uv/uvio.rs index cc9eb2ada4d17..25d912ce42eb5 100644 --- a/src/libcore/rt/uv/uvio.rs +++ b/src/libcore/rt/uv/uvio.rs @@ -301,7 +301,7 @@ impl RtioTcpStream for UvTcpStream { assert!(nread >= 0); Ok(nread as uint) } else { - Err(standard_error(OtherIoError)) + Err(uv_error_to_io_error(status.unwrap())) }; unsafe { (*result_cell_ptr).put_back(result); } @@ -330,7 +330,7 @@ impl RtioTcpStream for UvTcpStream { let result = if status.is_none() { Ok(()) } else { - Err(standard_error(OtherIoError)) + Err(uv_error_to_io_error(status.unwrap())) }; unsafe { (*result_cell_ptr).put_back(result); } diff --git a/src/libcore/rt/uv/uvll.rs b/src/libcore/rt/uv/uvll.rs index 2a2812c671847..76abf2a195d5c 100644 --- a/src/libcore/rt/uv/uvll.rs +++ b/src/libcore/rt/uv/uvll.rs @@ -39,6 +39,7 @@ pub static EOF: c_int = 1; pub static EADDRINFO: c_int = 2; pub static EACCES: c_int = 3; pub static ECONNREFUSED: c_int = 12; +pub static ECONNRESET: c_int = 13; pub struct uv_err_t { code: c_int, From afcf4f2639ea58f847d0c7260a46d65738179fae Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 9 May 2013 15:04:12 -0700 Subject: [PATCH 36/55] core::rt: Don't abort when reporting an unknown uv error --- src/libcore/rt/uv/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libcore/rt/uv/mod.rs b/src/libcore/rt/uv/mod.rs index 3d9aef6cb61d4..871fd2a9042f9 100644 --- a/src/libcore/rt/uv/mod.rs +++ b/src/libcore/rt/uv/mod.rs @@ -267,8 +267,9 @@ pub fn uv_error_to_io_error(uverr: UvError) -> IoError { EACCES => PermissionDenied, ECONNREFUSED => ConnectionRefused, ECONNRESET => ConnectionReset, - e => { - abort!("unknown uv error code: %u", e as uint); + _ => { + // XXX: Need to map remaining uv error types + OtherIoError } }; From 013b7760b7bbc43ee56179588f8fe1a81d4567e5 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 9 May 2013 17:21:13 -0700 Subject: [PATCH 37/55] core: Turn task::unkillable, etc. into no-ops in newsched. #6377 Not necessary just yet but they make ARC not work. --- src/libcore/rt/mod.rs | 1 - src/libcore/task/mod.rs | 50 +++++++++++++++++++++++++++-------------- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/libcore/rt/mod.rs b/src/libcore/rt/mod.rs index cebc87f8c2352..3e2e10504cf9b 100644 --- a/src/libcore/rt/mod.rs +++ b/src/libcore/rt/mod.rs @@ -113,7 +113,6 @@ mod local_heap; pub mod logging; /// Tools for testing the runtime -#[cfg(test)] pub mod test; /// Reference counting diff --git a/src/libcore/task/mod.rs b/src/libcore/task/mod.rs index d57bd5528bce4..a6edee38e188a 100644 --- a/src/libcore/task/mod.rs +++ b/src/libcore/task/mod.rs @@ -43,6 +43,7 @@ use task::rt::{task_id, sched_id}; use util; use util::replace; use unstable::finally::Finally; +use rt::{context, OldTaskContext}; #[cfg(test)] use comm::SharedChan; @@ -558,23 +559,33 @@ pub fn get_scheduler() -> Scheduler { * ~~~ */ pub unsafe fn unkillable(f: &fn() -> U) -> U { - let t = rt::rust_get_task(); - do (|| { - rt::rust_task_inhibit_kill(t); + if context() == OldTaskContext { + let t = rt::rust_get_task(); + do (|| { + rt::rust_task_inhibit_kill(t); + f() + }).finally { + rt::rust_task_allow_kill(t); + } + } else { + // FIXME #6377 f() - }).finally { - rt::rust_task_allow_kill(t); } } /// The inverse of unkillable. Only ever to be used nested in unkillable(). pub unsafe fn rekillable(f: &fn() -> U) -> U { - let t = rt::rust_get_task(); - do (|| { - rt::rust_task_allow_kill(t); + if context() == OldTaskContext { + let t = rt::rust_get_task(); + do (|| { + rt::rust_task_allow_kill(t); + f() + }).finally { + rt::rust_task_inhibit_kill(t); + } + } else { + // FIXME #6377 f() - }).finally { - rt::rust_task_inhibit_kill(t); } } @@ -583,14 +594,19 @@ pub unsafe fn rekillable(f: &fn() -> U) -> U { * For use with exclusive ARCs, which use pthread mutexes directly. */ pub unsafe fn atomically(f: &fn() -> U) -> U { - let t = rt::rust_get_task(); - do (|| { - rt::rust_task_inhibit_kill(t); - rt::rust_task_inhibit_yield(t); + if context() == OldTaskContext { + let t = rt::rust_get_task(); + do (|| { + rt::rust_task_inhibit_kill(t); + rt::rust_task_inhibit_yield(t); + f() + }).finally { + rt::rust_task_allow_yield(t); + rt::rust_task_allow_kill(t); + } + } else { + // FIXME #6377 f() - }).finally { - rt::rust_task_allow_yield(t); - rt::rust_task_allow_kill(t); } } From b764d4cb4f0c893caf5a6395db9b1e10a167a28f Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 9 May 2013 17:37:31 -0700 Subject: [PATCH 38/55] core::rt: Begin implementing Reader extension methods --- src/libcore/rt/io/extensions.rs | 266 +++++++++++++++++++++++++++++++- src/libcore/rt/io/mock.rs | 50 ++++++ src/libcore/rt/io/mod.rs | 14 +- 3 files changed, 325 insertions(+), 5 deletions(-) create mode 100644 src/libcore/rt/io/mock.rs diff --git a/src/libcore/rt/io/extensions.rs b/src/libcore/rt/io/extensions.rs index bb025b0ccb6d5..665b8a578e396 100644 --- a/src/libcore/rt/io/extensions.rs +++ b/src/libcore/rt/io/extensions.rs @@ -13,26 +13,103 @@ // XXX: Not sure how this should be structured // XXX: Iteration should probably be considered separately +use vec; +use rt::io::Reader; +use option::{Option, Some, None}; +use unstable::finally::Finally; + pub trait ReaderUtil { + /// Reads a single byte. Returns `None` on EOF. + /// + /// # Failure + /// + /// Raises the same conditions as the `read` method. Returns + /// `None` if the condition is handled. + fn read_byte(&mut self) -> Option; + + /// Reads `len` bytes and appends them to a vector. + /// + /// May push fewer than the requested number of bytes on error + /// or EOF. Returns true on success, false on EOF or error. + /// + /// # Failure + /// + /// Raises the same conditions as `read`. Returns `false` if + /// the condition is handled. + fn push_bytes(&mut self, buf: &mut ~[u8], len: uint) -> bool; + /// Reads `len` bytes and gives you back a new vector /// /// # Failure /// - /// Raises the `io_error` condition on error. Returns an empty - /// vector if the condition is handled. + /// Raises the same conditions as the `read` method. May return + /// less than the requested number of bytes on error or EOF. fn read_bytes(&mut self, len: uint) -> ~[u8]; /// Reads all remaining bytes from the stream. /// /// # Failure /// - /// Raises the `io_error` condition on error. Returns an empty - /// vector if the condition is handled. + /// Raises the same conditions as the `read` method. fn read_to_end(&mut self) -> ~[u8]; } +impl ReaderUtil for T { + fn read_byte(&mut self) -> Option { + let mut buf = [0]; + match self.read(buf) { + Some(nread) if nread == 0 => { + debug!("read 0 bytes. trying again"); + self.read_byte() + } + Some(nread) => Some(buf[0]), + None => None + } + } + + fn push_bytes(&mut self, buf: &mut ~[u8], len: uint) -> bool { + unsafe { + let start_len = buf.len(); + let mut total_read = 0; + let mut eof = false; + + vec::reserve_at_least(buf, start_len + len); + vec::raw::set_len(buf, start_len + len); + + do (|| { + while total_read < len { + let slice = vec::mut_slice(*buf, start_len + total_read, buf.len()); + match self.read(slice) { + Some(nread) => { + total_read += nread; + } + None => { + eof = true; + break; + } + } + } + }).finally { + vec::raw::set_len(buf, start_len + total_read); + } + + return !eof; + } + } + + fn read_bytes(&mut self, len: uint) -> ~[u8] { + let mut buf = vec::with_capacity(len); + self.push_bytes(&mut buf, len); + return buf; + } + + fn read_to_end(&mut self) -> ~[u8] { + fail!() + } +} + pub trait ReaderByteConversions { /// Reads `n` little-endian unsigned integer bytes. /// @@ -467,3 +544,184 @@ pub trait WriterByteConversions { /// Raises the `io_error` condition on error. fn write_i8(&mut self, n: i8); } + +#[cfg(test)] +mod test { + use super::*; + use option::{Some, None}; + use cell::Cell; + use rt::io::mem::MemReader; + use rt::io::mock::*; + use rt::io::{io_error, placeholder_error}; + + #[test] + fn read_byte() { + let mut reader = MemReader::new(~[10]); + let byte = reader.read_byte(); + assert!(byte == Some(10)); + } + + #[test] + fn read_byte_0_bytes() { + let mut reader = MockReader::new(); + let count = Cell(0); + reader.read = |buf| { + do count.with_mut_ref |count| { + if *count == 0 { + *count = 1; + Some(0) + } else { + buf[0] = 10; + Some(1) + } + } + }; + let byte = reader.read_byte(); + assert!(byte == Some(10)); + } + + #[test] + fn read_byte_eof() { + let mut reader = MockReader::new(); + reader.read = |_| None; + let byte = reader.read_byte(); + assert!(byte == None); + } + + #[test] + fn read_byte_error() { + let mut reader = MockReader::new(); + reader.read = |_| { + io_error::cond.raise(placeholder_error()); + None + }; + do io_error::cond.trap(|_| { + }).in { + let byte = reader.read_byte(); + assert!(byte == None); + } + } + + #[test] + fn read_bytes() { + let mut reader = MemReader::new(~[10, 11, 12, 13]); + let bytes = reader.read_bytes(4); + assert!(bytes == ~[10, 11, 12, 13]); + } + + #[test] + fn read_bytes_partial() { + let mut reader = MockReader::new(); + let count = Cell(0); + reader.read = |buf| { + do count.with_mut_ref |count| { + if *count == 0 { + *count = 1; + buf[0] = 10; + buf[1] = 11; + Some(2) + } else { + buf[0] = 12; + buf[1] = 13; + Some(2) + } + } + }; + let bytes = reader.read_bytes(4); + assert!(bytes == ~[10, 11, 12, 13]); + } + + #[test] + fn push_bytes() { + let mut reader = MemReader::new(~[10, 11, 12, 13]); + let mut buf = ~[8, 9]; + assert!(reader.push_bytes(&mut buf, 4)); + assert!(buf == ~[8, 9, 10, 11, 12, 13]); + } + + #[test] + fn push_bytes_partial() { + let mut reader = MockReader::new(); + let count = Cell(0); + reader.read = |buf| { + do count.with_mut_ref |count| { + if *count == 0 { + *count = 1; + buf[0] = 10; + buf[1] = 11; + Some(2) + } else { + buf[0] = 12; + buf[1] = 13; + Some(2) + } + } + }; + let mut buf = ~[8, 9]; + assert!(reader.push_bytes(&mut buf, 4)); + assert!(buf == ~[8, 9, 10, 11, 12, 13]); + } + + #[test] + fn push_bytes_eof() { + let mut reader = MemReader::new(~[10, 11]); + let mut buf = ~[8, 9]; + assert!(!reader.push_bytes(&mut buf, 4)); + assert!(buf == ~[8, 9, 10, 11]); + } + + #[test] + fn push_bytes_error() { + let mut reader = MockReader::new(); + let count = Cell(0); + reader.read = |buf| { + do count.with_mut_ref |count| { + if *count == 0 { + *count = 1; + buf[0] = 10; + Some(1) + } else { + io_error::cond.raise(placeholder_error()); + None + } + } + }; + let mut buf = ~[8, 9]; + do io_error::cond.trap(|_| { } ).in { + assert!(!reader.push_bytes(&mut buf, 4)); + } + assert!(buf == ~[8, 9, 10]); + } + + #[test] + #[should_fail] + #[ignore(cfg(windows))] + fn push_bytes_fail_reset_len() { + use unstable::finally::Finally; + + // push_bytes unsafely sets the vector length. This is testing that + // upon failure the length is reset correctly. + let mut reader = MockReader::new(); + let count = Cell(0); + reader.read = |buf| { + do count.with_mut_ref |count| { + if *count == 0 { + *count = 1; + buf[0] = 10; + Some(1) + } else { + io_error::cond.raise(placeholder_error()); + None + } + } + }; + let buf = @mut ~[8, 9]; + do (|| { + reader.push_bytes(&mut *buf, 4); + }).finally { + // NB: Using rtassert here to trigger abort on failure since this is a should_fail test + rtassert!(*buf == ~[8, 9, 10]); + } + } + +} diff --git a/src/libcore/rt/io/mock.rs b/src/libcore/rt/io/mock.rs new file mode 100644 index 0000000000000..b580b752bd985 --- /dev/null +++ b/src/libcore/rt/io/mock.rs @@ -0,0 +1,50 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use option::{Option, None}; +use rt::io::{Reader, Writer}; + +pub struct MockReader { + read: ~fn(buf: &mut [u8]) -> Option, + eof: ~fn() -> bool +} + +impl MockReader { + pub fn new() -> MockReader { + MockReader { + read: |_| None, + eof: || false + } + } +} + +impl Reader for MockReader { + fn read(&mut self, buf: &mut [u8]) -> Option { (self.read)(buf) } + fn eof(&mut self) -> bool { (self.eof)() } +} + +pub struct MockWriter { + write: ~fn(buf: &[u8]), + flush: ~fn() +} + +impl MockWriter { + pub fn new() -> MockWriter { + MockWriter { + write: |_| (), + flush: || () + } + } +} + +impl Writer for MockWriter { + fn write(&mut self, buf: &[u8]) { (self.write)(buf) } + fn flush(&mut self) { (self.flush)() } +} \ No newline at end of file diff --git a/src/libcore/rt/io/mod.rs b/src/libcore/rt/io/mod.rs index ab4b83f7cf0ea..f3b0cd22c17c7 100644 --- a/src/libcore/rt/io/mod.rs +++ b/src/libcore/rt/io/mod.rs @@ -316,6 +316,8 @@ pub mod native { } } +/// Mock implementations for testing +mod mock; /// The type passed to I/O condition handlers to indicate error /// @@ -350,7 +352,8 @@ condition! { pub trait Reader { /// Read bytes, up to the length of `buf` and place them in `buf`. - /// Returns the number of bytes read, or `None` on EOF. + /// Returns the number of bytes read, or `None` on EOF. The number + /// of bytes read my be less than the number requested, even 0. /// /// # Failure /// @@ -361,6 +364,7 @@ pub trait Reader { /// This doesn't take a `len` argument like the old `read`. /// Will people often need to slice their vectors to call this /// and will that be annoying? + /// Is it actually possible for 0 bytes to be read successfully? fn read(&mut self, buf: &mut [u8]) -> Option; /// Return whether the Reader has reached the end of the stream. @@ -467,3 +471,11 @@ pub fn standard_error(kind: IoErrorKind) -> IoError { _ => fail!() } } + +pub fn placeholder_error() -> IoError { + IoError { + kind: OtherIoError, + desc: "Placeholder error. You shouldn't be seeing this", + detail: None + } +} \ No newline at end of file From 76e097761e0bb11ebe57bd18c13a0c645c655108 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sun, 12 May 2013 21:24:48 -0700 Subject: [PATCH 39/55] core::rt: `read` raises `read_error` --- src/libcore/rt/io/extensions.rs | 238 +------------------------------- src/libcore/rt/io/mod.rs | 20 ++- src/libcore/rt/io/net/tcp.rs | 4 +- src/libcore/rt/io/option.rs | 8 +- 4 files changed, 29 insertions(+), 241 deletions(-) diff --git a/src/libcore/rt/io/extensions.rs b/src/libcore/rt/io/extensions.rs index 665b8a578e396..1c68934e80b05 100644 --- a/src/libcore/rt/io/extensions.rs +++ b/src/libcore/rt/io/extensions.rs @@ -114,434 +114,208 @@ pub trait ReaderByteConversions { /// Reads `n` little-endian unsigned integer bytes. /// /// `n` must be between 1 and 8, inclusive. - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. Returns `0` if - /// the condition is handled. fn read_le_uint_n(&mut self, nbytes: uint) -> u64; /// Reads `n` little-endian signed integer bytes. /// /// `n` must be between 1 and 8, inclusive. - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. Returns `0` if - /// the condition is handled. fn read_le_int_n(&mut self, nbytes: uint) -> i64; /// Reads `n` big-endian unsigned integer bytes. /// /// `n` must be between 1 and 8, inclusive. - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. Returns `0` if - /// the condition is handled. fn read_be_uint_n(&mut self, nbytes: uint) -> u64; /// Reads `n` big-endian signed integer bytes. /// /// `n` must be between 1 and 8, inclusive. - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. Returns `0` if - /// the condition is handled. fn read_be_int_n(&mut self, nbytes: uint) -> i64; /// Reads a little-endian unsigned integer. /// /// The number of bytes returned is system-dependant. - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. Returns `0` if - /// the condition is handled. fn read_le_uint(&mut self) -> uint; /// Reads a little-endian integer. /// /// The number of bytes returned is system-dependant. - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. Returns `0` if - /// the condition is handled. fn read_le_int(&mut self) -> int; /// Reads a big-endian unsigned integer. /// /// The number of bytes returned is system-dependant. - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. Returns `0` if - /// the condition is handled. fn read_be_uint(&mut self) -> uint; /// Reads a big-endian integer. /// /// The number of bytes returned is system-dependant. - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. Returns `0` if - /// the condition is handled. fn read_be_int(&mut self) -> int; /// Reads a big-endian `u64`. /// /// `u64`s are 8 bytes long. - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. Returns `0` if - /// the condition is handled. fn read_be_u64(&mut self) -> u64; /// Reads a big-endian `u32`. /// /// `u32`s are 4 bytes long. - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. Returns `0` if - /// the condition is handled. fn read_be_u32(&mut self) -> u32; /// Reads a big-endian `u16`. /// /// `u16`s are 2 bytes long. - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. Returns `0` if - /// the condition is handled. fn read_be_u16(&mut self) -> u16; /// Reads a big-endian `i64`. /// /// `i64`s are 8 bytes long. - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. Returns `0` if - /// the condition is handled. fn read_be_i64(&mut self) -> i64; /// Reads a big-endian `i32`. /// /// `i32`s are 4 bytes long. - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. Returns `0` if - /// the condition is handled. fn read_be_i32(&mut self) -> i32; /// Reads a big-endian `i16`. /// /// `i16`s are 2 bytes long. - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. Returns `0` if - /// the condition is handled. fn read_be_i16(&mut self) -> i16; /// Reads a big-endian `f64`. /// /// `f64`s are 8 byte, IEEE754 double-precision floating point numbers. - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. Returns `0` if - /// the condition is handled. fn read_be_f64(&mut self) -> f64; /// Reads a big-endian `f32`. /// /// `f32`s are 4 byte, IEEE754 single-precision floating point numbers. - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. Returns `0` if - /// the condition is handled. fn read_be_f32(&mut self) -> f32; /// Reads a little-endian `u64`. /// /// `u64`s are 8 bytes long. - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. Returns `0` if - /// the condition is handled. fn read_le_u64(&mut self) -> u64; /// Reads a little-endian `u32`. /// /// `u32`s are 4 bytes long. - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. Returns `0` if - /// the condition is handled. fn read_le_u32(&mut self) -> u32; /// Reads a little-endian `u16`. /// /// `u16`s are 2 bytes long. - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. Returns `0` if - /// the condition is handled. fn read_le_u16(&mut self) -> u16; /// Reads a little-endian `i64`. /// /// `i64`s are 8 bytes long. - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. Returns `0` if - /// the condition is handled. fn read_le_i64(&mut self) -> i64; /// Reads a little-endian `i32`. /// /// `i32`s are 4 bytes long. - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. Returns `0` if - /// the condition is handled. fn read_le_i32(&mut self) -> i32; /// Reads a little-endian `i16`. /// /// `i16`s are 2 bytes long. - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. Returns `0` if - /// the condition is handled. fn read_le_i16(&mut self) -> i16; /// Reads a little-endian `f64`. /// /// `f64`s are 8 byte, IEEE754 double-precision floating point numbers. - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. Returns `0` if - /// the condition is handled. fn read_le_f64(&mut self) -> f64; /// Reads a little-endian `f32`. /// /// `f32`s are 4 byte, IEEE754 single-precision floating point numbers. - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. Returns `0` if - /// the condition is handled. fn read_le_f32(&mut self) -> f32; /// Read a u8. /// /// `u8`s are 1 byte. - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. Returns `0` if - /// the condition is handled. fn read_u8(&mut self) -> u8; /// Read an i8. /// /// `i8`s are 1 byte. - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. Returns `0` if - /// the condition is handled. fn read_i8(&mut self) -> i8; } pub trait WriterByteConversions { /// Write the result of passing n through `int::to_str_bytes`. - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. fn write_int(&mut self, n: int); /// Write the result of passing n through `uint::to_str_bytes`. - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. fn write_uint(&mut self, n: uint); /// Write a little-endian uint (number of bytes depends on system). - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. fn write_le_uint(&mut self, n: uint); /// Write a little-endian int (number of bytes depends on system). - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. fn write_le_int(&mut self, n: int); /// Write a big-endian uint (number of bytes depends on system). - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. fn write_be_uint(&mut self, n: uint); /// Write a big-endian int (number of bytes depends on system). - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. fn write_be_int(&mut self, n: int); /// Write a big-endian u64 (8 bytes). - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. fn write_be_u64(&mut self, n: u64); /// Write a big-endian u32 (4 bytes). - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. fn write_be_u32(&mut self, n: u32); /// Write a big-endian u16 (2 bytes). - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. fn write_be_u16(&mut self, n: u16); /// Write a big-endian i64 (8 bytes). - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. fn write_be_i64(&mut self, n: i64); /// Write a big-endian i32 (4 bytes). - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. fn write_be_i32(&mut self, n: i32); /// Write a big-endian i16 (2 bytes). - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. fn write_be_i16(&mut self, n: i16); /// Write a big-endian IEEE754 double-precision floating-point (8 bytes). - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. fn write_be_f64(&mut self, f: f64); /// Write a big-endian IEEE754 single-precision floating-point (4 bytes). - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. fn write_be_f32(&mut self, f: f32); /// Write a little-endian u64 (8 bytes). - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. fn write_le_u64(&mut self, n: u64); /// Write a little-endian u32 (4 bytes). - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. fn write_le_u32(&mut self, n: u32); /// Write a little-endian u16 (2 bytes). - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. fn write_le_u16(&mut self, n: u16); /// Write a little-endian i64 (8 bytes). - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. fn write_le_i64(&mut self, n: i64); /// Write a little-endian i32 (4 bytes). - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. fn write_le_i32(&mut self, n: i32); /// Write a little-endian i16 (2 bytes). - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. fn write_le_i16(&mut self, n: i16); /// Write a little-endian IEEE754 double-precision floating-point /// (8 bytes). - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. fn write_le_f64(&mut self, f: f64); /// Write a litten-endian IEEE754 single-precision floating-point /// (4 bytes). - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. fn write_le_f32(&mut self, f: f32); /// Write a u8 (1 byte). - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. fn write_u8(&mut self, n: u8); /// Write a i8 (1 byte). - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. fn write_i8(&mut self, n: i8); } @@ -552,7 +326,7 @@ mod test { use cell::Cell; use rt::io::mem::MemReader; use rt::io::mock::*; - use rt::io::{io_error, placeholder_error}; + use rt::io::{read_error, placeholder_error}; #[test] fn read_byte() { @@ -592,10 +366,10 @@ mod test { fn read_byte_error() { let mut reader = MockReader::new(); reader.read = |_| { - io_error::cond.raise(placeholder_error()); + read_error::cond.raise(placeholder_error()); None }; - do io_error::cond.trap(|_| { + do read_error::cond.trap(|_| { }).in { let byte = reader.read_byte(); assert!(byte == None); @@ -681,13 +455,13 @@ mod test { buf[0] = 10; Some(1) } else { - io_error::cond.raise(placeholder_error()); + read_error::cond.raise(placeholder_error()); None } } }; let mut buf = ~[8, 9]; - do io_error::cond.trap(|_| { } ).in { + do read_error::cond.trap(|_| { } ).in { assert!(!reader.push_bytes(&mut buf, 4)); } assert!(buf == ~[8, 9, 10]); @@ -710,7 +484,7 @@ mod test { buf[0] = 10; Some(1) } else { - io_error::cond.raise(placeholder_error()); + read_error::cond.raise(placeholder_error()); None } } diff --git a/src/libcore/rt/io/mod.rs b/src/libcore/rt/io/mod.rs index f3b0cd22c17c7..4ec5e83c38261 100644 --- a/src/libcore/rt/io/mod.rs +++ b/src/libcore/rt/io/mod.rs @@ -350,17 +350,31 @@ condition! { /*pub*/ io_error: super::IoError -> (); } +// XXX: Can't put doc comments on macros +// Raised by `read` on error +condition! { + // FIXME (#6009): uncomment `pub` after expansion support lands. + /*pub*/ read_error: super::IoError -> (); +} + pub trait Reader { /// Read bytes, up to the length of `buf` and place them in `buf`. - /// Returns the number of bytes read, or `None` on EOF. The number - /// of bytes read my be less than the number requested, even 0. + /// Returns the number of bytes read. The number of bytes read my + /// be less than the number requested, even 0. Returns `None` on EOF. /// /// # Failure /// - /// Raises the `io_error` condition on error, then returns `None`. + /// Raises the `read_error` condition on error. If the condition + /// is handled then no guarantee is made about the number of bytes + /// read and the contents of `buf`. If the condition is handled + /// returns `None` (XXX see below). /// /// # XXX /// + /// * Should raise error on eof + /// * If the condition is handled it should still return the bytes read, + /// in which case there's no need to return Option + /// /// This doesn't take a `len` argument like the old `read`. /// Will people often need to slice their vectors to call this /// and will that be annoying? diff --git a/src/libcore/rt/io/net/tcp.rs b/src/libcore/rt/io/net/tcp.rs index 4a25334a2fcbc..f5b3c20f651e1 100644 --- a/src/libcore/rt/io/net/tcp.rs +++ b/src/libcore/rt/io/net/tcp.rs @@ -13,7 +13,7 @@ use result::{Ok, Err}; use rt::sched::local_sched::unsafe_borrow_io; use rt::io::net::ip::IpAddr; use rt::io::{Reader, Writer, Listener}; -use rt::io::{io_error, EndOfFile}; +use rt::io::{io_error, read_error, EndOfFile}; use rt::rtio::{IoFactory, RtioTcpListener, RtioTcpListenerObject, RtioTcpStream, RtioTcpStreamObject}; @@ -58,7 +58,7 @@ impl Reader for TcpStream { Err(ioerr) => { // EOF is indicated by returning None if ioerr.kind != EndOfFile { - io_error::cond.raise(ioerr); + read_error::cond.raise(ioerr); } return None; } diff --git a/src/libcore/rt/io/option.rs b/src/libcore/rt/io/option.rs index 95f8711cb5bd5..6ae747f8b4b4c 100644 --- a/src/libcore/rt/io/option.rs +++ b/src/libcore/rt/io/option.rs @@ -18,7 +18,7 @@ use option::*; use super::{Reader, Writer, Listener}; -use super::{standard_error, PreviousIoError, io_error, IoError}; +use super::{standard_error, PreviousIoError, io_error, read_error, IoError}; fn prev_io_error() -> IoError { standard_error(PreviousIoError) @@ -45,7 +45,7 @@ impl Reader for Option { match *self { Some(ref mut reader) => reader.read(buf), None => { - io_error::cond.raise(prev_io_error()); + read_error::cond.raise(prev_io_error()); None } } @@ -79,7 +79,7 @@ mod test { use option::*; use super::super::mem::*; use rt::test::*; - use super::super::{PreviousIoError, io_error}; + use super::super::{PreviousIoError, io_error, read_error}; #[test] fn test_option_writer() { @@ -133,7 +133,7 @@ mod test { let mut buf = []; let mut called = false; - do io_error::cond.trap(|err| { + do read_error::cond.trap(|err| { assert!(err.kind == PreviousIoError); called = true; }).in { From 4724966b0656761da94e24e73b028cd0d3420a7e Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 10 May 2013 17:07:41 -0700 Subject: [PATCH 40/55] core::rt: Add uv timer bindings --- src/libcore/rt/local_sched.rs | 5 +- src/libcore/rt/uv/mod.rs | 9 +- src/libcore/rt/uv/timer.rs | 186 ++++++++++++++++++++++++++++++++++ src/libcore/rt/uv/uvll.rs | 10 +- src/libstd/uv_ll.rs | 8 +- src/rt/rust_uv.cpp | 2 +- 6 files changed, 207 insertions(+), 13 deletions(-) create mode 100644 src/libcore/rt/uv/timer.rs diff --git a/src/libcore/rt/local_sched.rs b/src/libcore/rt/local_sched.rs index 1ef1fd33a83a5..ffc19c5b5e476 100644 --- a/src/libcore/rt/local_sched.rs +++ b/src/libcore/rt/local_sched.rs @@ -97,7 +97,10 @@ pub unsafe fn unsafe_borrow_io() -> *mut IoFactoryObject { } fn tls_key() -> tls::Key { - maybe_tls_key().get() + match maybe_tls_key() { + Some(key) => key, + None => abort!("runtime tls key not initialized") + } } fn maybe_tls_key() -> Option { diff --git a/src/libcore/rt/uv/mod.rs b/src/libcore/rt/uv/mod.rs index 871fd2a9042f9..ee3c5ceffd236 100644 --- a/src/libcore/rt/uv/mod.rs +++ b/src/libcore/rt/uv/mod.rs @@ -59,6 +59,7 @@ use rt::io::IoError; pub use self::file::FsRequest; pub use self::net::{StreamWatcher, TcpWatcher}; pub use self::idle::IdleWatcher; +pub use self::timer::TimerWatcher; /// The implementation of `rtio` for libuv pub mod uvio; @@ -69,6 +70,7 @@ pub mod uvll; pub mod file; pub mod net; pub mod idle; +pub mod timer; /// XXX: Loop(*handle) is buggy with destructors. Normal structs /// with dtors may not be destructured, but tuple structs can, @@ -125,6 +127,7 @@ pub type NullCallback = ~fn(); pub type IdleCallback = ~fn(IdleWatcher, Option); pub type ConnectionCallback = ~fn(StreamWatcher, Option); pub type FsCallback = ~fn(FsRequest, Option); +pub type TimerCallback = ~fn(TimerWatcher, Option); /// Callbacks used by StreamWatchers, set as custom data on the foreign handle @@ -134,7 +137,8 @@ struct WatcherData { connect_cb: Option, close_cb: Option, alloc_cb: Option, - idle_cb: Option + idle_cb: Option, + timer_cb: Option } pub trait WatcherInterop { @@ -162,7 +166,8 @@ impl> WatcherInterop for W { connect_cb: None, close_cb: None, alloc_cb: None, - idle_cb: None + idle_cb: None, + timer_cb: None }; let data = transmute::<~WatcherData, *c_void>(data); uvll::set_data_for_uv_handle(self.native_handle(), data); diff --git a/src/libcore/rt/uv/timer.rs b/src/libcore/rt/uv/timer.rs new file mode 100644 index 0000000000000..1045a77da12fa --- /dev/null +++ b/src/libcore/rt/uv/timer.rs @@ -0,0 +1,186 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use libc::{c_void, c_int}; +use option::Some; +use rt::uv::uvll; +use rt::uv::{Watcher, Loop, NativeHandle, TimerCallback, NullCallback}; +use rt::uv::status_to_maybe_uv_error; + +pub struct TimerWatcher(*uvll::uv_timer_t); +impl Watcher for TimerWatcher { } + +impl TimerWatcher { + pub fn new(loop_: &mut Loop) -> TimerWatcher { + unsafe { + let handle = uvll::malloc_handle(uvll::UV_TIMER); + assert!(handle.is_not_null()); + assert!(0 == uvll::timer_init(loop_.native_handle(), handle)); + let mut watcher: TimerWatcher = NativeHandle::from_native_handle(handle); + watcher.install_watcher_data(); + return watcher; + } + } + + pub fn start(&mut self, timeout: u64, repeat: u64, cb: TimerCallback) { + { + let data = self.get_watcher_data(); + data.timer_cb = Some(cb); + } + + unsafe { + uvll::timer_start(self.native_handle(), timer_cb, timeout, repeat); + } + + extern fn timer_cb(handle: *uvll::uv_timer_t, status: c_int) { + let mut watcher: TimerWatcher = NativeHandle::from_native_handle(handle); + let data = watcher.get_watcher_data(); + let cb = data.timer_cb.get_ref(); + let status = status_to_maybe_uv_error(handle, status); + (*cb)(watcher, status); + } + } + + pub fn stop(&mut self) { + unsafe { + uvll::timer_stop(self.native_handle()); + } + } + + pub fn close(self, cb: NullCallback) { + let mut watcher = self; + { + let data = watcher.get_watcher_data(); + assert!(data.close_cb.is_none()); + data.close_cb = Some(cb); + } + + unsafe { + uvll::close(watcher.native_handle(), close_cb); + } + + extern fn close_cb(handle: *uvll::uv_timer_t) { + let mut watcher: TimerWatcher = NativeHandle::from_native_handle(handle); + { + let mut data = watcher.get_watcher_data(); + data.close_cb.swap_unwrap()(); + } + watcher.drop_watcher_data(); + unsafe { + uvll::free_handle(handle as *c_void); + } + } + } +} + +impl NativeHandle<*uvll::uv_timer_t> for TimerWatcher { + fn from_native_handle(handle: *uvll::uv_timer_t) -> TimerWatcher { + TimerWatcher(handle) + } + fn native_handle(&self) -> *uvll::uv_idle_t { + match self { &TimerWatcher(ptr) => ptr } + } +} + +#[cfg(test)] +mod test { + use super::*; + use rt::uv::Loop; + use unstable::run_in_bare_thread; + + #[test] + fn smoke_test() { + do run_in_bare_thread { + let mut count = 0; + let count_ptr: *mut int = &mut count; + let mut loop_ = Loop::new(); + let mut timer = TimerWatcher::new(&mut loop_); + do timer.start(10, 0) |timer, status| { + assert!(status.is_none()); + unsafe { *count_ptr += 1 }; + timer.close(||()); + } + loop_.run(); + loop_.close(); + assert!(count == 1); + } + } + + #[test] + fn start_twice() { + do run_in_bare_thread { + let mut count = 0; + let count_ptr: *mut int = &mut count; + let mut loop_ = Loop::new(); + let mut timer = TimerWatcher::new(&mut loop_); + do timer.start(10, 0) |timer, status| { + let mut timer = timer; + assert!(status.is_none()); + unsafe { *count_ptr += 1 }; + do timer.start(10, 0) |timer, status| { + let mut timer = timer; + assert!(status.is_none()); + unsafe { *count_ptr += 1 }; + timer.close(||()); + } + } + loop_.run(); + loop_.close(); + assert!(count == 2); + } + } + + #[test] + fn repeat_stop() { + do run_in_bare_thread { + let mut count = 0; + let count_ptr: *mut int = &mut count; + let mut loop_ = Loop::new(); + let mut timer = TimerWatcher::new(&mut loop_); + do timer.start(10, 20) |timer, status| { + assert!(status.is_none()); + unsafe { + *count_ptr += 1; + + if *count_ptr == 10 { + + // Stop the timer and do something else + let mut timer = timer; + timer.stop(); + // Freeze timer so it can be captured + let timer = timer; + + let mut loop_ = timer.event_loop(); + let mut timer2 = TimerWatcher::new(&mut loop_); + do timer2.start(10, 0) |timer2, status| { + + unsafe { *count_ptr += 1; } + + let mut timer2 = timer2; + timer2.close(||()); + + // Restart the original timer + let mut timer = timer; + do timer.start(10, 0) |timer, status| { + unsafe { *count_ptr += 1; } + let mut timer = timer; + timer.close(||()); + } + } + } + }; + } + loop_.run(); + loop_.close(); + assert!(count == 12); + } + } + +} diff --git a/src/libcore/rt/uv/uvll.rs b/src/libcore/rt/uv/uvll.rs index 76abf2a195d5c..94e6b82ab8fa9 100644 --- a/src/libcore/rt/uv/uvll.rs +++ b/src/libcore/rt/uv/uvll.rs @@ -268,9 +268,9 @@ pub unsafe fn buf_init(input: *u8, len: uint) -> uv_buf_t { pub unsafe fn timer_init(loop_ptr: *c_void, timer_ptr: *uv_timer_t) -> c_int { return rust_uv_timer_init(loop_ptr, timer_ptr); } -pub unsafe fn timer_start(timer_ptr: *uv_timer_t, cb: *u8, timeout: uint, - repeat: uint) -> c_int { - return rust_uv_timer_start(timer_ptr, cb, timeout as c_uint, repeat as c_uint); +pub unsafe fn timer_start(timer_ptr: *uv_timer_t, cb: *u8, timeout: u64, + repeat: u64) -> c_int { + return rust_uv_timer_start(timer_ptr, cb, timeout, repeat); } pub unsafe fn timer_stop(timer_ptr: *uv_timer_t) -> c_int { return rust_uv_timer_stop(timer_ptr); @@ -431,8 +431,8 @@ extern { timer_handle: *uv_timer_t) -> c_int; fn rust_uv_timer_start(timer_handle: *uv_timer_t, cb: *u8, - timeout: c_uint, - repeat: c_uint) -> c_int; + timeout: libc::uint64_t, + repeat: libc::uint64_t) -> c_int; fn rust_uv_timer_stop(handle: *uv_timer_t) -> c_int; fn rust_uv_malloc_buf_base_of(sug_size: size_t) -> *u8; diff --git a/src/libstd/uv_ll.rs b/src/libstd/uv_ll.rs index a14c048b8ded5..96ceb1002d8b6 100644 --- a/src/libstd/uv_ll.rs +++ b/src/libstd/uv_ll.rs @@ -819,8 +819,8 @@ extern { unsafe fn rust_uv_timer_start( timer_handle: *uv_timer_t, cb: *u8, - timeout: libc::c_uint, - repeat: libc::c_uint) -> libc::c_int; + timeout: libc::uint64_t, + repeat: libc::uint64_t) -> libc::c_int; unsafe fn rust_uv_timer_stop(handle: *uv_timer_t) -> libc::c_int; unsafe fn rust_uv_getaddrinfo(loop_ptr: *libc::c_void, @@ -1084,8 +1084,8 @@ pub unsafe fn timer_init(loop_ptr: *libc::c_void, } pub unsafe fn timer_start(timer_ptr: *uv_timer_t, cb: *u8, timeout: uint, repeat: uint) -> libc::c_int { - return rust_uv_timer_start(timer_ptr, cb, timeout as libc::c_uint, - repeat as libc::c_uint); + return rust_uv_timer_start(timer_ptr, cb, timeout as libc::uint64_t, + repeat as libc::uint64_t); } pub unsafe fn timer_stop(timer_ptr: *uv_timer_t) -> libc::c_int { return rust_uv_timer_stop(timer_ptr); diff --git a/src/rt/rust_uv.cpp b/src/rt/rust_uv.cpp index 8cf2bd4b4acb9..fefcbbcacf7d4 100644 --- a/src/rt/rust_uv.cpp +++ b/src/rt/rust_uv.cpp @@ -229,7 +229,7 @@ rust_uv_timer_init(uv_loop_t* loop, uv_timer_t* timer) { extern "C" int rust_uv_timer_start(uv_timer_t* the_timer, uv_timer_cb cb, - uint32_t timeout, uint32_t repeat) { + int64_t timeout, int64_t repeat) { return uv_timer_start(the_timer, cb, timeout, repeat); } From c42b03de17b2d0c567dc85417ec22518d69fd8ae Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 11 May 2013 00:42:16 -0700 Subject: [PATCH 41/55] core::rt: Fix scheduling logic for enqueued tasks --- src/libcore/macros.rs | 4 +- src/libcore/rt/mod.rs | 6 +- src/libcore/rt/rtio.rs | 1 + src/libcore/rt/sched.rs | 275 ++++++++++++++++++++--------------- src/libcore/rt/test.rs | 12 +- src/libcore/rt/uv/uvio.rs | 12 +- src/libcore/rt/work_queue.rs | 4 + 7 files changed, 188 insertions(+), 126 deletions(-) diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs index 04ee086b9ae4b..fda48b6ffb7d9 100644 --- a/src/libcore/macros.rs +++ b/src/libcore/macros.rs @@ -11,7 +11,7 @@ #[macro_escape]; // Some basic logging -macro_rules! rtdebug ( +macro_rules! rtdebug_ ( ($( $arg:expr),+) => ( { dumb_println(fmt!( $($arg),+ )); @@ -26,7 +26,7 @@ macro_rules! rtdebug ( ) // An alternate version with no output, for turning off logging -macro_rules! rtdebug_ ( +macro_rules! rtdebug ( ($( $arg:expr),+) => ( $(let _ = $arg)*; ) ) diff --git a/src/libcore/rt/mod.rs b/src/libcore/rt/mod.rs index 3e2e10504cf9b..4ed76003eeb60 100644 --- a/src/libcore/rt/mod.rs +++ b/src/libcore/rt/mod.rs @@ -147,7 +147,7 @@ pub fn start(_argc: int, _argv: **u8, crate_map: *u8, main: ~fn()) -> int { let mut sched = ~Scheduler::new(loop_); let main_task = ~Task::new(&mut sched.stack_pool, main); - sched.task_queue.push_back(main_task); + sched.enqueue_task(main_task); sched.run(); return 0; @@ -225,11 +225,11 @@ fn test_context() { assert!(context() == SchedulerContext); let task = Cell(task); do local_sched::borrow |sched| { - sched.task_queue.push_back(task.take()); + sched.enqueue_task(task.take()); } } }; - sched.task_queue.push_back(task); + sched.enqueue_task(task); sched.run(); } } diff --git a/src/libcore/rt/rtio.rs b/src/libcore/rt/rtio.rs index 497ff8841b6bd..4b5eda22ff5de 100644 --- a/src/libcore/rt/rtio.rs +++ b/src/libcore/rt/rtio.rs @@ -25,6 +25,7 @@ pub type RtioTcpListenerObject = uvio::UvTcpListener; pub trait EventLoop { fn run(&mut self); fn callback(&mut self, ~fn()); + fn callback_ms(&mut self, ms: u64, ~fn()); /// The asynchronous I/O services. Not all event loops may provide one fn io<'a>(&'a mut self) -> Option<&'a mut IoFactoryObject>; } diff --git a/src/libcore/rt/sched.rs b/src/libcore/rt/sched.rs index 395f9099571a0..3469997833c5d 100644 --- a/src/libcore/rt/sched.rs +++ b/src/libcore/rt/sched.rs @@ -31,7 +31,7 @@ pub mod local_sched; /// thread local storage and the running task is owned by the /// scheduler. pub struct Scheduler { - task_queue: WorkQueue<~Task>, + priv task_queue: WorkQueue<~Task>, stack_pool: StackPool, /// The event loop used to drive the scheduler and perform I/O event_loop: ~EventLoopObject, @@ -91,44 +91,56 @@ pub impl Scheduler { fn run(~self) -> ~Scheduler { assert!(!self.in_task_context()); - // Give ownership of the scheduler (self) to the thread - local_sched::put(self); + let mut self_sched = self; unsafe { - let scheduler = local_sched::unsafe_borrow(); - fn run_scheduler_once() { - let scheduler = local_sched::take(); - if scheduler.resume_task_from_queue() { - // Ok, a task ran. Nice! We'll do it again later - do local_sched::borrow |scheduler| { - scheduler.event_loop.callback(run_scheduler_once); - } - } - } + let event_loop: *mut ~EventLoopObject = { + let event_loop: *mut ~EventLoopObject = &mut self_sched.event_loop; + event_loop + }; - let scheduler = &mut *scheduler; - scheduler.event_loop.callback(run_scheduler_once); - scheduler.event_loop.run(); + // Give ownership of the scheduler (self) to the thread + local_sched::put(self_sched); + + (*event_loop).run(); } - return local_sched::take(); + let sched = local_sched::take(); + assert!(sched.task_queue.is_empty()); + return sched; + } + + /// Schedule a task to be executed later. + /// + /// Pushes the task onto the work stealing queue and tells the event loop + /// to run it later. Always use this instead of pushing to the work queue + /// directly. + fn enqueue_task(&mut self, task: ~Task) { + self.task_queue.push_front(task); + self.event_loop.callback(resume_task_from_queue); + + fn resume_task_from_queue() { + let scheduler = local_sched::take(); + scheduler.resume_task_from_queue(); + } } // * Scheduler-context operations - fn resume_task_from_queue(~self) -> bool { + fn resume_task_from_queue(~self) { assert!(!self.in_task_context()); + rtdebug!("looking in work queue for task to schedule"); + let mut this = self; match this.task_queue.pop_front() { Some(task) => { + rtdebug!("resuming task from work queue"); this.resume_task_immediately(task); - return true; } None => { rtdebug!("no tasks in queue"); local_sched::put(this); - return false; } } } @@ -158,7 +170,7 @@ pub impl Scheduler { do self.switch_running_tasks_and_then(task) |last_task| { let last_task = Cell(last_task); do local_sched::borrow |sched| { - sched.task_queue.push_front(last_task.take()); + sched.enqueue_task(last_task.take()); } } } @@ -385,118 +397,153 @@ pub impl Task { } } -#[test] -fn test_simple_scheduling() { - do run_in_bare_thread { - let mut task_ran = false; - let task_ran_ptr: *mut bool = &mut task_ran; - - let mut sched = ~UvEventLoop::new_scheduler(); - let task = ~do Task::new(&mut sched.stack_pool) { - unsafe { *task_ran_ptr = true; } - }; - sched.task_queue.push_back(task); - sched.run(); - assert!(task_ran); +#[cfg(test)] +mod test { + use int; + use cell::Cell; + use rt::uv::uvio::UvEventLoop; + use unstable::run_in_bare_thread; + use task::spawn; + use rt::test::*; + use super::*; + + #[test] + fn test_simple_scheduling() { + do run_in_bare_thread { + let mut task_ran = false; + let task_ran_ptr: *mut bool = &mut task_ran; + + let mut sched = ~UvEventLoop::new_scheduler(); + let task = ~do Task::new(&mut sched.stack_pool) { + unsafe { *task_ran_ptr = true; } + }; + sched.enqueue_task(task); + sched.run(); + assert!(task_ran); + } } -} -#[test] -fn test_several_tasks() { - do run_in_bare_thread { - let total = 10; - let mut task_count = 0; - let task_count_ptr: *mut int = &mut task_count; + #[test] + fn test_several_tasks() { + do run_in_bare_thread { + let total = 10; + let mut task_count = 0; + let task_count_ptr: *mut int = &mut task_count; - let mut sched = ~UvEventLoop::new_scheduler(); - for int::range(0, total) |_| { - let task = ~do Task::new(&mut sched.stack_pool) { - unsafe { *task_count_ptr = *task_count_ptr + 1; } - }; - sched.task_queue.push_back(task); + let mut sched = ~UvEventLoop::new_scheduler(); + for int::range(0, total) |_| { + let task = ~do Task::new(&mut sched.stack_pool) { + unsafe { *task_count_ptr = *task_count_ptr + 1; } + }; + sched.enqueue_task(task); + } + sched.run(); + assert!(task_count == total); } - sched.run(); - assert!(task_count == total); } -} -#[test] -fn test_swap_tasks_then() { - do run_in_bare_thread { - let mut count = 0; - let count_ptr: *mut int = &mut count; - - let mut sched = ~UvEventLoop::new_scheduler(); - let task1 = ~do Task::new(&mut sched.stack_pool) { - unsafe { *count_ptr = *count_ptr + 1; } - let mut sched = local_sched::take(); - let task2 = ~do Task::new(&mut sched.stack_pool) { + #[test] + fn test_swap_tasks_then() { + do run_in_bare_thread { + let mut count = 0; + let count_ptr: *mut int = &mut count; + + let mut sched = ~UvEventLoop::new_scheduler(); + let task1 = ~do Task::new(&mut sched.stack_pool) { unsafe { *count_ptr = *count_ptr + 1; } - }; - // Context switch directly to the new task - do sched.switch_running_tasks_and_then(task2) |task1| { - let task1 = Cell(task1); - do local_sched::borrow |sched| { - sched.task_queue.push_front(task1.take()); + let mut sched = local_sched::take(); + let task2 = ~do Task::new(&mut sched.stack_pool) { + unsafe { *count_ptr = *count_ptr + 1; } + }; + // Context switch directly to the new task + do sched.switch_running_tasks_and_then(task2) |task1| { + let task1 = Cell(task1); + do local_sched::borrow |sched| { + sched.enqueue_task(task1.take()); + } } - } - unsafe { *count_ptr = *count_ptr + 1; } - }; - sched.task_queue.push_back(task1); - sched.run(); - assert!(count == 3); + unsafe { *count_ptr = *count_ptr + 1; } + }; + sched.enqueue_task(task1); + sched.run(); + assert!(count == 3); + } } -} -#[bench] #[test] #[ignore(reason = "long test")] -fn test_run_a_lot_of_tasks_queued() { - do run_in_bare_thread { - static MAX: int = 1000000; - let mut count = 0; - let count_ptr: *mut int = &mut count; + #[bench] #[test] #[ignore(reason = "long test")] + fn test_run_a_lot_of_tasks_queued() { + do run_in_bare_thread { + static MAX: int = 1000000; + let mut count = 0; + let count_ptr: *mut int = &mut count; - let mut sched = ~UvEventLoop::new_scheduler(); + let mut sched = ~UvEventLoop::new_scheduler(); - let start_task = ~do Task::new(&mut sched.stack_pool) { - run_task(count_ptr); - }; - sched.task_queue.push_back(start_task); - sched.run(); + let start_task = ~do Task::new(&mut sched.stack_pool) { + run_task(count_ptr); + }; + sched.enqueue_task(start_task); + sched.run(); - assert!(count == MAX); + assert!(count == MAX); - fn run_task(count_ptr: *mut int) { - do local_sched::borrow |sched| { - let task = ~do Task::new(&mut sched.stack_pool) { - unsafe { - *count_ptr = *count_ptr + 1; - if *count_ptr != MAX { - run_task(count_ptr); + fn run_task(count_ptr: *mut int) { + do local_sched::borrow |sched| { + let task = ~do Task::new(&mut sched.stack_pool) { + unsafe { + *count_ptr = *count_ptr + 1; + if *count_ptr != MAX { + run_task(count_ptr); + } } + }; + sched.enqueue_task(task); + } + }; + } + } + + #[test] + fn test_block_task() { + do run_in_bare_thread { + let mut sched = ~UvEventLoop::new_scheduler(); + let task = ~do Task::new(&mut sched.stack_pool) { + let sched = local_sched::take(); + assert!(sched.in_task_context()); + do sched.deschedule_running_task_and_then() |task| { + let task = Cell(task); + do local_sched::borrow |sched| { + assert!(!sched.in_task_context()); + sched.enqueue_task(task.take()); } - }; - sched.task_queue.push_back(task); - } - }; + } + }; + sched.enqueue_task(task); + sched.run(); + } } -} -#[test] -fn test_block_task() { - do run_in_bare_thread { - let mut sched = ~UvEventLoop::new_scheduler(); - let task = ~do Task::new(&mut sched.stack_pool) { - let sched = local_sched::take(); - assert!(sched.in_task_context()); - do sched.deschedule_running_task_and_then() |task| { - let task = Cell(task); - do local_sched::borrow |sched| { - assert!(!sched.in_task_context()); - sched.task_queue.push_back(task.take()); + #[test] + fn test_io_callback() { + // This is a regression test that when there are no schedulable tasks + // in the work queue, but we are performing I/O, that once we do put + // something in the work queue again the scheduler picks it up and doesn't + // exit before emptying the work queue + do run_in_newsched_task { + do spawn { + let sched = local_sched::take(); + do sched.deschedule_running_task_and_then |task| { + let mut sched = local_sched::take(); + let task = Cell(task); + do sched.event_loop.callback_ms(10) { + rtdebug!("in callback"); + let mut sched = local_sched::take(); + sched.enqueue_task(task.take()); + local_sched::put(sched); + } + local_sched::put(sched); } } - }; - sched.task_queue.push_back(task); - sched.run(); + } } } diff --git a/src/libcore/rt/test.rs b/src/libcore/rt/test.rs index 8d0ae0caf4d62..cfd1748ded35a 100644 --- a/src/libcore/rt/test.rs +++ b/src/libcore/rt/test.rs @@ -28,7 +28,7 @@ pub fn run_in_newsched_task(f: ~fn()) { let task = ~Task::with_local(&mut sched.stack_pool, LocalServices::without_unwinding(), f.take()); - sched.task_queue.push_back(task); + sched.enqueue_task(task); sched.run(); } } @@ -59,7 +59,7 @@ pub fn spawntask_immediately(f: ~fn()) { do sched.switch_running_tasks_and_then(task) |task| { let task = Cell(task); do local_sched::borrow |sched| { - sched.task_queue.push_front(task.take()); + sched.enqueue_task(task.take()); } } } @@ -73,7 +73,7 @@ pub fn spawntask_later(f: ~fn()) { LocalServices::without_unwinding(), f); - sched.task_queue.push_front(task); + sched.enqueue_task(task); local_sched::put(sched); } @@ -94,11 +94,11 @@ pub fn spawntask_random(f: ~fn()) { do sched.switch_running_tasks_and_then(task) |task| { let task = Cell(task); do local_sched::borrow |sched| { - sched.task_queue.push_front(task.take()); + sched.enqueue_task(task.take()); } } } else { - sched.task_queue.push_front(task); + sched.enqueue_task(task); local_sched::put(sched); } } @@ -132,7 +132,7 @@ pub fn spawntask_try(f: ~fn()) -> Result<(), ()> { do sched.switch_running_tasks_and_then(old_task.take()) |new_task| { let new_task = Cell(new_task); do local_sched::borrow |sched| { - sched.task_queue.push_front(new_task.take()); + sched.enqueue_task(new_task.take()); } } } diff --git a/src/libcore/rt/uv/uvio.rs b/src/libcore/rt/uv/uvio.rs index 25d912ce42eb5..a25d18101a7fd 100644 --- a/src/libcore/rt/uv/uvio.rs +++ b/src/libcore/rt/uv/uvio.rs @@ -73,6 +73,16 @@ impl EventLoop for UvEventLoop { } } + fn callback_ms(&mut self, ms: u64, f: ~fn()) { + let mut timer = TimerWatcher::new(self.uvio.uv_loop()); + do timer.start(ms, 0) |timer, status| { + assert!(status.is_none()); + let mut timer = timer; + timer.close(||()); + f(); + } + } + fn io<'a>(&'a mut self) -> Option<&'a mut IoFactoryObject> { Some(&mut self.uvio) } @@ -419,7 +429,7 @@ fn test_read_and_block() { do scheduler.deschedule_running_task_and_then |task| { let task = Cell(task); do local_sched::borrow |scheduler| { - scheduler.task_queue.push_back(task.take()); + scheduler.enqueue_task(task.take()); } } } diff --git a/src/libcore/rt/work_queue.rs b/src/libcore/rt/work_queue.rs index 495cd75a0bf8e..f82b5847ef2b8 100644 --- a/src/libcore/rt/work_queue.rs +++ b/src/libcore/rt/work_queue.rs @@ -46,4 +46,8 @@ pub impl WorkQueue { None } } + + fn is_empty(&self) -> bool { + return self.queue.is_empty(); + } } From 56c0b188b66f71f55e2d577ca4a23830a31433e6 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 11 May 2013 19:46:43 -0700 Subject: [PATCH 42/55] rt: Rename sched_key to rt_key It is more general-purpose than holding scheduler pointers --- src/libcore/rt/local_sched.rs | 4 ++-- src/rt/rust_builtin.cpp | 14 +++++++------- src/rt/rustrt.def.in | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/libcore/rt/local_sched.rs b/src/libcore/rt/local_sched.rs index ffc19c5b5e476..895354d2218e5 100644 --- a/src/libcore/rt/local_sched.rs +++ b/src/libcore/rt/local_sched.rs @@ -105,7 +105,7 @@ fn tls_key() -> tls::Key { fn maybe_tls_key() -> Option { unsafe { - let key: *mut c_void = rust_get_sched_tls_key(); + let key: *mut c_void = rust_get_rt_tls_key(); let key: &mut tls::Key = cast::transmute(key); let key = *key; // Check that the key has been initialized. @@ -130,7 +130,7 @@ fn maybe_tls_key() -> Option { extern { #[fast_ffi] - fn rust_get_sched_tls_key() -> *mut c_void; + fn rust_get_rt_tls_key() -> *mut c_void; } #[test] diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp index 39a6f5bfd1b7b..1a64066b5a946 100644 --- a/src/rt/rust_builtin.cpp +++ b/src/rt/rust_builtin.cpp @@ -830,14 +830,14 @@ rust_get_rt_env() { } #ifndef _WIN32 -pthread_key_t sched_key = -1; +pthread_key_t rt_key = -1; #else -DWORD sched_key = -1; +DWORD rt_key = -1; #endif extern "C" void* -rust_get_sched_tls_key() { - return &sched_key; +rust_get_rt_tls_key() { + return &rt_key; } // Initialize the global state required by the new scheduler @@ -852,10 +852,10 @@ rust_initialize_global_state() { if (!initialized) { #ifndef _WIN32 - assert(!pthread_key_create(&sched_key, NULL)); + assert(!pthread_key_create(&rt_key, NULL)); #else - sched_key = TlsAlloc(); - assert(sched_key != TLS_OUT_OF_INDEXES); + rt_key = TlsAlloc(); + assert(rt_key != TLS_OUT_OF_INDEXES); #endif initialized = true; diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index f1ddb17c499a1..cdc282440b830 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -196,7 +196,7 @@ rust_get_global_data_ptr rust_inc_kernel_live_count rust_dec_kernel_live_count rust_exchange_count -rust_get_sched_tls_key +rust_get_rt_tls_key swap_registers rust_readdir rust_opendir From 7f5746f6d2bcc048aca5ddfbfdf41497ab874836 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sun, 12 May 2013 15:08:07 -0700 Subject: [PATCH 43/55] core::rt: Rename Sched.task_queue to work_queue --- src/libcore/rt/sched.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libcore/rt/sched.rs b/src/libcore/rt/sched.rs index 3469997833c5d..1e4a9c87a68fe 100644 --- a/src/libcore/rt/sched.rs +++ b/src/libcore/rt/sched.rs @@ -31,7 +31,7 @@ pub mod local_sched; /// thread local storage and the running task is owned by the /// scheduler. pub struct Scheduler { - priv task_queue: WorkQueue<~Task>, + priv work_queue: WorkQueue<~Task>, stack_pool: StackPool, /// The event loop used to drive the scheduler and perform I/O event_loop: ~EventLoopObject, @@ -76,7 +76,7 @@ pub impl Scheduler { Scheduler { event_loop: event_loop, - task_queue: WorkQueue::new(), + work_queue: WorkQueue::new(), stack_pool: StackPool::new(), saved_context: Context::empty(), current_task: None, @@ -106,7 +106,7 @@ pub impl Scheduler { } let sched = local_sched::take(); - assert!(sched.task_queue.is_empty()); + assert!(sched.work_queue.is_empty()); return sched; } @@ -116,7 +116,7 @@ pub impl Scheduler { /// to run it later. Always use this instead of pushing to the work queue /// directly. fn enqueue_task(&mut self, task: ~Task) { - self.task_queue.push_front(task); + self.work_queue.push_front(task); self.event_loop.callback(resume_task_from_queue); fn resume_task_from_queue() { @@ -133,7 +133,7 @@ pub impl Scheduler { rtdebug!("looking in work queue for task to schedule"); let mut this = self; - match this.task_queue.pop_front() { + match this.work_queue.pop_front() { Some(task) => { rtdebug!("resuming task from work queue"); this.resume_task_immediately(task); From 390dde571ede57133f249692ab020f83fd5d22ee Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sun, 12 May 2013 15:26:19 -0700 Subject: [PATCH 44/55] core::rt: Rename Task to Coroutine --- src/libcore/rt/mod.rs | 10 +++--- src/libcore/rt/sched.rs | 66 +++++++++++++++++++-------------------- src/libcore/rt/test.rs | 34 ++++++++++---------- src/libcore/rt/tube.rs | 4 +-- src/libcore/task/spawn.rs | 2 +- 5 files changed, 58 insertions(+), 58 deletions(-) diff --git a/src/libcore/rt/mod.rs b/src/libcore/rt/mod.rs index 4ed76003eeb60..7a772ff0f3b96 100644 --- a/src/libcore/rt/mod.rs +++ b/src/libcore/rt/mod.rs @@ -67,7 +67,7 @@ use ptr::Ptr; /// The global (exchange) heap. pub mod global_heap; -/// The Scheduler and Task types. +/// The Scheduler and Coroutine types. mod sched; /// Thread-local access to the current Scheduler. @@ -138,14 +138,14 @@ pub mod tube; /// The return value is used as the process return code. 0 on success, 101 on error. pub fn start(_argc: int, _argv: **u8, crate_map: *u8, main: ~fn()) -> int { - use self::sched::{Scheduler, Task}; + use self::sched::{Scheduler, Coroutine}; use self::uv::uvio::UvEventLoop; init(crate_map); let loop_ = ~UvEventLoop::new(); let mut sched = ~Scheduler::new(loop_); - let main_task = ~Task::new(&mut sched.stack_pool, main); + let main_task = ~Coroutine::new(&mut sched.stack_pool, main); sched.enqueue_task(main_task); sched.run(); @@ -210,7 +210,7 @@ pub fn context() -> RuntimeContext { #[test] fn test_context() { use unstable::run_in_bare_thread; - use self::sched::{local_sched, Task}; + use self::sched::{local_sched, Coroutine}; use rt::uv::uvio::UvEventLoop; use cell::Cell; @@ -218,7 +218,7 @@ fn test_context() { do run_in_bare_thread { assert!(context() == GlobalContext); let mut sched = ~UvEventLoop::new_scheduler(); - let task = ~do Task::new(&mut sched.stack_pool) { + let task = ~do Coroutine::new(&mut sched.stack_pool) { assert!(context() == TaskContext); let sched = local_sched::take(); do sched.deschedule_running_task_and_then() |task| { diff --git a/src/libcore/rt/sched.rs b/src/libcore/rt/sched.rs index 1e4a9c87a68fe..d92eaf89e5f3c 100644 --- a/src/libcore/rt/sched.rs +++ b/src/libcore/rt/sched.rs @@ -26,12 +26,12 @@ use cell::Cell; // A more convenient name for external callers, e.g. `local_sched::take()` pub mod local_sched; -/// The Scheduler is responsible for coordinating execution of Tasks +/// The Scheduler is responsible for coordinating execution of Coroutines /// on a single thread. When the scheduler is running it is owned by /// thread local storage and the running task is owned by the /// scheduler. pub struct Scheduler { - priv work_queue: WorkQueue<~Task>, + priv work_queue: WorkQueue<~Coroutine>, stack_pool: StackPool, /// The event loop used to drive the scheduler and perform I/O event_loop: ~EventLoopObject, @@ -39,7 +39,7 @@ pub struct Scheduler { /// Always valid when a task is executing, otherwise not priv saved_context: Context, /// The currently executing task - current_task: Option<~Task>, + current_task: Option<~Coroutine>, /// An action performed after a context switch on behalf of the /// code running before the context switch priv cleanup_job: Option @@ -49,17 +49,17 @@ pub struct Scheduler { // complaining type UnsafeTaskReceiver = sys::Closure; trait ClosureConverter { - fn from_fn(&fn(~Task)) -> Self; - fn to_fn(self) -> &fn(~Task); + fn from_fn(&fn(~Coroutine)) -> Self; + fn to_fn(self) -> &fn(~Coroutine); } impl ClosureConverter for UnsafeTaskReceiver { - fn from_fn(f: &fn(~Task)) -> UnsafeTaskReceiver { unsafe { transmute(f) } } - fn to_fn(self) -> &fn(~Task) { unsafe { transmute(self) } } + fn from_fn(f: &fn(~Coroutine)) -> UnsafeTaskReceiver { unsafe { transmute(f) } } + fn to_fn(self) -> &fn(~Coroutine) { unsafe { transmute(self) } } } enum CleanupJob { DoNothing, - GiveTask(~Task, UnsafeTaskReceiver) + GiveTask(~Coroutine, UnsafeTaskReceiver) } pub impl Scheduler { @@ -115,7 +115,7 @@ pub impl Scheduler { /// Pushes the task onto the work stealing queue and tells the event loop /// to run it later. Always use this instead of pushing to the work queue /// directly. - fn enqueue_task(&mut self, task: ~Task) { + fn enqueue_task(&mut self, task: ~Coroutine) { self.work_queue.push_front(task); self.event_loop.callback(resume_task_from_queue); @@ -164,7 +164,7 @@ pub impl Scheduler { abort!("control reached end of task"); } - fn schedule_new_task(~self, task: ~Task) { + fn schedule_new_task(~self, task: ~Coroutine) { assert!(self.in_task_context()); do self.switch_running_tasks_and_then(task) |last_task| { @@ -177,7 +177,7 @@ pub impl Scheduler { // Core scheduling ops - fn resume_task_immediately(~self, task: ~Task) { + fn resume_task_immediately(~self, task: ~Coroutine) { let mut this = self; assert!(!this.in_task_context()); @@ -215,7 +215,7 @@ pub impl Scheduler { /// The closure here is a *stack* closure that lives in the /// running task. It gets transmuted to the scheduler's lifetime /// and called while the task is blocked. - fn deschedule_running_task_and_then(~self, f: &fn(~Task)) { + fn deschedule_running_task_and_then(~self, f: &fn(~Coroutine)) { let mut this = self; assert!(this.in_task_context()); @@ -223,7 +223,7 @@ pub impl Scheduler { unsafe { let blocked_task = this.current_task.swap_unwrap(); - let f_fake_region = transmute::<&fn(~Task), &fn(~Task)>(f); + let f_fake_region = transmute::<&fn(~Coroutine), &fn(~Coroutine)>(f); let f_opaque = ClosureConverter::from_fn(f_fake_region); this.enqueue_cleanup_job(GiveTask(blocked_task, f_opaque)); } @@ -245,14 +245,14 @@ pub impl Scheduler { /// Switch directly to another task, without going through the scheduler. /// You would want to think hard about doing this, e.g. if there are /// pending I/O events it would be a bad idea. - fn switch_running_tasks_and_then(~self, next_task: ~Task, f: &fn(~Task)) { + fn switch_running_tasks_and_then(~self, next_task: ~Coroutine, f: &fn(~Coroutine)) { let mut this = self; assert!(this.in_task_context()); rtdebug!("switching tasks"); let old_running_task = this.current_task.swap_unwrap(); - let f_fake_region = unsafe { transmute::<&fn(~Task), &fn(~Task)>(f) }; + let f_fake_region = unsafe { transmute::<&fn(~Coroutine), &fn(~Coroutine)>(f) }; let f_opaque = ClosureConverter::from_fn(f_fake_region); this.enqueue_cleanup_job(GiveTask(old_running_task, f_opaque)); this.current_task = Some(next_task); @@ -318,7 +318,7 @@ pub impl Scheduler { // because borrowck thinks the three patterns are conflicting // borrows unsafe { - let last_task = transmute::, Option<&mut Task>>(last_task); + let last_task = transmute::, Option<&mut Coroutine>>(last_task); let last_task_context = match last_task { Some(t) => Some(&mut t.saved_context), None => None }; @@ -333,9 +333,9 @@ pub impl Scheduler { } } -static TASK_MIN_STACK_SIZE: uint = 10000000; // XXX: Too much stack +static MIN_STACK_SIZE: uint = 10000000; // XXX: Too much stack -pub struct Task { +pub struct Coroutine { /// The segment of stack on which the task is currently running or, /// if the task is blocked, on which the task will resume execution priv current_stack_segment: StackSegment, @@ -346,19 +346,19 @@ pub struct Task { local_services: LocalServices } -pub impl Task { - fn new(stack_pool: &mut StackPool, start: ~fn()) -> Task { - Task::with_local(stack_pool, LocalServices::new(), start) +pub impl Coroutine { + fn new(stack_pool: &mut StackPool, start: ~fn()) -> Coroutine { + Coroutine::with_local(stack_pool, LocalServices::new(), start) } fn with_local(stack_pool: &mut StackPool, local_services: LocalServices, - start: ~fn()) -> Task { - let start = Task::build_start_wrapper(start); - let mut stack = stack_pool.take_segment(TASK_MIN_STACK_SIZE); + start: ~fn()) -> Coroutine { + let start = Coroutine::build_start_wrapper(start); + let mut stack = stack_pool.take_segment(MIN_STACK_SIZE); // NB: Context holds a pointer to that ~fn let initial_context = Context::new(start, &mut stack); - return Task { + return Coroutine { current_stack_segment: stack, saved_context: initial_context, local_services: local_services @@ -390,7 +390,7 @@ pub impl Task { /// Destroy the task and try to reuse its components fn recycle(~self, stack_pool: &mut StackPool) { match self { - ~Task {current_stack_segment, _} => { + ~Coroutine {current_stack_segment, _} => { stack_pool.give_segment(current_stack_segment); } } @@ -414,7 +414,7 @@ mod test { let task_ran_ptr: *mut bool = &mut task_ran; let mut sched = ~UvEventLoop::new_scheduler(); - let task = ~do Task::new(&mut sched.stack_pool) { + let task = ~do Coroutine::new(&mut sched.stack_pool) { unsafe { *task_ran_ptr = true; } }; sched.enqueue_task(task); @@ -432,7 +432,7 @@ mod test { let mut sched = ~UvEventLoop::new_scheduler(); for int::range(0, total) |_| { - let task = ~do Task::new(&mut sched.stack_pool) { + let task = ~do Coroutine::new(&mut sched.stack_pool) { unsafe { *task_count_ptr = *task_count_ptr + 1; } }; sched.enqueue_task(task); @@ -449,10 +449,10 @@ mod test { let count_ptr: *mut int = &mut count; let mut sched = ~UvEventLoop::new_scheduler(); - let task1 = ~do Task::new(&mut sched.stack_pool) { + let task1 = ~do Coroutine::new(&mut sched.stack_pool) { unsafe { *count_ptr = *count_ptr + 1; } let mut sched = local_sched::take(); - let task2 = ~do Task::new(&mut sched.stack_pool) { + let task2 = ~do Coroutine::new(&mut sched.stack_pool) { unsafe { *count_ptr = *count_ptr + 1; } }; // Context switch directly to the new task @@ -479,7 +479,7 @@ mod test { let mut sched = ~UvEventLoop::new_scheduler(); - let start_task = ~do Task::new(&mut sched.stack_pool) { + let start_task = ~do Coroutine::new(&mut sched.stack_pool) { run_task(count_ptr); }; sched.enqueue_task(start_task); @@ -489,7 +489,7 @@ mod test { fn run_task(count_ptr: *mut int) { do local_sched::borrow |sched| { - let task = ~do Task::new(&mut sched.stack_pool) { + let task = ~do Coroutine::new(&mut sched.stack_pool) { unsafe { *count_ptr = *count_ptr + 1; if *count_ptr != MAX { @@ -507,7 +507,7 @@ mod test { fn test_block_task() { do run_in_bare_thread { let mut sched = ~UvEventLoop::new_scheduler(); - let task = ~do Task::new(&mut sched.stack_pool) { + let task = ~do Coroutine::new(&mut sched.stack_pool) { let sched = local_sched::take(); assert!(sched.in_task_context()); do sched.deschedule_running_task_and_then() |task| { diff --git a/src/libcore/rt/test.rs b/src/libcore/rt/test.rs index cfd1748ded35a..1294b9bcf4765 100644 --- a/src/libcore/rt/test.rs +++ b/src/libcore/rt/test.rs @@ -18,16 +18,16 @@ use rt::local_services::LocalServices; /// will abort the process. pub fn run_in_newsched_task(f: ~fn()) { use unstable::run_in_bare_thread; - use super::sched::Task; + use super::sched::Coroutine; use rt::uv::uvio::UvEventLoop; let f = Cell(f); do run_in_bare_thread { let mut sched = ~UvEventLoop::new_scheduler(); - let task = ~Task::with_local(&mut sched.stack_pool, - LocalServices::without_unwinding(), - f.take()); + let task = ~Coroutine::with_local(&mut sched.stack_pool, + LocalServices::without_unwinding(), + f.take()); sched.enqueue_task(task); sched.run(); } @@ -38,9 +38,9 @@ pub fn spawntask(f: ~fn()) { use super::sched::*; let mut sched = local_sched::take(); - let task = ~Task::with_local(&mut sched.stack_pool, - LocalServices::without_unwinding(), - f); + let task = ~Coroutine::with_local(&mut sched.stack_pool, + LocalServices::without_unwinding(), + f); do sched.switch_running_tasks_and_then(task) |task| { let task = Cell(task); let sched = local_sched::take(); @@ -53,9 +53,9 @@ pub fn spawntask_immediately(f: ~fn()) { use super::sched::*; let mut sched = local_sched::take(); - let task = ~Task::with_local(&mut sched.stack_pool, - LocalServices::without_unwinding(), - f); + let task = ~Coroutine::with_local(&mut sched.stack_pool, + LocalServices::without_unwinding(), + f); do sched.switch_running_tasks_and_then(task) |task| { let task = Cell(task); do local_sched::borrow |sched| { @@ -69,9 +69,9 @@ pub fn spawntask_later(f: ~fn()) { use super::sched::*; let mut sched = local_sched::take(); - let task = ~Task::with_local(&mut sched.stack_pool, - LocalServices::without_unwinding(), - f); + let task = ~Coroutine::with_local(&mut sched.stack_pool, + LocalServices::without_unwinding(), + f); sched.enqueue_task(task); local_sched::put(sched); @@ -86,9 +86,9 @@ pub fn spawntask_random(f: ~fn()) { let run_now: bool = Rand::rand(&mut rng); let mut sched = local_sched::take(); - let task = ~Task::with_local(&mut sched.stack_pool, - LocalServices::without_unwinding(), - f); + let task = ~Coroutine::with_local(&mut sched.stack_pool, + LocalServices::without_unwinding(), + f); if run_now { do sched.switch_running_tasks_and_then(task) |task| { @@ -122,7 +122,7 @@ pub fn spawntask_try(f: ~fn()) -> Result<(), ()> { let old_task = Cell(old_task); let f = f.take(); let mut sched = local_sched::take(); - let new_task = ~do Task::new(&mut sched.stack_pool) { + let new_task = ~do Coroutine::new(&mut sched.stack_pool) { do (|| { (f.take())() }).finally { diff --git a/src/libcore/rt/tube.rs b/src/libcore/rt/tube.rs index 8e7bf72fa6308..bc9269f08faa9 100644 --- a/src/libcore/rt/tube.rs +++ b/src/libcore/rt/tube.rs @@ -16,14 +16,14 @@ use option::*; use clone::Clone; use super::rc::RC; -use rt::sched::Task; +use rt::sched::Coroutine; use rt::{context, TaskContext, SchedulerContext}; use rt::local_sched; use vec::OwnedVector; use container::Container; struct TubeState { - blocked_task: Option<~Task>, + blocked_task: Option<~Coroutine>, buf: ~[T] } diff --git a/src/libcore/task/spawn.rs b/src/libcore/task/spawn.rs index fc38702bc1605..5f9642604d0c1 100644 --- a/src/libcore/task/spawn.rs +++ b/src/libcore/task/spawn.rs @@ -581,7 +581,7 @@ fn spawn_raw_newsched(_opts: TaskOpts, f: ~fn()) { use rt::sched::*; let mut sched = local_sched::take(); - let task = ~Task::new(&mut sched.stack_pool, f); + let task = ~Coroutine::new(&mut sched.stack_pool, f); sched.schedule_new_task(task); } From 1c1f11e649c52135435f6ea747426568edac56a2 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 13 May 2013 14:25:56 -0700 Subject: [PATCH 45/55] core::rt: Warnings --- src/libcore/rt/io/extensions.rs | 6 ++++-- src/libcore/rt/io/net/tcp.rs | 2 +- src/libcore/rt/sched.rs | 4 ---- src/libcore/rt/uv/idle.rs | 2 +- src/libcore/rt/uv/net.rs | 4 ++-- src/libcore/rt/uv/timer.rs | 9 +++------ src/libcore/rt/uv/uvio.rs | 3 +-- 7 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/libcore/rt/io/extensions.rs b/src/libcore/rt/io/extensions.rs index 1c68934e80b05..97841f0db46c6 100644 --- a/src/libcore/rt/io/extensions.rs +++ b/src/libcore/rt/io/extensions.rs @@ -17,6 +17,7 @@ use vec; use rt::io::Reader; use option::{Option, Some, None}; use unstable::finally::Finally; +use util; pub trait ReaderUtil { @@ -60,11 +61,12 @@ impl ReaderUtil for T { fn read_byte(&mut self) -> Option { let mut buf = [0]; match self.read(buf) { - Some(nread) if nread == 0 => { + Some(0) => { debug!("read 0 bytes. trying again"); self.read_byte() } - Some(nread) => Some(buf[0]), + Some(1) => Some(buf[0]), + Some(_) => util::unreachable(), None => None } } diff --git a/src/libcore/rt/io/net/tcp.rs b/src/libcore/rt/io/net/tcp.rs index f5b3c20f651e1..04f4edd7e39f5 100644 --- a/src/libcore/rt/io/net/tcp.rs +++ b/src/libcore/rt/io/net/tcp.rs @@ -243,7 +243,7 @@ mod test { } do spawntask_immediately { - let stream = TcpStream::connect(addr); + let _stream = TcpStream::connect(addr); // Close } } diff --git a/src/libcore/rt/sched.rs b/src/libcore/rt/sched.rs index d92eaf89e5f3c..5c1a3410087c4 100644 --- a/src/libcore/rt/sched.rs +++ b/src/libcore/rt/sched.rs @@ -19,10 +19,6 @@ use super::context::Context; use super::local_services::LocalServices; use cell::Cell; -#[cfg(test)] use rt::uv::uvio::UvEventLoop; -#[cfg(test)] use unstable::run_in_bare_thread; -#[cfg(test)] use int; - // A more convenient name for external callers, e.g. `local_sched::take()` pub mod local_sched; diff --git a/src/libcore/rt/uv/idle.rs b/src/libcore/rt/uv/idle.rs index fecb9391caa54..2cf0b5c487288 100644 --- a/src/libcore/rt/uv/idle.rs +++ b/src/libcore/rt/uv/idle.rs @@ -71,7 +71,7 @@ pub impl IdleWatcher { unsafe { let mut idle_watcher: IdleWatcher = NativeHandle::from_native_handle(handle); { - let mut data = idle_watcher.get_watcher_data(); + let data = idle_watcher.get_watcher_data(); data.close_cb.swap_unwrap()(); } idle_watcher.drop_watcher_data(); diff --git a/src/libcore/rt/uv/net.rs b/src/libcore/rt/uv/net.rs index fd78b552119b5..bdd5588014c01 100644 --- a/src/libcore/rt/uv/net.rs +++ b/src/libcore/rt/uv/net.rs @@ -131,7 +131,7 @@ pub impl StreamWatcher { extern fn close_cb(handle: *uvll::uv_stream_t) { let mut stream_watcher: StreamWatcher = NativeHandle::from_native_handle(handle); { - let mut data = stream_watcher.get_watcher_data(); + let data = stream_watcher.get_watcher_data(); data.close_cb.swap_unwrap()(); } stream_watcher.drop_watcher_data(); @@ -373,7 +373,7 @@ mod test { assert!(status.is_none()); let mut server_stream_watcher = server_stream_watcher; let mut loop_ = loop_; - let mut client_tcp_watcher = TcpWatcher::new(&mut loop_); + let client_tcp_watcher = TcpWatcher::new(&mut loop_); let mut client_tcp_watcher = client_tcp_watcher.as_stream(); server_stream_watcher.accept(client_tcp_watcher); let count_cell = Cell(0); diff --git a/src/libcore/rt/uv/timer.rs b/src/libcore/rt/uv/timer.rs index 1045a77da12fa..5557a58098751 100644 --- a/src/libcore/rt/uv/timer.rs +++ b/src/libcore/rt/uv/timer.rs @@ -69,7 +69,7 @@ impl TimerWatcher { extern fn close_cb(handle: *uvll::uv_timer_t) { let mut watcher: TimerWatcher = NativeHandle::from_native_handle(handle); { - let mut data = watcher.get_watcher_data(); + let data = watcher.get_watcher_data(); data.close_cb.swap_unwrap()(); } watcher.drop_watcher_data(); @@ -125,7 +125,6 @@ mod test { assert!(status.is_none()); unsafe { *count_ptr += 1 }; do timer.start(10, 0) |timer, status| { - let mut timer = timer; assert!(status.is_none()); unsafe { *count_ptr += 1 }; timer.close(||()); @@ -159,18 +158,16 @@ mod test { let mut loop_ = timer.event_loop(); let mut timer2 = TimerWatcher::new(&mut loop_); - do timer2.start(10, 0) |timer2, status| { + do timer2.start(10, 0) |timer2, _| { unsafe { *count_ptr += 1; } - let mut timer2 = timer2; timer2.close(||()); // Restart the original timer let mut timer = timer; - do timer.start(10, 0) |timer, status| { + do timer.start(10, 0) |timer, _| { unsafe { *count_ptr += 1; } - let mut timer = timer; timer.close(||()); } } diff --git a/src/libcore/rt/uv/uvio.rs b/src/libcore/rt/uv/uvio.rs index a25d18101a7fd..ce4eb6aff8701 100644 --- a/src/libcore/rt/uv/uvio.rs +++ b/src/libcore/rt/uv/uvio.rs @@ -77,7 +77,6 @@ impl EventLoop for UvEventLoop { let mut timer = TimerWatcher::new(self.uvio.uv_loop()); do timer.start(ms, 0) |timer, status| { assert!(status.is_none()); - let mut timer = timer; timer.close(||()); f(); } @@ -235,7 +234,7 @@ impl RtioTcpListener for UvTcpListener { 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 = 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); From 28a13ec8d76ba9a4f9645991e1260882a3b7dc68 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 13 May 2013 15:23:52 -0700 Subject: [PATCH 46/55] core::rt: Make push_bytes raise read_error on EOF --- src/libcore/rt/io/extensions.rs | 50 +++++++++++++++++++++------------ src/libcore/rt/io/mod.rs | 14 ++++++++- 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/src/libcore/rt/io/extensions.rs b/src/libcore/rt/io/extensions.rs index 97841f0db46c6..addafcf1d99c1 100644 --- a/src/libcore/rt/io/extensions.rs +++ b/src/libcore/rt/io/extensions.rs @@ -14,7 +14,7 @@ // XXX: Iteration should probably be considered separately use vec; -use rt::io::Reader; +use rt::io::{Reader, read_error, standard_error, EndOfFile}; use option::{Option, Some, None}; use unstable::finally::Finally; use util; @@ -36,16 +36,19 @@ pub trait ReaderUtil { /// /// # Failure /// - /// Raises the same conditions as `read`. Returns `false` if - /// the condition is handled. - fn push_bytes(&mut self, buf: &mut ~[u8], len: uint) -> bool; + /// Raises the same conditions as `read`. Additionally raises `read_error` + /// on EOF. If `read_error` is handled then `push_bytes` returns without + /// pushing any bytes onto `buf` - that is, `buf` has the same length + /// upon exit as it did on entry. + fn push_bytes(&mut self, buf: &mut ~[u8], len: uint); - /// Reads `len` bytes and gives you back a new vector + /// Reads `len` bytes and gives you back a new vector of length `len` /// /// # Failure /// - /// Raises the same conditions as the `read` method. May return - /// less than the requested number of bytes on error or EOF. + /// Raises the same conditions as `read`. Additionally raises `read_error` + /// on EOF. If `read_error` is handled then the returned vector has + /// length 0. fn read_bytes(&mut self, len: uint) -> ~[u8]; /// Reads all remaining bytes from the stream. @@ -71,11 +74,10 @@ impl ReaderUtil for T { } } - fn push_bytes(&mut self, buf: &mut ~[u8], len: uint) -> bool { + fn push_bytes(&mut self, buf: &mut ~[u8], len: uint) { unsafe { let start_len = buf.len(); let mut total_read = 0; - let mut eof = false; vec::reserve_at_least(buf, start_len + len); vec::raw::set_len(buf, start_len + len); @@ -88,7 +90,9 @@ impl ReaderUtil for T { total_read += nread; } None => { - eof = true; + read_error::cond.raise(standard_error(EndOfFile)); + // Reset the vector length as though we didn't read anything + total_read = 0; break; } } @@ -96,8 +100,6 @@ impl ReaderUtil for T { }).finally { vec::raw::set_len(buf, start_len + total_read); } - - return !eof; } } @@ -407,11 +409,20 @@ mod test { assert!(bytes == ~[10, 11, 12, 13]); } + #[test] + fn read_bytes_eof() { + let mut reader = MemReader::new(~[10, 11]); + do read_error::cond.trap(|_| { + }).in { + assert!(reader.read_bytes(4) == ~[]); + } + } + #[test] fn push_bytes() { let mut reader = MemReader::new(~[10, 11, 12, 13]); let mut buf = ~[8, 9]; - assert!(reader.push_bytes(&mut buf, 4)); + reader.push_bytes(&mut buf, 4); assert!(buf == ~[8, 9, 10, 11, 12, 13]); } @@ -434,7 +445,7 @@ mod test { } }; let mut buf = ~[8, 9]; - assert!(reader.push_bytes(&mut buf, 4)); + reader.push_bytes(&mut buf, 4); assert!(buf == ~[8, 9, 10, 11, 12, 13]); } @@ -442,8 +453,11 @@ mod test { fn push_bytes_eof() { let mut reader = MemReader::new(~[10, 11]); let mut buf = ~[8, 9]; - assert!(!reader.push_bytes(&mut buf, 4)); - assert!(buf == ~[8, 9, 10, 11]); + do read_error::cond.trap(|_| { + }).in { + reader.push_bytes(&mut buf, 4); + assert!(buf == ~[8, 9]); + } } #[test] @@ -464,9 +478,9 @@ mod test { }; let mut buf = ~[8, 9]; do read_error::cond.trap(|_| { } ).in { - assert!(!reader.push_bytes(&mut buf, 4)); + reader.push_bytes(&mut buf, 4); } - assert!(buf == ~[8, 9, 10]); + assert!(buf == ~[8, 9]); } #[test] diff --git a/src/libcore/rt/io/mod.rs b/src/libcore/rt/io/mod.rs index 4ec5e83c38261..7e611a0b8bc91 100644 --- a/src/libcore/rt/io/mod.rs +++ b/src/libcore/rt/io/mod.rs @@ -260,8 +260,11 @@ pub use self::net::tcp::TcpStream; pub use self::net::udp::UdpStream; // Some extension traits that all Readers and Writers get. +#[cfg(not(stage0))] // Requires condition! fixes pub use self::extensions::ReaderUtil; +#[cfg(not(stage0))] // Requires condition! fixes pub use self::extensions::ReaderByteConversions; +#[cfg(not(stage0))] // Requires condition! fixes pub use self::extensions::WriterByteConversions; /// Synchronous, non-blocking file I/O. @@ -295,6 +298,7 @@ pub mod flate; pub mod comm_adapters; /// Extension traits +#[cfg(not(stage0))] // Requires condition! fixes mod extensions; /// Non-I/O things needed by the I/O module @@ -373,7 +377,8 @@ pub trait Reader { /// /// * Should raise error on eof /// * If the condition is handled it should still return the bytes read, - /// in which case there's no need to return Option + /// in which case there's no need to return Option - but then you *have* + /// to install a handler to detect eof. /// /// This doesn't take a `len` argument like the old `read`. /// Will people often need to slice their vectors to call this @@ -482,6 +487,13 @@ pub fn standard_error(kind: IoErrorKind) -> IoError { detail: None } } + EndOfFile => { + IoError { + kind: EndOfFile, + desc: "End of file", + detail: None + } + } _ => fail!() } } From d45dc8df7278649d101ce28fae1d934559e0e3c2 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 13 May 2013 16:56:16 -0700 Subject: [PATCH 47/55] core::rt: More work on Reader extensions and error handling --- src/libcore/rt/io/extensions.rs | 184 +++++++++++++++++++++----------- src/libcore/rt/io/mod.rs | 8 +- 2 files changed, 127 insertions(+), 65 deletions(-) diff --git a/src/libcore/rt/io/extensions.rs b/src/libcore/rt/io/extensions.rs index addafcf1d99c1..4a5193f086b33 100644 --- a/src/libcore/rt/io/extensions.rs +++ b/src/libcore/rt/io/extensions.rs @@ -14,7 +14,7 @@ // XXX: Iteration should probably be considered separately use vec; -use rt::io::{Reader, read_error, standard_error, EndOfFile}; +use rt::io::{Reader, read_error, standard_error, EndOfFile, DEFAULT_BUF_SIZE}; use option::{Option, Some, None}; use unstable::finally::Finally; use util; @@ -37,9 +37,8 @@ pub trait ReaderUtil { /// # Failure /// /// Raises the same conditions as `read`. Additionally raises `read_error` - /// on EOF. If `read_error` is handled then `push_bytes` returns without - /// pushing any bytes onto `buf` - that is, `buf` has the same length - /// upon exit as it did on entry. + /// on EOF. If `read_error` is handled then `push_bytes` may push less + /// than the requested number of bytes. fn push_bytes(&mut self, buf: &mut ~[u8], len: uint); /// Reads `len` bytes and gives you back a new vector of length `len` @@ -47,8 +46,8 @@ pub trait ReaderUtil { /// # Failure /// /// Raises the same conditions as `read`. Additionally raises `read_error` - /// on EOF. If `read_error` is handled then the returned vector has - /// length 0. + /// on EOF. If `read_error` is handled then the returned vector may + /// contain less than the requested number of bytes. fn read_bytes(&mut self, len: uint) -> ~[u8]; /// Reads all remaining bytes from the stream. @@ -60,60 +59,6 @@ pub trait ReaderUtil { } -impl ReaderUtil for T { - fn read_byte(&mut self) -> Option { - let mut buf = [0]; - match self.read(buf) { - Some(0) => { - debug!("read 0 bytes. trying again"); - self.read_byte() - } - Some(1) => Some(buf[0]), - Some(_) => util::unreachable(), - None => None - } - } - - fn push_bytes(&mut self, buf: &mut ~[u8], len: uint) { - unsafe { - let start_len = buf.len(); - let mut total_read = 0; - - vec::reserve_at_least(buf, start_len + len); - vec::raw::set_len(buf, start_len + len); - - do (|| { - while total_read < len { - let slice = vec::mut_slice(*buf, start_len + total_read, buf.len()); - match self.read(slice) { - Some(nread) => { - total_read += nread; - } - None => { - read_error::cond.raise(standard_error(EndOfFile)); - // Reset the vector length as though we didn't read anything - total_read = 0; - break; - } - } - } - }).finally { - vec::raw::set_len(buf, start_len + total_read); - } - } - } - - fn read_bytes(&mut self, len: uint) -> ~[u8] { - let mut buf = vec::with_capacity(len); - self.push_bytes(&mut buf, len); - return buf; - } - - fn read_to_end(&mut self) -> ~[u8] { - fail!() - } -} - pub trait ReaderByteConversions { /// Reads `n` little-endian unsigned integer bytes. /// @@ -323,6 +268,71 @@ pub trait WriterByteConversions { fn write_i8(&mut self, n: i8); } +impl ReaderUtil for T { + fn read_byte(&mut self) -> Option { + let mut buf = [0]; + match self.read(buf) { + Some(0) => { + debug!("read 0 bytes. trying again"); + self.read_byte() + } + Some(1) => Some(buf[0]), + Some(_) => util::unreachable(), + None => None + } + } + + fn push_bytes(&mut self, buf: &mut ~[u8], len: uint) { + unsafe { + let start_len = buf.len(); + let mut total_read = 0; + + vec::reserve_at_least(buf, start_len + len); + vec::raw::set_len(buf, start_len + len); + + do (|| { + while total_read < len { + let slice = vec::mut_slice(*buf, start_len + total_read, buf.len()); + match self.read(slice) { + Some(nread) => { + total_read += nread; + } + None => { + read_error::cond.raise(standard_error(EndOfFile)); + break; + } + } + } + }).finally { + vec::raw::set_len(buf, start_len + total_read); + } + } + } + + fn read_bytes(&mut self, len: uint) -> ~[u8] { + let mut buf = vec::with_capacity(len); + self.push_bytes(&mut buf, len); + return buf; + } + + fn read_to_end(&mut self) -> ~[u8] { + let mut buf = vec::with_capacity(DEFAULT_BUF_SIZE); + let mut keep_reading = true; + do read_error::cond.trap(|e| { + if e.kind == EndOfFile { + keep_reading = false; + } else { + read_error::cond.raise(e) + } + }).in { + while keep_reading { + self.push_bytes(&mut buf, DEFAULT_BUF_SIZE) + } + } + return buf; + } +} + #[cfg(test)] mod test { use super::*; @@ -414,7 +424,7 @@ mod test { let mut reader = MemReader::new(~[10, 11]); do read_error::cond.trap(|_| { }).in { - assert!(reader.read_bytes(4) == ~[]); + assert!(reader.read_bytes(4) == ~[10, 11]); } } @@ -456,7 +466,7 @@ mod test { do read_error::cond.trap(|_| { }).in { reader.push_bytes(&mut buf, 4); - assert!(buf == ~[8, 9]); + assert!(buf == ~[8, 9, 10, 11]); } } @@ -480,7 +490,7 @@ mod test { do read_error::cond.trap(|_| { } ).in { reader.push_bytes(&mut buf, 4); } - assert!(buf == ~[8, 9]); + assert!(buf == ~[8, 9, 10]); } #[test] @@ -514,4 +524,52 @@ mod test { } } + #[test] + fn read_to_end() { + let mut reader = MockReader::new(); + let count = Cell(0); + reader.read = |buf| { + do count.with_mut_ref |count| { + if *count == 0 { + *count = 1; + buf[0] = 10; + buf[1] = 11; + Some(2) + } else if *count == 1 { + *count = 2; + buf[0] = 12; + buf[1] = 13; + Some(2) + } else { + None + } + } + }; + let buf = reader.read_to_end(); + assert!(buf == ~[10, 11, 12, 13]); + } + + #[test] + #[should_fail] + #[ignore(cfg(windows))] + fn read_to_end_error() { + let mut reader = MockReader::new(); + let count = Cell(0); + reader.read = |buf| { + do count.with_mut_ref |count| { + if *count == 0 { + *count = 1; + buf[0] = 10; + buf[1] = 11; + Some(2) + } else { + read_error::cond.raise(placeholder_error()); + None + } + } + }; + let buf = reader.read_to_end(); + assert!(buf == ~[10, 11]); + } + } diff --git a/src/libcore/rt/io/mod.rs b/src/libcore/rt/io/mod.rs index 7e611a0b8bc91..14784fa95672e 100644 --- a/src/libcore/rt/io/mod.rs +++ b/src/libcore/rt/io/mod.rs @@ -187,7 +187,7 @@ In particular code written to ignore errors and expect conditions to be unhandle will start passing around null or zero objects when wrapped in a condition handler. * XXX: How should we use condition handlers that return values? - +* XXX: Should EOF raise default conditions when EOF is not an error? # Issues withi/o scheduler affinity, work stealing, task pinning @@ -323,6 +323,10 @@ pub mod native { /// Mock implementations for testing mod mock; +/// The default buffer size for various I/O operations +/// XXX: Not pub +pub static DEFAULT_BUF_SIZE: uint = 1024 * 64; + /// The type passed to I/O condition handlers to indicate error /// /// # XXX @@ -375,7 +379,7 @@ pub trait Reader { /// /// # XXX /// - /// * Should raise error on eof + /// * Should raise_default error on eof? /// * If the condition is handled it should still return the bytes read, /// in which case there's no need to return Option - but then you *have* /// to install a handler to detect eof. From 2bc1e6ba606e3f08a49bbadc556558cb42b7ea32 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 13 May 2013 19:14:14 -0700 Subject: [PATCH 48/55] core::rt: Copy many of the old io extensions to the new io Some resolve problem is keeping the tests from working --- src/libcore/rt/io/extensions.rs | 340 +++++++++++++++++++++++++++++++- 1 file changed, 334 insertions(+), 6 deletions(-) diff --git a/src/libcore/rt/io/extensions.rs b/src/libcore/rt/io/extensions.rs index 4a5193f086b33..a3804d2d6ef1a 100644 --- a/src/libcore/rt/io/extensions.rs +++ b/src/libcore/rt/io/extensions.rs @@ -13,11 +13,16 @@ // XXX: Not sure how this should be structured // XXX: Iteration should probably be considered separately +use uint; +use int; use vec; -use rt::io::{Reader, read_error, standard_error, EndOfFile, DEFAULT_BUF_SIZE}; +use rt::io::{Reader, Writer}; +use rt::io::{read_error, standard_error, EndOfFile, DEFAULT_BUF_SIZE}; use option::{Option, Some, None}; use unstable::finally::Finally; use util; +use cast; +use io::{u64_to_le_bytes, u64_to_be_bytes}; pub trait ReaderUtil { @@ -212,7 +217,7 @@ pub trait WriterByteConversions { fn write_be_int(&mut self, n: int); /// Write a big-endian u64 (8 bytes). - fn write_be_u64(&mut self, n: u64); + fn write_be_u64_(&mut self, n: u64); /// Write a big-endian u32 (4 bytes). fn write_be_u32(&mut self, n: u32); @@ -236,7 +241,7 @@ pub trait WriterByteConversions { fn write_be_f32(&mut self, f: f32); /// Write a little-endian u64 (8 bytes). - fn write_le_u64(&mut self, n: u64); + fn write_le_u64_(&mut self, n: u64); /// Write a little-endian u32 (4 bytes). fn write_le_u32(&mut self, n: u32); @@ -333,13 +338,262 @@ impl ReaderUtil for T { } } +impl ReaderByteConversions for T { + fn read_le_uint_n(&mut self, nbytes: uint) -> u64 { + assert!(nbytes > 0 && nbytes <= 8); + + let mut val = 0u64, pos = 0, i = nbytes; + while i > 0 { + val += (self.read_u8() as u64) << pos; + pos += 8; + i -= 1; + } + val + } + + fn read_le_int_n(&mut self, nbytes: uint) -> i64 { + extend_sign(self.read_le_uint_n(nbytes), nbytes) + } + + fn read_be_uint_n(&mut self, nbytes: uint) -> u64 { + assert!(nbytes > 0 && nbytes <= 8); + + let mut val = 0u64, i = nbytes; + while i > 0 { + i -= 1; + val += (self.read_u8() as u64) << i * 8; + } + val + } + + fn read_be_int_n(&mut self, nbytes: uint) -> i64 { + extend_sign(self.read_be_uint_n(nbytes), nbytes) + } + + fn read_le_uint(&mut self) -> uint { + self.read_le_uint_n(uint::bytes) as uint + } + + fn read_le_int(&mut self) -> int { + self.read_le_int_n(int::bytes) as int + } + + fn read_be_uint(&mut self) -> uint { + self.read_be_uint_n(uint::bytes) as uint + } + + fn read_be_int(&mut self) -> int { + self.read_be_int_n(int::bytes) as int + } + + fn read_be_u64(&mut self) -> u64 { + self.read_be_uint_n(8) as u64 + } + + fn read_be_u32(&mut self) -> u32 { + self.read_be_uint_n(4) as u32 + } + + fn read_be_u16(&mut self) -> u16 { + self.read_be_uint_n(2) as u16 + } + + fn read_be_i64(&mut self) -> i64 { + self.read_be_int_n(8) as i64 + } + + fn read_be_i32(&mut self) -> i32 { + self.read_be_int_n(4) as i32 + } + + fn read_be_i16(&mut self) -> i16 { + self.read_be_int_n(2) as i16 + } + + fn read_be_f64(&mut self) -> f64 { + unsafe { + cast::transmute::(self.read_be_u64()) + } + } + + fn read_be_f32(&mut self) -> f32 { + unsafe { + cast::transmute::(self.read_be_u32()) + } + } + + fn read_le_u64(&mut self) -> u64 { + self.read_le_uint_n(8) as u64 + } + + fn read_le_u32(&mut self) -> u32 { + self.read_le_uint_n(4) as u32 + } + + fn read_le_u16(&mut self) -> u16 { + self.read_le_uint_n(2) as u16 + } + + fn read_le_i64(&mut self) -> i64 { + self.read_le_int_n(8) as i64 + } + + fn read_le_i32(&mut self) -> i32 { + self.read_le_int_n(4) as i32 + } + + fn read_le_i16(&mut self) -> i16 { + self.read_le_int_n(2) as i16 + } + + fn read_le_f64(&mut self) -> f64 { + unsafe { + cast::transmute::(self.read_le_u64()) + } + } + + fn read_le_f32(&mut self) -> f32 { + unsafe { + cast::transmute::(self.read_le_u32()) + } + } + + fn read_u8(&mut self) -> u8 { + match self.read_byte() { + Some(b) => b as u8, + None => 0 + } + } + + fn read_i8(&mut self) -> i8 { + match self.read_byte() { + Some(b) => b as i8, + None => 0 + } + } + +} + +impl WriterByteConversions for T { + fn write_int(&mut self, n: int) { + int::to_str_bytes(n, 10u, |bytes| self.write(bytes)) + } + + fn write_uint(&mut self, n: uint) { + uint::to_str_bytes(n, 10u, |bytes| self.write(bytes)) + } + + fn write_le_uint(&mut self, n: uint) { + u64_to_le_bytes(n as u64, uint::bytes, |v| self.write(v)) + } + + fn write_le_int(&mut self, n: int) { + u64_to_le_bytes(n as u64, int::bytes, |v| self.write(v)) + } + + fn write_be_uint(&mut self, n: uint) { + u64_to_be_bytes(n as u64, uint::bytes, |v| self.write(v)) + } + + fn write_be_int(&mut self, n: int) { + u64_to_be_bytes(n as u64, int::bytes, |v| self.write(v)) + } + + fn write_be_u64_(&mut self, n: u64) { + u64_to_be_bytes(n, 8u, |v| self.write(v)) + } + + fn write_be_u32(&mut self, n: u32) { + u64_to_be_bytes(n as u64, 4u, |v| self.write(v)) + } + + fn write_be_u16(&mut self, n: u16) { + u64_to_be_bytes(n as u64, 2u, |v| self.write(v)) + } + + fn write_be_i64(&mut self, n: i64) { + u64_to_be_bytes(n as u64, 8u, |v| self.write(v)) + } + + fn write_be_i32(&mut self, n: i32) { + u64_to_be_bytes(n as u64, 4u, |v| self.write(v)) + } + + fn write_be_i16(&mut self, n: i16) { + u64_to_be_bytes(n as u64, 2u, |v| self.write(v)) + } + + fn write_be_f64(&mut self, f: f64) { + unsafe { + self.write_be_u64_(cast::transmute(f)) + } + } + + fn write_be_f32(&mut self, f: f32) { + unsafe { + self.write_be_u32(cast::transmute(f)) + } + } + + fn write_le_u64_(&mut self, n: u64) { + u64_to_le_bytes(n, 8u, |v| self.write(v)) + } + + fn write_le_u32(&mut self, n: u32) { + u64_to_le_bytes(n as u64, 4u, |v| self.write(v)) + } + + fn write_le_u16(&mut self, n: u16) { + u64_to_le_bytes(n as u64, 2u, |v| self.write(v)) + } + + fn write_le_i64(&mut self, n: i64) { + u64_to_le_bytes(n as u64, 8u, |v| self.write(v)) + } + + fn write_le_i32(&mut self, n: i32) { + u64_to_le_bytes(n as u64, 4u, |v| self.write(v)) + } + + fn write_le_i16(&mut self, n: i16) { + u64_to_le_bytes(n as u64, 2u, |v| self.write(v)) + } + + fn write_le_f64(&mut self, f: f64) { + unsafe { + self.write_le_u64_(cast::transmute(f)) + } + } + + fn write_le_f32(&mut self, f: f32) { + unsafe { + self.write_le_u32(cast::transmute(f)) + } + } + + fn write_u8(&mut self, n: u8) { + self.write([n]) + } + + fn write_i8(&mut self, n: i8) { + self.write([n as u8]) + } +} + +fn extend_sign(val: u64, nbytes: uint) -> i64 { + let shift = (8 - nbytes) * 8; + (val << shift) as i64 >> shift +} + #[cfg(test)] mod test { - use super::*; + use super::{ReaderUtil, ReaderByteConversions, WriterByteConversions}; + use u64; + use i32; use option::{Some, None}; use cell::Cell; - use rt::io::mem::MemReader; - use rt::io::mock::*; + use rt::io::mem::{MemReader, MemWriter}; + use rt::io::mock::MockReader; use rt::io::{read_error, placeholder_error}; #[test] @@ -572,4 +826,78 @@ mod test { assert!(buf == ~[10, 11]); } + // XXX: Some problem with resolve here + /*#[test] + fn test_read_write_le() { + let uints = [0, 1, 2, 42, 10_123, 100_123_456, u64::max_value]; + + let mut writer = MemWriter::new(); + for uints.each |i| { + writer.write_le_u64(*i); + } + + let mut reader = MemReader::new(writer.inner()); + for uints.each |i| { + assert!(reader.read_le_u64() == *i); + } + } + + #[test] + fn test_read_write_be() { + let uints = [0, 1, 2, 42, 10_123, 100_123_456, u64::max_value]; + + let mut writer = MemWriter::new(); + for uints.each |i| { + writer.write_be_u64(*i); + } + + let mut reader = MemReader::new(writer.inner()); + for uints.each |i| { + assert!(reader.read_be_u64() == *i); + } + } + + #[test] + fn test_read_be_int_n() { + let ints = [i32::min_value, -123456, -42, -5, 0, 1, i32::max_value]; + + let mut writer = MemWriter::new(); + for ints.each |i| { + writer.write_be_i32(*i); + } + + let mut reader = MemReader::new(writer.inner()); + for ints.each |i| { + // this tests that the sign extension is working + // (comparing the values as i32 would not test this) + assert!(reader.read_be_int_n(4) == *i as i64); + } + } + + #[test] + fn test_read_f32() { + //big-endian floating-point 8.1250 + let buf = ~[0x41, 0x02, 0x00, 0x00]; + + let mut writer = MemWriter::new(); + writer.write(buf); + + let mut reader = MemReader::new(writer.inner()); + let f = reader.read_be_f32(); + assert!(f == 8.1250); + } + + #[test] + fn test_read_write_f32() { + let f:f32 = 8.1250; + + let mut writer = MemWriter::new(); + writer.write_be_f32(f); + writer.write_le_f32(f); + + let mut reader = MemReader::new(writer.inner()); + assert!(reader.read_be_f32() == 8.1250); + assert!(reader.read_le_f32() == 8.1250); + }*/ + } From d951da82768685cacba7444dae10ede2a71efedb Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Tue, 14 May 2013 21:18:47 -0700 Subject: [PATCH 49/55] core::rt: Fix TCP test on mac --- src/libcore/rt/io/mod.rs | 3 ++- src/libcore/rt/io/net/tcp.rs | 3 ++- src/libcore/rt/uv/mod.rs | 4 +++- src/libcore/rt/uv/uvll.rs | 1 + 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/libcore/rt/io/mod.rs b/src/libcore/rt/io/mod.rs index 14784fa95672e..802e069a738f0 100644 --- a/src/libcore/rt/io/mod.rs +++ b/src/libcore/rt/io/mod.rs @@ -348,7 +348,8 @@ pub enum IoErrorKind { ConnectionFailed, Closed, ConnectionRefused, - ConnectionReset + ConnectionReset, + BrokenPipe } // XXX: Can't put doc comments on macros diff --git a/src/libcore/rt/io/net/tcp.rs b/src/libcore/rt/io/net/tcp.rs index 04f4edd7e39f5..eec3614d12978 100644 --- a/src/libcore/rt/io/net/tcp.rs +++ b/src/libcore/rt/io/net/tcp.rs @@ -233,7 +233,8 @@ mod test { loop { let mut stop = false; do io_error::cond.trap(|e| { - assert!(e.kind == ConnectionReset); + // NB: ECONNRESET on linux, EPIPE on mac + assert!(e.kind == ConnectionReset || e.kind == BrokenPipe); stop = true; }).in { stream.write(buf); diff --git a/src/libcore/rt/uv/mod.rs b/src/libcore/rt/uv/mod.rs index ee3c5ceffd236..93cafb835884a 100644 --- a/src/libcore/rt/uv/mod.rs +++ b/src/libcore/rt/uv/mod.rs @@ -272,7 +272,9 @@ pub fn uv_error_to_io_error(uverr: UvError) -> IoError { EACCES => PermissionDenied, ECONNREFUSED => ConnectionRefused, ECONNRESET => ConnectionReset, - _ => { + EPIPE => BrokenPipe, + e => { + rtdebug!("e %u", e as uint); // XXX: Need to map remaining uv error types OtherIoError } diff --git a/src/libcore/rt/uv/uvll.rs b/src/libcore/rt/uv/uvll.rs index 94e6b82ab8fa9..02659ab1eb910 100644 --- a/src/libcore/rt/uv/uvll.rs +++ b/src/libcore/rt/uv/uvll.rs @@ -40,6 +40,7 @@ pub static EADDRINFO: c_int = 2; pub static EACCES: c_int = 3; pub static ECONNREFUSED: c_int = 12; pub static ECONNRESET: c_int = 13; +pub static EPIPE: c_int = 36; pub struct uv_err_t { code: c_int, From 018dfaf9a6a25f5dba0ac642ff6c426c549bc4d7 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Wed, 15 May 2013 13:57:08 -0700 Subject: [PATCH 50/55] core::rt: Unignore a fixed TCP test --- src/libcore/rt/io/net/tcp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/rt/io/net/tcp.rs b/src/libcore/rt/io/net/tcp.rs index eec3614d12978..a833e92fc1088 100644 --- a/src/libcore/rt/io/net/tcp.rs +++ b/src/libcore/rt/io/net/tcp.rs @@ -314,7 +314,7 @@ mod test { } } - #[test] #[ignore(reason = "hangs on mac")] + #[test] fn multiple_connect_interleaved_lazy_schedule() { do run_in_newsched_task { let addr = next_test_ip4(); From f5987b03b8d65a2b885519b7b9a0ea33cda33bc5 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Wed, 15 May 2013 17:20:48 -0700 Subject: [PATCH 51/55] core::rt: implement `oneshot` and `stream`. --- src/libcore/rt/comm.rs | 599 ++++++++++++++++++++++++++++++++++++++++ src/libcore/rt/mod.rs | 3 + src/libcore/rt/sched.rs | 11 + src/libcore/rt/test.rs | 33 ++- 4 files changed, 645 insertions(+), 1 deletion(-) create mode 100644 src/libcore/rt/comm.rs diff --git a/src/libcore/rt/comm.rs b/src/libcore/rt/comm.rs new file mode 100644 index 0000000000000..9fcb70cfc7d66 --- /dev/null +++ b/src/libcore/rt/comm.rs @@ -0,0 +1,599 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use option::*; +use cast; +use util; +use ops::Drop; +use kinds::Owned; +use rt::sched::Coroutine; +use rt::local_sched; +#[cfg(stage0)] +use unstable::intrinsics::{atomic_xchg}; +#[cfg(not(stage0))] +use unstable::intrinsics::{atomic_xchg, atomic_load}; +use util::Void; +use comm::{GenericChan, GenericSmartChan, GenericPort, Peekable}; +use cell::Cell; + +/// A combined refcount / ~Task pointer. +/// +/// Can be equal to the following values: +/// +/// * 2 - both endpoints are alive +/// * 1 - either the sender or the receiver is dead, determined by context +/// * - A pointer to a Task that can be transmuted to ~Task +type State = int; + +static STATE_BOTH: State = 2; +static STATE_ONE: State = 1; + +struct Packet { + state: State, + payload: Option, +} + +pub struct PortOne { + // XXX: Hack extra allocation to make by-val self work + inner: ~PortOneHack +} + +pub struct ChanOne { + // XXX: Hack extra allocation to make by-val self work + inner: ~ChanOneHack +} + +pub struct PortOneHack { + void_packet: *mut Void, + suppress_finalize: bool +} + +pub struct ChanOneHack { + void_packet: *mut Void, + suppress_finalize: bool +} + +pub fn oneshot() -> (PortOne, ChanOne) { + let packet: ~Packet = ~Packet { + state: STATE_BOTH, + payload: None + }; + + unsafe { + let packet: *mut Void = cast::transmute(packet); + let port = PortOne { + inner: ~PortOneHack { + void_packet: packet, + suppress_finalize: false + } + }; + let chan = ChanOne { + inner: ~ChanOneHack { + void_packet: packet, + suppress_finalize: false + } + }; + return (port, chan); + } +} + +impl PortOne { + pub fn recv(self) -> T { + match self.try_recv() { + Some(val) => val, + None => { + fail!("receiving on closed channel"); + } + } + } + + pub fn try_recv(self) -> Option { + let mut this = self; + + { + let self_ptr: *mut PortOne = &mut this; + + // XXX: Optimize this to not require the two context switches when data is available + + // Switch to the scheduler + let sched = local_sched::take(); + do sched.deschedule_running_task_and_then |task| { + unsafe { + let task_as_state: State = cast::transmute(task); + let oldstate = atomic_xchg(&mut (*(*self_ptr).inner.packet()).state, task_as_state); + match oldstate { + STATE_BOTH => { + // Data has not been sent. Now we're blocked. + } + STATE_ONE => { + // Channel is closed. Switch back and check the data. + let task: ~Coroutine = cast::transmute(task_as_state); + let sched = local_sched::take(); + sched.resume_task_immediately(task); + } + _ => util::unreachable() + } + } + } + } + + // Task resumes. + + // No further memory barrier is needed here to access the + // payload. Some scenarios: + // + // 1) We encountered STATE_ONE above - the atomic_xchg was the acq barrier. We're fine. + // 2) We encountered STATE_BOTH above and blocked. The sending task work-stole us + // and ran on its thread. The work stealing had a memory barrier. + // 3) We encountered STATE_BOTH above and blocked, but the receiving task (this task) + // is pinned to some other scheduler, so the sending task had to give us to + // a different scheduler for resuming. That send synchronized memory. + + unsafe { + let payload = util::replace(&mut (*this.inner.packet()).payload, None); + + // The sender has closed up shop. Drop the packet. + let _packet: ~Packet = cast::transmute(this.inner.void_packet); + // Supress the finalizer. We're done here. + this.inner.suppress_finalize = true; + + return payload; + } + } +} + +impl Peekable for PortOne { + #[cfg(stage0)] + fn peek(&self) -> bool { fail!() } + + #[cfg(not(stage0))] + fn peek(&self) -> bool { + unsafe { + let packet: *mut Packet = self.inner.packet(); + let oldstate = atomic_load(&mut (*packet).state); + match oldstate { + STATE_BOTH => false, + STATE_ONE => (*packet).payload.is_some(), + _ => util::unreachable() + } + } + } +} + +impl ChanOne { + + pub fn send(self, val: T) { + self.try_send(val); + } + + pub fn try_send(self, val: T) -> bool { + let mut this = self; + let mut recvr_active = true; + + unsafe { + assert!((*this.inner.packet()).payload.is_none()); + (*this.inner.packet()).payload = Some(val); + + let oldstate = atomic_xchg(&mut (*this.inner.packet()).state, STATE_ONE); + match oldstate { + STATE_BOTH => { + // Port is not recving yet. Nothing to do + } + STATE_ONE => { + // Port has closed. Need to clean up. + let _packet: ~Packet = cast::transmute(this.inner.void_packet); + recvr_active = false; + } + _ => { + // Port is blocked. Wake it up. + let recvr: ~Coroutine = cast::transmute(oldstate); + let sched = local_sched::take(); + sched.schedule_task(recvr); + } + } + } + + // Suppress the finalizer. We're done here. + this.inner.suppress_finalize = true; + return recvr_active; + } +} + +#[unsafe_destructor] +impl Drop for PortOneHack { + fn finalize(&self) { + if self.suppress_finalize { return } + + unsafe { + let this = cast::transmute_mut(self); + let oldstate = atomic_xchg(&mut (*this.packet()).state, STATE_ONE); + match oldstate { + STATE_BOTH => { + /* cleanup is the chan's responsibility */ + }, + STATE_ONE => { + let _packet: ~Packet = cast::transmute(this.void_packet); + } + _ => { + util::unreachable() + } + } + } + } +} + +#[unsafe_destructor] +impl Drop for ChanOneHack { + fn finalize(&self) { + if self.suppress_finalize { return } + + unsafe { + let this = cast::transmute_mut(self); + let oldstate = atomic_xchg(&mut (*this.packet()).state, STATE_ONE); + match oldstate { + STATE_BOTH => { + /* cleanup is the port's responsibility */ + }, + STATE_ONE => { + let _packet: ~Packet = cast::transmute(this.void_packet); + }, + _ => { + // The port is blocked recving for a message we will never send. Wake it. + assert!((*this.packet()).payload.is_none()); + let recvr: ~Coroutine = cast::transmute(oldstate); + let sched = local_sched::take(); + sched.schedule_task(recvr); + } + } + } + } +} + +impl PortOneHack { + fn packet(&self) -> *mut Packet { + unsafe { + let p: *mut ~Packet = cast::transmute(&self.void_packet); + let p: *mut Packet = &mut **p; + return p; + } + } +} + +impl ChanOneHack { + fn packet(&self) -> *mut Packet { + unsafe { + let p: *mut ~Packet = cast::transmute(&self.void_packet); + let p: *mut Packet = &mut **p; + return p; + } + } +} + +struct StreamPayload(T, PortOne>); + +pub struct Port { + // FIXME #5372. Using Cell because we don't take &mut self + next: Cell>> +} + +pub struct Chan { + // FIXME #5372. Using Cell because we don't take &mut self + next: Cell>> +} + +pub fn stream() -> (Port, Chan) { + let (pone, cone) = oneshot(); + let port = Port { next: Cell(pone) }; + let chan = Chan { next: Cell(cone) }; + return (port, chan); +} + +impl GenericPort for Port { + fn recv(&self) -> T { + match self.try_recv() { + Some(val) => val, + None => { + fail!("receiving on closed channel"); + } + } + } + + fn try_recv(&self) -> Option { + let pone = self.next.take(); + match pone.try_recv() { + Some(StreamPayload(val, next)) => { + self.next.put_back(next); + Some(val) + } + None => None + } + } +} + +impl Peekable for Port { + fn peek(&self) -> bool { + self.next.with_mut_ref(|p| p.peek()) + } +} + +impl GenericChan for Chan { + fn send(&self, val: T) { + self.try_send(val); + } +} + +impl GenericSmartChan for Chan { + fn try_send(&self, val: T) -> bool { + let (next_pone, next_cone) = oneshot(); + let cone = self.next.take(); + self.next.put_back(next_cone); + cone.try_send(StreamPayload(val, next_pone)) + } +} + +#[cfg(test)] +mod test { + use super::*; + use option::*; + use rt::test::*; + use cell::Cell; + use iter::Times; + + #[test] + fn oneshot_single_thread_close_port_first() { + // Simple test of closing without sending + do run_in_newsched_task { + let (port, _chan) = oneshot::(); + { let _p = port; } + } + } + + #[test] + fn oneshot_single_thread_close_chan_first() { + // Simple test of closing without sending + do run_in_newsched_task { + let (_port, chan) = oneshot::(); + { let _c = chan; } + } + } + + #[test] + fn oneshot_single_thread_send_port_close() { + // Testing that the sender cleans up the payload if receiver is closed + do run_in_newsched_task { + let (port, chan) = oneshot::<~int>(); + { let _p = port; } + chan.send(~0); + } + } + + #[test] + fn oneshot_single_thread_recv_chan_close() { + // Receiving on a closed chan will fail + do run_in_newsched_task { + let res = do spawntask_try { + let (port, chan) = oneshot::<~int>(); + { let _c = chan; } + port.recv(); + }; + assert!(res.is_err()); + } + } + + #[test] + fn oneshot_single_thread_send_then_recv() { + do run_in_newsched_task { + let (port, chan) = oneshot::<~int>(); + chan.send(~10); + assert!(port.recv() == ~10); + } + } + + #[test] + fn oneshot_single_thread_try_send_open() { + do run_in_newsched_task { + let (port, chan) = oneshot::(); + assert!(chan.try_send(10)); + assert!(port.recv() == 10); + } + } + + #[test] + fn oneshot_single_thread_try_send_closed() { + do run_in_newsched_task { + let (port, chan) = oneshot::(); + { let _p = port; } + assert!(!chan.try_send(10)); + } + } + + #[test] + fn oneshot_single_thread_try_recv_open() { + do run_in_newsched_task { + let (port, chan) = oneshot::(); + chan.send(10); + assert!(port.try_recv() == Some(10)); + } + } + + #[test] + fn oneshot_single_thread_try_recv_closed() { + do run_in_newsched_task { + let (port, chan) = oneshot::(); + { let _c = chan; } + assert!(port.try_recv() == None); + } + } + + #[test] + fn oneshot_single_thread_peek_data() { + do run_in_newsched_task { + let (port, chan) = oneshot::(); + assert!(!port.peek()); + chan.send(10); + assert!(port.peek()); + } + } + + #[test] + fn oneshot_single_thread_peek_close() { + do run_in_newsched_task { + let (port, chan) = oneshot::(); + { let _c = chan; } + assert!(!port.peek()); + assert!(!port.peek()); + } + } + + #[test] + fn oneshot_single_thread_peek_open() { + do run_in_newsched_task { + let (port, chan) = oneshot::(); + assert!(!port.peek()); + } + } + + #[test] + fn oneshot_multi_task_recv_then_send() { + do run_in_newsched_task { + let (port, chan) = oneshot::<~int>(); + let port_cell = Cell(port); + do spawntask_immediately { + assert!(port_cell.take().recv() == ~10); + } + + chan.send(~10); + } + } + + #[test] + fn oneshot_multi_task_recv_then_close() { + do run_in_newsched_task { + let (port, chan) = oneshot::<~int>(); + let port_cell = Cell(port); + let chan_cell = Cell(chan); + do spawntask_later { + let _cell = chan_cell.take(); + } + let res = do spawntask_try { + assert!(port_cell.take().recv() == ~10); + }; + assert!(res.is_err()); + } + } + + #[test] + fn oneshot_multi_thread_close_stress() { + for stress_factor().times { + do run_in_newsched_task { + let (port, chan) = oneshot::(); + let port_cell = Cell(port); + let _thread = do spawntask_thread { + let _p = port_cell.take(); + }; + let _chan = chan; + } + } + } + + #[test] + fn oneshot_multi_thread_send_close_stress() { + for stress_factor().times { + do run_in_newsched_task { + let (port, chan) = oneshot::(); + let chan_cell = Cell(chan); + let port_cell = Cell(port); + let _thread1 = do spawntask_thread { + let _p = port_cell.take(); + }; + let _thread2 = do spawntask_thread { + let c = chan_cell.take(); + c.send(1); + }; + } + } + } + + #[test] + fn oneshot_multi_thread_recv_close_stress() { + for stress_factor().times { + do run_in_newsched_task { + let (port, chan) = oneshot::(); + let chan_cell = Cell(chan); + let port_cell = Cell(port); + let _thread1 = do spawntask_thread { + let port_cell = Cell(port_cell.take()); + let res = do spawntask_try { + port_cell.take().recv(); + }; + assert!(res.is_err()); + }; + let _thread2 = do spawntask_thread { + let chan_cell = Cell(chan_cell.take()); + do spawntask { + chan_cell.take(); + } + }; + } + } + } + + #[test] + fn oneshot_multi_thread_send_recv_stress() { + for stress_factor().times { + do run_in_newsched_task { + let (port, chan) = oneshot::<~int>(); + let chan_cell = Cell(chan); + let port_cell = Cell(port); + let _thread1 = do spawntask_thread { + chan_cell.take().send(~10); + }; + let _thread2 = do spawntask_thread { + assert!(port_cell.take().recv() == ~10); + }; + } + } + } + + #[test] + fn stream_send_recv() { + for stress_factor().times { + do run_in_newsched_task { + let (port, chan) = stream::<~int>(); + + send(chan, 0); + recv(port, 0); + + fn send(chan: Chan<~int>, i: int) { + if i == 10 { return } + + let chan_cell = Cell(chan); + let _thread = do spawntask_thread { + let chan = chan_cell.take(); + chan.send(~i); + send(chan, i + 1); + }; + } + + fn recv(port: Port<~int>, i: int) { + if i == 10 { return } + + let port_cell = Cell(port); + let _thread = do spawntask_thread { + let port = port_cell.take(); + assert!(port.recv() == ~i); + recv(port, i + 1); + }; + } + } + } + } +} + diff --git a/src/libcore/rt/mod.rs b/src/libcore/rt/mod.rs index 7a772ff0f3b96..dab627188d0f5 100644 --- a/src/libcore/rt/mod.rs +++ b/src/libcore/rt/mod.rs @@ -122,6 +122,9 @@ pub mod rc; /// scheduler and task context pub mod tube; +/// Simple reimplementation of core::comm +pub mod comm; + /// Set up a default runtime configuration, given compiler-supplied arguments. /// /// This is invoked by the `start` _language item_ (unstable::lang) to diff --git a/src/libcore/rt/sched.rs b/src/libcore/rt/sched.rs index 5c1a3410087c4..c66f20e01b2d7 100644 --- a/src/libcore/rt/sched.rs +++ b/src/libcore/rt/sched.rs @@ -171,6 +171,17 @@ pub impl Scheduler { } } + fn schedule_task(~self, task: ~Coroutine) { + assert!(self.in_task_context()); + + do self.switch_running_tasks_and_then(task) |last_task| { + let last_task = Cell(last_task); + do local_sched::borrow |sched| { + sched.enqueue_task(last_task.take()); + } + } + } + // Core scheduling ops fn resume_task_immediately(~self, task: ~Coroutine) { diff --git a/src/libcore/rt/test.rs b/src/libcore/rt/test.rs index 1294b9bcf4765..d739d0110ba35 100644 --- a/src/libcore/rt/test.rs +++ b/src/libcore/rt/test.rs @@ -8,17 +8,20 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use uint; +use option::*; use cell::Cell; use result::{Result, Ok, Err}; use super::io::net::ip::{IpAddr, Ipv4}; use rt::local_services::LocalServices; +use rt::thread::Thread; /// Creates a new scheduler in a new thread and runs a task in it, /// then waits for the scheduler to exit. Failure of the task /// will abort the process. pub fn run_in_newsched_task(f: ~fn()) { + use super::sched::*; use unstable::run_in_bare_thread; - use super::sched::Coroutine; use rt::uv::uvio::UvEventLoop; let f = Cell(f); @@ -144,6 +147,23 @@ pub fn spawntask_try(f: ~fn()) -> Result<(), ()> { if !failed { Ok(()) } else { Err(()) } } +// Spawn a new task in a new scheduler and return a thread handle. +pub fn spawntask_thread(f: ~fn()) -> Thread { + use rt::sched::*; + use rt::uv::uvio::UvEventLoop; + + let f = Cell(f); + let thread = do Thread::start { + let mut sched = ~UvEventLoop::new_scheduler(); + let task = ~Coroutine::with_local(&mut sched.stack_pool, + LocalServices::without_unwinding(), + f.take()); + sched.enqueue_task(task); + sched.run(); + }; + return thread; +} + /// Get a port number, starting at 9600, for use in tests pub fn next_test_port() -> u16 { unsafe { @@ -158,3 +178,14 @@ pub fn next_test_port() -> u16 { pub fn next_test_ip4() -> IpAddr { Ipv4(127, 0, 0, 1, next_test_port()) } + +/// Get a constant that represents the number of times to repeat stress tests. Default 1. +pub fn stress_factor() -> uint { + use os::getenv; + + match getenv("RUST_RT_STRESS") { + Some(val) => uint::from_str(val).get(), + None => 1 + } +} + From 26becc308e4b9a0f5be1c7c2895c7761b778e01f Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 16 May 2013 23:12:22 -0700 Subject: [PATCH 52/55] core: Wire up oneshot pipes to newsched --- src/libcore/comm.rs | 350 ++++++++++++++++++++----------- src/libcore/rt/local_services.rs | 22 ++ src/libstd/future.rs | 10 +- src/libstd/workcache.rs | 7 +- 4 files changed, 253 insertions(+), 136 deletions(-) diff --git a/src/libcore/comm.rs b/src/libcore/comm.rs index 34c60202b3f12..da3ae0e6c5dfe 100644 --- a/src/libcore/comm.rs +++ b/src/libcore/comm.rs @@ -22,6 +22,8 @@ use vec; use vec::OwnedVector; use util::replace; use unstable::sync::{Exclusive, exclusive}; +use rtcomm = rt::comm; +use rt; use pipes::{recv, try_recv, wait_many, peek, PacketHeader}; @@ -335,180 +337,280 @@ impl ::clone::Clone for SharedChan { } } -/*proto! oneshot ( - Oneshot:send { - send(T) -> ! +pub struct PortOne { + inner: Either, rtcomm::PortOne> +} + +pub struct ChanOne { + inner: Either, rtcomm::ChanOne> +} + +pub fn oneshot() -> (PortOne, ChanOne) { + let (port, chan) = match rt::context() { + rt::OldTaskContext => match pipesy::oneshot() { + (p, c) => (Left(p), Left(c)), + }, + _ => match rtcomm::oneshot() { + (p, c) => (Right(p), Right(c)) + } + }; + let port = PortOne { + inner: port + }; + let chan = ChanOne { + inner: chan + }; + return (port, chan); +} + +impl PortOne { + pub fn recv(self) -> T { + let PortOne { inner } = self; + match inner { + Left(p) => p.recv(), + Right(p) => p.recv() + } } -)*/ -#[allow(non_camel_case_types)] -pub mod oneshot { - priv use core::kinds::Owned; - use ptr::to_mut_unsafe_ptr; + pub fn try_recv(self) -> Option { + let PortOne { inner } = self; + match inner { + Left(p) => p.try_recv(), + Right(p) => p.try_recv() + } + } +} - pub fn init() -> (client::Oneshot, server::Oneshot) { - pub use core::pipes::HasBuffer; +impl ChanOne { + pub fn send(self, data: T) { + let ChanOne { inner } = self; + match inner { + Left(p) => p.send(data), + Right(p) => p.send(data) + } + } - let buffer = ~::core::pipes::Buffer { - header: ::core::pipes::BufferHeader(), - data: __Buffer { - Oneshot: ::core::pipes::mk_packet::>() - }, - }; - do ::core::pipes::entangle_buffer(buffer) |buffer, data| { - data.Oneshot.set_buffer(buffer); - to_mut_unsafe_ptr(&mut data.Oneshot) + pub fn try_send(self, data: T) -> bool { + let ChanOne { inner } = self; + match inner { + Left(p) => p.try_send(data), + Right(p) => p.try_send(data) } } - #[allow(non_camel_case_types)] - pub enum Oneshot { pub send(T), } - #[allow(non_camel_case_types)] - pub struct __Buffer { - Oneshot: ::core::pipes::Packet>, +} + +pub fn recv_one(port: PortOne) -> T { + let PortOne { inner } = port; + match inner { + Left(p) => pipesy::recv_one(p), + Right(p) => p.recv() } +} - #[allow(non_camel_case_types)] - pub mod client { +pub fn try_recv_one(port: PortOne) -> Option { + let PortOne { inner } = port; + match inner { + Left(p) => pipesy::try_recv_one(p), + Right(p) => p.try_recv() + } +} + +pub fn send_one(chan: ChanOne, data: T) { + let ChanOne { inner } = chan; + match inner { + Left(c) => pipesy::send_one(c, data), + Right(c) => c.send(data) + } +} + +pub fn try_send_one(chan: ChanOne, data: T) -> bool { + let ChanOne { inner } = chan; + match inner { + Left(c) => pipesy::try_send_one(c, data), + Right(c) => c.try_send(data) + } +} +mod pipesy { + + use kinds::Owned; + use option::{Option, Some, None}; + use pipes::{recv, try_recv}; + + /*proto! oneshot ( + Oneshot:send { + send(T) -> ! + } + )*/ + + #[allow(non_camel_case_types)] + pub mod oneshot { priv use core::kinds::Owned; + use ptr::to_mut_unsafe_ptr; - #[allow(non_camel_case_types)] - pub fn try_send(pipe: Oneshot, x_0: T) -> - ::core::option::Option<()> { - { - use super::send; - let message = send(x_0); - if ::core::pipes::send(pipe, message) { - ::core::pipes::rt::make_some(()) - } else { ::core::pipes::rt::make_none() } + pub fn init() -> (client::Oneshot, server::Oneshot) { + pub use core::pipes::HasBuffer; + + let buffer = ~::core::pipes::Buffer { + header: ::core::pipes::BufferHeader(), + data: __Buffer { + Oneshot: ::core::pipes::mk_packet::>() + }, + }; + do ::core::pipes::entangle_buffer(buffer) |buffer, data| { + data.Oneshot.set_buffer(buffer); + to_mut_unsafe_ptr(&mut data.Oneshot) } } + #[allow(non_camel_case_types)] + pub enum Oneshot { pub send(T), } + #[allow(non_camel_case_types)] + pub struct __Buffer { + Oneshot: ::core::pipes::Packet>, + } #[allow(non_camel_case_types)] - pub fn send(pipe: Oneshot, x_0: T) { - { - use super::send; - let message = send(x_0); - ::core::pipes::send(pipe, message); + pub mod client { + + priv use core::kinds::Owned; + + #[allow(non_camel_case_types)] + pub fn try_send(pipe: Oneshot, x_0: T) -> + ::core::option::Option<()> { + { + use super::send; + let message = send(x_0); + if ::core::pipes::send(pipe, message) { + ::core::pipes::rt::make_some(()) + } else { ::core::pipes::rt::make_none() } + } } + + #[allow(non_camel_case_types)] + pub fn send(pipe: Oneshot, x_0: T) { + { + use super::send; + let message = send(x_0); + ::core::pipes::send(pipe, message); + } + } + + #[allow(non_camel_case_types)] + pub type Oneshot = + ::core::pipes::SendPacketBuffered, + super::__Buffer>; } #[allow(non_camel_case_types)] - pub type Oneshot = - ::core::pipes::SendPacketBuffered, - super::__Buffer>; + pub mod server { + #[allow(non_camel_case_types)] + pub type Oneshot = + ::core::pipes::RecvPacketBuffered, + super::__Buffer>; + } } - #[allow(non_camel_case_types)] - pub mod server { - #[allow(non_camel_case_types)] - pub type Oneshot = - ::core::pipes::RecvPacketBuffered, - super::__Buffer>; + /// The send end of a oneshot pipe. + pub struct ChanOne { + contents: oneshot::client::Oneshot } -} - -/// The send end of a oneshot pipe. -pub struct ChanOne { - contents: oneshot::client::Oneshot -} -impl ChanOne { - pub fn new(contents: oneshot::client::Oneshot) -> ChanOne { - ChanOne { - contents: contents + impl ChanOne { + pub fn new(contents: oneshot::client::Oneshot) -> ChanOne { + ChanOne { + contents: contents + } } } -} -/// The receive end of a oneshot pipe. -pub struct PortOne { - contents: oneshot::server::Oneshot -} + /// The receive end of a oneshot pipe. + pub struct PortOne { + contents: oneshot::server::Oneshot + } -impl PortOne { - pub fn new(contents: oneshot::server::Oneshot) -> PortOne { - PortOne { - contents: contents + impl PortOne { + pub fn new(contents: oneshot::server::Oneshot) -> PortOne { + PortOne { + contents: contents + } } } -} -/// Initialiase a (send-endpoint, recv-endpoint) oneshot pipe pair. -pub fn oneshot() -> (PortOne, ChanOne) { - let (chan, port) = oneshot::init(); - (PortOne::new(port), ChanOne::new(chan)) -} + /// Initialiase a (send-endpoint, recv-endpoint) oneshot pipe pair. + pub fn oneshot() -> (PortOne, ChanOne) { + let (chan, port) = oneshot::init(); + (PortOne::new(port), ChanOne::new(chan)) + } -pub impl PortOne { - fn recv(self) -> T { recv_one(self) } - fn try_recv(self) -> Option { try_recv_one(self) } - fn unwrap(self) -> oneshot::server::Oneshot { - match self { - PortOne { contents: s } => s + pub impl PortOne { + fn recv(self) -> T { recv_one(self) } + fn try_recv(self) -> Option { try_recv_one(self) } + fn unwrap(self) -> oneshot::server::Oneshot { + match self { + PortOne { contents: s } => s + } } } -} -pub impl ChanOne { - fn send(self, data: T) { send_one(self, data) } - fn try_send(self, data: T) -> bool { try_send_one(self, data) } - fn unwrap(self) -> oneshot::client::Oneshot { - match self { - ChanOne { contents: s } => s + pub impl ChanOne { + fn send(self, data: T) { send_one(self, data) } + fn try_send(self, data: T) -> bool { try_send_one(self, data) } + fn unwrap(self) -> oneshot::client::Oneshot { + match self { + ChanOne { contents: s } => s + } } } -} -/** - * Receive a message from a oneshot pipe, failing if the connection was - * closed. - */ -pub fn recv_one(port: PortOne) -> T { - match port { - PortOne { contents: port } => { - let oneshot::send(message) = recv(port); - message + /** + * Receive a message from a oneshot pipe, failing if the connection was + * closed. + */ + pub fn recv_one(port: PortOne) -> T { + match port { + PortOne { contents: port } => { + let oneshot::send(message) = recv(port); + message + } } } -} -/// Receive a message from a oneshot pipe unless the connection was closed. -pub fn try_recv_one (port: PortOne) -> Option { - match port { - PortOne { contents: port } => { - let message = try_recv(port); + /// Receive a message from a oneshot pipe unless the connection was closed. + pub fn try_recv_one (port: PortOne) -> Option { + match port { + PortOne { contents: port } => { + let message = try_recv(port); - if message.is_none() { - None - } else { - let oneshot::send(message) = message.unwrap(); - Some(message) + if message.is_none() { + None + } else { + let oneshot::send(message) = message.unwrap(); + Some(message) + } } } } -} -/// Send a message on a oneshot pipe, failing if the connection was closed. -pub fn send_one(chan: ChanOne, data: T) { - match chan { - ChanOne { contents: chan } => oneshot::client::send(chan, data), + /// Send a message on a oneshot pipe, failing if the connection was closed. + pub fn send_one(chan: ChanOne, data: T) { + match chan { + ChanOne { contents: chan } => oneshot::client::send(chan, data), + } } -} -/** - * Send a message on a oneshot pipe, or return false if the connection was - * closed. - */ -pub fn try_send_one(chan: ChanOne, data: T) -> bool { - match chan { - ChanOne { contents: chan } => { - oneshot::client::try_send(chan, data).is_some() + /** + * Send a message on a oneshot pipe, or return false if the connection was + * closed. + */ + pub fn try_send_one(chan: ChanOne, data: T) -> bool { + match chan { + ChanOne { contents: chan } => { + oneshot::client::try_send(chan, data).is_some() + } } } -} - +} /// Returns the index of an endpoint that is ready to receive. pub fn selecti(endpoints: &mut [T]) -> uint { diff --git a/src/libcore/rt/local_services.rs b/src/libcore/rt/local_services.rs index 98bfc2fa1686f..35c703bb35074 100644 --- a/src/libcore/rt/local_services.rs +++ b/src/libcore/rt/local_services.rs @@ -244,5 +244,27 @@ mod test { info!("here i am. logging in a newsched task"); } } + + #[test] + fn comm_oneshot() { + use comm::*; + + do run_in_newsched_task { + let (port, chan) = oneshot(); + send_one(chan, 10); + assert!(recv_one(port) == 10); + } + } + + #[test] + fn comm_stream() { + use comm::*; + + do run_in_newsched_task() { + let (port, chan) = oneshot(); + chan.send(10); + assert!(port.recv() == 10); + } + } } diff --git a/src/libstd/future.rs b/src/libstd/future.rs index be33c0f4663ed..b8ae03c0f2bfe 100644 --- a/src/libstd/future.rs +++ b/src/libstd/future.rs @@ -23,8 +23,7 @@ use core::cast; use core::cell::Cell; -use core::comm::{PortOne, oneshot, send_one}; -use core::pipes::recv; +use core::comm::{PortOne, oneshot, send_one, recv_one}; use core::task; use core::util::replace; @@ -105,11 +104,8 @@ pub fn from_port(port: PortOne) -> Future { */ let port = Cell(port); - do from_fn || { - let port = port.take().unwrap(); - match recv(port) { - oneshot::send(data) => data - } + do from_fn { + recv_one(port.take()) } } diff --git a/src/libstd/workcache.rs b/src/libstd/workcache.rs index f173df60df893..3889650d012e0 100644 --- a/src/libstd/workcache.rs +++ b/src/libstd/workcache.rs @@ -15,11 +15,10 @@ use sort; use core::cell::Cell; use core::cmp; -use core::comm::{PortOne, oneshot, send_one}; +use core::comm::{PortOne, oneshot, send_one, recv_one}; use core::either::{Either, Left, Right}; use core::hashmap::HashMap; use core::io; -use core::pipes::recv; use core::run; use core::to_bytes; use core::util::replace; @@ -389,9 +388,7 @@ fn unwrap fail!(), Some(Left(v)) => v, Some(Right(port)) => { - let (exe, v) = match recv(port.unwrap()) { - oneshot::send(data) => data - }; + let (exe, v) = recv_one(port); let s = json_encode(&v); From df9e41278eb1e3e653ccd6b4dfab4d7303f64c02 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 17 May 2013 17:47:10 -0700 Subject: [PATCH 53/55] core: Wire up `stream` to newsched --- src/libcore/comm.rs | 349 +++++++++++++++++++------------ src/libcore/rt/comm.rs | 248 ++++++++++++---------- src/libcore/rt/local_services.rs | 2 +- 3 files changed, 357 insertions(+), 242 deletions(-) diff --git a/src/libcore/comm.rs b/src/libcore/comm.rs index da3ae0e6c5dfe..59eb915c239fe 100644 --- a/src/libcore/comm.rs +++ b/src/libcore/comm.rs @@ -25,7 +25,7 @@ use unstable::sync::{Exclusive, exclusive}; use rtcomm = rt::comm; use rt; -use pipes::{recv, try_recv, wait_many, peek, PacketHeader}; +use pipes::{wait_many, PacketHeader}; // FIXME #5160: Making this public exposes some plumbing from // pipes. Needs some refactoring @@ -61,76 +61,14 @@ pub trait Peekable { fn peek(&self) -> bool; } - -// Streams - Make pipes a little easier in general. - -/*proto! streamp ( - Open:send { - data(T) -> Open - } -)*/ - -#[allow(non_camel_case_types)] -pub mod streamp { - priv use core::kinds::Owned; - - pub fn init() -> (client::Open, server::Open) { - pub use core::pipes::HasBuffer; - ::core::pipes::entangle() - } - - #[allow(non_camel_case_types)] - pub enum Open { pub data(T, server::Open), } - - #[allow(non_camel_case_types)] - pub mod client { - priv use core::kinds::Owned; - - #[allow(non_camel_case_types)] - pub fn try_data(pipe: Open, x_0: T) -> - ::core::option::Option> { - { - use super::data; - let (c, s) = ::core::pipes::entangle(); - let message = data(x_0, s); - if ::core::pipes::send(pipe, message) { - ::core::pipes::rt::make_some(c) - } else { ::core::pipes::rt::make_none() } - } - } - - #[allow(non_camel_case_types)] - pub fn data(pipe: Open, x_0: T) -> Open { - { - use super::data; - let (c, s) = ::core::pipes::entangle(); - let message = data(x_0, s); - ::core::pipes::send(pipe, message); - c - } - } - - #[allow(non_camel_case_types)] - pub type Open = ::core::pipes::SendPacket>; - } - - #[allow(non_camel_case_types)] - pub mod server { - #[allow(non_camel_case_types)] - pub type Open = ::core::pipes::RecvPacket>; - } -} - /// An endpoint that can send many messages. -#[unsafe_mut_field(endp)] pub struct Chan { - endp: Option> + inner: Either, rtcomm::Chan> } /// An endpoint that can receive many messages. -#[unsafe_mut_field(endp)] pub struct Port { - endp: Option>, + inner: Either, rtcomm::Port> } /** Creates a `(Port, Chan)` pair. @@ -139,100 +77,75 @@ These allow sending or receiving an unlimited number of messages. */ pub fn stream() -> (Port, Chan) { - let (c, s) = streamp::init(); - - (Port { - endp: Some(s) - }, Chan { - endp: Some(c) - }) + let (port, chan) = match rt::context() { + rt::OldTaskContext => match pipesy::stream() { + (p, c) => (Left(p), Left(c)) + }, + _ => match rtcomm::stream() { + (p, c) => (Right(p), Right(c)) + } + }; + let port = Port { inner: port }; + let chan = Chan { inner: chan }; + return (port, chan); } impl GenericChan for Chan { - #[inline(always)] fn send(&self, x: T) { - unsafe { - let self_endp = transmute_mut(&self.endp); - let endp = replace(self_endp, None); - *self_endp = Some(streamp::client::data(endp.unwrap(), x)) + match self.inner { + Left(ref chan) => chan.send(x), + Right(ref chan) => chan.send(x) } } } impl GenericSmartChan for Chan { - #[inline(always)] fn try_send(&self, x: T) -> bool { - unsafe { - let self_endp = transmute_mut(&self.endp); - let endp = replace(self_endp, None); - match streamp::client::try_data(endp.unwrap(), x) { - Some(next) => { - *self_endp = Some(next); - true - } - None => false - } + match self.inner { + Left(ref chan) => chan.try_send(x), + Right(ref chan) => chan.try_send(x) } } } impl GenericPort for Port { - #[inline(always)] fn recv(&self) -> T { - unsafe { - let self_endp = transmute_mut(&self.endp); - let endp = replace(self_endp, None); - let streamp::data(x, endp) = recv(endp.unwrap()); - *self_endp = Some(endp); - x + match self.inner { + Left(ref port) => port.recv(), + Right(ref port) => port.recv() } } - #[inline(always)] fn try_recv(&self) -> Option { - unsafe { - let self_endp = transmute_mut(&self.endp); - let endp = replace(self_endp, None); - match try_recv(endp.unwrap()) { - Some(streamp::data(x, endp)) => { - *self_endp = Some(endp); - Some(x) - } - None => None - } + match self.inner { + Left(ref port) => port.try_recv(), + Right(ref port) => port.try_recv() } } } impl Peekable for Port { - #[inline(always)] fn peek(&self) -> bool { - unsafe { - let self_endp = transmute_mut(&self.endp); - let mut endp = replace(self_endp, None); - let peek = match endp { - Some(ref mut endp) => peek(endp), - None => fail!("peeking empty stream") - }; - *self_endp = endp; - peek + match self.inner { + Left(ref port) => port.peek(), + Right(ref port) => port.peek() } } } impl Selectable for Port { fn header(&mut self) -> *mut PacketHeader { - match self.endp { - Some(ref mut endp) => endp.header(), - None => fail!("peeking empty stream") - } + match self.inner { + Left(ref mut port) => port.header(), + Right(_) => fail!("can't select on newsched ports") + } } } /// Treat many ports as one. #[unsafe_mut_field(ports)] pub struct PortSet { - ports: ~[Port], + ports: ~[pipesy::Port], } pub impl PortSet { @@ -243,6 +156,11 @@ pub impl PortSet { } fn add(&self, port: Port) { + let Port { inner } = port; + let port = match inner { + Left(p) => p, + Right(_) => fail!("PortSet not implemented") + }; unsafe { let self_ports = transmute_mut(&self.ports); self_ports.push(port) @@ -290,7 +208,7 @@ impl Peekable for PortSet { // It'd be nice to use self.port.each, but that version isn't // pure. for uint::range(0, vec::uniq_len(&const self.ports)) |i| { - let port: &Port = &self.ports[i]; + let port: &pipesy::Port = &self.ports[i]; if port.peek() { return true; } @@ -301,12 +219,17 @@ impl Peekable for PortSet { /// A channel that can be shared between many senders. pub struct SharedChan { - ch: Exclusive> + ch: Exclusive> } impl SharedChan { /// Converts a `chan` into a `shared_chan`. pub fn new(c: Chan) -> SharedChan { + let Chan { inner } = c; + let c = match inner { + Left(c) => c, + Right(_) => fail!("SharedChan not implemented") + }; SharedChan { ch: exclusive(c) } } } @@ -354,12 +277,8 @@ pub fn oneshot() -> (PortOne, ChanOne) { (p, c) => (Right(p), Right(c)) } }; - let port = PortOne { - inner: port - }; - let chan = ChanOne { - inner: chan - }; + let port = PortOne { inner: port }; + let chan = ChanOne { inner: chan }; return (port, chan); } @@ -435,7 +354,10 @@ mod pipesy { use kinds::Owned; use option::{Option, Some, None}; - use pipes::{recv, try_recv}; + use pipes::{recv, try_recv, peek, PacketHeader}; + use super::{GenericChan, GenericSmartChan, GenericPort, Peekable, Selectable}; + use cast::transmute_mut; + use util::replace; /*proto! oneshot ( Oneshot:send { @@ -610,6 +532,173 @@ mod pipesy { } } + // Streams - Make pipes a little easier in general. + + /*proto! streamp ( + Open:send { + data(T) -> Open + } + )*/ + + #[allow(non_camel_case_types)] + pub mod streamp { + priv use core::kinds::Owned; + + pub fn init() -> (client::Open, server::Open) { + pub use core::pipes::HasBuffer; + ::core::pipes::entangle() + } + + #[allow(non_camel_case_types)] + pub enum Open { pub data(T, server::Open), } + + #[allow(non_camel_case_types)] + pub mod client { + priv use core::kinds::Owned; + + #[allow(non_camel_case_types)] + pub fn try_data(pipe: Open, x_0: T) -> + ::core::option::Option> { + { + use super::data; + let (c, s) = ::core::pipes::entangle(); + let message = data(x_0, s); + if ::core::pipes::send(pipe, message) { + ::core::pipes::rt::make_some(c) + } else { ::core::pipes::rt::make_none() } + } + } + + #[allow(non_camel_case_types)] + pub fn data(pipe: Open, x_0: T) -> Open { + { + use super::data; + let (c, s) = ::core::pipes::entangle(); + let message = data(x_0, s); + ::core::pipes::send(pipe, message); + c + } + } + + #[allow(non_camel_case_types)] + pub type Open = ::core::pipes::SendPacket>; + } + + #[allow(non_camel_case_types)] + pub mod server { + #[allow(non_camel_case_types)] + pub type Open = ::core::pipes::RecvPacket>; + } + } + + /// An endpoint that can send many messages. + #[unsafe_mut_field(endp)] + pub struct Chan { + endp: Option> + } + + /// An endpoint that can receive many messages. + #[unsafe_mut_field(endp)] + pub struct Port { + endp: Option>, + } + + /** Creates a `(Port, Chan)` pair. + + These allow sending or receiving an unlimited number of messages. + + */ + pub fn stream() -> (Port, Chan) { + let (c, s) = streamp::init(); + + (Port { + endp: Some(s) + }, Chan { + endp: Some(c) + }) + } + + impl GenericChan for Chan { + #[inline(always)] + fn send(&self, x: T) { + unsafe { + let self_endp = transmute_mut(&self.endp); + let endp = replace(self_endp, None); + *self_endp = Some(streamp::client::data(endp.unwrap(), x)) + } + } + } + + impl GenericSmartChan for Chan { + #[inline(always)] + fn try_send(&self, x: T) -> bool { + unsafe { + let self_endp = transmute_mut(&self.endp); + let endp = replace(self_endp, None); + match streamp::client::try_data(endp.unwrap(), x) { + Some(next) => { + *self_endp = Some(next); + true + } + None => false + } + } + } + } + + impl GenericPort for Port { + #[inline(always)] + fn recv(&self) -> T { + unsafe { + let self_endp = transmute_mut(&self.endp); + let endp = replace(self_endp, None); + let streamp::data(x, endp) = recv(endp.unwrap()); + *self_endp = Some(endp); + x + } + } + + #[inline(always)] + fn try_recv(&self) -> Option { + unsafe { + let self_endp = transmute_mut(&self.endp); + let endp = replace(self_endp, None); + match try_recv(endp.unwrap()) { + Some(streamp::data(x, endp)) => { + *self_endp = Some(endp); + Some(x) + } + None => None + } + } + } + } + + impl Peekable for Port { + #[inline(always)] + fn peek(&self) -> bool { + unsafe { + let self_endp = transmute_mut(&self.endp); + let mut endp = replace(self_endp, None); + let peek = match endp { + Some(ref mut endp) => peek(endp), + None => fail!("peeking empty stream") + }; + *self_endp = endp; + peek + } + } + } + + impl Selectable for Port { + fn header(&mut self) -> *mut PacketHeader { + match self.endp { + Some(ref mut endp) => endp.header(), + None => fail!("peeking empty stream") + } + } +} + } /// Returns the index of an endpoint that is ready to receive. diff --git a/src/libcore/rt/comm.rs b/src/libcore/rt/comm.rs index 9fcb70cfc7d66..4b5732b2d3aec 100644 --- a/src/libcore/rt/comm.rs +++ b/src/libcore/rt/comm.rs @@ -8,6 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +//! Ports and channels. +//! +//! XXX: Carefully consider whether the sequentially consistent +//! atomics here can be converted to acq/rel. I'm not sure they can, +//! because there is data being transerred in both directions (the payload +//! goes from sender to receiver and the task pointer goes the other way). + use option::*; use cast; use util; @@ -29,33 +36,37 @@ use cell::Cell; /// /// * 2 - both endpoints are alive /// * 1 - either the sender or the receiver is dead, determined by context -/// * - A pointer to a Task that can be transmuted to ~Task +/// * - A pointer to a blocked Task that can be transmuted to ~Task type State = int; static STATE_BOTH: State = 2; static STATE_ONE: State = 1; +/// The heap-allocated structure shared between two endpoints. struct Packet { state: State, payload: Option, } -pub struct PortOne { +/// A one-shot channel. +pub struct ChanOne { // XXX: Hack extra allocation to make by-val self work - inner: ~PortOneHack + inner: ~ChanOneHack } -pub struct ChanOne { + +/// A one-shot port. +pub struct PortOne { // XXX: Hack extra allocation to make by-val self work - inner: ~ChanOneHack + inner: ~PortOneHack } -pub struct PortOneHack { +pub struct ChanOneHack { void_packet: *mut Void, suppress_finalize: bool } -pub struct ChanOneHack { +pub struct PortOneHack { void_packet: *mut Void, suppress_finalize: bool } @@ -84,6 +95,54 @@ pub fn oneshot() -> (PortOne, ChanOne) { } } +impl ChanOne { + + pub fn send(self, val: T) { + self.try_send(val); + } + + pub fn try_send(self, val: T) -> bool { + let mut this = self; + let mut recvr_active = true; + let packet = this.inner.packet(); + + unsafe { + + // Install the payload + assert!((*packet).payload.is_none()); + (*packet).payload = Some(val); + + // Atomically swap out the old state to figure out what + // the port's up to, issuing a release barrier to prevent + // reordering of the payload write. This also issues an + // acquire barrier that keeps the subsequent access of the + // ~Task pointer from being reordered. + let oldstate = atomic_xchg(&mut (*packet).state, STATE_ONE); + match oldstate { + STATE_BOTH => { + // Port is not waiting yet. Nothing to do + } + STATE_ONE => { + // Port has closed. Need to clean up. + let _packet: ~Packet = cast::transmute(this.inner.void_packet); + recvr_active = false; + } + task_as_state => { + // Port is blocked. Wake it up. + let recvr: ~Coroutine = cast::transmute(task_as_state); + let sched = local_sched::take(); + sched.schedule_task(recvr); + } + } + } + + // Suppress the synchronizing actions in the finalizer. We're done with the packet. + this.inner.suppress_finalize = true; + return recvr_active; + } +} + + impl PortOne { pub fn recv(self) -> T { match self.try_recv() { @@ -96,30 +155,31 @@ impl PortOne { pub fn try_recv(self) -> Option { let mut this = self; - - { - let self_ptr: *mut PortOne = &mut this; - - // XXX: Optimize this to not require the two context switches when data is available - - // Switch to the scheduler - let sched = local_sched::take(); - do sched.deschedule_running_task_and_then |task| { - unsafe { - let task_as_state: State = cast::transmute(task); - let oldstate = atomic_xchg(&mut (*(*self_ptr).inner.packet()).state, task_as_state); - match oldstate { - STATE_BOTH => { - // Data has not been sent. Now we're blocked. - } - STATE_ONE => { - // Channel is closed. Switch back and check the data. - let task: ~Coroutine = cast::transmute(task_as_state); - let sched = local_sched::take(); - sched.resume_task_immediately(task); - } - _ => util::unreachable() + let packet = this.inner.packet(); + + // XXX: Optimize this to not require the two context switches when data is available + + // Switch to the scheduler to put the ~Task into the Packet state. + let sched = local_sched::take(); + do sched.deschedule_running_task_and_then |task| { + unsafe { + // Atomically swap the task pointer into the Packet state, issuing + // an acquire barrier to prevent reordering of the subsequent read + // of the payload. Also issues a release barrier to prevent reordering + // of any previous writes to the task structure. + let task_as_state: State = cast::transmute(task); + let oldstate = atomic_xchg(&mut (*packet).state, task_as_state); + match oldstate { + STATE_BOTH => { + // Data has not been sent. Now we're blocked. + } + STATE_ONE => { + // Channel is closed. Switch back and check the data. + let task: ~Coroutine = cast::transmute(task_as_state); + let sched = local_sched::take(); + sched.resume_task_immediately(task); } + _ => util::unreachable() } } } @@ -130,20 +190,20 @@ impl PortOne { // payload. Some scenarios: // // 1) We encountered STATE_ONE above - the atomic_xchg was the acq barrier. We're fine. - // 2) We encountered STATE_BOTH above and blocked. The sending task work-stole us - // and ran on its thread. The work stealing had a memory barrier. + // 2) We encountered STATE_BOTH above and blocked. The sending task then ran us + // and ran on its thread. The sending task issued a read barrier when taking the + // pointer to the receiving task. // 3) We encountered STATE_BOTH above and blocked, but the receiving task (this task) // is pinned to some other scheduler, so the sending task had to give us to // a different scheduler for resuming. That send synchronized memory. unsafe { - let payload = util::replace(&mut (*this.inner.packet()).payload, None); + let payload = util::replace(&mut (*packet).payload, None); // The sender has closed up shop. Drop the packet. let _packet: ~Packet = cast::transmute(this.inner.void_packet); - // Supress the finalizer. We're done here. + // Suppress the synchronizing actions in the finalizer. We're done with the packet. this.inner.suppress_finalize = true; - return payload; } } @@ -167,47 +227,8 @@ impl Peekable for PortOne { } } -impl ChanOne { - - pub fn send(self, val: T) { - self.try_send(val); - } - - pub fn try_send(self, val: T) -> bool { - let mut this = self; - let mut recvr_active = true; - - unsafe { - assert!((*this.inner.packet()).payload.is_none()); - (*this.inner.packet()).payload = Some(val); - - let oldstate = atomic_xchg(&mut (*this.inner.packet()).state, STATE_ONE); - match oldstate { - STATE_BOTH => { - // Port is not recving yet. Nothing to do - } - STATE_ONE => { - // Port has closed. Need to clean up. - let _packet: ~Packet = cast::transmute(this.inner.void_packet); - recvr_active = false; - } - _ => { - // Port is blocked. Wake it up. - let recvr: ~Coroutine = cast::transmute(oldstate); - let sched = local_sched::take(); - sched.schedule_task(recvr); - } - } - } - - // Suppress the finalizer. We're done here. - this.inner.suppress_finalize = true; - return recvr_active; - } -} - #[unsafe_destructor] -impl Drop for PortOneHack { +impl Drop for ChanOneHack { fn finalize(&self) { if self.suppress_finalize { return } @@ -216,13 +237,17 @@ impl Drop for PortOneHack { let oldstate = atomic_xchg(&mut (*this.packet()).state, STATE_ONE); match oldstate { STATE_BOTH => { - /* cleanup is the chan's responsibility */ + // Port still active. It will destroy the Packet. }, STATE_ONE => { let _packet: ~Packet = cast::transmute(this.void_packet); - } - _ => { - util::unreachable() + }, + task_as_state => { + // The port is blocked waiting for a message we will never send. Wake it. + assert!((*this.packet()).payload.is_none()); + let recvr: ~Coroutine = cast::transmute(task_as_state); + let sched = local_sched::take(); + sched.schedule_task(recvr); } } } @@ -230,7 +255,7 @@ impl Drop for PortOneHack { } #[unsafe_destructor] -impl Drop for ChanOneHack { +impl Drop for PortOneHack { fn finalize(&self) { if self.suppress_finalize { return } @@ -239,24 +264,20 @@ impl Drop for ChanOneHack { let oldstate = atomic_xchg(&mut (*this.packet()).state, STATE_ONE); match oldstate { STATE_BOTH => { - /* cleanup is the port's responsibility */ + // Chan still active. It will destroy the packet. }, STATE_ONE => { let _packet: ~Packet = cast::transmute(this.void_packet); - }, + } _ => { - // The port is blocked recving for a message we will never send. Wake it. - assert!((*this.packet()).payload.is_none()); - let recvr: ~Coroutine = cast::transmute(oldstate); - let sched = local_sched::take(); - sched.schedule_task(recvr); + util::unreachable() } } } } } -impl PortOneHack { +impl ChanOneHack { fn packet(&self) -> *mut Packet { unsafe { let p: *mut ~Packet = cast::transmute(&self.void_packet); @@ -266,7 +287,7 @@ impl PortOneHack { } } -impl ChanOneHack { +impl PortOneHack { fn packet(&self) -> *mut Packet { unsafe { let p: *mut ~Packet = cast::transmute(&self.void_packet); @@ -276,18 +297,23 @@ impl ChanOneHack { } } -struct StreamPayload(T, PortOne>); - -pub struct Port { - // FIXME #5372. Using Cell because we don't take &mut self - next: Cell>> +struct StreamPayload { + val: T, + next: PortOne> } +/// A channel with unbounded size. pub struct Chan { // FIXME #5372. Using Cell because we don't take &mut self next: Cell>> } +/// An port with unbounded size. +pub struct Port { + // FIXME #5372. Using Cell because we don't take &mut self + next: Cell>> +} + pub fn stream() -> (Port, Chan) { let (pone, cone) = oneshot(); let port = Port { next: Cell(pone) }; @@ -295,6 +321,21 @@ pub fn stream() -> (Port, Chan) { return (port, chan); } +impl GenericChan for Chan { + fn send(&self, val: T) { + self.try_send(val); + } +} + +impl GenericSmartChan for Chan { + fn try_send(&self, val: T) -> bool { + let (next_pone, next_cone) = oneshot(); + let cone = self.next.take(); + self.next.put_back(next_cone); + cone.try_send(StreamPayload { val: val, next: next_pone }) + } +} + impl GenericPort for Port { fn recv(&self) -> T { match self.try_recv() { @@ -308,7 +349,7 @@ impl GenericPort for Port { fn try_recv(&self) -> Option { let pone = self.next.take(); match pone.try_recv() { - Some(StreamPayload(val, next)) => { + Some(StreamPayload { val, next }) => { self.next.put_back(next); Some(val) } @@ -323,21 +364,6 @@ impl Peekable for Port { } } -impl GenericChan for Chan { - fn send(&self, val: T) { - self.try_send(val); - } -} - -impl GenericSmartChan for Chan { - fn try_send(&self, val: T) -> bool { - let (next_pone, next_cone) = oneshot(); - let cone = self.next.take(); - self.next.put_back(next_cone); - cone.try_send(StreamPayload(val, next_pone)) - } -} - #[cfg(test)] mod test { use super::*; @@ -563,7 +589,7 @@ mod test { } #[test] - fn stream_send_recv() { + fn stream_send_recv_stress() { for stress_factor().times { do run_in_newsched_task { let (port, chan) = stream::<~int>(); diff --git a/src/libcore/rt/local_services.rs b/src/libcore/rt/local_services.rs index 35c703bb35074..8d6873be8cd56 100644 --- a/src/libcore/rt/local_services.rs +++ b/src/libcore/rt/local_services.rs @@ -261,7 +261,7 @@ mod test { use comm::*; do run_in_newsched_task() { - let (port, chan) = oneshot(); + let (port, chan) = stream(); chan.send(10); assert!(port.recv() == 10); } From 633af4c8abeeb8b7c65b1a504276da72e4f4234c Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 17 May 2013 18:11:47 -0700 Subject: [PATCH 54/55] Whitespace --- src/libcore/rt/io/extensions.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcore/rt/io/extensions.rs b/src/libcore/rt/io/extensions.rs index a3804d2d6ef1a..ceff2ecd77de0 100644 --- a/src/libcore/rt/io/extensions.rs +++ b/src/libcore/rt/io/extensions.rs @@ -572,11 +572,11 @@ impl WriterByteConversions for T { } fn write_u8(&mut self, n: u8) { - self.write([n]) + self.write([n]) } fn write_i8(&mut self, n: i8) { - self.write([n as u8]) + self.write([n as u8]) } } From 8daa5ec9eac7148674cd63e5281c56925a3bc7b7 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 18 May 2013 16:35:58 -0700 Subject: [PATCH 55/55] xfail-fast run-pass/core-rt-smoke --- src/test/run-pass/core-rt-smoke.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/run-pass/core-rt-smoke.rs b/src/test/run-pass/core-rt-smoke.rs index fb08cda3b2577..3a0b4b6d40da9 100644 --- a/src/test/run-pass/core-rt-smoke.rs +++ b/src/test/run-pass/core-rt-smoke.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// xfail-fast + // A simple test of starting the runtime manually #[start]