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:
commit
58b5c618cf
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
200
src/libstd/io/native/mod.rs
Normal 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())
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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() }
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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) -> ! {
|
||||
|
@ -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;
|
||||
|
||||
|
19
src/test/run-pass/native-print-no-runtime.rs
Normal file
19
src/test/run-pass/native-print-no-runtime.rs
Normal 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
|
||||
}
|
17
src/test/run-pass/native-print-no-uv.rs
Normal file
17
src/test/run-pass/native-print-no-uv.rs
Normal 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");
|
||||
}
|
Loading…
Reference in New Issue
Block a user