diff options
| -rw-r--r-- | src/libstd/logging.rs | 2 | ||||
| -rw-r--r-- | src/libstd/rt/io/mod.rs | 2 | ||||
| -rw-r--r-- | src/libstd/rt/io/native/file.rs | 6 | ||||
| -rw-r--r-- | src/libstd/rt/io/stdio.rs | 68 | ||||
| -rw-r--r-- | src/libstd/rt/logging.rs | 20 | ||||
| -rw-r--r-- | src/libstd/rt/rtio.rs | 3 | ||||
| -rw-r--r-- | src/libstd/rt/task.rs | 44 | ||||
| -rw-r--r-- | src/libstd/rt/util.rs | 13 | ||||
| -rw-r--r-- | src/libstd/rt/uv/mod.rs | 11 | ||||
| -rw-r--r-- | src/libstd/rt/uv/uvio.rs | 86 | ||||
| -rw-r--r-- | src/libstd/rt/uv/uvll.rs | 5 | ||||
| -rw-r--r-- | src/rt/rust_uv.cpp | 5 | ||||
| -rw-r--r-- | src/rt/rustrt.def.in | 1 |
13 files changed, 166 insertions, 100 deletions
diff --git a/src/libstd/logging.rs b/src/libstd/logging.rs index 5e1ef3658b3..35a3ca3cff0 100644 --- a/src/libstd/logging.rs +++ b/src/libstd/logging.rs @@ -112,7 +112,7 @@ pub fn log(_level: u32, args: &fmt::Arguments) { } None => { // There is no logger anywhere, just write to stderr - let mut logger = StdErrLogger; + let mut logger = StdErrLogger::new(); logger.log(args); } } diff --git a/src/libstd/rt/io/mod.rs b/src/libstd/rt/io/mod.rs index 1a5c197fd52..240210880bf 100644 --- a/src/libstd/rt/io/mod.rs +++ b/src/libstd/rt/io/mod.rs @@ -370,6 +370,7 @@ pub enum IoErrorKind { PathAlreadyExists, PathDoesntExist, MismatchedFileTypeForOperation, + ResourceUnavailable, IoUnavailable, } @@ -392,6 +393,7 @@ impl ToStr for IoErrorKind { PathDoesntExist => ~"PathDoesntExist", MismatchedFileTypeForOperation => ~"MismatchedFileTypeForOperation", IoUnavailable => ~"IoUnavailable", + ResourceUnavailable => ~"ResourceUnavailable", } } } diff --git a/src/libstd/rt/io/native/file.rs b/src/libstd/rt/io/native/file.rs index d6820981181..a2b2289679a 100644 --- a/src/libstd/rt/io/native/file.rs +++ b/src/libstd/rt/io/native/file.rs @@ -21,6 +21,12 @@ fn raise_error() { // XXX: this should probably be a bit more descriptive... let (kind, desc) = match os::errno() as i32 { libc::EOF => (EndOfFile, "end of file"), + + // These two constants can have the same value on some systems, but + // different values on others, so we can't use a match clause + x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => + (ResourceUnavailable, "resource temporarily unavailable"), + _ => (OtherIoError, "unknown error"), }; diff --git a/src/libstd/rt/io/stdio.rs b/src/libstd/rt/io/stdio.rs index 52838425422..294df9a6442 100644 --- a/src/libstd/rt/io/stdio.rs +++ b/src/libstd/rt/io/stdio.rs @@ -8,6 +8,24 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +/*! + +This modules provides bindings to the local event loop's TTY interface, using it +to have synchronous, but non-blocking versions of stdio. These handles can be +inspected for information about terminal dimensions or related information +about the stream or terminal that it is attached to. + +# Example + +```rust +use std::rt::io; + +let mut out = io::stdout(); +out.write(bytes!("Hello, world!")); +``` + +*/ + use fmt; use libc; use option::{Option, Some, None}; @@ -15,13 +33,14 @@ use result::{Ok, Err}; use rt::rtio::{IoFactory, RtioTTY, with_local_io}; use super::{Reader, Writer, io_error}; -/// Creates a new non-blocking handle to the stdin of the current process. -/// -/// See `stdout()` for notes about this function. -pub fn stdin() -> StdReader { +#[fixed_stack_segment] #[inline(never)] +fn tty<T>(fd: libc::c_int, f: &fn(~RtioTTY) -> T) -> T { do with_local_io |io| { - match io.tty_open(libc::STDIN_FILENO, true, false) { - Ok(tty) => Some(StdReader { inner: tty }), + // Always pass in readable as true, otherwise libuv turns our writes + // into blocking writes. We also need to dup the file descriptor because + // the tty will be closed when it's dropped. + match io.tty_open(unsafe { libc::dup(fd) }, true) { + Ok(tty) => Some(f(tty)), Err(e) => { io_error::cond.raise(e); None @@ -30,6 +49,13 @@ pub fn stdin() -> StdReader { }.unwrap() } +/// Creates a new non-blocking handle to the stdin of the current process. +/// +/// See `stdout()` for notes about this function. +pub fn stdin() -> StdReader { + do tty(libc::STDIN_FILENO) |tty| { StdReader { inner: tty } } +} + /// Creates a new non-blocking handle to the stdout of the current process. /// /// Note that this is a fairly expensive operation in that at least one memory @@ -37,30 +63,14 @@ pub fn stdin() -> StdReader { /// task context because the stream returned will be a non-blocking object using /// the local scheduler to perform the I/O. pub fn stdout() -> StdWriter { - do with_local_io |io| { - match io.tty_open(libc::STDOUT_FILENO, false, false) { - Ok(tty) => Some(StdWriter { inner: tty }), - Err(e) => { - io_error::cond.raise(e); - None - } - } - }.unwrap() + do tty(libc::STDOUT_FILENO) |tty| { StdWriter { inner: tty } } } /// Creates a new non-blocking handle to the stderr of the current process. /// /// See `stdout()` for notes about this function. pub fn stderr() -> StdWriter { - do with_local_io |io| { - match io.tty_open(libc::STDERR_FILENO, false, false) { - Ok(tty) => Some(StdWriter { inner: tty }), - Err(e) => { - io_error::cond.raise(e); - None - } - } - }.unwrap() + do tty(libc::STDERR_FILENO) |tty| { StdWriter { inner: tty } } } /// Prints a string to the stdout of the current process. No newline is emitted @@ -115,6 +125,11 @@ impl StdReader { Err(e) => io_error::cond.raise(e), } } + + /// Returns whether this tream is attached to a TTY instance or not. + /// + /// This is similar to libc's isatty() function + pub fn isatty(&self) -> bool { self.inner.isatty() } } impl Reader for StdReader { @@ -170,6 +185,11 @@ impl StdWriter { Err(e) => io_error::cond.raise(e), } } + + /// Returns whether this tream is attached to a TTY instance or not. + /// + /// This is similar to libc's isatty() function + pub fn isatty(&self) -> bool { self.inner.isatty() } } impl Writer for StdWriter { diff --git a/src/libstd/rt/logging.rs b/src/libstd/rt/logging.rs index 660d1cd4359..31650ede700 100644 --- a/src/libstd/rt/logging.rs +++ b/src/libstd/rt/logging.rs @@ -12,6 +12,7 @@ use fmt; use from_str::from_str; use libc::exit; use option::{Some, None, Option}; +use rt::io; use rt::crate_map::{ModEntry, CrateMap, iter_crate_map, get_crate_map}; use str::StrSlice; use u32; @@ -166,14 +167,23 @@ pub trait Logger { fn log(&mut self, args: &fmt::Arguments); } -pub struct StdErrLogger; +/// This logger emits output to the stderr of the process, and contains a lazily +/// initialized event-loop driven handle to the stream. +pub struct StdErrLogger { + priv handle: Option<io::stdio::StdWriter>, +} + +impl StdErrLogger { + pub fn new() -> StdErrLogger { StdErrLogger { handle: None } } +} impl Logger for StdErrLogger { fn log(&mut self, args: &fmt::Arguments) { - // FIXME(#6846): this should not call the blocking version of println, - // or at least the default loggers for tasks shouldn't do - // that - ::rt::util::dumb_println(args); + // First time logging? Get a handle to the stderr of this process. + if self.handle.is_none() { + self.handle = Some(io::stderr()); + } + fmt::writeln(self.handle.get_mut_ref() as &mut io::Writer, args); } } diff --git a/src/libstd/rt/rtio.rs b/src/libstd/rt/rtio.rs index c779be01046..4a4ce4edcc2 100644 --- a/src/libstd/rt/rtio.rs +++ b/src/libstd/rt/rtio.rs @@ -97,7 +97,7 @@ pub trait IoFactory { fn unix_bind(&mut self, path: &CString) -> Result<~RtioUnixListener, IoError>; fn unix_connect(&mut self, path: &CString) -> Result<~RtioPipe, IoError>; - fn tty_open(&mut self, fd: c_int, readable: bool, close_on_drop: bool) + fn tty_open(&mut self, fd: c_int, readable: bool) -> Result<~RtioTTY, IoError>; } @@ -182,6 +182,7 @@ pub trait RtioTTY { fn write(&mut self, buf: &[u8]) -> Result<(), IoError>; fn set_raw(&mut self, raw: bool) -> Result<(), IoError>; fn get_winsize(&mut self) -> Result<(int, int), IoError>; + fn isatty(&self) -> bool; } pub trait PausibleIdleCallback { diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs index c4f352501a0..16ae28743c5 100644 --- a/src/libstd/rt/task.rs +++ b/src/libstd/rt/task.rs @@ -132,7 +132,7 @@ impl Task { heap: LocalHeap::new(), gc: GarbageCollector, storage: LocalStorage(None), - logger: StdErrLogger, + logger: StdErrLogger::new(), unwinder: Unwinder { unwinding: false }, taskgroup: None, death: Death::new(), @@ -166,7 +166,7 @@ impl Task { heap: LocalHeap::new(), gc: GarbageCollector, storage: LocalStorage(None), - logger: StdErrLogger, + logger: StdErrLogger::new(), unwinder: Unwinder { unwinding: false }, taskgroup: None, death: Death::new(), @@ -188,7 +188,7 @@ impl Task { heap: LocalHeap::new(), gc: GarbageCollector, storage: LocalStorage(None), - logger: StdErrLogger, + logger: StdErrLogger::new(), unwinder: Unwinder { unwinding: false }, taskgroup: None, // FIXME(#7544) make watching optional @@ -549,6 +549,7 @@ pub fn begin_unwind(msg: *c_char, file: *c_char, line: size_t) -> ! { use rt::logging::Logger; use str::Str; use c_str::CString; + use unstable::intrinsics; unsafe { let msg = CString::new(msg, false); @@ -557,35 +558,32 @@ pub fn begin_unwind(msg: *c_char, file: *c_char, line: size_t) -> ! { Some(s) => s, None => rtabort!("message wasn't utf8?") }; - if in_green_task_context() { - // Be careful not to allocate in this block, if we're failing we may - // have been failing due to a lack of memory in the first place... - do Local::borrow |task: &mut Task| { - let n = task.name.as_ref().map(|n| n.as_slice()).unwrap_or("<unnamed>"); - - match file.as_str() { - Some(file) => { - format_args!(|args| { task.logger.log(args) }, - "task '{}' failed at '{}', {}:{}", - n, msg, file, line); - } - None => { - format_args!(|args| { task.logger.log(args) }, - "task '{}' failed at '{}'", n, msg); - } - } - } - } else { + if !in_green_task_context() { match file.as_str() { Some(file) => { rterrln!("failed in non-task context at '{}', {}:{}", msg, file, line as int); } - None => rterrln!("failed in non-task context at '{}'", msg), + None => rterrln!("failed in non-task context at '{}'", msg) } + intrinsics::abort(); } + // Be careful not to allocate in this block, if we're failing we may + // have been failing due to a lack of memory in the first place... let task: *mut Task = Local::unsafe_borrow(); + let n = (*task).name.as_ref().map(|n| n.as_slice()).unwrap_or("<unnamed>"); + match file.as_str() { + Some(file) => { + format_args!(|args| { (*task).logger.log(args) }, + "task '{}' failed at '{}', {}:{}", + n, msg, file, line); + } + None => { + format_args!(|args| { (*task).logger.log(args) }, + "task '{}' failed at '{}'", n, msg); + } + } if (*task).unwinder.unwinding { rtabort!("unwinding again"); } diff --git a/src/libstd/rt/util.rs b/src/libstd/rt/util.rs index 647d88c26f2..f15aa01db95 100644 --- a/src/libstd/rt/util.rs +++ b/src/libstd/rt/util.rs @@ -71,9 +71,18 @@ pub fn default_sched_threads() -> uint { pub fn dumb_println(args: &fmt::Arguments) { use rt::io::native::stdio::stderr; - use rt::io::Writer; + use rt::io::{Writer, io_error, ResourceUnavailable}; let mut out = stderr(); - fmt::writeln(&mut out as &mut Writer, args); + + let mut again = true; + do io_error::cond.trap(|e| { + again = e.kind == ResourceUnavailable; + }).inside { + while again { + again = false; + fmt::writeln(&mut out as &mut Writer, args); + } + } } pub fn abort(msg: &str) -> ! { diff --git a/src/libstd/rt/uv/mod.rs b/src/libstd/rt/uv/mod.rs index a03264af7e1..18c99157707 100644 --- a/src/libstd/rt/uv/mod.rs +++ b/src/libstd/rt/uv/mod.rs @@ -170,6 +170,7 @@ pub trait WatcherInterop { fn get_watcher_data<'r>(&'r mut self) -> &'r mut WatcherData; fn drop_watcher_data(&mut self); fn close(self, cb: NullCallback); + fn close_async(self); } impl<H, W: Watcher + NativeHandle<*H>> WatcherInterop for W { @@ -235,6 +236,16 @@ impl<H, W: Watcher + NativeHandle<*H>> WatcherInterop for W { unsafe { uvll::free_handle(handle as *c_void) } } } + + fn close_async(self) { + unsafe { uvll::close(self.native_handle(), close_cb); } + + extern fn close_cb(handle: *uvll::uv_handle_t) { + let mut h: Handle = NativeHandle::from_native_handle(handle); + h.drop_watcher_data(); + unsafe { uvll::free_handle(handle as *c_void) } + } + } } // XXX: Need to define the error constants like EOF so they can be diff --git a/src/libstd/rt/uv/uvio.rs b/src/libstd/rt/uv/uvio.rs index 322588973ae..915c7d0da52 100644 --- a/src/libstd/rt/uv/uvio.rs +++ b/src/libstd/rt/uv/uvio.rs @@ -868,13 +868,13 @@ impl IoFactory for UvIoFactory { return ret; } - fn tty_open(&mut self, fd: c_int, readable: bool, close_on_drop: bool) + fn tty_open(&mut self, fd: c_int, readable: bool) -> Result<~RtioTTY, IoError> { match tty::TTY::new(self.uv_loop(), fd, readable) { Ok(tty) => Ok(~UvTTY { home: get_handle_to_current_scheduler!(), tty: tty, - close_on_drop: close_on_drop, + fd: fd, } as ~RtioTTY), Err(e) => Err(uv_error_to_io_error(e)) } @@ -1748,7 +1748,7 @@ impl RtioUnixListener for UvUnixListener { pub struct UvTTY { tty: tty::TTY, home: SchedHandle, - close_on_drop: bool, + fd: c_int, } impl HomingIO for UvTTY { @@ -1757,20 +1757,48 @@ impl HomingIO for UvTTY { impl Drop for UvTTY { fn drop(&mut self) { - if self.close_on_drop { - let scheduler: ~Scheduler = Local::take(); - do scheduler.deschedule_running_task_and_then |_, task| { - let task = Cell::new(task); - do self.tty.close { - let scheduler: ~Scheduler = Local::take(); - scheduler.resume_blocked_task_immediately(task.take()); - } + // TTY handles are used for the logger in a task, so this destructor is + // run when a task is destroyed. When a task is being destroyed, a local + // scheduler isn't available, so we can't do the normal "take the + // scheduler and resume once close is done". Instead close operations on + // a TTY are asynchronous. + + self.tty.close_async(); + } +} + +impl RtioTTY for UvTTY { + fn read(&mut self, buf: &mut [u8]) -> Result<uint, IoError> { + do self.home_for_io_with_sched |self_, scheduler| { + read_stream(self_.tty.as_stream(), scheduler, buf) + } + } + + fn write(&mut self, buf: &[u8]) -> Result<(), IoError> { + do self.home_for_io_with_sched |self_, scheduler| { + write_stream(self_.tty.as_stream(), scheduler, buf) + } + } + + fn set_raw(&mut self, raw: bool) -> Result<(), IoError> { + do self.home_for_io |self_| { + match self_.tty.set_mode(raw) { + Ok(p) => Ok(p), Err(e) => Err(uv_error_to_io_error(e)) } - } else { - self.tty.drop_watcher_data(); - unsafe { uvll::free_handle(self.tty.native_handle()) } } } + + fn get_winsize(&mut self) -> Result<(int, int), IoError> { + do self.home_for_io |self_| { + match self_.tty.get_winsize() { + Ok(p) => Ok(p), Err(e) => Err(uv_error_to_io_error(e)) + } + } + } + + fn isatty(&self) -> bool { + unsafe { uvll::guess_handle(self.fd) == uvll::UV_TTY } + } } pub struct UvUnixAcceptor { @@ -1808,36 +1836,6 @@ impl RtioUnixAcceptor for UvUnixAcceptor { } } -impl RtioTTY for UvTTY { - fn read(&mut self, buf: &mut [u8]) -> Result<uint, IoError> { - do self.home_for_io_with_sched |self_, scheduler| { - read_stream(self_.tty.as_stream(), scheduler, buf) - } - } - - fn write(&mut self, buf: &[u8]) -> Result<(), IoError> { - do self.home_for_io_with_sched |self_, scheduler| { - write_stream(self_.tty.as_stream(), scheduler, buf) - } - } - - fn set_raw(&mut self, raw: bool) -> Result<(), IoError> { - do self.home_for_io |self_| { - match self_.tty.set_mode(raw) { - Ok(p) => Ok(p), Err(e) => Err(uv_error_to_io_error(e)) - } - } - } - - fn get_winsize(&mut self) -> Result<(int, int), IoError> { - do self.home_for_io |self_| { - match self_.tty.get_winsize() { - Ok(p) => Ok(p), Err(e) => Err(uv_error_to_io_error(e)) - } - } - } -} - // this function is full of lies unsafe fn local_io() -> &'static mut IoFactory { do Local::borrow |sched: &mut Scheduler| { diff --git a/src/libstd/rt/uv/uvll.rs b/src/libstd/rt/uv/uvll.rs index e78b2579779..8f8aea9d121 100644 --- a/src/libstd/rt/uv/uvll.rs +++ b/src/libstd/rt/uv/uvll.rs @@ -986,6 +986,10 @@ pub unsafe fn tty_get_winsize(tty: *uv_tty_t, width: *c_int, #[fixed_stack_segment]; #[inline(never)]; rust_uv_tty_get_winsize(tty, width, height) } +pub unsafe fn guess_handle(fd: c_int) -> uv_handle_type { + #[fixed_stack_segment]; #[inline(never)]; + rust_uv_guess_handle(fd) +} pub struct uv_err_data { priv err_name: ~str, @@ -1140,6 +1144,7 @@ extern { fn rust_uv_tty_set_mode(tty: *uv_tty_t, mode: c_int) -> c_int; fn rust_uv_tty_get_winsize(tty: *uv_tty_t, width: *c_int, height: *c_int) -> c_int; + fn rust_uv_guess_handle(fd: c_int) -> uv_handle_type; // These should all really be constants... #[rust_stack] pub fn rust_SOCK_STREAM() -> c_int; diff --git a/src/rt/rust_uv.cpp b/src/rt/rust_uv.cpp index a47b3446d34..7ab57e6909a 100644 --- a/src/rt/rust_uv.cpp +++ b/src/rt/rust_uv.cpp @@ -681,3 +681,8 @@ extern "C" int rust_uv_tty_get_winsize(uv_tty_t *tty, int *width, int *height) { return uv_tty_get_winsize(tty, width, height); } + +extern "C" uv_handle_type +rust_uv_guess_handle(int fd) { + return uv_guess_handle(fd); +} diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index 58862da31db..6c3988d6b01 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -217,3 +217,4 @@ rust_uv_pipe_connect rust_uv_tty_init rust_uv_tty_set_mode rust_uv_tty_get_winsize +rust_uv_guess_handle |
