diff --git a/src/librustuv/macros.rs b/src/librustuv/macros.rs index 01f2bfd648cd5..71744c717d4bd 100644 --- a/src/librustuv/macros.rs +++ b/src/librustuv/macros.rs @@ -33,9 +33,9 @@ macro_rules! get_handle_to_current_scheduler( ) pub fn dumb_println(args: &fmt::Arguments) { - use std::io::native::stdio::stderr; - use std::io::Writer; - - let mut out = stderr(); - fmt::writeln(&mut out as &mut Writer, args); + use std::io::native::file::FileDesc; + use std::io; + use std::libc; + let mut out = FileDesc::new(libc::STDERR_FILENO, false); + fmt::writeln(&mut out as &mut io::Writer, args); } diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 8858d0a14adf2..2aed3bdb8496f 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -306,24 +306,7 @@ pub mod timer; /// Buffered I/O wrappers pub mod buffered; -/// Thread-blocking implementations -pub mod native { - /// Posix file I/O - pub mod file; - /// Process spawning and child management - pub mod process; - /// Posix stdio - pub mod stdio; - - /// Sockets - /// # XXX - implement this - pub mod net { - pub mod tcp { } - pub mod udp { } - #[cfg(unix)] - pub mod unix { } - } -} +pub mod native; /// Signal handling pub mod signal; diff --git a/src/libstd/io/native/file.rs b/src/libstd/io/native/file.rs index 0f1a64edb6072..5e39460ba6a4d 100644 --- a/src/libstd/io/native/file.rs +++ b/src/libstd/io/native/file.rs @@ -12,42 +12,16 @@ #[allow(non_camel_case_types)]; +use io::IoError; +use io; use libc; +use ops::Drop; +use option::{Some, None, Option}; use os; -use prelude::*; -use super::super::*; - -#[cfg(windows)] -fn get_err(errno: i32) -> (IoErrorKind, &'static str) { - match errno { - libc::EOF => (EndOfFile, "end of file"), - _ => (OtherIoError, "unknown error"), - } -} - -#[cfg(not(windows))] -fn get_err(errno: i32) -> (IoErrorKind, &'static str) { - // XXX: this should probably be a bit more descriptive... - match errno { - 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"), - } -} - -fn raise_error() { - let (kind, desc) = get_err(os::errno() as i32); - io_error::cond.raise(IoError { - kind: kind, - desc: desc, - detail: Some(os::last_os_error()) - }); -} +use ptr::RawPtr; +use result::{Result, Ok, Err}; +use rt::rtio; +use vec::ImmutableVector; fn keep_going(data: &[u8], f: &fn(*u8, uint) -> i64) -> i64 { #[cfg(windows)] static eintr: int = 0; // doesn't matter @@ -95,10 +69,8 @@ impl FileDesc { pub fn new(fd: fd_t, close_on_drop: bool) -> FileDesc { FileDesc { fd: fd, close_on_drop: close_on_drop } } -} -impl Reader for FileDesc { - fn read(&mut self, buf: &mut [u8]) -> Option { + fn inner_read(&mut self, buf: &mut [u8]) -> Result { #[cfg(windows)] type rlen = libc::c_uint; #[cfg(not(windows))] type rlen = libc::size_t; let ret = do keep_going(buf) |buf, len| { @@ -107,20 +79,14 @@ impl Reader for FileDesc { } }; if ret == 0 { - None + Err(io::standard_error(io::EndOfFile)) } else if ret < 0 { - raise_error(); - None + Err(super::last_error()) } else { - Some(ret as uint) + Ok(ret as uint) } } - - fn eof(&mut self) -> bool { false } -} - -impl Writer for FileDesc { - fn write(&mut self, buf: &[u8]) { + fn inner_write(&mut self, buf: &[u8]) -> Result<(), IoError> { #[cfg(windows)] type wlen = libc::c_uint; #[cfg(not(windows))] type wlen = libc::size_t; let ret = do keep_going(buf) |buf, len| { @@ -129,14 +95,84 @@ impl Writer for FileDesc { } }; if ret < 0 { - raise_error(); + Err(super::last_error()) + } else { + Ok(()) } } } +impl io::Reader for FileDesc { + fn read(&mut self, buf: &mut [u8]) -> Option { + match self.inner_read(buf) { Ok(n) => Some(n), Err(*) => None } + } + fn eof(&mut self) -> bool { false } +} + +impl io::Writer for FileDesc { + fn write(&mut self, buf: &[u8]) { + self.inner_write(buf); + } +} + +impl rtio::RtioFileStream for FileDesc { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner_read(buf).map(|i| i as int) + } + fn write(&mut self, buf: &[u8]) -> Result<(), IoError> { + self.inner_write(buf) + } + fn pread(&mut self, _buf: &mut [u8], _offset: u64) -> Result { + Err(super::unimpl()) + } + fn pwrite(&mut self, _buf: &[u8], _offset: u64) -> Result<(), IoError> { + Err(super::unimpl()) + } + fn seek(&mut self, _pos: i64, _whence: io::SeekStyle) -> Result { + Err(super::unimpl()) + } + fn tell(&self) -> Result { + Err(super::unimpl()) + } + fn fsync(&mut self) -> Result<(), IoError> { + Err(super::unimpl()) + } + fn datasync(&mut self) -> Result<(), IoError> { + Err(super::unimpl()) + } + fn truncate(&mut self, _offset: i64) -> Result<(), IoError> { + Err(super::unimpl()) + } +} + +impl rtio::RtioPipe for FileDesc { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner_read(buf) + } + fn write(&mut self, buf: &[u8]) -> Result<(), IoError> { + self.inner_write(buf) + } +} + +impl rtio::RtioTTY for FileDesc { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner_read(buf) + } + fn write(&mut self, buf: &[u8]) -> Result<(), IoError> { + self.inner_write(buf) + } + fn set_raw(&mut self, _raw: bool) -> Result<(), IoError> { + Err(super::unimpl()) + } + fn get_winsize(&mut self) -> Result<(int, int), IoError> { + Err(super::unimpl()) + } +} + impl Drop for FileDesc { fn drop(&mut self) { - if self.close_on_drop { + // closing stdio file handles makes no sense, so never do it + if self.close_on_drop && self.fd > libc::STDERR_FILENO { unsafe { libc::close(self.fd); } } } @@ -154,8 +190,8 @@ impl CFile { pub fn new(file: *libc::FILE) -> CFile { CFile { file: file } } } -impl Reader for CFile { - fn read(&mut self, buf: &mut [u8]) -> Option { +impl rtio::RtioFileStream for CFile { + fn read(&mut self, buf: &mut [u8]) -> Result { let ret = do keep_going(buf) |buf, len| { unsafe { libc::fread(buf as *mut libc::c_void, 1, len as libc::size_t, @@ -163,22 +199,15 @@ impl Reader for CFile { } }; if ret == 0 { - None + Err(io::standard_error(io::EndOfFile)) } else if ret < 0 { - raise_error(); - None + Err(super::last_error()) } else { - Some(ret as uint) + Ok(ret as int) } } - fn eof(&mut self) -> bool { - unsafe { libc::feof(self.file) != 0 } - } -} - -impl Writer for CFile { - fn write(&mut self, buf: &[u8]) { + fn write(&mut self, buf: &[u8]) -> Result<(), IoError> { let ret = do keep_going(buf) |buf, len| { unsafe { libc::fwrite(buf as *libc::c_void, 1, len as libc::size_t, @@ -186,35 +215,47 @@ impl Writer for CFile { } }; if ret < 0 { - raise_error(); + Err(super::last_error()) + } else { + Ok(()) } } - fn flush(&mut self) { - if unsafe { libc::fflush(self.file) } < 0 { - raise_error(); + fn pread(&mut self, _buf: &mut [u8], _offset: u64) -> Result { + Err(super::unimpl()) + } + fn pwrite(&mut self, _buf: &[u8], _offset: u64) -> Result<(), IoError> { + Err(super::unimpl()) + } + fn seek(&mut self, pos: i64, style: io::SeekStyle) -> Result { + let whence = match style { + io::SeekSet => libc::SEEK_SET, + io::SeekEnd => libc::SEEK_END, + io::SeekCur => libc::SEEK_CUR, + }; + let n = unsafe { libc::fseek(self.file, pos as libc::c_long, whence) }; + if n < 0 { + Err(super::last_error()) + } else { + Ok(n as u64) } } -} - -impl Seek for CFile { - fn tell(&self) -> u64 { + fn tell(&self) -> Result { let ret = unsafe { libc::ftell(self.file) }; if ret < 0 { - raise_error(); + Err(super::last_error()) + } else { + Ok(ret as u64) } - return ret as u64; } - - fn seek(&mut self, pos: i64, style: SeekStyle) { - let whence = match style { - SeekSet => libc::SEEK_SET, - SeekEnd => libc::SEEK_END, - SeekCur => libc::SEEK_CUR, - }; - if unsafe { libc::fseek(self.file, pos as libc::c_long, whence) } < 0 { - raise_error(); - } + fn fsync(&mut self) -> Result<(), IoError> { + Err(super::unimpl()) + } + fn datasync(&mut self) -> Result<(), IoError> { + Err(super::unimpl()) + } + fn truncate(&mut self, _offset: i64) -> Result<(), IoError> { + Err(super::unimpl()) } } @@ -228,9 +269,9 @@ impl Drop for CFile { mod tests { use libc; use os; - use prelude::*; - use io::{io_error, SeekSet}; - use super::*; + use io::{io_error, SeekSet, Writer, Reader}; + use result::Ok; + use super::{CFile, FileDesc}; #[ignore(cfg(target_os = "freebsd"))] // hmm, maybe pipes have a tiny buffer fn test_file_desc() { @@ -241,10 +282,10 @@ mod tests { let mut reader = FileDesc::new(input, true); let mut writer = FileDesc::new(out, true); - writer.write(bytes!("test")); + writer.inner_write(bytes!("test")); let mut buf = [0u8, ..4]; - match reader.read(buf) { - Some(4) => { + match reader.inner_read(buf) { + Ok(4) => { assert_eq!(buf[0], 't' as u8); assert_eq!(buf[1], 'e' as u8); assert_eq!(buf[2], 's' as u8); @@ -253,17 +294,8 @@ mod tests { r => fail!("invalid read: {:?}", r) } - let mut raised = false; - do io_error::cond.trap(|_| { raised = true; }).inside { - writer.read(buf); - } - assert!(raised); - - raised = false; - do io_error::cond.trap(|_| { raised = true; }).inside { - reader.write(buf); - } - assert!(raised); + assert!(writer.inner_read(buf).is_err()); + assert!(reader.inner_write(buf).is_err()); } } @@ -278,7 +310,7 @@ mod tests { let mut buf = [0u8, ..4]; file.seek(0, SeekSet); match file.read(buf) { - Some(4) => { + Ok(4) => { assert_eq!(buf[0], 't' as u8); assert_eq!(buf[1], 'e' as u8); assert_eq!(buf[2], 's' as u8); diff --git a/src/libstd/io/native/mod.rs b/src/libstd/io/native/mod.rs new file mode 100644 index 0000000000000..d7aa6c14fcd9f --- /dev/null +++ b/src/libstd/io/native/mod.rs @@ -0,0 +1,200 @@ +// 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. + +//! Native thread-blocking I/O implementation +//! +//! This module contains the implementation of native thread-blocking +//! implementations of I/O on all platforms. This module is not intended to be +//! used directly, but rather the rust runtime will fall back to using it if +//! necessary. +//! +//! Rust code normally runs inside of green tasks with a local scheduler using +//! asynchronous I/O to cooperate among tasks. This model is not always +//! available, however, and that's where these native implementations come into +//! play. The only dependencies of these modules are the normal system libraries +//! that you would find on the respective platform. + +use c_str::CString; +use comm::SharedChan; +use libc::c_int; +use libc; +use option::{Option, None, Some}; +use os; +use path::Path; +use result::{Result, Ok, Err}; +use rt::rtio; +use rt::rtio::{RtioTcpStream, RtioTcpListener, RtioUdpSocket, RtioUnixListener, + RtioPipe, RtioFileStream, RtioProcess, RtioSignal, RtioTTY, + CloseBehavior, RtioTimer}; +use io; +use io::IoError; +use io::net::ip::SocketAddr; +use io::process::ProcessConfig; +use io::signal::Signum; +use ai = io::net::addrinfo; + +// Local re-exports +pub use self::file::FileDesc; +pub use self::process::Process; + +// Native I/O implementations +pub mod file; +pub mod process; + +type IoResult = Result; + +fn unimpl() -> IoError { + IoError { + kind: io::IoUnavailable, + desc: "unimplemented I/O interface", + detail: None, + } +} + +fn last_error() -> IoError { + #[cfg(windows)] + fn get_err(errno: i32) -> (io::IoErrorKind, &'static str) { + match errno { + libc::EOF => (io::EndOfFile, "end of file"), + _ => (io::OtherIoError, "unknown error"), + } + } + + #[cfg(not(windows))] + fn get_err(errno: i32) -> (io::IoErrorKind, &'static str) { + // XXX: this should probably be a bit more descriptive... + match errno { + libc::EOF => (io::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 => + (io::ResourceUnavailable, "resource temporarily unavailable"), + + _ => (io::OtherIoError, "unknown error"), + } + } + + let (kind, desc) = get_err(os::errno() as i32); + IoError { + kind: kind, + desc: desc, + detail: Some(os::last_os_error()) + } +} + +/// Implementation of rt::rtio's IoFactory trait to generate handles to the +/// native I/O functionality. +pub struct IoFactory; + +impl rtio::IoFactory for IoFactory { + // networking + fn tcp_connect(&mut self, _addr: SocketAddr) -> IoResult<~RtioTcpStream> { + Err(unimpl()) + } + fn tcp_bind(&mut self, _addr: SocketAddr) -> IoResult<~RtioTcpListener> { + Err(unimpl()) + } + fn udp_bind(&mut self, _addr: SocketAddr) -> IoResult<~RtioUdpSocket> { + Err(unimpl()) + } + fn unix_bind(&mut self, _path: &CString) -> IoResult<~RtioUnixListener> { + Err(unimpl()) + } + fn unix_connect(&mut self, _path: &CString) -> IoResult<~RtioPipe> { + Err(unimpl()) + } + fn get_host_addresses(&mut self, _host: Option<&str>, _servname: Option<&str>, + _hint: Option) -> IoResult<~[ai::Info]> { + Err(unimpl()) + } + + // filesystem operations + fn fs_from_raw_fd(&mut self, fd: c_int, + close: CloseBehavior) -> ~RtioFileStream { + let close = match close { + rtio::CloseSynchronously | rtio::CloseAsynchronously => true, + rtio::DontClose => false + }; + ~file::FileDesc::new(fd, close) as ~RtioFileStream + } + fn fs_open(&mut self, _path: &CString, _fm: io::FileMode, _fa: io::FileAccess) + -> IoResult<~RtioFileStream> { + Err(unimpl()) + } + fn fs_unlink(&mut self, _path: &CString) -> IoResult<()> { + Err(unimpl()) + } + fn fs_stat(&mut self, _path: &CString) -> IoResult { + Err(unimpl()) + } + fn fs_mkdir(&mut self, _path: &CString, + _mode: io::FilePermission) -> IoResult<()> { + Err(unimpl()) + } + fn fs_chmod(&mut self, _path: &CString, + _mode: io::FilePermission) -> IoResult<()> { + Err(unimpl()) + } + fn fs_rmdir(&mut self, _path: &CString) -> IoResult<()> { + Err(unimpl()) + } + fn fs_rename(&mut self, _path: &CString, _to: &CString) -> IoResult<()> { + Err(unimpl()) + } + fn fs_readdir(&mut self, _path: &CString, _flags: c_int) -> IoResult<~[Path]> { + Err(unimpl()) + } + fn fs_lstat(&mut self, _path: &CString) -> IoResult { + Err(unimpl()) + } + fn fs_chown(&mut self, _path: &CString, _uid: int, _gid: int) -> IoResult<()> { + Err(unimpl()) + } + fn fs_readlink(&mut self, _path: &CString) -> IoResult { + Err(unimpl()) + } + fn fs_symlink(&mut self, _src: &CString, _dst: &CString) -> IoResult<()> { + Err(unimpl()) + } + fn fs_link(&mut self, _src: &CString, _dst: &CString) -> IoResult<()> { + Err(unimpl()) + } + fn fs_utime(&mut self, _src: &CString, _atime: u64, + _mtime: u64) -> IoResult<()> { + Err(unimpl()) + } + + // misc + fn timer_init(&mut self) -> IoResult<~RtioTimer> { + Err(unimpl()) + } + fn spawn(&mut self, config: ProcessConfig) + -> IoResult<(~RtioProcess, ~[Option<~RtioPipe>])> { + process::Process::spawn(config).map(|(p, io)| { + (~p as ~RtioProcess, + io.move_iter().map(|p| p.map(|p| ~p as ~RtioPipe)).collect()) + }) + } + fn pipe_open(&mut self, _fd: c_int) -> IoResult<~RtioPipe> { + Err(unimpl()) + } + fn tty_open(&mut self, fd: c_int, _readable: bool) -> IoResult<~RtioTTY> { + if unsafe { libc::isatty(fd) } != 0 { + Ok(~file::FileDesc::new(fd, true) as ~RtioTTY) + } else { + Err(unimpl()) + } + } + fn signal(&mut self, _signal: Signum, _channel: SharedChan) + -> IoResult<~RtioSignal> { + Err(unimpl()) + } +} diff --git a/src/libstd/io/native/process.rs b/src/libstd/io/native/process.rs index de03ac1c07d50..71c6ce78a1ed1 100644 --- a/src/libstd/io/native/process.rs +++ b/src/libstd/io/native/process.rs @@ -9,14 +9,17 @@ // except according to those terms. use cast; +use io; use libc::{pid_t, c_void, c_int}; use libc; use os; use prelude::*; use ptr; -use io; +use rt::rtio; use super::file; +use p = io::process; + /** * A value representing a child process. * @@ -33,13 +36,6 @@ pub struct Process { /// pid being re-used until the handle is closed. priv handle: *(), - /// Currently known stdin of the child, if any - priv input: Option, - /// Currently known stdout of the child, if any - priv output: Option, - /// Currently known stderr of the child, if any - priv error: Option, - /// None until finish() is called. priv exit_code: Option, } @@ -64,35 +60,44 @@ impl Process { /// these are `None`, then this module will bind the input/output to an /// os pipe instead. This process takes ownership of these file /// descriptors, closing them upon destruction of the process. - pub fn new(prog: &str, args: &[~str], env: Option<~[(~str, ~str)]>, - cwd: Option<&Path>, - stdin: Option, - stdout: Option, - stderr: Option) -> Process { - let (in_pipe, in_fd) = match stdin { - None => { - let pipe = os::pipe(); - (Some(pipe), pipe.input) - }, - Some(fd) => (None, fd) - }; - let (out_pipe, out_fd) = match stdout { - None => { - let pipe = os::pipe(); - (Some(pipe), pipe.out) - }, - Some(fd) => (None, fd) - }; - let (err_pipe, err_fd) = match stderr { - None => { - let pipe = os::pipe(); - (Some(pipe), pipe.out) - }, - Some(fd) => (None, fd) - }; + pub fn spawn(config: p::ProcessConfig) + -> Result<(Process, ~[Option]), io::IoError> + { + // right now we only handle stdin/stdout/stderr. + if config.io.len() > 3 { + return Err(super::unimpl()); + } + + fn get_io(io: &[p::StdioContainer], + ret: &mut ~[Option], + idx: uint) -> (Option, c_int) { + if idx >= io.len() { return (None, -1); } + ret.push(None); + match io[idx] { + p::Ignored => (None, -1), + p::InheritFd(fd) => (None, fd), + p::CreatePipe(readable, _writable) => { + let pipe = os::pipe(); + let (theirs, ours) = if readable { + (pipe.input, pipe.out) + } else { + (pipe.out, pipe.input) + }; + ret[idx] = Some(file::FileDesc::new(ours, true)); + (Some(pipe), theirs) + } + } + } + + let mut ret_io = ~[]; + let (in_pipe, in_fd) = get_io(config.io, &mut ret_io, 0); + let (out_pipe, out_fd) = get_io(config.io, &mut ret_io, 1); + let (err_pipe, err_fd) = get_io(config.io, &mut ret_io, 2); - let res = spawn_process_os(prog, args, env, cwd, - in_fd, out_fd, err_fd); + let env = config.env.map(|a| a.to_owned()); + let cwd = config.cwd.map(|a| Path::new(a)); + let res = spawn_process_os(config.program, config.args, env, + cwd.as_ref(), in_fd, out_fd, err_fd); unsafe { for pipe in in_pipe.iter() { libc::close(pipe.input); } @@ -100,97 +105,26 @@ impl Process { for pipe in err_pipe.iter() { libc::close(pipe.out); } } - Process { - pid: res.pid, - handle: res.handle, - input: in_pipe.map(|pipe| file::FileDesc::new(pipe.out, true)), - output: out_pipe.map(|pipe| file::FileDesc::new(pipe.input, true)), - error: err_pipe.map(|pipe| file::FileDesc::new(pipe.input, true)), - exit_code: None, - } - } - - /// Returns the unique id of the process - pub fn id(&self) -> pid_t { self.pid } - - /** - * Returns an io::Writer that can be used to write to this Process's stdin. - * - * Fails if there is no stdinavailable (it's already been removed by - * take_input) - */ - pub fn input<'a>(&'a mut self) -> &'a mut io::Writer { - match self.input { - Some(ref mut fd) => fd as &mut io::Writer, - None => fail!("This process has no stdin") - } - } - - /** - * Returns an io::Reader that can be used to read from this Process's - * stdout. - * - * Fails if there is no stdin available (it's already been removed by - * take_output) - */ - pub fn output<'a>(&'a mut self) -> &'a mut io::Reader { - match self.input { - Some(ref mut fd) => fd as &mut io::Reader, - None => fail!("This process has no stdout") - } - } - - /** - * Returns an io::Reader that can be used to read from this Process's - * stderr. - * - * Fails if there is no stdin available (it's already been removed by - * take_error) - */ - pub fn error<'a>(&'a mut self) -> &'a mut io::Reader { - match self.error { - Some(ref mut fd) => fd as &mut io::Reader, - None => fail!("This process has no stderr") - } - } - - /** - * Takes the stdin of this process, transferring ownership to the caller. - * Note that when the return value is destroyed, the handle will be closed - * for the child process. - */ - pub fn take_input(&mut self) -> Option<~io::Writer> { - self.input.take().map(|fd| ~fd as ~io::Writer) - } - - /** - * Takes the stdout of this process, transferring ownership to the caller. - * Note that when the return value is destroyed, the handle will be closed - * for the child process. - */ - pub fn take_output(&mut self) -> Option<~io::Reader> { - self.output.take().map(|fd| ~fd as ~io::Reader) + Ok((Process { pid: res.pid, handle: res.handle, exit_code: None }, ret_io)) } +} - /** - * Takes the stderr of this process, transferring ownership to the caller. - * Note that when the return value is destroyed, the handle will be closed - * for the child process. - */ - pub fn take_error(&mut self) -> Option<~io::Reader> { - self.error.take().map(|fd| ~fd as ~io::Reader) - } +impl rtio::RtioProcess for Process { + fn id(&self) -> pid_t { self.pid } - pub fn wait(&mut self) -> int { - for &code in self.exit_code.iter() { - return code; - } - let code = waitpid(self.pid); - self.exit_code = Some(code); - return code; + fn wait(&mut self) -> p::ProcessExit { + let code = match self.exit_code { + Some(code) => code, + None => { + let code = waitpid(self.pid); + self.exit_code = Some(code); + code + } + }; + return p::ExitStatus(code); // XXX: this is wrong } - pub fn signal(&mut self, signum: int) -> Result<(), io::IoError> { + fn kill(&mut self, signum: int) -> Result<(), io::IoError> { // if the process has finished, and therefore had waitpid called, // and we kill it, then on unix we might ending up killing a // newer process that happens to have the same (re-used) id @@ -207,8 +141,7 @@ impl Process { #[cfg(windows)] unsafe fn killpid(pid: pid_t, signal: int) -> Result<(), io::IoError> { match signal { - io::process::PleaseExitSignal | - io::process::MustDieSignal => { + io::process::PleaseExitSignal | io::process::MustDieSignal => { libc::funcs::extra::kernel32::TerminateProcess( cast::transmute(pid), 1); Ok(()) @@ -231,11 +164,6 @@ impl Process { impl Drop for Process { fn drop(&mut self) { - // close all these handles - self.take_input(); - self.take_output(); - self.take_error(); - self.wait(); free_handle(self.handle); } } diff --git a/src/libstd/io/native/stdio.rs b/src/libstd/io/native/stdio.rs deleted file mode 100644 index 68748ab49a3ea..0000000000000 --- a/src/libstd/io/native/stdio.rs +++ /dev/null @@ -1,63 +0,0 @@ -// 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; -use option::Option; -use io::{Reader, Writer}; -use super::file; - -/// Creates a new handle to the stdin of this process -pub fn stdin() -> StdIn { StdIn::new() } -/// Creates a new handle to the stdout of this process -pub fn stdout() -> StdOut { StdOut::new(libc::STDOUT_FILENO) } -/// Creates a new handle to the stderr of this process -pub fn stderr() -> StdOut { StdOut::new(libc::STDERR_FILENO) } - -pub fn print(s: &str) { - stdout().write(s.as_bytes()) -} - -pub fn println(s: &str) { - let mut out = stdout(); - out.write(s.as_bytes()); - out.write(['\n' as u8]); -} - -pub struct StdIn { - priv fd: file::FileDesc -} - -impl StdIn { - /// Duplicates the stdin file descriptor, returning an io::Reader - pub fn new() -> StdIn { - StdIn { fd: file::FileDesc::new(libc::STDIN_FILENO, false) } - } -} - -impl Reader for StdIn { - fn read(&mut self, buf: &mut [u8]) -> Option { self.fd.read(buf) } - fn eof(&mut self) -> bool { self.fd.eof() } -} - -pub struct StdOut { - priv fd: file::FileDesc -} - -impl StdOut { - /// Duplicates the specified file descriptor, returning an io::Writer - pub fn new(fd: file::fd_t) -> StdOut { - StdOut { fd: file::FileDesc::new(fd, false) } - } -} - -impl Writer for StdOut { - fn write(&mut self, buf: &[u8]) { self.fd.write(buf) } - fn flush(&mut self) { self.fd.flush() } -} diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index 302d757987388..6df9ea6c60a6d 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -126,17 +126,23 @@ fn with_task_stdout(f: &fn(&mut Writer)) { use rt::task::Task; unsafe { - // Logging may require scheduling operations, so we can't remove the - // task from TLS right now, hence the unsafe borrow. Sad. - let task: *mut Task = Local::unsafe_borrow(); + let task: Option<*mut Task> = Local::try_unsafe_borrow(); + match task { + Some(task) => { + match (*task).stdout_handle { + Some(ref mut handle) => f(*handle), + None => { + let handle = ~LineBufferedWriter::new(stdout()); + let mut handle = handle as ~Writer; + f(handle); + (*task).stdout_handle = Some(handle); + } + } + } - match (*task).stdout_handle { - Some(ref mut handle) => f(*handle), None => { - let handle = stdout(); - let mut handle = ~LineBufferedWriter::new(handle) as ~Writer; - f(handle); - (*task).stdout_handle = Some(handle); + let mut io = stdout(); + f(&mut io as &mut Writer); } } } diff --git a/src/libstd/rt/basic.rs b/src/libstd/rt/basic.rs index a8f762c4c8fc1..42ecbf5dc7856 100644 --- a/src/libstd/rt/basic.rs +++ b/src/libstd/rt/basic.rs @@ -18,6 +18,7 @@ use cast; use rt::rtio::{EventLoop, IoFactory, RemoteCallback, PausibleIdleCallback, Callback}; use unstable::sync::Exclusive; +use io::native; use util; /// This is the only exported function from this module. @@ -30,7 +31,8 @@ struct BasicLoop { idle: Option<*mut BasicPausible>, // only one is allowed remotes: ~[(uint, ~Callback)], next_remote: uint, - messages: Exclusive<~[Message]> + messages: Exclusive<~[Message]>, + io: ~IoFactory, } enum Message { RunRemote(uint), RemoveRemote(uint) } @@ -54,6 +56,7 @@ impl BasicLoop { next_remote: 0, remotes: ~[], messages: Exclusive::new(~[]), + io: ~native::IoFactory as ~IoFactory, } } @@ -167,8 +170,9 @@ impl EventLoop for BasicLoop { ~BasicRemote::new(self.messages.clone(), id) as ~RemoteCallback } - /// This has no bindings for local I/O - fn io<'a>(&'a mut self, _: &fn(&'a mut IoFactory)) {} + fn io<'a>(&'a mut self, f: &fn(&'a mut IoFactory)) { + f(self.io) + } } struct BasicRemote { diff --git a/src/libstd/rt/rtio.rs b/src/libstd/rt/rtio.rs index ca1fd413a5628..35fb8baa6ce68 100644 --- a/src/libstd/rt/rtio.rs +++ b/src/libstd/rt/rtio.rs @@ -78,20 +78,28 @@ pub enum CloseBehavior { pub fn with_local_io(f: &fn(&mut IoFactory) -> Option) -> Option { use rt::sched::Scheduler; use rt::local::Local; - use io::{io_error, standard_error, IoUnavailable}; + use io::native; unsafe { - let sched: *mut Scheduler = Local::unsafe_borrow(); - let mut io = None; - (*sched).event_loop.io(|i| io = Some(i)); - match io { - Some(io) => f(io), - None => { - io_error::cond.raise(standard_error(IoUnavailable)); - None + // First, attempt to use the local scheduler's I/O services + let sched: Option<*mut Scheduler> = Local::try_unsafe_borrow(); + match sched { + Some(sched) => { + let mut io = None; + (*sched).event_loop.io(|i| io = Some(i)); + match io { + Some(io) => return f(io), + None => {} + } } + None => {} } } + + // If we don't have a scheduler or the scheduler doesn't have I/O services, + // then fall back to the native I/O services. + let mut io = native::IoFactory; + f(&mut io as &mut IoFactory) } pub trait IoFactory { diff --git a/src/libstd/rt/util.rs b/src/libstd/rt/util.rs index 42546cb3c6a99..b5b76f6af6606 100644 --- a/src/libstd/rt/util.rs +++ b/src/libstd/rt/util.rs @@ -68,25 +68,11 @@ pub fn default_sched_threads() -> uint { } pub fn dumb_println(args: &fmt::Arguments) { - use io::native::stdio::stderr; - use io::{Writer, io_error, ResourceUnavailable}; - use rt::task::Task; - use rt::local::Local; - - let mut out = stderr(); - if Local::exists(None::) { - 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); - } - } - } else { - fmt::writeln(&mut out as &mut Writer, args); - } + use io::native::file::FileDesc; + use io; + use libc; + let mut out = FileDesc::new(libc::STDERR_FILENO, false); + fmt::writeln(&mut out as &mut io::Writer, args); } pub fn abort(msg: &str) -> ! { diff --git a/src/test/bench/shootout-k-nucleotide-pipes.rs b/src/test/bench/shootout-k-nucleotide-pipes.rs index 0937c71f91f4c..5cc8e161f5ffd 100644 --- a/src/test/bench/shootout-k-nucleotide-pipes.rs +++ b/src/test/bench/shootout-k-nucleotide-pipes.rs @@ -154,7 +154,7 @@ fn make_sequence_processor(sz: uint, // given a FASTA file on stdin, process sequence THREE fn main() { use std::io::Reader; - use std::io::native::stdio; + use std::io::stdio; use std::io::mem::MemReader; use std::io::buffered::BufferedReader; diff --git a/src/test/run-pass/native-print-no-runtime.rs b/src/test/run-pass/native-print-no-runtime.rs new file mode 100644 index 0000000000000..b468b53d2f75e --- /dev/null +++ b/src/test/run-pass/native-print-no-runtime.rs @@ -0,0 +1,19 @@ +// 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. + +// xfail-fast + +#[no_uv]; + +#[start] +fn main(_: int, _: **u8) -> int { + println!("hello"); + 0 +} diff --git a/src/test/run-pass/native-print-no-uv.rs b/src/test/run-pass/native-print-no-uv.rs new file mode 100644 index 0000000000000..d3b6d6059849d --- /dev/null +++ b/src/test/run-pass/native-print-no-uv.rs @@ -0,0 +1,17 @@ +// 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. + +// xfail-fast + +#[no_uv]; + +fn main() { + println!("hello"); +}