auto merge of #10457 : alexcrichton/rust/native-io, r=brson

This commit re-organizes the io::native module slightly in order to have a
working implementation of rtio::IoFactory which uses native implementations. The
goal is to seamlessly multiplex among libuv/native implementations wherever
necessary.

Right now most of the native I/O is unimplemented, but we have existing bindings
for file descriptors and processes which have been hooked up. What this means is
that you can now invoke println!() from libstd with no local task, no local
scheduler, and even without libuv.

There's still plenty of work to do on the native I/O factory, but this is the
first steps into making it an official portion of the standard library. I don't
expect anyone to reach into io::native directly, but rather only std::io
primitives will be used. Each std::io interface seamlessly falls back onto the
native I/O implementation if the local scheduler doesn't have a libuv one
(hurray trait ojects!)
This commit is contained in:
bors 2013-11-13 19:46:18 -08:00
commit 58b5c618cf
13 changed files with 479 additions and 359 deletions

View File

@ -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);
}

View File

@ -306,24 +306,7 @@
/// 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;

View File

@ -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<uint> {
fn inner_read(&mut self, buf: &mut [u8]) -> Result<uint, IoError> {
#[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 @@ fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
}
};
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 @@ fn write(&mut self, buf: &[u8]) {
}
};
if ret < 0 {
raise_error();
Err(super::last_error())
} else {
Ok(())
}
}
}
impl io::Reader for FileDesc {
fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
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<int, IoError> {
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<int, IoError> {
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<u64, IoError> {
Err(super::unimpl())
}
fn tell(&self) -> Result<u64, IoError> {
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<uint, IoError> {
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<uint, IoError> {
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<uint> {
impl rtio::RtioFileStream for CFile {
fn read(&mut self, buf: &mut [u8]) -> Result<int, IoError> {
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 @@ fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
}
};
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 @@ fn write(&mut self, buf: &[u8]) {
}
};
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<int, IoError> {
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<u64, IoError> {
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<u64, IoError> {
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 @@ fn drop(&mut self) {
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 @@ fn test_file_desc() {
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 @@ fn test_file_desc() {
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 @@ fn test_cfile() {
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);

200
src/libstd/io/native/mod.rs Normal file
View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! 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<T> = Result<T, IoError>;
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<ai::Hint>) -> 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<io::FileStat> {
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<io::FileStat> {
Err(unimpl())
}
fn fs_chown(&mut self, _path: &CString, _uid: int, _gid: int) -> IoResult<()> {
Err(unimpl())
}
fn fs_readlink(&mut self, _path: &CString) -> IoResult<Path> {
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<Signum>)
-> IoResult<~RtioSignal> {
Err(unimpl())
}
}

View File

@ -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<file::FileDesc>,
/// Currently known stdout of the child, if any
priv output: Option<file::FileDesc>,
/// Currently known stderr of the child, if any
priv error: Option<file::FileDesc>,
/// None until finish() is called.
priv exit_code: Option<int>,
}
@ -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<file::fd_t>,
stdout: Option<file::fd_t>,
stderr: Option<file::fd_t>) -> 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<file::FileDesc>]), io::IoError>
{
// right now we only handle stdin/stdout/stderr.
if config.io.len() > 3 {
return Err(super::unimpl());
}
let res = spawn_process_os(prog, args, env, cwd,
in_fd, out_fd, err_fd);
fn get_io(io: &[p::StdioContainer],
ret: &mut ~[Option<file::FileDesc>],
idx: uint) -> (Option<os::Pipe>, 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 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 @@ pub fn new(prog: &str, args: &[~str], env: Option<~[(~str, ~str)]>,
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,
}
Ok((Process { pid: res.pid, handle: res.handle, exit_code: None }, ret_io))
}
}
impl rtio::RtioProcess for Process {
fn id(&self) -> pid_t { self.pid }
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
}
/// 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)
}
/**
* 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)
}
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;
}
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 @@ pub fn signal(&mut self, signum: int) -> Result<(), io::IoError> {
#[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 @@ unsafe fn killpid(pid: pid_t, signal: int) -> Result<(), io::IoError> {
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);
}
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use 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<uint> { 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() }
}

View File

@ -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);
}
}
}

View File

@ -18,6 +18,7 @@
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 @@ fn new() -> BasicLoop {
next_remote: 0,
remotes: ~[],
messages: Exclusive::new(~[]),
io: ~native::IoFactory as ~IoFactory,
}
}
@ -167,8 +170,9 @@ fn remote_callback(&mut self, f: ~Callback) -> ~RemoteCallback {
~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 {

View File

@ -78,20 +78,28 @@ pub enum CloseBehavior {
pub fn with_local_io<T>(f: &fn(&mut IoFactory) -> Option<T>) -> Option<T> {
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 {

View File

@ -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::<Task>) {
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) -> ! {

View File

@ -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;

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// xfail-fast
#[no_uv];
#[start]
fn main(_: int, _: **u8) -> int {
println!("hello");
0
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// xfail-fast
#[no_uv];
fn main() {
println!("hello");
}