auto merge of #18557 : aturon/rust/io-removal, r=alexcrichton
This PR includes a sequence of commits that gradually dismantles the `librustrt` `rtio` system -- the main trait previously used to abstract over green and native io. It also largely dismantles `libnative`, moving much of its code into `libstd` and refactoring as it does so. TL;DR: * Before this PR: `rustc hello.rs && wc -c hello` produces 715,996 * After this PR: `rustc hello.rs && wc -c hello` produces 368,100 That is, this PR reduces the footprint of hello world by ~50%. This is a major step toward #17325 (i.e. toward implementing the [runtime removal RFC](https://github.com/rust-lang/rfcs/pull/230).) What remains is to pull out the scheduling, synchronization and task infrastructure, and to remove `libgreen`. These will be done soon in a follow-up PR. Part of the work here is eliminating the `rtio` abstraction, which in many cases means bringing the implementation of io closer to the actual API presented in `std::io`. Another aspect of this PR is the creation of two new, *private* modules within `std` that implement io: * The `sys` module, which represents a platform-specific implementation of a number of low-level abstractions that are used directly within `std::io` and `std::os`. These "abstractions" are left largely the same as they were in `libnative` (except for the removal of `Arc` in file descriptors), but they are expected to evolve greatly over time. Organizationally, there are `sys/unix/` and `sys/windows/` directories which both implement the entire `sys` module hierarchy; this means that nearly all of the platform-specific code is isolated and you can get a handle on each platform in isolation. * The `sys_common` module, which is rooted at `sys/common`, and provides a few pieces of private, low-level, but cross-platform functionality. In the long term, the `sys` modules will provide hooks for exposing high-level platform-specific APIs as part of `libstd`. The first such API will be access to file descriptors from `std::io` abstractions, but a bit of design work remains before that step can be taken. The `sys_common` module includes some traits (like `AsFileDesc`) which allow communication of private details between modules in disparate locations in the hierarchy; this helps overcome the relatively simple hierarchical privacy system in Rust. To emphasize: the organization in `sys` is *very preliminary* and the main goal was to migrate away from `rtio` as quickly and simply as possible. The design will certainly evolve over time, and all of the details are currently private. Along the way, this PR also entirely removes signal handling, since it was only supported on `librustuv` which was removed a while ago. Because of the removal of APIs from `libnative` and `librustrt`, and the removal of signal handling, this is a: [breaking-change] Some of these APIs will return in public from from `std` over time. r? @alexcrichton
This commit is contained in:
commit
16d80de231
51 changed files with 4051 additions and 3798 deletions
|
@ -18,7 +18,7 @@
|
|||
use alloc::arc::Arc;
|
||||
use std::sync::atomic;
|
||||
use std::mem;
|
||||
use std::rt::rtio::{EventLoop, IoFactory, RemoteCallback};
|
||||
use std::rt::rtio::{EventLoop, RemoteCallback};
|
||||
use std::rt::rtio::{PausableIdleCallback, Callback};
|
||||
use std::rt::exclusive::Exclusive;
|
||||
|
||||
|
@ -150,8 +150,6 @@ impl EventLoop for BasicLoop {
|
|||
Box<RemoteCallback + Send>
|
||||
}
|
||||
|
||||
fn io<'a>(&'a mut self) -> Option<&'a mut IoFactory> { None }
|
||||
|
||||
fn has_active_io(&self) -> bool { false }
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@ use std::mem;
|
|||
use std::rt::Runtime;
|
||||
use std::rt::local::Local;
|
||||
use std::rt::mutex::NativeMutex;
|
||||
use std::rt::rtio;
|
||||
use std::rt::task::{Task, BlockedTask, TaskOpts};
|
||||
|
||||
struct SimpleTask {
|
||||
|
@ -79,9 +78,10 @@ impl Runtime for SimpleTask {
|
|||
_f: proc():Send) {
|
||||
panic!()
|
||||
}
|
||||
fn local_io<'a>(&'a mut self) -> Option<rtio::LocalIo<'a>> { None }
|
||||
|
||||
fn stack_bounds(&self) -> (uint, uint) { panic!() }
|
||||
fn stack_guard(&self) -> Option<uint> { panic!() }
|
||||
|
||||
fn can_block(&self) -> bool { true }
|
||||
fn wrap(self: Box<SimpleTask>) -> Box<Any+'static> { panic!() }
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ use std::raw;
|
|||
use std::rt::Runtime;
|
||||
use std::rt::local::Local;
|
||||
use std::rt::mutex::NativeMutex;
|
||||
use std::rt::rtio;
|
||||
use std::rt::stack;
|
||||
use std::rt::task::{Task, BlockedTask, TaskOpts};
|
||||
use std::rt;
|
||||
|
@ -468,14 +467,6 @@ impl Runtime for GreenTask {
|
|||
sched.run_task(me, sibling)
|
||||
}
|
||||
|
||||
// Local I/O is provided by the scheduler's event loop
|
||||
fn local_io<'a>(&'a mut self) -> Option<rtio::LocalIo<'a>> {
|
||||
match self.sched.as_mut().unwrap().event_loop.io() {
|
||||
Some(io) => Some(rtio::LocalIo::new(io)),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn stack_bounds(&self) -> (uint, uint) {
|
||||
let c = self.coroutine.as_ref()
|
||||
.expect("GreenTask.stack_bounds called without a coroutine");
|
||||
|
|
|
@ -1,554 +0,0 @@
|
|||
// Copyright 2013-2014 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.
|
||||
|
||||
//! Blocking posix-based file I/O
|
||||
|
||||
use alloc::arc::Arc;
|
||||
use libc::{mod, c_int, c_void};
|
||||
use std::c_str::CString;
|
||||
use std::mem;
|
||||
use std::rt::rtio::{mod, IoResult};
|
||||
|
||||
use io::{retry, keep_going};
|
||||
use io::util;
|
||||
|
||||
pub type fd_t = libc::c_int;
|
||||
|
||||
struct Inner {
|
||||
fd: fd_t,
|
||||
close_on_drop: bool,
|
||||
}
|
||||
|
||||
pub struct FileDesc {
|
||||
inner: Arc<Inner>
|
||||
}
|
||||
|
||||
impl FileDesc {
|
||||
/// Create a `FileDesc` from an open C file descriptor.
|
||||
///
|
||||
/// The `FileDesc` will take ownership of the specified file descriptor and
|
||||
/// close it upon destruction if the `close_on_drop` flag is true, otherwise
|
||||
/// it will not close the file descriptor when this `FileDesc` is dropped.
|
||||
///
|
||||
/// Note that all I/O operations done on this object will be *blocking*, but
|
||||
/// they do not require the runtime to be active.
|
||||
pub fn new(fd: fd_t, close_on_drop: bool) -> FileDesc {
|
||||
FileDesc { inner: Arc::new(Inner {
|
||||
fd: fd,
|
||||
close_on_drop: close_on_drop
|
||||
}) }
|
||||
}
|
||||
|
||||
// FIXME(#10465) these functions should not be public, but anything in
|
||||
// native::io wanting to use them is forced to have all the
|
||||
// rtio traits in scope
|
||||
pub fn inner_read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
|
||||
let ret = retry(|| unsafe {
|
||||
libc::read(self.fd(),
|
||||
buf.as_mut_ptr() as *mut libc::c_void,
|
||||
buf.len() as libc::size_t)
|
||||
});
|
||||
if ret == 0 {
|
||||
Err(util::eof())
|
||||
} else if ret < 0 {
|
||||
Err(super::last_error())
|
||||
} else {
|
||||
Ok(ret as uint)
|
||||
}
|
||||
}
|
||||
pub fn inner_write(&mut self, buf: &[u8]) -> IoResult<()> {
|
||||
let ret = keep_going(buf, |buf, len| {
|
||||
unsafe {
|
||||
libc::write(self.fd(), buf as *const libc::c_void,
|
||||
len as libc::size_t) as i64
|
||||
}
|
||||
});
|
||||
if ret < 0 {
|
||||
Err(super::last_error())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fd(&self) -> fd_t { self.inner.fd }
|
||||
}
|
||||
|
||||
impl rtio::RtioFileStream for FileDesc {
|
||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<int> {
|
||||
self.inner_read(buf).map(|i| i as int)
|
||||
}
|
||||
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
||||
self.inner_write(buf)
|
||||
}
|
||||
fn pread(&mut self, buf: &mut [u8], offset: u64) -> IoResult<int> {
|
||||
match retry(|| unsafe {
|
||||
libc::pread(self.fd(), buf.as_ptr() as *mut _,
|
||||
buf.len() as libc::size_t,
|
||||
offset as libc::off_t)
|
||||
}) {
|
||||
-1 => Err(super::last_error()),
|
||||
n => Ok(n as int)
|
||||
}
|
||||
}
|
||||
fn pwrite(&mut self, buf: &[u8], offset: u64) -> IoResult<()> {
|
||||
super::mkerr_libc(retry(|| unsafe {
|
||||
libc::pwrite(self.fd(), buf.as_ptr() as *const _,
|
||||
buf.len() as libc::size_t, offset as libc::off_t)
|
||||
}))
|
||||
}
|
||||
fn seek(&mut self, pos: i64, whence: rtio::SeekStyle) -> IoResult<u64> {
|
||||
let whence = match whence {
|
||||
rtio::SeekSet => libc::SEEK_SET,
|
||||
rtio::SeekEnd => libc::SEEK_END,
|
||||
rtio::SeekCur => libc::SEEK_CUR,
|
||||
};
|
||||
let n = unsafe { libc::lseek(self.fd(), pos as libc::off_t, whence) };
|
||||
if n < 0 {
|
||||
Err(super::last_error())
|
||||
} else {
|
||||
Ok(n as u64)
|
||||
}
|
||||
}
|
||||
fn tell(&self) -> IoResult<u64> {
|
||||
let n = unsafe { libc::lseek(self.fd(), 0, libc::SEEK_CUR) };
|
||||
if n < 0 {
|
||||
Err(super::last_error())
|
||||
} else {
|
||||
Ok(n as u64)
|
||||
}
|
||||
}
|
||||
fn fsync(&mut self) -> IoResult<()> {
|
||||
super::mkerr_libc(retry(|| unsafe { libc::fsync(self.fd()) }))
|
||||
}
|
||||
fn datasync(&mut self) -> IoResult<()> {
|
||||
return super::mkerr_libc(os_datasync(self.fd()));
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
fn os_datasync(fd: c_int) -> c_int {
|
||||
unsafe { libc::fcntl(fd, libc::F_FULLFSYNC) }
|
||||
}
|
||||
#[cfg(target_os = "linux")]
|
||||
fn os_datasync(fd: c_int) -> c_int {
|
||||
retry(|| unsafe { libc::fdatasync(fd) })
|
||||
}
|
||||
#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "linux")))]
|
||||
fn os_datasync(fd: c_int) -> c_int {
|
||||
retry(|| unsafe { libc::fsync(fd) })
|
||||
}
|
||||
}
|
||||
fn truncate(&mut self, offset: i64) -> IoResult<()> {
|
||||
super::mkerr_libc(retry(|| unsafe {
|
||||
libc::ftruncate(self.fd(), offset as libc::off_t)
|
||||
}))
|
||||
}
|
||||
|
||||
fn fstat(&mut self) -> IoResult<rtio::FileStat> {
|
||||
let mut stat: libc::stat = unsafe { mem::zeroed() };
|
||||
match unsafe { libc::fstat(self.fd(), &mut stat) } {
|
||||
0 => Ok(mkstat(&stat)),
|
||||
_ => Err(super::last_error()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl rtio::RtioPipe for FileDesc {
|
||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
|
||||
self.inner_read(buf)
|
||||
}
|
||||
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
||||
self.inner_write(buf)
|
||||
}
|
||||
fn clone(&self) -> Box<rtio::RtioPipe + Send> {
|
||||
box FileDesc { inner: self.inner.clone() } as Box<rtio::RtioPipe + Send>
|
||||
}
|
||||
|
||||
// Only supported on named pipes currently. Note that this doesn't have an
|
||||
// impact on the std::io primitives, this is never called via
|
||||
// std::io::PipeStream. If the functionality is exposed in the future, then
|
||||
// these methods will need to be implemented.
|
||||
fn close_read(&mut self) -> IoResult<()> {
|
||||
Err(super::unimpl())
|
||||
}
|
||||
fn close_write(&mut self) -> IoResult<()> {
|
||||
Err(super::unimpl())
|
||||
}
|
||||
fn set_timeout(&mut self, _t: Option<u64>) {}
|
||||
fn set_read_timeout(&mut self, _t: Option<u64>) {}
|
||||
fn set_write_timeout(&mut self, _t: Option<u64>) {}
|
||||
}
|
||||
|
||||
impl rtio::RtioTTY for FileDesc {
|
||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
|
||||
self.inner_read(buf)
|
||||
}
|
||||
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
||||
self.inner_write(buf)
|
||||
}
|
||||
fn set_raw(&mut self, _raw: bool) -> IoResult<()> {
|
||||
Err(super::unimpl())
|
||||
}
|
||||
fn get_winsize(&mut self) -> IoResult<(int, int)> {
|
||||
Err(super::unimpl())
|
||||
}
|
||||
fn isatty(&self) -> bool { false }
|
||||
}
|
||||
|
||||
impl Drop for Inner {
|
||||
fn drop(&mut self) {
|
||||
// closing stdio file handles makes no sense, so never do it. Also, note
|
||||
// that errors are ignored when closing a file descriptor. The reason
|
||||
// for this is that if an error occurs we don't actually know if the
|
||||
// file descriptor was closed or not, and if we retried (for something
|
||||
// like EINTR), we might close another valid file descriptor (opened
|
||||
// after we closed ours.
|
||||
if self.close_on_drop && self.fd > libc::STDERR_FILENO {
|
||||
let n = unsafe { libc::close(self.fd) };
|
||||
if n != 0 {
|
||||
println!("error {} when closing file descriptor {}", n,
|
||||
self.fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CFile {
|
||||
file: *mut libc::FILE,
|
||||
fd: FileDesc,
|
||||
}
|
||||
|
||||
impl CFile {
|
||||
/// Create a `CFile` from an open `FILE` pointer.
|
||||
///
|
||||
/// The `CFile` takes ownership of the `FILE` pointer and will close it upon
|
||||
/// destruction.
|
||||
pub fn new(file: *mut libc::FILE) -> CFile {
|
||||
CFile {
|
||||
file: file,
|
||||
fd: FileDesc::new(unsafe { libc::fileno(file) }, false)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flush(&mut self) -> IoResult<()> {
|
||||
super::mkerr_libc(retry(|| unsafe { libc::fflush(self.file) }))
|
||||
}
|
||||
}
|
||||
|
||||
impl rtio::RtioFileStream for CFile {
|
||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<int> {
|
||||
let ret = keep_going(buf, |buf, len| {
|
||||
unsafe {
|
||||
libc::fread(buf as *mut libc::c_void, 1, len as libc::size_t,
|
||||
self.file) as i64
|
||||
}
|
||||
});
|
||||
if ret == 0 {
|
||||
Err(util::eof())
|
||||
} else if ret < 0 {
|
||||
Err(super::last_error())
|
||||
} else {
|
||||
Ok(ret as int)
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
||||
let ret = keep_going(buf, |buf, len| {
|
||||
unsafe {
|
||||
libc::fwrite(buf as *const libc::c_void, 1, len as libc::size_t,
|
||||
self.file) as i64
|
||||
}
|
||||
});
|
||||
if ret < 0 {
|
||||
Err(super::last_error())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn pread(&mut self, buf: &mut [u8], offset: u64) -> IoResult<int> {
|
||||
self.flush().and_then(|()| self.fd.pread(buf, offset))
|
||||
}
|
||||
fn pwrite(&mut self, buf: &[u8], offset: u64) -> IoResult<()> {
|
||||
self.flush().and_then(|()| self.fd.pwrite(buf, offset))
|
||||
}
|
||||
fn seek(&mut self, pos: i64, style: rtio::SeekStyle) -> IoResult<u64> {
|
||||
let whence = match style {
|
||||
rtio::SeekSet => libc::SEEK_SET,
|
||||
rtio::SeekEnd => libc::SEEK_END,
|
||||
rtio::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)
|
||||
}
|
||||
}
|
||||
fn tell(&self) -> IoResult<u64> {
|
||||
let ret = unsafe { libc::ftell(self.file) };
|
||||
if ret < 0 {
|
||||
Err(super::last_error())
|
||||
} else {
|
||||
Ok(ret as u64)
|
||||
}
|
||||
}
|
||||
fn fsync(&mut self) -> IoResult<()> {
|
||||
self.flush().and_then(|()| self.fd.fsync())
|
||||
}
|
||||
fn datasync(&mut self) -> IoResult<()> {
|
||||
self.flush().and_then(|()| self.fd.datasync())
|
||||
}
|
||||
fn truncate(&mut self, offset: i64) -> IoResult<()> {
|
||||
self.flush().and_then(|()| self.fd.truncate(offset))
|
||||
}
|
||||
|
||||
fn fstat(&mut self) -> IoResult<rtio::FileStat> {
|
||||
self.flush().and_then(|()| self.fd.fstat())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for CFile {
|
||||
fn drop(&mut self) {
|
||||
unsafe { let _ = libc::fclose(self.file); }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open(path: &CString, fm: rtio::FileMode, fa: rtio::FileAccess)
|
||||
-> IoResult<FileDesc>
|
||||
{
|
||||
let flags = match fm {
|
||||
rtio::Open => 0,
|
||||
rtio::Append => libc::O_APPEND,
|
||||
rtio::Truncate => libc::O_TRUNC,
|
||||
};
|
||||
// Opening with a write permission must silently create the file.
|
||||
let (flags, mode) = match fa {
|
||||
rtio::Read => (flags | libc::O_RDONLY, 0),
|
||||
rtio::Write => (flags | libc::O_WRONLY | libc::O_CREAT,
|
||||
libc::S_IRUSR | libc::S_IWUSR),
|
||||
rtio::ReadWrite => (flags | libc::O_RDWR | libc::O_CREAT,
|
||||
libc::S_IRUSR | libc::S_IWUSR),
|
||||
};
|
||||
|
||||
match retry(|| unsafe { libc::open(path.as_ptr(), flags, mode) }) {
|
||||
-1 => Err(super::last_error()),
|
||||
fd => Ok(FileDesc::new(fd, true)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mkdir(p: &CString, mode: uint) -> IoResult<()> {
|
||||
super::mkerr_libc(unsafe { libc::mkdir(p.as_ptr(), mode as libc::mode_t) })
|
||||
}
|
||||
|
||||
pub fn readdir(p: &CString) -> IoResult<Vec<CString>> {
|
||||
use libc::{dirent_t};
|
||||
use libc::{opendir, readdir_r, closedir};
|
||||
|
||||
fn prune(root: &CString, dirs: Vec<Path>) -> Vec<CString> {
|
||||
let root = unsafe { CString::new(root.as_ptr(), false) };
|
||||
let root = Path::new(root);
|
||||
|
||||
dirs.into_iter().filter(|path| {
|
||||
path.as_vec() != b"." && path.as_vec() != b".."
|
||||
}).map(|path| root.join(path).to_c_str()).collect()
|
||||
}
|
||||
|
||||
extern {
|
||||
fn rust_dirent_t_size() -> libc::c_int;
|
||||
fn rust_list_dir_val(ptr: *mut dirent_t) -> *const libc::c_char;
|
||||
}
|
||||
|
||||
let size = unsafe { rust_dirent_t_size() };
|
||||
let mut buf = Vec::<u8>::with_capacity(size as uint);
|
||||
let ptr = buf.as_mut_slice().as_mut_ptr() as *mut dirent_t;
|
||||
|
||||
let dir_ptr = unsafe {opendir(p.as_ptr())};
|
||||
|
||||
if dir_ptr as uint != 0 {
|
||||
let mut paths = vec!();
|
||||
let mut entry_ptr = 0 as *mut dirent_t;
|
||||
while unsafe { readdir_r(dir_ptr, ptr, &mut entry_ptr) == 0 } {
|
||||
if entry_ptr.is_null() { break }
|
||||
let cstr = unsafe {
|
||||
CString::new(rust_list_dir_val(entry_ptr), false)
|
||||
};
|
||||
paths.push(Path::new(cstr));
|
||||
}
|
||||
assert_eq!(unsafe { closedir(dir_ptr) }, 0);
|
||||
Ok(prune(p, paths))
|
||||
} else {
|
||||
Err(super::last_error())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unlink(p: &CString) -> IoResult<()> {
|
||||
super::mkerr_libc(unsafe { libc::unlink(p.as_ptr()) })
|
||||
}
|
||||
|
||||
pub fn rename(old: &CString, new: &CString) -> IoResult<()> {
|
||||
super::mkerr_libc(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) })
|
||||
}
|
||||
|
||||
pub fn chmod(p: &CString, mode: uint) -> IoResult<()> {
|
||||
super::mkerr_libc(retry(|| unsafe {
|
||||
libc::chmod(p.as_ptr(), mode as libc::mode_t)
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn rmdir(p: &CString) -> IoResult<()> {
|
||||
super::mkerr_libc(unsafe { libc::rmdir(p.as_ptr()) })
|
||||
}
|
||||
|
||||
pub fn chown(p: &CString, uid: int, gid: int) -> IoResult<()> {
|
||||
super::mkerr_libc(retry(|| unsafe {
|
||||
libc::chown(p.as_ptr(), uid as libc::uid_t,
|
||||
gid as libc::gid_t)
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn readlink(p: &CString) -> IoResult<CString> {
|
||||
let p = p.as_ptr();
|
||||
let mut len = unsafe { libc::pathconf(p as *mut _, libc::_PC_NAME_MAX) };
|
||||
if len == -1 {
|
||||
len = 1024; // FIXME: read PATH_MAX from C ffi?
|
||||
}
|
||||
let mut buf: Vec<u8> = Vec::with_capacity(len as uint);
|
||||
match unsafe {
|
||||
libc::readlink(p, buf.as_ptr() as *mut libc::c_char,
|
||||
len as libc::size_t) as libc::c_int
|
||||
} {
|
||||
-1 => Err(super::last_error()),
|
||||
n => {
|
||||
assert!(n > 0);
|
||||
unsafe { buf.set_len(n as uint); }
|
||||
Ok(buf.as_slice().to_c_str())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn symlink(src: &CString, dst: &CString) -> IoResult<()> {
|
||||
super::mkerr_libc(unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) })
|
||||
}
|
||||
|
||||
pub fn link(src: &CString, dst: &CString) -> IoResult<()> {
|
||||
super::mkerr_libc(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })
|
||||
}
|
||||
|
||||
fn mkstat(stat: &libc::stat) -> rtio::FileStat {
|
||||
// FileStat times are in milliseconds
|
||||
fn mktime(secs: u64, nsecs: u64) -> u64 { secs * 1000 + nsecs / 1000000 }
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
fn flags(stat: &libc::stat) -> u64 { stat.st_flags as u64 }
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn flags(_stat: &libc::stat) -> u64 { 0 }
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
fn gen(stat: &libc::stat) -> u64 { stat.st_gen as u64 }
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn gen(_stat: &libc::stat) -> u64 { 0 }
|
||||
|
||||
rtio::FileStat {
|
||||
size: stat.st_size as u64,
|
||||
kind: stat.st_mode as u64,
|
||||
perm: stat.st_mode as u64,
|
||||
created: mktime(stat.st_ctime as u64, stat.st_ctime_nsec as u64),
|
||||
modified: mktime(stat.st_mtime as u64, stat.st_mtime_nsec as u64),
|
||||
accessed: mktime(stat.st_atime as u64, stat.st_atime_nsec as u64),
|
||||
device: stat.st_dev as u64,
|
||||
inode: stat.st_ino as u64,
|
||||
rdev: stat.st_rdev as u64,
|
||||
nlink: stat.st_nlink as u64,
|
||||
uid: stat.st_uid as u64,
|
||||
gid: stat.st_gid as u64,
|
||||
blksize: stat.st_blksize as u64,
|
||||
blocks: stat.st_blocks as u64,
|
||||
flags: flags(stat),
|
||||
gen: gen(stat),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stat(p: &CString) -> IoResult<rtio::FileStat> {
|
||||
let mut stat: libc::stat = unsafe { mem::zeroed() };
|
||||
match unsafe { libc::stat(p.as_ptr(), &mut stat) } {
|
||||
0 => Ok(mkstat(&stat)),
|
||||
_ => Err(super::last_error()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lstat(p: &CString) -> IoResult<rtio::FileStat> {
|
||||
let mut stat: libc::stat = unsafe { mem::zeroed() };
|
||||
match unsafe { libc::lstat(p.as_ptr(), &mut stat) } {
|
||||
0 => Ok(mkstat(&stat)),
|
||||
_ => Err(super::last_error()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn utime(p: &CString, atime: u64, mtime: u64) -> IoResult<()> {
|
||||
let buf = libc::utimbuf {
|
||||
actime: (atime / 1000) as libc::time_t,
|
||||
modtime: (mtime / 1000) as libc::time_t,
|
||||
};
|
||||
super::mkerr_libc(unsafe { libc::utime(p.as_ptr(), &buf) })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{CFile, FileDesc};
|
||||
use libc;
|
||||
use std::os;
|
||||
use std::rt::rtio::{RtioFileStream, SeekSet};
|
||||
|
||||
#[cfg_attr(target_os = "freebsd", ignore)] // hmm, maybe pipes have a tiny buffer
|
||||
#[test]
|
||||
fn test_file_desc() {
|
||||
// Run this test with some pipes so we don't have to mess around with
|
||||
// opening or closing files.
|
||||
let os::Pipe { reader, writer } = unsafe { os::pipe().unwrap() };
|
||||
let mut reader = FileDesc::new(reader, true);
|
||||
let mut writer = FileDesc::new(writer, true);
|
||||
|
||||
writer.inner_write(b"test").ok().unwrap();
|
||||
let mut buf = [0u8, ..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);
|
||||
assert_eq!(buf[3], 't' as u8);
|
||||
}
|
||||
r => panic!("invalid read: {}", r),
|
||||
}
|
||||
|
||||
assert!(writer.inner_read(buf).is_err());
|
||||
assert!(reader.inner_write(buf).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cfile() {
|
||||
unsafe {
|
||||
let f = libc::tmpfile();
|
||||
assert!(!f.is_null());
|
||||
let mut file = CFile::new(f);
|
||||
|
||||
file.write(b"test").ok().unwrap();
|
||||
let mut buf = [0u8, ..4];
|
||||
let _ = file.seek(0, SeekSet).ok().unwrap();
|
||||
match file.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);
|
||||
assert_eq!(buf[3], 't' as u8);
|
||||
}
|
||||
r => panic!("invalid read: {}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,311 +0,0 @@
|
|||
// Copyright 2013-2014 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.
|
||||
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use libc::{mod, c_int};
|
||||
use std::c_str::CString;
|
||||
use std::os;
|
||||
use std::rt::rtio::{mod, IoResult, IoError};
|
||||
use std::num;
|
||||
|
||||
// Local re-exports
|
||||
pub use self::file::FileDesc;
|
||||
pub use self::process::Process;
|
||||
|
||||
mod helper_thread;
|
||||
|
||||
// Native I/O implementations
|
||||
pub mod addrinfo;
|
||||
pub mod net;
|
||||
pub mod process;
|
||||
mod util;
|
||||
|
||||
#[cfg(unix)]
|
||||
#[path = "file_unix.rs"]
|
||||
pub mod file;
|
||||
#[cfg(windows)]
|
||||
#[path = "file_windows.rs"]
|
||||
pub mod file;
|
||||
|
||||
#[cfg(any(target_os = "macos",
|
||||
target_os = "ios",
|
||||
target_os = "freebsd",
|
||||
target_os = "dragonfly",
|
||||
target_os = "android",
|
||||
target_os = "linux"))]
|
||||
#[path = "timer_unix.rs"]
|
||||
pub mod timer;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[path = "timer_windows.rs"]
|
||||
pub mod timer;
|
||||
|
||||
#[cfg(unix)]
|
||||
#[path = "pipe_unix.rs"]
|
||||
pub mod pipe;
|
||||
|
||||
#[cfg(windows)]
|
||||
#[path = "pipe_windows.rs"]
|
||||
pub mod pipe;
|
||||
|
||||
#[cfg(windows)]
|
||||
#[path = "tty_windows.rs"]
|
||||
mod tty;
|
||||
|
||||
#[cfg(unix)] #[path = "c_unix.rs"] mod c;
|
||||
#[cfg(windows)] #[path = "c_windows.rs"] mod c;
|
||||
|
||||
fn unimpl() -> IoError {
|
||||
#[cfg(unix)] use libc::ENOSYS as ERROR;
|
||||
#[cfg(windows)] use libc::ERROR_CALL_NOT_IMPLEMENTED as ERROR;
|
||||
IoError {
|
||||
code: ERROR as uint,
|
||||
extra: 0,
|
||||
detail: Some("not yet supported by the `native` runtime, maybe try `green`.".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn last_error() -> IoError {
|
||||
let errno = os::errno() as uint;
|
||||
IoError {
|
||||
code: os::errno() as uint,
|
||||
extra: 0,
|
||||
detail: Some(os::error_string(errno)),
|
||||
}
|
||||
}
|
||||
|
||||
// unix has nonzero values as errors
|
||||
fn mkerr_libc <Int: num::Zero>(ret: Int) -> IoResult<()> {
|
||||
if !ret.is_zero() {
|
||||
Err(last_error())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// windows has zero values as errors
|
||||
#[cfg(windows)]
|
||||
fn mkerr_winbool(ret: libc::c_int) -> IoResult<()> {
|
||||
if ret == 0 {
|
||||
Err(last_error())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[inline]
|
||||
fn retry<I> (f: || -> I) -> I { f() } // PR rust-lang/rust/#17020
|
||||
|
||||
#[cfg(unix)]
|
||||
#[inline]
|
||||
fn retry<I: PartialEq + num::One + Neg<I>> (f: || -> I) -> I {
|
||||
let minus_one = -num::one::<I>();
|
||||
loop {
|
||||
let n = f();
|
||||
if n == minus_one && os::errno() == libc::EINTR as int { }
|
||||
else { return n }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn keep_going(data: &[u8], f: |*const u8, uint| -> i64) -> i64 {
|
||||
let origamt = data.len();
|
||||
let mut data = data.as_ptr();
|
||||
let mut amt = origamt;
|
||||
while amt > 0 {
|
||||
let ret = retry(|| f(data, amt));
|
||||
if ret == 0 {
|
||||
break
|
||||
} else if ret != -1 {
|
||||
amt -= ret as uint;
|
||||
data = unsafe { data.offset(ret as int) };
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return (origamt - amt) as i64;
|
||||
}
|
||||
|
||||
/// Implementation of rt::rtio's IoFactory trait to generate handles to the
|
||||
/// native I/O functionality.
|
||||
pub struct IoFactory {
|
||||
_cannot_construct_outside_of_this_module: ()
|
||||
}
|
||||
|
||||
impl IoFactory {
|
||||
pub fn new() -> IoFactory {
|
||||
net::init();
|
||||
IoFactory { _cannot_construct_outside_of_this_module: () }
|
||||
}
|
||||
}
|
||||
|
||||
impl rtio::IoFactory for IoFactory {
|
||||
// networking
|
||||
fn tcp_connect(&mut self, addr: rtio::SocketAddr,
|
||||
timeout: Option<u64>)
|
||||
-> IoResult<Box<rtio::RtioTcpStream + Send>>
|
||||
{
|
||||
net::TcpStream::connect(addr, timeout).map(|s| {
|
||||
box s as Box<rtio::RtioTcpStream + Send>
|
||||
})
|
||||
}
|
||||
fn tcp_bind(&mut self, addr: rtio::SocketAddr)
|
||||
-> IoResult<Box<rtio::RtioTcpListener + Send>> {
|
||||
net::TcpListener::bind(addr).map(|s| {
|
||||
box s as Box<rtio::RtioTcpListener + Send>
|
||||
})
|
||||
}
|
||||
fn udp_bind(&mut self, addr: rtio::SocketAddr)
|
||||
-> IoResult<Box<rtio::RtioUdpSocket + Send>> {
|
||||
net::UdpSocket::bind(addr).map(|u| {
|
||||
box u as Box<rtio::RtioUdpSocket + Send>
|
||||
})
|
||||
}
|
||||
fn unix_bind(&mut self, path: &CString)
|
||||
-> IoResult<Box<rtio::RtioUnixListener + Send>> {
|
||||
pipe::UnixListener::bind(path).map(|s| {
|
||||
box s as Box<rtio::RtioUnixListener + Send>
|
||||
})
|
||||
}
|
||||
fn unix_connect(&mut self, path: &CString,
|
||||
timeout: Option<u64>) -> IoResult<Box<rtio::RtioPipe + Send>> {
|
||||
pipe::UnixStream::connect(path, timeout).map(|s| {
|
||||
box s as Box<rtio::RtioPipe + Send>
|
||||
})
|
||||
}
|
||||
fn get_host_addresses(&mut self, host: Option<&str>, servname: Option<&str>,
|
||||
hint: Option<rtio::AddrinfoHint>)
|
||||
-> IoResult<Vec<rtio::AddrinfoInfo>>
|
||||
{
|
||||
addrinfo::GetAddrInfoRequest::run(host, servname, hint)
|
||||
}
|
||||
|
||||
// filesystem operations
|
||||
fn fs_from_raw_fd(&mut self, fd: c_int, close: rtio::CloseBehavior)
|
||||
-> Box<rtio::RtioFileStream + Send> {
|
||||
let close = match close {
|
||||
rtio::CloseSynchronously | rtio::CloseAsynchronously => true,
|
||||
rtio::DontClose => false
|
||||
};
|
||||
box file::FileDesc::new(fd, close) as Box<rtio::RtioFileStream + Send>
|
||||
}
|
||||
fn fs_open(&mut self, path: &CString, fm: rtio::FileMode,
|
||||
fa: rtio::FileAccess)
|
||||
-> IoResult<Box<rtio::RtioFileStream + Send>>
|
||||
{
|
||||
file::open(path, fm, fa).map(|fd| box fd as Box<rtio::RtioFileStream + Send>)
|
||||
}
|
||||
fn fs_unlink(&mut self, path: &CString) -> IoResult<()> {
|
||||
file::unlink(path)
|
||||
}
|
||||
fn fs_stat(&mut self, path: &CString) -> IoResult<rtio::FileStat> {
|
||||
file::stat(path)
|
||||
}
|
||||
fn fs_mkdir(&mut self, path: &CString, mode: uint) -> IoResult<()> {
|
||||
file::mkdir(path, mode)
|
||||
}
|
||||
fn fs_chmod(&mut self, path: &CString, mode: uint) -> IoResult<()> {
|
||||
file::chmod(path, mode)
|
||||
}
|
||||
fn fs_rmdir(&mut self, path: &CString) -> IoResult<()> {
|
||||
file::rmdir(path)
|
||||
}
|
||||
fn fs_rename(&mut self, path: &CString, to: &CString) -> IoResult<()> {
|
||||
file::rename(path, to)
|
||||
}
|
||||
fn fs_readdir(&mut self, path: &CString, _flags: c_int) -> IoResult<Vec<CString>> {
|
||||
file::readdir(path)
|
||||
}
|
||||
fn fs_lstat(&mut self, path: &CString) -> IoResult<rtio::FileStat> {
|
||||
file::lstat(path)
|
||||
}
|
||||
fn fs_chown(&mut self, path: &CString, uid: int, gid: int) -> IoResult<()> {
|
||||
file::chown(path, uid, gid)
|
||||
}
|
||||
fn fs_readlink(&mut self, path: &CString) -> IoResult<CString> {
|
||||
file::readlink(path)
|
||||
}
|
||||
fn fs_symlink(&mut self, src: &CString, dst: &CString) -> IoResult<()> {
|
||||
file::symlink(src, dst)
|
||||
}
|
||||
fn fs_link(&mut self, src: &CString, dst: &CString) -> IoResult<()> {
|
||||
file::link(src, dst)
|
||||
}
|
||||
fn fs_utime(&mut self, src: &CString, atime: u64,
|
||||
mtime: u64) -> IoResult<()> {
|
||||
file::utime(src, atime, mtime)
|
||||
}
|
||||
|
||||
// misc
|
||||
fn timer_init(&mut self) -> IoResult<Box<rtio::RtioTimer + Send>> {
|
||||
timer::Timer::new().map(|t| box t as Box<rtio::RtioTimer + Send>)
|
||||
}
|
||||
fn spawn(&mut self, cfg: rtio::ProcessConfig)
|
||||
-> IoResult<(Box<rtio::RtioProcess + Send>,
|
||||
Vec<Option<Box<rtio::RtioPipe + Send>>>)> {
|
||||
process::Process::spawn(cfg).map(|(p, io)| {
|
||||
(box p as Box<rtio::RtioProcess + Send>,
|
||||
io.into_iter().map(|p| p.map(|p| {
|
||||
box p as Box<rtio::RtioPipe + Send>
|
||||
})).collect())
|
||||
})
|
||||
}
|
||||
fn kill(&mut self, pid: libc::pid_t, signum: int) -> IoResult<()> {
|
||||
process::Process::kill(pid, signum)
|
||||
}
|
||||
fn pipe_open(&mut self, fd: c_int) -> IoResult<Box<rtio::RtioPipe + Send>> {
|
||||
Ok(box file::FileDesc::new(fd, true) as Box<rtio::RtioPipe + Send>)
|
||||
}
|
||||
#[cfg(unix)]
|
||||
fn tty_open(&mut self, fd: c_int, _readable: bool)
|
||||
-> IoResult<Box<rtio::RtioTTY + Send>> {
|
||||
if unsafe { libc::isatty(fd) } != 0 {
|
||||
Ok(box file::FileDesc::new(fd, true) as Box<rtio::RtioTTY + Send>)
|
||||
} else {
|
||||
Err(IoError {
|
||||
code: libc::ENOTTY as uint,
|
||||
extra: 0,
|
||||
detail: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
#[cfg(windows)]
|
||||
fn tty_open(&mut self, fd: c_int, _readable: bool)
|
||||
-> IoResult<Box<rtio::RtioTTY + Send>> {
|
||||
if tty::is_tty(fd) {
|
||||
Ok(box tty::WindowsTTY::new(fd) as Box<rtio::RtioTTY + Send>)
|
||||
} else {
|
||||
Err(IoError {
|
||||
code: libc::ERROR_INVALID_HANDLE as uint,
|
||||
extra: 0,
|
||||
detail: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
fn signal(&mut self, _signal: int, _cb: Box<rtio::Callback>)
|
||||
-> IoResult<Box<rtio::RtioSignal + Send>> {
|
||||
Err(unimpl())
|
||||
}
|
||||
}
|
|
@ -1,209 +0,0 @@
|
|||
// Copyright 2014 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 std::cmp;
|
||||
use std::mem;
|
||||
use std::os;
|
||||
use std::ptr;
|
||||
use std::rt::rtio::{IoResult, IoError};
|
||||
|
||||
use super::c;
|
||||
use super::net;
|
||||
use super::{retry, last_error};
|
||||
|
||||
#[deriving(Show)]
|
||||
pub enum SocketStatus {
|
||||
Readable,
|
||||
Writable,
|
||||
}
|
||||
|
||||
pub fn timeout(desc: &'static str) -> IoError {
|
||||
#[cfg(unix)] use libc::ETIMEDOUT as ERROR;
|
||||
#[cfg(windows)] use libc::ERROR_OPERATION_ABORTED as ERROR;
|
||||
IoError {
|
||||
code: ERROR as uint,
|
||||
extra: 0,
|
||||
detail: Some(desc.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn short_write(n: uint, desc: &'static str) -> IoError {
|
||||
#[cfg(unix)] use libc::EAGAIN as ERROR;
|
||||
#[cfg(windows)] use libc::ERROR_OPERATION_ABORTED as ERROR;
|
||||
IoError {
|
||||
code: ERROR as uint,
|
||||
extra: n,
|
||||
detail: Some(desc.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eof() -> IoError {
|
||||
IoError {
|
||||
code: libc::EOF as uint,
|
||||
extra: 0,
|
||||
detail: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn ms_to_timeval(ms: u64) -> libc::timeval {
|
||||
libc::timeval {
|
||||
tv_sec: (ms / 1000) as libc::c_long,
|
||||
tv_usec: ((ms % 1000) * 1000) as libc::c_long,
|
||||
}
|
||||
}
|
||||
#[cfg(not(windows))]
|
||||
pub fn ms_to_timeval(ms: u64) -> libc::timeval {
|
||||
libc::timeval {
|
||||
tv_sec: (ms / 1000) as libc::time_t,
|
||||
tv_usec: ((ms % 1000) * 1000) as libc::suseconds_t,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn wouldblock() -> bool {
|
||||
let err = os::errno();
|
||||
err == libc::EWOULDBLOCK as int || err == libc::EAGAIN as int
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn wouldblock() -> bool {
|
||||
let err = os::errno();
|
||||
err == libc::WSAEWOULDBLOCK as uint
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn set_nonblocking(fd: net::sock_t, nb: bool) -> IoResult<()> {
|
||||
let set = nb as libc::c_int;
|
||||
super::mkerr_libc(retry(|| unsafe { c::ioctl(fd, c::FIONBIO, &set) }))
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn set_nonblocking(fd: net::sock_t, nb: bool) -> IoResult<()> {
|
||||
let mut set = nb as libc::c_ulong;
|
||||
if unsafe { c::ioctlsocket(fd, c::FIONBIO, &mut set) != 0 } {
|
||||
Err(last_error())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// See http://developerweb.net/viewtopic.php?id=3196 for where this is
|
||||
// derived from.
|
||||
pub fn connect_timeout(fd: net::sock_t,
|
||||
addrp: *const libc::sockaddr,
|
||||
len: libc::socklen_t,
|
||||
timeout_ms: u64) -> IoResult<()> {
|
||||
use std::os;
|
||||
#[cfg(unix)] use libc::EINPROGRESS as INPROGRESS;
|
||||
#[cfg(windows)] use libc::WSAEINPROGRESS as INPROGRESS;
|
||||
#[cfg(unix)] use libc::EWOULDBLOCK as WOULDBLOCK;
|
||||
#[cfg(windows)] use libc::WSAEWOULDBLOCK as WOULDBLOCK;
|
||||
|
||||
// Make sure the call to connect() doesn't block
|
||||
try!(set_nonblocking(fd, true));
|
||||
|
||||
let ret = match unsafe { libc::connect(fd, addrp, len) } {
|
||||
// If the connection is in progress, then we need to wait for it to
|
||||
// finish (with a timeout). The current strategy for doing this is
|
||||
// to use select() with a timeout.
|
||||
-1 if os::errno() as int == INPROGRESS as int ||
|
||||
os::errno() as int == WOULDBLOCK as int => {
|
||||
let mut set: c::fd_set = unsafe { mem::zeroed() };
|
||||
c::fd_set(&mut set, fd);
|
||||
match await(fd, &mut set, timeout_ms) {
|
||||
0 => Err(timeout("connection timed out")),
|
||||
-1 => Err(last_error()),
|
||||
_ => {
|
||||
let err: libc::c_int = try!(
|
||||
net::getsockopt(fd, libc::SOL_SOCKET, libc::SO_ERROR));
|
||||
if err == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(IoError {
|
||||
code: err as uint,
|
||||
extra: 0,
|
||||
detail: Some(os::error_string(err as uint)),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-1 => Err(last_error()),
|
||||
_ => Ok(()),
|
||||
};
|
||||
|
||||
// be sure to turn blocking I/O back on
|
||||
try!(set_nonblocking(fd, false));
|
||||
return ret;
|
||||
|
||||
#[cfg(unix)]
|
||||
fn await(fd: net::sock_t, set: &mut c::fd_set,
|
||||
timeout: u64) -> libc::c_int {
|
||||
let start = ::io::timer::now();
|
||||
retry(|| unsafe {
|
||||
// Recalculate the timeout each iteration (it is generally
|
||||
// undefined what the value of the 'tv' is after select
|
||||
// returns EINTR).
|
||||
let mut tv = ms_to_timeval(timeout - (::io::timer::now() - start));
|
||||
c::select(fd + 1, ptr::null_mut(), set as *mut _,
|
||||
ptr::null_mut(), &mut tv)
|
||||
})
|
||||
}
|
||||
#[cfg(windows)]
|
||||
fn await(_fd: net::sock_t, set: &mut c::fd_set,
|
||||
timeout: u64) -> libc::c_int {
|
||||
let mut tv = ms_to_timeval(timeout);
|
||||
unsafe { c::select(1, ptr::null_mut(), set, ptr::null_mut(), &mut tv) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn await(fds: &[net::sock_t], deadline: Option<u64>,
|
||||
status: SocketStatus) -> IoResult<()> {
|
||||
let mut set: c::fd_set = unsafe { mem::zeroed() };
|
||||
let mut max = 0;
|
||||
for &fd in fds.iter() {
|
||||
c::fd_set(&mut set, fd);
|
||||
max = cmp::max(max, fd + 1);
|
||||
}
|
||||
if cfg!(windows) {
|
||||
max = fds.len() as net::sock_t;
|
||||
}
|
||||
|
||||
let (read, write) = match status {
|
||||
Readable => (&mut set as *mut _, ptr::null_mut()),
|
||||
Writable => (ptr::null_mut(), &mut set as *mut _),
|
||||
};
|
||||
let mut tv: libc::timeval = unsafe { mem::zeroed() };
|
||||
|
||||
match retry(|| {
|
||||
let now = ::io::timer::now();
|
||||
let tvp = match deadline {
|
||||
None => ptr::null_mut(),
|
||||
Some(deadline) => {
|
||||
// If we're past the deadline, then pass a 0 timeout to
|
||||
// select() so we can poll the status
|
||||
let ms = if deadline < now {0} else {deadline - now};
|
||||
tv = ms_to_timeval(ms);
|
||||
&mut tv as *mut _
|
||||
}
|
||||
};
|
||||
let r = unsafe {
|
||||
c::select(max as libc::c_int, read, write, ptr::null_mut(), tvp)
|
||||
};
|
||||
r
|
||||
}) {
|
||||
-1 => Err(last_error()),
|
||||
0 => Err(timeout("timed out")),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
|
@ -74,7 +74,6 @@ use std::str;
|
|||
|
||||
pub use task::NativeTaskBuilder;
|
||||
|
||||
pub mod io;
|
||||
pub mod task;
|
||||
|
||||
#[cfg(any(windows, android))]
|
||||
|
|
|
@ -19,13 +19,11 @@ use std::mem;
|
|||
use std::rt::bookkeeping;
|
||||
use std::rt::local::Local;
|
||||
use std::rt::mutex::NativeMutex;
|
||||
use std::rt::rtio;
|
||||
use std::rt::stack;
|
||||
use std::rt::task::{Task, BlockedTask, TaskOpts};
|
||||
use std::rt::thread::Thread;
|
||||
use std::rt;
|
||||
|
||||
use io;
|
||||
use std::task::{TaskBuilder, Spawner};
|
||||
|
||||
/// Creates a new Task which is ready to execute as a 1:1 task.
|
||||
|
@ -42,7 +40,6 @@ fn ops() -> Box<Ops> {
|
|||
box Ops {
|
||||
lock: unsafe { NativeMutex::new() },
|
||||
awoken: false,
|
||||
io: io::IoFactory::new(),
|
||||
// these *should* get overwritten
|
||||
stack_bounds: (0, 0),
|
||||
stack_guard: 0
|
||||
|
@ -112,7 +109,6 @@ impl<S: Spawner> NativeTaskBuilder for TaskBuilder<S> {
|
|||
struct Ops {
|
||||
lock: NativeMutex, // native synchronization
|
||||
awoken: bool, // used to prevent spurious wakeups
|
||||
io: io::IoFactory, // local I/O factory
|
||||
|
||||
// This field holds the known bounds of the stack in (lo, hi) form. Not all
|
||||
// native tasks necessarily know their precise bounds, hence this is
|
||||
|
@ -272,10 +268,6 @@ impl rt::Runtime for Ops {
|
|||
|
||||
NativeSpawner.spawn(opts, f);
|
||||
}
|
||||
|
||||
fn local_io<'a>(&'a mut self) -> Option<rtio::LocalIo<'a>> {
|
||||
Some(rtio::LocalIo::new(&mut self.io as &mut rtio::IoFactory))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -90,7 +90,6 @@ pub trait Runtime {
|
|||
cur_task: Box<Task>,
|
||||
opts: TaskOpts,
|
||||
f: proc():Send);
|
||||
fn local_io<'a>(&'a mut self) -> Option<rtio::LocalIo<'a>>;
|
||||
/// The (low, high) edges of the current stack.
|
||||
fn stack_bounds(&self) -> (uint, uint); // (lo, hi)
|
||||
/// The last writable byte of the stack next to the guard page
|
||||
|
|
|
@ -12,16 +12,6 @@
|
|||
|
||||
use core::prelude::*;
|
||||
use alloc::boxed::Box;
|
||||
use collections::string::String;
|
||||
use collections::vec::Vec;
|
||||
use core::fmt;
|
||||
use core::mem;
|
||||
use libc::c_int;
|
||||
use libc;
|
||||
|
||||
use c_str::CString;
|
||||
use local::Local;
|
||||
use task::Task;
|
||||
|
||||
pub trait EventLoop {
|
||||
fn run(&mut self);
|
||||
|
@ -31,8 +21,7 @@ pub trait EventLoop {
|
|||
fn remote_callback(&mut self, Box<Callback + Send>)
|
||||
-> Box<RemoteCallback + Send>;
|
||||
|
||||
/// The asynchronous I/O services. Not all event loops may provide one.
|
||||
fn io<'a>(&'a mut self) -> Option<&'a mut IoFactory>;
|
||||
// last vestige of IoFactory
|
||||
fn has_active_io(&self) -> bool;
|
||||
}
|
||||
|
||||
|
@ -50,405 +39,7 @@ pub trait RemoteCallback {
|
|||
fn fire(&mut self);
|
||||
}
|
||||
|
||||
/// Description of what to do when a file handle is closed
|
||||
pub enum CloseBehavior {
|
||||
/// Do not close this handle when the object is destroyed
|
||||
DontClose,
|
||||
/// Synchronously close the handle, meaning that the task will block when
|
||||
/// the handle is destroyed until it has been fully closed.
|
||||
CloseSynchronously,
|
||||
/// Asynchronously closes a handle, meaning that the task will *not* block
|
||||
/// when the handle is destroyed, but the handle will still get deallocated
|
||||
/// and cleaned up (but this will happen asynchronously on the local event
|
||||
/// loop).
|
||||
CloseAsynchronously,
|
||||
}
|
||||
|
||||
/// Data needed to spawn a process. Serializes the `std::io::process::Command`
|
||||
/// builder.
|
||||
pub struct ProcessConfig<'a> {
|
||||
/// Path to the program to run.
|
||||
pub program: &'a CString,
|
||||
|
||||
/// Arguments to pass to the program (doesn't include the program itself).
|
||||
pub args: &'a [CString],
|
||||
|
||||
/// Optional environment to specify for the program. If this is None, then
|
||||
/// it will inherit the current process's environment.
|
||||
pub env: Option<&'a [(&'a CString, &'a CString)]>,
|
||||
|
||||
/// Optional working directory for the new process. If this is None, then
|
||||
/// the current directory of the running process is inherited.
|
||||
pub cwd: Option<&'a CString>,
|
||||
|
||||
/// Configuration for the child process's stdin handle (file descriptor 0).
|
||||
/// This field defaults to `CreatePipe(true, false)` so the input can be
|
||||
/// written to.
|
||||
pub stdin: StdioContainer,
|
||||
|
||||
/// Configuration for the child process's stdout handle (file descriptor 1).
|
||||
/// This field defaults to `CreatePipe(false, true)` so the output can be
|
||||
/// collected.
|
||||
pub stdout: StdioContainer,
|
||||
|
||||
/// Configuration for the child process's stdout handle (file descriptor 2).
|
||||
/// This field defaults to `CreatePipe(false, true)` so the output can be
|
||||
/// collected.
|
||||
pub stderr: StdioContainer,
|
||||
|
||||
/// Any number of streams/file descriptors/pipes may be attached to this
|
||||
/// process. This list enumerates the file descriptors and such for the
|
||||
/// process to be spawned, and the file descriptors inherited will start at
|
||||
/// 3 and go to the length of this array. The first three file descriptors
|
||||
/// (stdin/stdout/stderr) are configured with the `stdin`, `stdout`, and
|
||||
/// `stderr` fields.
|
||||
pub extra_io: &'a [StdioContainer],
|
||||
|
||||
/// Sets the child process's user id. This translates to a `setuid` call in
|
||||
/// the child process. Setting this value on windows will cause the spawn to
|
||||
/// fail. Failure in the `setuid` call on unix will also cause the spawn to
|
||||
/// fail.
|
||||
pub uid: Option<uint>,
|
||||
|
||||
/// Similar to `uid`, but sets the group id of the child process. This has
|
||||
/// the same semantics as the `uid` field.
|
||||
pub gid: Option<uint>,
|
||||
|
||||
/// If true, the child process is spawned in a detached state. On unix, this
|
||||
/// means that the child is the leader of a new process group.
|
||||
pub detach: bool,
|
||||
}
|
||||
|
||||
pub struct LocalIo<'a> {
|
||||
factory: &'a mut IoFactory+'a,
|
||||
}
|
||||
|
||||
#[unsafe_destructor]
|
||||
impl<'a> Drop for LocalIo<'a> {
|
||||
fn drop(&mut self) {
|
||||
// FIXME(pcwalton): Do nothing here for now, but eventually we may want
|
||||
// something. For now this serves to make `LocalIo` noncopyable.
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> LocalIo<'a> {
|
||||
/// Returns the local I/O: either the local scheduler's I/O services or
|
||||
/// the native I/O services.
|
||||
pub fn borrow() -> Option<LocalIo<'a>> {
|
||||
// FIXME(#11053): bad
|
||||
//
|
||||
// This is currently very unsafely implemented. We don't actually
|
||||
// *take* the local I/O so there's a very real possibility that we
|
||||
// can have two borrows at once. Currently there is not a clear way
|
||||
// to actually borrow the local I/O factory safely because even if
|
||||
// ownership were transferred down to the functions that the I/O
|
||||
// factory implements it's just too much of a pain to know when to
|
||||
// relinquish ownership back into the local task (but that would be
|
||||
// the safe way of implementing this function).
|
||||
//
|
||||
// In order to get around this, we just transmute a copy out of the task
|
||||
// in order to have what is likely a static lifetime (bad).
|
||||
let mut t: Box<Task> = match Local::try_take() {
|
||||
Some(t) => t,
|
||||
None => return None,
|
||||
};
|
||||
let ret = t.local_io().map(|t| {
|
||||
unsafe { mem::transmute_copy(&t) }
|
||||
});
|
||||
Local::put(t);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pub fn maybe_raise<T>(f: |io: &mut IoFactory| -> IoResult<T>)
|
||||
-> IoResult<T>
|
||||
{
|
||||
#[cfg(unix)] use libc::EINVAL as ERROR;
|
||||
#[cfg(windows)] use libc::ERROR_CALL_NOT_IMPLEMENTED as ERROR;
|
||||
match LocalIo::borrow() {
|
||||
Some(mut io) => f(io.get()),
|
||||
None => Err(IoError {
|
||||
code: ERROR as uint,
|
||||
extra: 0,
|
||||
detail: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new<'a>(io: &'a mut IoFactory+'a) -> LocalIo<'a> {
|
||||
LocalIo { factory: io }
|
||||
}
|
||||
|
||||
/// Returns the underlying I/O factory as a trait reference.
|
||||
#[inline]
|
||||
pub fn get<'a>(&'a mut self) -> &'a mut IoFactory {
|
||||
let f: &'a mut IoFactory = self.factory;
|
||||
f
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IoFactory {
|
||||
// networking
|
||||
fn tcp_connect(&mut self, addr: SocketAddr,
|
||||
timeout: Option<u64>) -> IoResult<Box<RtioTcpStream + Send>>;
|
||||
fn tcp_bind(&mut self, addr: SocketAddr)
|
||||
-> IoResult<Box<RtioTcpListener + Send>>;
|
||||
fn udp_bind(&mut self, addr: SocketAddr)
|
||||
-> IoResult<Box<RtioUdpSocket + Send>>;
|
||||
fn unix_bind(&mut self, path: &CString)
|
||||
-> IoResult<Box<RtioUnixListener + Send>>;
|
||||
fn unix_connect(&mut self, path: &CString,
|
||||
timeout: Option<u64>) -> IoResult<Box<RtioPipe + Send>>;
|
||||
fn get_host_addresses(&mut self, host: Option<&str>, servname: Option<&str>,
|
||||
hint: Option<AddrinfoHint>)
|
||||
-> IoResult<Vec<AddrinfoInfo>>;
|
||||
|
||||
// filesystem operations
|
||||
fn fs_from_raw_fd(&mut self, fd: c_int, close: CloseBehavior)
|
||||
-> Box<RtioFileStream + Send>;
|
||||
fn fs_open(&mut self, path: &CString, fm: FileMode, fa: FileAccess)
|
||||
-> IoResult<Box<RtioFileStream + Send>>;
|
||||
fn fs_unlink(&mut self, path: &CString) -> IoResult<()>;
|
||||
fn fs_stat(&mut self, path: &CString) -> IoResult<FileStat>;
|
||||
fn fs_mkdir(&mut self, path: &CString, mode: uint) -> IoResult<()>;
|
||||
fn fs_chmod(&mut self, path: &CString, mode: uint) -> IoResult<()>;
|
||||
fn fs_rmdir(&mut self, path: &CString) -> IoResult<()>;
|
||||
fn fs_rename(&mut self, path: &CString, to: &CString) -> IoResult<()>;
|
||||
fn fs_readdir(&mut self, path: &CString, flags: c_int) ->
|
||||
IoResult<Vec<CString>>;
|
||||
fn fs_lstat(&mut self, path: &CString) -> IoResult<FileStat>;
|
||||
fn fs_chown(&mut self, path: &CString, uid: int, gid: int) ->
|
||||
IoResult<()>;
|
||||
fn fs_readlink(&mut self, path: &CString) -> IoResult<CString>;
|
||||
fn fs_symlink(&mut self, src: &CString, dst: &CString) -> IoResult<()>;
|
||||
fn fs_link(&mut self, src: &CString, dst: &CString) -> IoResult<()>;
|
||||
fn fs_utime(&mut self, src: &CString, atime: u64, mtime: u64) ->
|
||||
IoResult<()>;
|
||||
|
||||
// misc
|
||||
fn timer_init(&mut self) -> IoResult<Box<RtioTimer + Send>>;
|
||||
fn spawn(&mut self, cfg: ProcessConfig)
|
||||
-> IoResult<(Box<RtioProcess + Send>,
|
||||
Vec<Option<Box<RtioPipe + Send>>>)>;
|
||||
fn kill(&mut self, pid: libc::pid_t, signal: int) -> IoResult<()>;
|
||||
fn pipe_open(&mut self, fd: c_int) -> IoResult<Box<RtioPipe + Send>>;
|
||||
fn tty_open(&mut self, fd: c_int, readable: bool)
|
||||
-> IoResult<Box<RtioTTY + Send>>;
|
||||
fn signal(&mut self, signal: int, cb: Box<Callback + Send>)
|
||||
-> IoResult<Box<RtioSignal + Send>>;
|
||||
}
|
||||
|
||||
pub trait RtioTcpListener : RtioSocket {
|
||||
fn listen(self: Box<Self>) -> IoResult<Box<RtioTcpAcceptor + Send>>;
|
||||
}
|
||||
|
||||
pub trait RtioTcpAcceptor : RtioSocket {
|
||||
fn accept(&mut self) -> IoResult<Box<RtioTcpStream + Send>>;
|
||||
fn accept_simultaneously(&mut self) -> IoResult<()>;
|
||||
fn dont_accept_simultaneously(&mut self) -> IoResult<()>;
|
||||
fn set_timeout(&mut self, timeout: Option<u64>);
|
||||
fn clone(&self) -> Box<RtioTcpAcceptor + Send>;
|
||||
fn close_accept(&mut self) -> IoResult<()>;
|
||||
}
|
||||
|
||||
pub trait RtioTcpStream : RtioSocket {
|
||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint>;
|
||||
fn write(&mut self, buf: &[u8]) -> IoResult<()>;
|
||||
fn peer_name(&mut self) -> IoResult<SocketAddr>;
|
||||
fn control_congestion(&mut self) -> IoResult<()>;
|
||||
fn nodelay(&mut self) -> IoResult<()>;
|
||||
fn keepalive(&mut self, delay_in_seconds: uint) -> IoResult<()>;
|
||||
fn letdie(&mut self) -> IoResult<()>;
|
||||
fn clone(&self) -> Box<RtioTcpStream + Send>;
|
||||
fn close_write(&mut self) -> IoResult<()>;
|
||||
fn close_read(&mut self) -> IoResult<()>;
|
||||
fn set_timeout(&mut self, timeout_ms: Option<u64>);
|
||||
fn set_read_timeout(&mut self, timeout_ms: Option<u64>);
|
||||
fn set_write_timeout(&mut self, timeout_ms: Option<u64>);
|
||||
}
|
||||
|
||||
pub trait RtioSocket {
|
||||
fn socket_name(&mut self) -> IoResult<SocketAddr>;
|
||||
}
|
||||
|
||||
pub trait RtioUdpSocket : RtioSocket {
|
||||
fn recv_from(&mut self, buf: &mut [u8]) -> IoResult<(uint, SocketAddr)>;
|
||||
fn send_to(&mut self, buf: &[u8], dst: SocketAddr) -> IoResult<()>;
|
||||
|
||||
fn join_multicast(&mut self, multi: IpAddr) -> IoResult<()>;
|
||||
fn leave_multicast(&mut self, multi: IpAddr) -> IoResult<()>;
|
||||
|
||||
fn loop_multicast_locally(&mut self) -> IoResult<()>;
|
||||
fn dont_loop_multicast_locally(&mut self) -> IoResult<()>;
|
||||
|
||||
fn multicast_time_to_live(&mut self, ttl: int) -> IoResult<()>;
|
||||
fn time_to_live(&mut self, ttl: int) -> IoResult<()>;
|
||||
|
||||
fn hear_broadcasts(&mut self) -> IoResult<()>;
|
||||
fn ignore_broadcasts(&mut self) -> IoResult<()>;
|
||||
|
||||
fn clone(&self) -> Box<RtioUdpSocket + Send>;
|
||||
fn set_timeout(&mut self, timeout_ms: Option<u64>);
|
||||
fn set_read_timeout(&mut self, timeout_ms: Option<u64>);
|
||||
fn set_write_timeout(&mut self, timeout_ms: Option<u64>);
|
||||
}
|
||||
|
||||
pub trait RtioTimer {
|
||||
fn sleep(&mut self, msecs: u64);
|
||||
fn oneshot(&mut self, msecs: u64, cb: Box<Callback + Send>);
|
||||
fn period(&mut self, msecs: u64, cb: Box<Callback + Send>);
|
||||
}
|
||||
|
||||
pub trait RtioFileStream {
|
||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<int>;
|
||||
fn write(&mut self, buf: &[u8]) -> IoResult<()>;
|
||||
fn pread(&mut self, buf: &mut [u8], offset: u64) -> IoResult<int>;
|
||||
fn pwrite(&mut self, buf: &[u8], offset: u64) -> IoResult<()>;
|
||||
fn seek(&mut self, pos: i64, whence: SeekStyle) -> IoResult<u64>;
|
||||
fn tell(&self) -> IoResult<u64>;
|
||||
fn fsync(&mut self) -> IoResult<()>;
|
||||
fn datasync(&mut self) -> IoResult<()>;
|
||||
fn truncate(&mut self, offset: i64) -> IoResult<()>;
|
||||
fn fstat(&mut self) -> IoResult<FileStat>;
|
||||
}
|
||||
|
||||
pub trait RtioProcess {
|
||||
fn id(&self) -> libc::pid_t;
|
||||
fn kill(&mut self, signal: int) -> IoResult<()>;
|
||||
fn wait(&mut self) -> IoResult<ProcessExit>;
|
||||
fn set_timeout(&mut self, timeout: Option<u64>);
|
||||
}
|
||||
|
||||
pub trait RtioPipe {
|
||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint>;
|
||||
fn write(&mut self, buf: &[u8]) -> IoResult<()>;
|
||||
fn clone(&self) -> Box<RtioPipe + Send>;
|
||||
|
||||
fn close_write(&mut self) -> IoResult<()>;
|
||||
fn close_read(&mut self) -> IoResult<()>;
|
||||
fn set_timeout(&mut self, timeout_ms: Option<u64>);
|
||||
fn set_read_timeout(&mut self, timeout_ms: Option<u64>);
|
||||
fn set_write_timeout(&mut self, timeout_ms: Option<u64>);
|
||||
}
|
||||
|
||||
pub trait RtioUnixListener {
|
||||
fn listen(self: Box<Self>) -> IoResult<Box<RtioUnixAcceptor + Send>>;
|
||||
}
|
||||
|
||||
pub trait RtioUnixAcceptor {
|
||||
fn accept(&mut self) -> IoResult<Box<RtioPipe + Send>>;
|
||||
fn set_timeout(&mut self, timeout: Option<u64>);
|
||||
fn clone(&self) -> Box<RtioUnixAcceptor + Send>;
|
||||
fn close_accept(&mut self) -> IoResult<()>;
|
||||
}
|
||||
|
||||
pub trait RtioTTY {
|
||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint>;
|
||||
fn write(&mut self, buf: &[u8]) -> IoResult<()>;
|
||||
fn set_raw(&mut self, raw: bool) -> IoResult<()>;
|
||||
fn get_winsize(&mut self) -> IoResult<(int, int)>;
|
||||
fn isatty(&self) -> bool;
|
||||
}
|
||||
|
||||
pub trait PausableIdleCallback {
|
||||
fn pause(&mut self);
|
||||
fn resume(&mut self);
|
||||
}
|
||||
|
||||
pub trait RtioSignal {}
|
||||
|
||||
#[deriving(Show)]
|
||||
pub struct IoError {
|
||||
pub code: uint,
|
||||
pub extra: uint,
|
||||
pub detail: Option<String>,
|
||||
}
|
||||
|
||||
pub type IoResult<T> = Result<T, IoError>;
|
||||
|
||||
#[deriving(PartialEq, Eq)]
|
||||
pub enum IpAddr {
|
||||
Ipv4Addr(u8, u8, u8, u8),
|
||||
Ipv6Addr(u16, u16, u16, u16, u16, u16, u16, u16),
|
||||
}
|
||||
|
||||
impl fmt::Show for IpAddr {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Ipv4Addr(a, b, c, d) => write!(fmt, "{}.{}.{}.{}", a, b, c, d),
|
||||
Ipv6Addr(a, b, c, d, e, f, g, h) => {
|
||||
write!(fmt,
|
||||
"{:04x}:{:04x}:{:04x}:{:04x}:{:04x}:{:04x}:{:04x}:{:04x}",
|
||||
a, b, c, d, e, f, g, h)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[deriving(PartialEq, Eq)]
|
||||
pub struct SocketAddr {
|
||||
pub ip: IpAddr,
|
||||
pub port: u16,
|
||||
}
|
||||
|
||||
pub enum StdioContainer {
|
||||
Ignored,
|
||||
InheritFd(i32),
|
||||
CreatePipe(bool, bool),
|
||||
}
|
||||
|
||||
pub enum ProcessExit {
|
||||
ExitStatus(int),
|
||||
ExitSignal(int),
|
||||
}
|
||||
|
||||
pub enum FileMode {
|
||||
Open,
|
||||
Append,
|
||||
Truncate,
|
||||
}
|
||||
|
||||
pub enum FileAccess {
|
||||
Read,
|
||||
Write,
|
||||
ReadWrite,
|
||||
}
|
||||
|
||||
pub struct FileStat {
|
||||
pub size: u64,
|
||||
pub kind: u64,
|
||||
pub perm: u64,
|
||||
pub created: u64,
|
||||
pub modified: u64,
|
||||
pub accessed: u64,
|
||||
pub device: u64,
|
||||
pub inode: u64,
|
||||
pub rdev: u64,
|
||||
pub nlink: u64,
|
||||
pub uid: u64,
|
||||
pub gid: u64,
|
||||
pub blksize: u64,
|
||||
pub blocks: u64,
|
||||
pub flags: u64,
|
||||
pub gen: u64,
|
||||
}
|
||||
|
||||
pub enum SeekStyle {
|
||||
SeekSet,
|
||||
SeekEnd,
|
||||
SeekCur,
|
||||
}
|
||||
|
||||
pub struct AddrinfoHint {
|
||||
pub family: uint,
|
||||
pub socktype: uint,
|
||||
pub protocol: uint,
|
||||
pub flags: uint,
|
||||
}
|
||||
|
||||
pub struct AddrinfoInfo {
|
||||
pub address: SocketAddr,
|
||||
pub family: uint,
|
||||
pub socktype: uint,
|
||||
pub protocol: uint,
|
||||
pub flags: uint,
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ use core::raw;
|
|||
use local_data;
|
||||
use Runtime;
|
||||
use local::Local;
|
||||
use rtio::LocalIo;
|
||||
use unwind;
|
||||
use unwind::Unwinder;
|
||||
use collections::str::SendStr;
|
||||
|
@ -421,13 +420,6 @@ impl Task {
|
|||
ops.maybe_yield(self);
|
||||
}
|
||||
|
||||
/// Acquires a handle to the I/O factory that this task contains, normally
|
||||
/// stored in the task's runtime. This factory may not always be available,
|
||||
/// which is why the return type is `Option`
|
||||
pub fn local_io<'a>(&'a mut self) -> Option<LocalIo<'a>> {
|
||||
self.imp.as_mut().unwrap().local_io()
|
||||
}
|
||||
|
||||
/// Returns the stack bounds for this task in (lo, hi) format. The stack
|
||||
/// bounds may not be known for all tasks, so the return value may be
|
||||
/// `None`.
|
||||
|
|
|
@ -52,28 +52,25 @@ fs::unlink(&path);
|
|||
|
||||
*/
|
||||
|
||||
use c_str::ToCStr;
|
||||
use clone::Clone;
|
||||
use io::standard_error;
|
||||
use io::{FilePermission, Write, UnstableFileStat, Open, FileAccess, FileMode};
|
||||
use io::{FilePermission, Write, Open, FileAccess, FileMode};
|
||||
use io::{IoResult, IoError, FileStat, SeekStyle, Seek, Writer, Reader};
|
||||
use io::{Read, Truncate, SeekCur, SeekSet, ReadWrite, SeekEnd, Append};
|
||||
use io::{Read, Truncate, ReadWrite, Append};
|
||||
use io::UpdateIoError;
|
||||
use io;
|
||||
use iter::{Iterator, Extend};
|
||||
use kinds::Send;
|
||||
use libc;
|
||||
use option::{Some, None, Option};
|
||||
use boxed::Box;
|
||||
use path::{Path, GenericPath};
|
||||
use path;
|
||||
use result::{Err, Ok};
|
||||
use rt::rtio::LocalIo;
|
||||
use rt::rtio;
|
||||
use slice::SlicePrelude;
|
||||
use string::String;
|
||||
use vec::Vec;
|
||||
|
||||
use sys::fs as fs_imp;
|
||||
use sys_common;
|
||||
|
||||
/// Unconstrained file access type that exposes read and write operations
|
||||
///
|
||||
/// Can be constructed via `File::open()`, `File::create()`, and
|
||||
|
@ -86,11 +83,17 @@ use vec::Vec;
|
|||
/// configured at creation time, via the `FileAccess` parameter to
|
||||
/// `File::open_mode()`.
|
||||
pub struct File {
|
||||
fd: Box<rtio::RtioFileStream + Send>,
|
||||
fd: fs_imp::FileDesc,
|
||||
path: Path,
|
||||
last_nread: int,
|
||||
}
|
||||
|
||||
impl sys_common::AsFileDesc for File {
|
||||
fn as_fd(&self) -> &fs_imp::FileDesc {
|
||||
&self.fd
|
||||
}
|
||||
}
|
||||
|
||||
impl File {
|
||||
/// Open a file at `path` in the mode specified by the `mode` and `access`
|
||||
/// arguments
|
||||
|
@ -133,26 +136,13 @@ impl File {
|
|||
pub fn open_mode(path: &Path,
|
||||
mode: FileMode,
|
||||
access: FileAccess) -> IoResult<File> {
|
||||
let rtio_mode = match mode {
|
||||
Open => rtio::Open,
|
||||
Append => rtio::Append,
|
||||
Truncate => rtio::Truncate,
|
||||
};
|
||||
let rtio_access = match access {
|
||||
Read => rtio::Read,
|
||||
Write => rtio::Write,
|
||||
ReadWrite => rtio::ReadWrite,
|
||||
};
|
||||
let err = LocalIo::maybe_raise(|io| {
|
||||
io.fs_open(&path.to_c_str(), rtio_mode, rtio_access).map(|fd| {
|
||||
File {
|
||||
path: path.clone(),
|
||||
fd: fd,
|
||||
last_nread: -1
|
||||
}
|
||||
})
|
||||
}).map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't open file", |e| {
|
||||
fs_imp::open(path, mode, access).map(|fd| {
|
||||
File {
|
||||
path: path.clone(),
|
||||
fd: fd,
|
||||
last_nread: -1
|
||||
}
|
||||
}).update_err("couldn't open file", |e| {
|
||||
format!("{}; path={}; mode={}; access={}", e, path.display(),
|
||||
mode_string(mode), access_string(access))
|
||||
})
|
||||
|
@ -194,7 +184,7 @@ impl File {
|
|||
/// ```
|
||||
pub fn create(path: &Path) -> IoResult<File> {
|
||||
File::open_mode(path, Truncate, Write)
|
||||
.update_desc("couldn't create file")
|
||||
.update_desc("couldn't create file")
|
||||
}
|
||||
|
||||
/// Returns the original path which was used to open this file.
|
||||
|
@ -206,9 +196,9 @@ impl File {
|
|||
/// device. This will flush any internal buffers necessary to perform this
|
||||
/// operation.
|
||||
pub fn fsync(&mut self) -> IoResult<()> {
|
||||
let err = self.fd.fsync().map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't fsync file",
|
||||
|e| format!("{}; path={}", e, self.path.display()))
|
||||
self.fd.fsync()
|
||||
.update_err("couldn't fsync file",
|
||||
|e| format!("{}; path={}", e, self.path.display()))
|
||||
}
|
||||
|
||||
/// This function is similar to `fsync`, except that it may not synchronize
|
||||
|
@ -216,9 +206,9 @@ impl File {
|
|||
/// must synchronize content, but don't need the metadata on disk. The goal
|
||||
/// of this method is to reduce disk operations.
|
||||
pub fn datasync(&mut self) -> IoResult<()> {
|
||||
let err = self.fd.datasync().map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't datasync file",
|
||||
|e| format!("{}; path={}", e, self.path.display()))
|
||||
self.fd.datasync()
|
||||
.update_err("couldn't datasync file",
|
||||
|e| format!("{}; path={}", e, self.path.display()))
|
||||
}
|
||||
|
||||
/// Either truncates or extends the underlying file, updating the size of
|
||||
|
@ -230,10 +220,9 @@ impl File {
|
|||
/// will be extended to `size` and have all of the intermediate data filled
|
||||
/// in with 0s.
|
||||
pub fn truncate(&mut self, size: i64) -> IoResult<()> {
|
||||
let err = self.fd.truncate(size).map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't truncate file", |e| {
|
||||
format!("{}; path={}; size={}", e, self.path.display(), size)
|
||||
})
|
||||
self.fd.truncate(size)
|
||||
.update_err("couldn't truncate file", |e|
|
||||
format!("{}; path={}; size={}", e, self.path.display(), size))
|
||||
}
|
||||
|
||||
/// Returns true if the stream has reached the end of the file.
|
||||
|
@ -251,12 +240,9 @@ impl File {
|
|||
|
||||
/// Queries information about the underlying file.
|
||||
pub fn stat(&mut self) -> IoResult<FileStat> {
|
||||
let err = match self.fd.fstat() {
|
||||
Ok(s) => Ok(from_rtio(s)),
|
||||
Err(e) => Err(IoError::from_rtio_error(e)),
|
||||
};
|
||||
err.update_err("couldn't fstat file",
|
||||
|e| format!("{}; path={}", e, self.path.display()))
|
||||
self.fd.fstat()
|
||||
.update_err("couldn't fstat file", |e|
|
||||
format!("{}; path={}", e, self.path.display()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -282,41 +268,9 @@ impl File {
|
|||
/// user lacks permissions to remove the file, or if some other filesystem-level
|
||||
/// error occurs.
|
||||
pub fn unlink(path: &Path) -> IoResult<()> {
|
||||
return match do_unlink(path) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(e) => {
|
||||
// On unix, a readonly file can be successfully removed. On windows,
|
||||
// however, it cannot. To keep the two platforms in line with
|
||||
// respect to their behavior, catch this case on windows, attempt to
|
||||
// change it to read-write, and then remove the file.
|
||||
if cfg!(windows) && e.kind == io::PermissionDenied {
|
||||
let stat = match stat(path) {
|
||||
Ok(stat) => stat,
|
||||
Err(..) => return Err(e),
|
||||
};
|
||||
if stat.perm.intersects(io::USER_WRITE) { return Err(e) }
|
||||
|
||||
match chmod(path, stat.perm | io::USER_WRITE) {
|
||||
Ok(()) => do_unlink(path),
|
||||
Err(..) => {
|
||||
// Try to put it back as we found it
|
||||
let _ = chmod(path, stat.perm);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fn do_unlink(path: &Path) -> IoResult<()> {
|
||||
let err = LocalIo::maybe_raise(|io| {
|
||||
io.fs_unlink(&path.to_c_str())
|
||||
}).map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't unlink path",
|
||||
|e| format!("{}; path={}", e, path.display()))
|
||||
}
|
||||
fs_imp::unlink(path)
|
||||
.update_err("couldn't unlink path", |e|
|
||||
format!("{}; path={}", e, path.display()))
|
||||
}
|
||||
|
||||
/// Given a path, query the file system to get information about a file,
|
||||
|
@ -341,12 +295,9 @@ pub fn unlink(path: &Path) -> IoResult<()> {
|
|||
/// to perform a `stat` call on the given `path` or if there is no entry in the
|
||||
/// filesystem at the provided path.
|
||||
pub fn stat(path: &Path) -> IoResult<FileStat> {
|
||||
let err = match LocalIo::maybe_raise(|io| io.fs_stat(&path.to_c_str())) {
|
||||
Ok(s) => Ok(from_rtio(s)),
|
||||
Err(e) => Err(IoError::from_rtio_error(e)),
|
||||
};
|
||||
err.update_err("couldn't stat path",
|
||||
|e| format!("{}; path={}", e, path.display()))
|
||||
fs_imp::stat(path)
|
||||
.update_err("couldn't stat path", |e|
|
||||
format!("{}; path={}", e, path.display()))
|
||||
}
|
||||
|
||||
/// Perform the same operation as the `stat` function, except that this
|
||||
|
@ -358,53 +309,9 @@ pub fn stat(path: &Path) -> IoResult<FileStat> {
|
|||
///
|
||||
/// See `stat`
|
||||
pub fn lstat(path: &Path) -> IoResult<FileStat> {
|
||||
let err = match LocalIo::maybe_raise(|io| io.fs_lstat(&path.to_c_str())) {
|
||||
Ok(s) => Ok(from_rtio(s)),
|
||||
Err(e) => Err(IoError::from_rtio_error(e)),
|
||||
};
|
||||
err.update_err("couldn't lstat path",
|
||||
|e| format!("{}; path={}", e, path.display()))
|
||||
}
|
||||
|
||||
fn from_rtio(s: rtio::FileStat) -> FileStat {
|
||||
#[cfg(windows)]
|
||||
type Mode = libc::c_int;
|
||||
#[cfg(unix)]
|
||||
type Mode = libc::mode_t;
|
||||
|
||||
let rtio::FileStat {
|
||||
size, kind, perm, created, modified,
|
||||
accessed, device, inode, rdev,
|
||||
nlink, uid, gid, blksize, blocks, flags, gen
|
||||
} = s;
|
||||
|
||||
FileStat {
|
||||
size: size,
|
||||
kind: match (kind as Mode) & libc::S_IFMT {
|
||||
libc::S_IFREG => io::TypeFile,
|
||||
libc::S_IFDIR => io::TypeDirectory,
|
||||
libc::S_IFIFO => io::TypeNamedPipe,
|
||||
libc::S_IFBLK => io::TypeBlockSpecial,
|
||||
libc::S_IFLNK => io::TypeSymlink,
|
||||
_ => io::TypeUnknown,
|
||||
},
|
||||
perm: FilePermission::from_bits_truncate(perm as u32),
|
||||
created: created,
|
||||
modified: modified,
|
||||
accessed: accessed,
|
||||
unstable: UnstableFileStat {
|
||||
device: device,
|
||||
inode: inode,
|
||||
rdev: rdev,
|
||||
nlink: nlink,
|
||||
uid: uid,
|
||||
gid: gid,
|
||||
blksize: blksize,
|
||||
blocks: blocks,
|
||||
flags: flags,
|
||||
gen: gen,
|
||||
},
|
||||
}
|
||||
fs_imp::lstat(path)
|
||||
.update_err("couldn't lstat path", |e|
|
||||
format!("{}; path={}", e, path.display()))
|
||||
}
|
||||
|
||||
/// Rename a file or directory to a new name.
|
||||
|
@ -424,12 +331,9 @@ fn from_rtio(s: rtio::FileStat) -> FileStat {
|
|||
/// the process lacks permissions to view the contents, or if some other
|
||||
/// intermittent I/O error occurs.
|
||||
pub fn rename(from: &Path, to: &Path) -> IoResult<()> {
|
||||
let err = LocalIo::maybe_raise(|io| {
|
||||
io.fs_rename(&from.to_c_str(), &to.to_c_str())
|
||||
}).map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't rename path", |e| {
|
||||
format!("{}; from={}; to={}", e, from.display(), to.display())
|
||||
})
|
||||
fs_imp::rename(from, to)
|
||||
.update_err("couldn't rename path", |e|
|
||||
format!("{}; from={}; to={}", e, from.display(), to.display()))
|
||||
}
|
||||
|
||||
/// Copies the contents of one file to another. This function will also
|
||||
|
@ -462,8 +366,9 @@ pub fn rename(from: &Path, to: &Path) -> IoResult<()> {
|
|||
/// being created and then destroyed by this operation.
|
||||
pub fn copy(from: &Path, to: &Path) -> IoResult<()> {
|
||||
fn update_err<T>(result: IoResult<T>, from: &Path, to: &Path) -> IoResult<T> {
|
||||
result.update_err("couldn't copy path",
|
||||
|e| format!("{}; from={}; to={}", e, from.display(), to.display()))
|
||||
result.update_err("couldn't copy path", |e| {
|
||||
format!("{}; from={}; to={}", e, from.display(), to.display())
|
||||
})
|
||||
}
|
||||
|
||||
if !from.is_file() {
|
||||
|
@ -512,45 +417,33 @@ pub fn copy(from: &Path, to: &Path) -> IoResult<()> {
|
|||
/// the process lacks permissions to change the attributes of the file, or if
|
||||
/// some other I/O error is encountered.
|
||||
pub fn chmod(path: &Path, mode: io::FilePermission) -> IoResult<()> {
|
||||
let err = LocalIo::maybe_raise(|io| {
|
||||
io.fs_chmod(&path.to_c_str(), mode.bits() as uint)
|
||||
}).map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't chmod path", |e| {
|
||||
format!("{}; path={}; mode={}", e, path.display(), mode)
|
||||
})
|
||||
fs_imp::chmod(path, mode.bits() as uint)
|
||||
.update_err("couldn't chmod path", |e|
|
||||
format!("{}; path={}; mode={}", e, path.display(), mode))
|
||||
}
|
||||
|
||||
/// Change the user and group owners of a file at the specified path.
|
||||
pub fn chown(path: &Path, uid: int, gid: int) -> IoResult<()> {
|
||||
let err = LocalIo::maybe_raise(|io| {
|
||||
io.fs_chown(&path.to_c_str(), uid, gid)
|
||||
}).map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't chown path", |e| {
|
||||
format!("{}; path={}; uid={}; gid={}", e, path.display(), uid, gid)
|
||||
})
|
||||
fs_imp::chown(path, uid, gid)
|
||||
.update_err("couldn't chown path", |e|
|
||||
format!("{}; path={}; uid={}; gid={}", e, path.display(), uid, gid))
|
||||
}
|
||||
|
||||
/// Creates a new hard link on the filesystem. The `dst` path will be a
|
||||
/// link pointing to the `src` path. Note that systems often require these
|
||||
/// two paths to both be located on the same filesystem.
|
||||
pub fn link(src: &Path, dst: &Path) -> IoResult<()> {
|
||||
let err = LocalIo::maybe_raise(|io| {
|
||||
io.fs_link(&src.to_c_str(), &dst.to_c_str())
|
||||
}).map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't link path", |e| {
|
||||
format!("{}; src={}; dest={}", e, src.display(), dst.display())
|
||||
})
|
||||
fs_imp::link(src, dst)
|
||||
.update_err("couldn't link path", |e|
|
||||
format!("{}; src={}; dest={}", e, src.display(), dst.display()))
|
||||
}
|
||||
|
||||
/// Creates a new symbolic link on the filesystem. The `dst` path will be a
|
||||
/// symlink pointing to the `src` path.
|
||||
pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> {
|
||||
let err = LocalIo::maybe_raise(|io| {
|
||||
io.fs_symlink(&src.to_c_str(), &dst.to_c_str())
|
||||
}).map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't symlink path", |e| {
|
||||
format!("{}; src={}; dest={}", e, src.display(), dst.display())
|
||||
})
|
||||
fs_imp::symlink(src, dst)
|
||||
.update_err("couldn't symlink path", |e|
|
||||
format!("{}; src={}; dest={}", e, src.display(), dst.display()))
|
||||
}
|
||||
|
||||
/// Reads a symlink, returning the file that the symlink points to.
|
||||
|
@ -560,11 +453,9 @@ pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> {
|
|||
/// This function will return an error on failure. Failure conditions include
|
||||
/// reading a file that does not exist or reading a file which is not a symlink.
|
||||
pub fn readlink(path: &Path) -> IoResult<Path> {
|
||||
let err = LocalIo::maybe_raise(|io| {
|
||||
Ok(Path::new(try!(io.fs_readlink(&path.to_c_str()))))
|
||||
}).map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't resolve symlink for path",
|
||||
|e| format!("{}; path={}", e, path.display()))
|
||||
fs_imp::readlink(path)
|
||||
.update_err("couldn't resolve symlink for path", |e|
|
||||
format!("{}; path={}", e, path.display()))
|
||||
}
|
||||
|
||||
/// Create a new, empty directory at the provided path
|
||||
|
@ -585,12 +476,9 @@ pub fn readlink(path: &Path) -> IoResult<Path> {
|
|||
/// This function will return an error if the user lacks permissions to make a
|
||||
/// new directory at the provided `path`, or if the directory already exists.
|
||||
pub fn mkdir(path: &Path, mode: FilePermission) -> IoResult<()> {
|
||||
let err = LocalIo::maybe_raise(|io| {
|
||||
io.fs_mkdir(&path.to_c_str(), mode.bits() as uint)
|
||||
}).map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't create directory", |e| {
|
||||
format!("{}; path={}; mode={}", e, path.display(), mode)
|
||||
})
|
||||
fs_imp::mkdir(path, mode.bits() as uint)
|
||||
.update_err("couldn't create directory", |e|
|
||||
format!("{}; path={}; mode={}", e, path.display(), mode))
|
||||
}
|
||||
|
||||
/// Remove an existing, empty directory
|
||||
|
@ -610,11 +498,9 @@ pub fn mkdir(path: &Path, mode: FilePermission) -> IoResult<()> {
|
|||
/// This function will return an error if the user lacks permissions to remove
|
||||
/// the directory at the provided `path`, or if the directory isn't empty.
|
||||
pub fn rmdir(path: &Path) -> IoResult<()> {
|
||||
let err = LocalIo::maybe_raise(|io| {
|
||||
io.fs_rmdir(&path.to_c_str())
|
||||
}).map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't remove directory",
|
||||
|e| format!("{}; path={}", e, path.display()))
|
||||
fs_imp::rmdir(path)
|
||||
.update_err("couldn't remove directory", |e|
|
||||
format!("{}; path={}", e, path.display()))
|
||||
}
|
||||
|
||||
/// Retrieve a vector containing all entries within a provided directory
|
||||
|
@ -650,13 +536,9 @@ pub fn rmdir(path: &Path) -> IoResult<()> {
|
|||
/// the process lacks permissions to view the contents or if the `path` points
|
||||
/// at a non-directory file
|
||||
pub fn readdir(path: &Path) -> IoResult<Vec<Path>> {
|
||||
let err = LocalIo::maybe_raise(|io| {
|
||||
Ok(try!(io.fs_readdir(&path.to_c_str(), 0)).into_iter().map(|a| {
|
||||
Path::new(a)
|
||||
}).collect())
|
||||
}).map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't read directory",
|
||||
|e| format!("{}; path={}", e, path.display()))
|
||||
fs_imp::readdir(path)
|
||||
.update_err("couldn't read directory",
|
||||
|e| format!("{}; path={}", e, path.display()))
|
||||
}
|
||||
|
||||
/// Returns an iterator which will recursively walk the directory structure
|
||||
|
@ -666,8 +548,7 @@ pub fn readdir(path: &Path) -> IoResult<Vec<Path>> {
|
|||
pub fn walk_dir(path: &Path) -> IoResult<Directories> {
|
||||
Ok(Directories {
|
||||
stack: try!(readdir(path).update_err("couldn't walk directory",
|
||||
|e| format!("{}; path={}",
|
||||
e, path.display())))
|
||||
|e| format!("{}; path={}", e, path.display())))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -681,12 +562,7 @@ impl Iterator<Path> for Directories {
|
|||
match self.stack.pop() {
|
||||
Some(path) => {
|
||||
if path.is_dir() {
|
||||
let result = readdir(&path)
|
||||
.update_err("couldn't advance Directories iterator",
|
||||
|e| format!("{}; path={}",
|
||||
e, path.display()));
|
||||
|
||||
match result {
|
||||
match readdir(&path) {
|
||||
Ok(dirs) => { self.stack.extend(dirs.into_iter()); }
|
||||
Err(..) => {}
|
||||
}
|
||||
|
@ -804,11 +680,9 @@ pub fn rmdir_recursive(path: &Path) -> IoResult<()> {
|
|||
/// be in milliseconds.
|
||||
// FIXME(#10301) these arguments should not be u64
|
||||
pub fn change_file_times(path: &Path, atime: u64, mtime: u64) -> IoResult<()> {
|
||||
let err = LocalIo::maybe_raise(|io| {
|
||||
io.fs_utime(&path.to_c_str(), atime, mtime)
|
||||
}).map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't change_file_times",
|
||||
|e| format!("{}; path={}", e, path.display()))
|
||||
fs_imp::utime(path, atime, mtime)
|
||||
.update_err("couldn't change_file_times", |e|
|
||||
format!("{}; path={}", e, path.display()))
|
||||
}
|
||||
|
||||
impl Reader for File {
|
||||
|
@ -819,12 +693,11 @@ impl Reader for File {
|
|||
e, file.path.display()))
|
||||
}
|
||||
|
||||
let result = update_err(self.fd.read(buf)
|
||||
.map_err(IoError::from_rtio_error), self);
|
||||
let result = update_err(self.fd.read(buf), self);
|
||||
|
||||
match result {
|
||||
Ok(read) => {
|
||||
self.last_nread = read;
|
||||
self.last_nread = read as int;
|
||||
match read {
|
||||
0 => update_err(Err(standard_error(io::EndOfFile)), self),
|
||||
_ => Ok(read as uint)
|
||||
|
@ -837,32 +710,27 @@ impl Reader for File {
|
|||
|
||||
impl Writer for File {
|
||||
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
||||
let err = self.fd.write(buf).map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't write to file",
|
||||
|e| format!("{}; path={}", e, self.path.display()))
|
||||
self.fd.write(buf)
|
||||
.update_err("couldn't write to file",
|
||||
|e| format!("{}; path={}", e, self.path.display()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for File {
|
||||
fn tell(&self) -> IoResult<u64> {
|
||||
let err = self.fd.tell().map_err(IoError::from_rtio_error);
|
||||
err.update_err("couldn't retrieve file cursor (`tell`)",
|
||||
|e| format!("{}; path={}", e, self.path.display()))
|
||||
self.fd.tell()
|
||||
.update_err("couldn't retrieve file cursor (`tell`)",
|
||||
|e| format!("{}; path={}", e, self.path.display()))
|
||||
}
|
||||
|
||||
fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
|
||||
let style = match style {
|
||||
SeekSet => rtio::SeekSet,
|
||||
SeekCur => rtio::SeekCur,
|
||||
SeekEnd => rtio::SeekEnd,
|
||||
};
|
||||
let err = match self.fd.seek(pos, style) {
|
||||
Ok(_) => {
|
||||
// successful seek resets EOF indicator
|
||||
self.last_nread = -1;
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(IoError::from_rtio_error(e)),
|
||||
Err(e) => Err(e),
|
||||
};
|
||||
err.update_err("couldn't seek in file",
|
||||
|e| format!("{}; path={}", e, self.path.display()))
|
||||
|
@ -942,6 +810,8 @@ fn access_string(access: FileAccess) -> &'static str {
|
|||
|
||||
#[cfg(test)]
|
||||
#[allow(unused_imports)]
|
||||
#[allow(unused_variables)]
|
||||
#[allow(unused_mut)]
|
||||
mod test {
|
||||
use prelude::*;
|
||||
use io::{SeekSet, SeekCur, SeekEnd, Read, Open, ReadWrite};
|
||||
|
|
|
@ -228,14 +228,13 @@ use error::{FromError, Error};
|
|||
use fmt;
|
||||
use int;
|
||||
use iter::Iterator;
|
||||
use libc;
|
||||
use mem::transmute;
|
||||
use ops::{BitOr, BitXor, BitAnd, Sub, Not};
|
||||
use option::{Option, Some, None};
|
||||
use os;
|
||||
use boxed::Box;
|
||||
use result::{Ok, Err, Result};
|
||||
use rt::rtio;
|
||||
use sys;
|
||||
use slice::{AsSlice, SlicePrelude};
|
||||
use str::{Str, StrPrelude};
|
||||
use str;
|
||||
|
@ -312,92 +311,12 @@ impl IoError {
|
|||
/// struct is filled with an allocated string describing the error
|
||||
/// in more detail, retrieved from the operating system.
|
||||
pub fn from_errno(errno: uint, detail: bool) -> IoError {
|
||||
|
||||
#[cfg(windows)]
|
||||
fn get_err(errno: i32) -> (IoErrorKind, &'static str) {
|
||||
match errno {
|
||||
libc::EOF => (EndOfFile, "end of file"),
|
||||
libc::ERROR_NO_DATA => (BrokenPipe, "the pipe is being closed"),
|
||||
libc::ERROR_FILE_NOT_FOUND => (FileNotFound, "file not found"),
|
||||
libc::ERROR_INVALID_NAME => (InvalidInput, "invalid file name"),
|
||||
libc::WSAECONNREFUSED => (ConnectionRefused, "connection refused"),
|
||||
libc::WSAECONNRESET => (ConnectionReset, "connection reset"),
|
||||
libc::ERROR_ACCESS_DENIED | libc::WSAEACCES =>
|
||||
(PermissionDenied, "permission denied"),
|
||||
libc::WSAEWOULDBLOCK => {
|
||||
(ResourceUnavailable, "resource temporarily unavailable")
|
||||
}
|
||||
libc::WSAENOTCONN => (NotConnected, "not connected"),
|
||||
libc::WSAECONNABORTED => (ConnectionAborted, "connection aborted"),
|
||||
libc::WSAEADDRNOTAVAIL => (ConnectionRefused, "address not available"),
|
||||
libc::WSAEADDRINUSE => (ConnectionRefused, "address in use"),
|
||||
libc::ERROR_BROKEN_PIPE => (EndOfFile, "the pipe has ended"),
|
||||
libc::ERROR_OPERATION_ABORTED =>
|
||||
(TimedOut, "operation timed out"),
|
||||
libc::WSAEINVAL => (InvalidInput, "invalid argument"),
|
||||
libc::ERROR_CALL_NOT_IMPLEMENTED =>
|
||||
(IoUnavailable, "function not implemented"),
|
||||
libc::ERROR_INVALID_HANDLE =>
|
||||
(MismatchedFileTypeForOperation,
|
||||
"invalid handle provided to function"),
|
||||
libc::ERROR_NOTHING_TO_TERMINATE =>
|
||||
(InvalidInput, "no process to kill"),
|
||||
|
||||
// libuv maps this error code to EISDIR. we do too. if it is found
|
||||
// to be incorrect, we can add in some more machinery to only
|
||||
// return this message when ERROR_INVALID_FUNCTION after certain
|
||||
// Windows calls.
|
||||
libc::ERROR_INVALID_FUNCTION => (InvalidInput,
|
||||
"illegal operation on a directory"),
|
||||
|
||||
_ => (OtherIoError, "unknown error")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn get_err(errno: i32) -> (IoErrorKind, &'static str) {
|
||||
// FIXME: this should probably be a bit more descriptive...
|
||||
match errno {
|
||||
libc::EOF => (EndOfFile, "end of file"),
|
||||
libc::ECONNREFUSED => (ConnectionRefused, "connection refused"),
|
||||
libc::ECONNRESET => (ConnectionReset, "connection reset"),
|
||||
libc::EPERM | libc::EACCES =>
|
||||
(PermissionDenied, "permission denied"),
|
||||
libc::EPIPE => (BrokenPipe, "broken pipe"),
|
||||
libc::ENOTCONN => (NotConnected, "not connected"),
|
||||
libc::ECONNABORTED => (ConnectionAborted, "connection aborted"),
|
||||
libc::EADDRNOTAVAIL => (ConnectionRefused, "address not available"),
|
||||
libc::EADDRINUSE => (ConnectionRefused, "address in use"),
|
||||
libc::ENOENT => (FileNotFound, "no such file or directory"),
|
||||
libc::EISDIR => (InvalidInput, "illegal operation on a directory"),
|
||||
libc::ENOSYS => (IoUnavailable, "function not implemented"),
|
||||
libc::EINVAL => (InvalidInput, "invalid argument"),
|
||||
libc::ENOTTY =>
|
||||
(MismatchedFileTypeForOperation,
|
||||
"file descriptor is not a TTY"),
|
||||
libc::ETIMEDOUT => (TimedOut, "operation timed out"),
|
||||
libc::ECANCELED => (TimedOut, "operation aborted"),
|
||||
|
||||
// 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")
|
||||
}
|
||||
}
|
||||
|
||||
let (kind, desc) = get_err(errno as i32);
|
||||
IoError {
|
||||
kind: kind,
|
||||
desc: desc,
|
||||
detail: if detail && kind == OtherIoError {
|
||||
Some(os::error_string(errno).as_slice().chars().map(|c| c.to_lowercase()).collect())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
let mut err = sys::decode_error(errno as i32);
|
||||
if detail && err.kind == OtherIoError {
|
||||
err.detail = Some(os::error_string(errno).as_slice().chars()
|
||||
.map(|c| c.to_lowercase()).collect())
|
||||
}
|
||||
err
|
||||
}
|
||||
|
||||
/// Retrieve the last error to occur as a (detailed) IoError.
|
||||
|
@ -409,17 +328,6 @@ impl IoError {
|
|||
pub fn last_error() -> IoError {
|
||||
IoError::from_errno(os::errno() as uint, true)
|
||||
}
|
||||
|
||||
fn from_rtio_error(err: rtio::IoError) -> IoError {
|
||||
let rtio::IoError { code, extra, detail } = err;
|
||||
let mut ioerr = IoError::from_errno(code, false);
|
||||
ioerr.detail = detail;
|
||||
ioerr.kind = match ioerr.kind {
|
||||
TimedOut if extra > 0 => ShortWrite(extra),
|
||||
k => k,
|
||||
};
|
||||
return ioerr;
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Show for IoError {
|
||||
|
|
|
@ -20,12 +20,10 @@ getaddrinfo()
|
|||
#![allow(missing_docs)]
|
||||
|
||||
use iter::Iterator;
|
||||
use io::{IoResult, IoError};
|
||||
use io::{IoResult};
|
||||
use io::net::ip::{SocketAddr, IpAddr};
|
||||
use option::{Option, Some, None};
|
||||
use result::{Ok, Err};
|
||||
use rt::rtio::{IoFactory, LocalIo};
|
||||
use rt::rtio;
|
||||
use sys;
|
||||
use vec::Vec;
|
||||
|
||||
/// Hints to the types of sockets that are desired when looking up hosts
|
||||
|
@ -94,31 +92,7 @@ pub fn get_host_addresses(host: &str) -> IoResult<Vec<IpAddr>> {
|
|||
#[allow(unused_variables)]
|
||||
fn lookup(hostname: Option<&str>, servname: Option<&str>, hint: Option<Hint>)
|
||||
-> IoResult<Vec<Info>> {
|
||||
let hint = hint.map(|Hint { family, socktype, protocol, flags }| {
|
||||
rtio::AddrinfoHint {
|
||||
family: family,
|
||||
socktype: 0, // FIXME: this should use the above variable
|
||||
protocol: 0, // FIXME: this should use the above variable
|
||||
flags: flags,
|
||||
}
|
||||
});
|
||||
match LocalIo::maybe_raise(|io| {
|
||||
io.get_host_addresses(hostname, servname, hint)
|
||||
}) {
|
||||
Ok(v) => Ok(v.into_iter().map(|info| {
|
||||
Info {
|
||||
address: SocketAddr {
|
||||
ip: super::from_rtio(info.address.ip),
|
||||
port: info.address.port,
|
||||
},
|
||||
family: info.family,
|
||||
socktype: None, // FIXME: this should use the above variable
|
||||
protocol: None, // FIXME: this should use the above variable
|
||||
flags: info.flags,
|
||||
}
|
||||
}).collect()),
|
||||
Err(e) => Err(IoError::from_rtio_error(e)),
|
||||
}
|
||||
sys::addrinfo::get_host_addresses(hostname, servname, hint)
|
||||
}
|
||||
|
||||
// Ignored on android since we cannot give tcp/ip
|
||||
|
|
|
@ -12,9 +12,8 @@
|
|||
|
||||
use io::{IoError, IoResult, InvalidInput};
|
||||
use option::None;
|
||||
use result::{Result, Ok, Err};
|
||||
use rt::rtio;
|
||||
use self::ip::{Ipv4Addr, Ipv6Addr, IpAddr, SocketAddr, ToSocketAddr};
|
||||
use result::{Ok, Err};
|
||||
use self::ip::{SocketAddr, ToSocketAddr};
|
||||
|
||||
pub use self::addrinfo::get_host_addresses;
|
||||
|
||||
|
@ -24,46 +23,6 @@ pub mod udp;
|
|||
pub mod ip;
|
||||
pub mod pipe;
|
||||
|
||||
fn to_rtio(ip: IpAddr) -> rtio::IpAddr {
|
||||
match ip {
|
||||
Ipv4Addr(a, b, c, d) => rtio::Ipv4Addr(a, b, c, d),
|
||||
Ipv6Addr(a, b, c, d, e, f, g, h) => {
|
||||
rtio::Ipv6Addr(a, b, c, d, e, f, g, h)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn from_rtio(ip: rtio::IpAddr) -> IpAddr {
|
||||
match ip {
|
||||
rtio::Ipv4Addr(a, b, c, d) => Ipv4Addr(a, b, c, d),
|
||||
rtio::Ipv6Addr(a, b, c, d, e, f, g, h) => {
|
||||
Ipv6Addr(a, b, c, d, e, f, g, h)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn with_addresses_io<A: ToSocketAddr, T>(
|
||||
addr: A,
|
||||
action: |&mut rtio::IoFactory, rtio::SocketAddr| -> Result<T, rtio::IoError>
|
||||
) -> Result<T, IoError> {
|
||||
const DEFAULT_ERROR: IoError = IoError {
|
||||
kind: InvalidInput,
|
||||
desc: "no addresses found for hostname",
|
||||
detail: None
|
||||
};
|
||||
|
||||
let addresses = try!(addr.to_socket_addr_all());
|
||||
let mut err = DEFAULT_ERROR;
|
||||
for addr in addresses.into_iter() {
|
||||
let addr = rtio::SocketAddr { ip: to_rtio(addr.ip), port: addr.port };
|
||||
match rtio::LocalIo::maybe_raise(|io| action(io, addr)) {
|
||||
Ok(r) => return Ok(r),
|
||||
Err(e) => err = IoError::from_rtio_error(e)
|
||||
}
|
||||
}
|
||||
Err(err)
|
||||
}
|
||||
|
||||
fn with_addresses<A: ToSocketAddr, T>(addr: A, action: |SocketAddr| -> IoResult<T>)
|
||||
-> IoResult<T> {
|
||||
const DEFAULT_ERROR: IoError = IoError {
|
||||
|
|
|
@ -26,17 +26,20 @@ instances as clients.
|
|||
|
||||
use prelude::*;
|
||||
|
||||
use io::{Listener, Acceptor, IoResult, IoError, TimedOut, standard_error};
|
||||
use rt::rtio::{IoFactory, LocalIo, RtioUnixListener};
|
||||
use rt::rtio::{RtioUnixAcceptor, RtioPipe};
|
||||
use io::{Listener, Acceptor, IoResult, TimedOut, standard_error};
|
||||
use time::Duration;
|
||||
|
||||
use sys::pipe::UnixStream as UnixStreamImp;
|
||||
use sys::pipe::UnixListener as UnixListenerImp;
|
||||
use sys::pipe::UnixAcceptor as UnixAcceptorImp;
|
||||
|
||||
/// A stream which communicates over a named pipe.
|
||||
pub struct UnixStream {
|
||||
obj: Box<RtioPipe + Send>,
|
||||
inner: UnixStreamImp,
|
||||
}
|
||||
|
||||
impl UnixStream {
|
||||
|
||||
/// Connect to a pipe named by `path`. This will attempt to open a
|
||||
/// connection to the underlying socket.
|
||||
///
|
||||
|
@ -53,9 +56,8 @@ impl UnixStream {
|
|||
/// stream.write([1, 2, 3]);
|
||||
/// ```
|
||||
pub fn connect<P: ToCStr>(path: &P) -> IoResult<UnixStream> {
|
||||
LocalIo::maybe_raise(|io| {
|
||||
io.unix_connect(&path.to_c_str(), None).map(|p| UnixStream { obj: p })
|
||||
}).map_err(IoError::from_rtio_error)
|
||||
UnixStreamImp::connect(&path.to_c_str(), None)
|
||||
.map(|inner| UnixStream { inner: inner })
|
||||
}
|
||||
|
||||
/// Connect to a pipe named by `path`, timing out if the specified number of
|
||||
|
@ -73,10 +75,8 @@ impl UnixStream {
|
|||
return Err(standard_error(TimedOut));
|
||||
}
|
||||
|
||||
LocalIo::maybe_raise(|io| {
|
||||
let s = io.unix_connect(&path.to_c_str(), Some(timeout.num_milliseconds() as u64));
|
||||
s.map(|p| UnixStream { obj: p })
|
||||
}).map_err(IoError::from_rtio_error)
|
||||
UnixStreamImp::connect(&path.to_c_str(), Some(timeout.num_milliseconds() as u64))
|
||||
.map(|inner| UnixStream { inner: inner })
|
||||
}
|
||||
|
||||
|
||||
|
@ -88,7 +88,7 @@ impl UnixStream {
|
|||
/// Note that this method affects all cloned handles associated with this
|
||||
/// stream, not just this one handle.
|
||||
pub fn close_read(&mut self) -> IoResult<()> {
|
||||
self.obj.close_read().map_err(IoError::from_rtio_error)
|
||||
self.inner.close_read()
|
||||
}
|
||||
|
||||
/// Closes the writing half of this connection.
|
||||
|
@ -99,7 +99,7 @@ impl UnixStream {
|
|||
/// Note that this method affects all cloned handles associated with this
|
||||
/// stream, not just this one handle.
|
||||
pub fn close_write(&mut self) -> IoResult<()> {
|
||||
self.obj.close_write().map_err(IoError::from_rtio_error)
|
||||
self.inner.close_write()
|
||||
}
|
||||
|
||||
/// Sets the read/write timeout for this socket.
|
||||
|
@ -107,7 +107,7 @@ impl UnixStream {
|
|||
/// For more information, see `TcpStream::set_timeout`
|
||||
#[experimental = "the timeout argument may change in type and value"]
|
||||
pub fn set_timeout(&mut self, timeout_ms: Option<u64>) {
|
||||
self.obj.set_timeout(timeout_ms)
|
||||
self.inner.set_timeout(timeout_ms)
|
||||
}
|
||||
|
||||
/// Sets the read timeout for this socket.
|
||||
|
@ -115,7 +115,7 @@ impl UnixStream {
|
|||
/// For more information, see `TcpStream::set_timeout`
|
||||
#[experimental = "the timeout argument may change in type and value"]
|
||||
pub fn set_read_timeout(&mut self, timeout_ms: Option<u64>) {
|
||||
self.obj.set_read_timeout(timeout_ms)
|
||||
self.inner.set_read_timeout(timeout_ms)
|
||||
}
|
||||
|
||||
/// Sets the write timeout for this socket.
|
||||
|
@ -123,36 +123,35 @@ impl UnixStream {
|
|||
/// For more information, see `TcpStream::set_timeout`
|
||||
#[experimental = "the timeout argument may change in type and value"]
|
||||
pub fn set_write_timeout(&mut self, timeout_ms: Option<u64>) {
|
||||
self.obj.set_write_timeout(timeout_ms)
|
||||
self.inner.set_write_timeout(timeout_ms)
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for UnixStream {
|
||||
fn clone(&self) -> UnixStream {
|
||||
UnixStream { obj: self.obj.clone() }
|
||||
UnixStream { inner: self.inner.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Reader for UnixStream {
|
||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
|
||||
self.obj.read(buf).map_err(IoError::from_rtio_error)
|
||||
self.inner.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl Writer for UnixStream {
|
||||
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
||||
self.obj.write(buf).map_err(IoError::from_rtio_error)
|
||||
self.inner.write(buf)
|
||||
}
|
||||
}
|
||||
|
||||
/// A value that can listen for incoming named pipe connection requests.
|
||||
pub struct UnixListener {
|
||||
/// The internal, opaque runtime Unix listener.
|
||||
obj: Box<RtioUnixListener + Send>,
|
||||
inner: UnixListenerImp,
|
||||
}
|
||||
|
||||
impl UnixListener {
|
||||
|
||||
/// Creates a new listener, ready to receive incoming connections on the
|
||||
/// specified socket. The server will be named by `path`.
|
||||
///
|
||||
|
@ -175,24 +174,22 @@ impl UnixListener {
|
|||
/// # }
|
||||
/// ```
|
||||
pub fn bind<P: ToCStr>(path: &P) -> IoResult<UnixListener> {
|
||||
LocalIo::maybe_raise(|io| {
|
||||
io.unix_bind(&path.to_c_str()).map(|s| UnixListener { obj: s })
|
||||
}).map_err(IoError::from_rtio_error)
|
||||
UnixListenerImp::bind(&path.to_c_str())
|
||||
.map(|inner| UnixListener { inner: inner })
|
||||
}
|
||||
}
|
||||
|
||||
impl Listener<UnixStream, UnixAcceptor> for UnixListener {
|
||||
fn listen(self) -> IoResult<UnixAcceptor> {
|
||||
self.obj.listen().map(|obj| {
|
||||
UnixAcceptor { obj: obj }
|
||||
}).map_err(IoError::from_rtio_error)
|
||||
self.inner.listen()
|
||||
.map(|inner| UnixAcceptor { inner: inner })
|
||||
}
|
||||
}
|
||||
|
||||
/// A value that can accept named pipe connections, returned from `listen()`.
|
||||
pub struct UnixAcceptor {
|
||||
/// The internal, opaque runtime Unix acceptor.
|
||||
obj: Box<RtioUnixAcceptor + Send>,
|
||||
inner: UnixAcceptorImp
|
||||
}
|
||||
|
||||
impl UnixAcceptor {
|
||||
|
@ -210,7 +207,7 @@ impl UnixAcceptor {
|
|||
#[experimental = "the name and arguments to this function are likely \
|
||||
to change"]
|
||||
pub fn set_timeout(&mut self, timeout_ms: Option<u64>) {
|
||||
self.obj.set_timeout(timeout_ms)
|
||||
self.inner.set_timeout(timeout_ms)
|
||||
}
|
||||
|
||||
/// Closes the accepting capabilities of this acceptor.
|
||||
|
@ -219,15 +216,15 @@ impl UnixAcceptor {
|
|||
/// more information can be found in that documentation.
|
||||
#[experimental]
|
||||
pub fn close_accept(&mut self) -> IoResult<()> {
|
||||
self.obj.close_accept().map_err(IoError::from_rtio_error)
|
||||
self.inner.close_accept()
|
||||
}
|
||||
}
|
||||
|
||||
impl Acceptor<UnixStream> for UnixAcceptor {
|
||||
fn accept(&mut self) -> IoResult<UnixStream> {
|
||||
self.obj.accept().map(|s| {
|
||||
UnixStream { obj: s }
|
||||
}).map_err(IoError::from_rtio_error)
|
||||
self.inner.accept().map(|s| {
|
||||
UnixStream { inner: s }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -246,7 +243,7 @@ impl Clone for UnixAcceptor {
|
|||
/// This function is useful for creating a handle to invoke `close_accept`
|
||||
/// on to wake up any other task blocked in `accept`.
|
||||
fn clone(&self) -> UnixAcceptor {
|
||||
UnixAcceptor { obj: self.obj.clone() }
|
||||
UnixAcceptor { inner: self.inner.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,19 +20,17 @@
|
|||
use clone::Clone;
|
||||
use io::IoResult;
|
||||
use iter::Iterator;
|
||||
use result::{Ok,Err};
|
||||
use result::Err;
|
||||
use io::net::ip::{SocketAddr, ToSocketAddr};
|
||||
use io::IoError;
|
||||
use io::{Reader, Writer, Listener, Acceptor};
|
||||
use io::{standard_error, TimedOut};
|
||||
use kinds::Send;
|
||||
use option::{None, Some, Option};
|
||||
use boxed::Box;
|
||||
use rt::rtio::{IoFactory, RtioSocket, RtioTcpListener};
|
||||
use rt::rtio::{RtioTcpAcceptor, RtioTcpStream};
|
||||
use rt::rtio;
|
||||
use time::Duration;
|
||||
|
||||
use sys::tcp::TcpStream as TcpStreamImp;
|
||||
use sys::tcp::TcpListener as TcpListenerImp;
|
||||
use sys::tcp::TcpAcceptor as TcpAcceptorImp;
|
||||
|
||||
/// A structure which represents a TCP stream between a local socket and a
|
||||
/// remote socket.
|
||||
///
|
||||
|
@ -50,12 +48,12 @@ use time::Duration;
|
|||
/// drop(stream); // close the connection
|
||||
/// ```
|
||||
pub struct TcpStream {
|
||||
obj: Box<RtioTcpStream + Send>,
|
||||
inner: TcpStreamImp,
|
||||
}
|
||||
|
||||
impl TcpStream {
|
||||
fn new(s: Box<RtioTcpStream + Send>) -> TcpStream {
|
||||
TcpStream { obj: s }
|
||||
fn new(s: TcpStreamImp) -> TcpStream {
|
||||
TcpStream { inner: s }
|
||||
}
|
||||
|
||||
/// Open a TCP connection to a remote host.
|
||||
|
@ -64,7 +62,9 @@ impl TcpStream {
|
|||
/// trait can be supplied for the address; see this trait documentation for
|
||||
/// concrete examples.
|
||||
pub fn connect<A: ToSocketAddr>(addr: A) -> IoResult<TcpStream> {
|
||||
super::with_addresses_io(addr, |io, addr| io.tcp_connect(addr, None).map(TcpStream::new))
|
||||
super::with_addresses(addr, |addr| {
|
||||
TcpStreamImp::connect(addr, None).map(TcpStream::new)
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a TCP connection to a remote socket address, timing out after
|
||||
|
@ -86,39 +86,26 @@ impl TcpStream {
|
|||
return Err(standard_error(TimedOut));
|
||||
}
|
||||
|
||||
super::with_addresses_io(addr, |io, addr|
|
||||
io.tcp_connect(addr, Some(timeout.num_milliseconds() as u64)).map(TcpStream::new)
|
||||
)
|
||||
super::with_addresses(addr, |addr| {
|
||||
TcpStreamImp::connect(addr, Some(timeout.num_milliseconds() as u64))
|
||||
.map(TcpStream::new)
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the socket address of the remote peer of this TCP connection.
|
||||
pub fn peer_name(&mut self) -> IoResult<SocketAddr> {
|
||||
match self.obj.peer_name() {
|
||||
Ok(rtio::SocketAddr { ip, port }) => {
|
||||
Ok(SocketAddr { ip: super::from_rtio(ip), port: port })
|
||||
}
|
||||
Err(e) => Err(IoError::from_rtio_error(e)),
|
||||
}
|
||||
self.inner.peer_name()
|
||||
}
|
||||
|
||||
/// Returns the socket address of the local half of this TCP connection.
|
||||
pub fn socket_name(&mut self) -> IoResult<SocketAddr> {
|
||||
match self.obj.socket_name() {
|
||||
Ok(rtio::SocketAddr { ip, port }) => {
|
||||
Ok(SocketAddr { ip: super::from_rtio(ip), port: port })
|
||||
}
|
||||
Err(e) => Err(IoError::from_rtio_error(e)),
|
||||
}
|
||||
self.inner.socket_name()
|
||||
}
|
||||
|
||||
/// Sets the nodelay flag on this connection to the boolean specified
|
||||
#[experimental]
|
||||
pub fn set_nodelay(&mut self, nodelay: bool) -> IoResult<()> {
|
||||
if nodelay {
|
||||
self.obj.nodelay()
|
||||
} else {
|
||||
self.obj.control_congestion()
|
||||
}.map_err(IoError::from_rtio_error)
|
||||
self.inner.set_nodelay(nodelay)
|
||||
}
|
||||
|
||||
/// Sets the keepalive timeout to the timeout specified.
|
||||
|
@ -128,10 +115,7 @@ impl TcpStream {
|
|||
/// specified time, in seconds.
|
||||
#[experimental]
|
||||
pub fn set_keepalive(&mut self, delay_in_seconds: Option<uint>) -> IoResult<()> {
|
||||
match delay_in_seconds {
|
||||
Some(i) => self.obj.keepalive(i),
|
||||
None => self.obj.letdie(),
|
||||
}.map_err(IoError::from_rtio_error)
|
||||
self.inner.set_keepalive(delay_in_seconds)
|
||||
}
|
||||
|
||||
/// Closes the reading half of this connection.
|
||||
|
@ -165,7 +149,7 @@ impl TcpStream {
|
|||
/// Note that this method affects all cloned handles associated with this
|
||||
/// stream, not just this one handle.
|
||||
pub fn close_read(&mut self) -> IoResult<()> {
|
||||
self.obj.close_read().map_err(IoError::from_rtio_error)
|
||||
self.inner.close_read()
|
||||
}
|
||||
|
||||
/// Closes the writing half of this connection.
|
||||
|
@ -176,7 +160,7 @@ impl TcpStream {
|
|||
/// Note that this method affects all cloned handles associated with this
|
||||
/// stream, not just this one handle.
|
||||
pub fn close_write(&mut self) -> IoResult<()> {
|
||||
self.obj.close_write().map_err(IoError::from_rtio_error)
|
||||
self.inner.close_write()
|
||||
}
|
||||
|
||||
/// Sets a timeout, in milliseconds, for blocking operations on this stream.
|
||||
|
@ -198,7 +182,7 @@ impl TcpStream {
|
|||
/// take a look at `set_read_timeout` and `set_write_timeout`.
|
||||
#[experimental = "the timeout argument may change in type and value"]
|
||||
pub fn set_timeout(&mut self, timeout_ms: Option<u64>) {
|
||||
self.obj.set_timeout(timeout_ms)
|
||||
self.inner.set_timeout(timeout_ms)
|
||||
}
|
||||
|
||||
/// Sets the timeout for read operations on this stream.
|
||||
|
@ -215,7 +199,7 @@ impl TcpStream {
|
|||
/// during the timeout period.
|
||||
#[experimental = "the timeout argument may change in type and value"]
|
||||
pub fn set_read_timeout(&mut self, timeout_ms: Option<u64>) {
|
||||
self.obj.set_read_timeout(timeout_ms)
|
||||
self.inner.set_read_timeout(timeout_ms)
|
||||
}
|
||||
|
||||
/// Sets the timeout for write operations on this stream.
|
||||
|
@ -242,7 +226,7 @@ impl TcpStream {
|
|||
/// asynchronous fashion after the call to write returns.
|
||||
#[experimental = "the timeout argument may change in type and value"]
|
||||
pub fn set_write_timeout(&mut self, timeout_ms: Option<u64>) {
|
||||
self.obj.set_write_timeout(timeout_ms)
|
||||
self.inner.set_write_timeout(timeout_ms)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -256,19 +240,19 @@ impl Clone for TcpStream {
|
|||
/// Instead, the first read will receive the first packet received, and the
|
||||
/// second read will receive the second packet.
|
||||
fn clone(&self) -> TcpStream {
|
||||
TcpStream { obj: self.obj.clone() }
|
||||
TcpStream { inner: self.inner.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Reader for TcpStream {
|
||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
|
||||
self.obj.read(buf).map_err(IoError::from_rtio_error)
|
||||
self.inner.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl Writer for TcpStream {
|
||||
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
||||
self.obj.write(buf).map_err(IoError::from_rtio_error)
|
||||
self.inner.write(buf)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -309,7 +293,7 @@ impl Writer for TcpStream {
|
|||
/// # }
|
||||
/// ```
|
||||
pub struct TcpListener {
|
||||
obj: Box<RtioTcpListener + Send>,
|
||||
inner: TcpListenerImp,
|
||||
}
|
||||
|
||||
impl TcpListener {
|
||||
|
@ -324,26 +308,20 @@ impl TcpListener {
|
|||
/// The address type can be any implementor of `ToSocketAddr` trait. See its
|
||||
/// documentation for concrete examples.
|
||||
pub fn bind<A: ToSocketAddr>(addr: A) -> IoResult<TcpListener> {
|
||||
super::with_addresses_io(addr, |io, addr| io.tcp_bind(addr).map(|l| TcpListener { obj: l }))
|
||||
super::with_addresses(addr, |addr| {
|
||||
TcpListenerImp::bind(addr).map(|inner| TcpListener { inner: inner })
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the local socket address of this listener.
|
||||
pub fn socket_name(&mut self) -> IoResult<SocketAddr> {
|
||||
match self.obj.socket_name() {
|
||||
Ok(rtio::SocketAddr { ip, port }) => {
|
||||
Ok(SocketAddr { ip: super::from_rtio(ip), port: port })
|
||||
}
|
||||
Err(e) => Err(IoError::from_rtio_error(e)),
|
||||
}
|
||||
self.inner.socket_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl Listener<TcpStream, TcpAcceptor> for TcpListener {
|
||||
fn listen(self) -> IoResult<TcpAcceptor> {
|
||||
match self.obj.listen() {
|
||||
Ok(acceptor) => Ok(TcpAcceptor { obj: acceptor }),
|
||||
Err(e) => Err(IoError::from_rtio_error(e)),
|
||||
}
|
||||
self.inner.listen(128).map(|a| TcpAcceptor { inner: a })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -351,7 +329,7 @@ impl Listener<TcpStream, TcpAcceptor> for TcpListener {
|
|||
/// a `TcpListener`'s `listen` method, and this object can be used to accept new
|
||||
/// `TcpStream` instances.
|
||||
pub struct TcpAcceptor {
|
||||
obj: Box<RtioTcpAcceptor + Send>,
|
||||
inner: TcpAcceptorImp,
|
||||
}
|
||||
|
||||
impl TcpAcceptor {
|
||||
|
@ -399,7 +377,7 @@ impl TcpAcceptor {
|
|||
/// ```
|
||||
#[experimental = "the type of the argument and name of this function are \
|
||||
subject to change"]
|
||||
pub fn set_timeout(&mut self, ms: Option<u64>) { self.obj.set_timeout(ms); }
|
||||
pub fn set_timeout(&mut self, ms: Option<u64>) { self.inner.set_timeout(ms); }
|
||||
|
||||
/// Closes the accepting capabilities of this acceptor.
|
||||
///
|
||||
|
@ -445,16 +423,13 @@ impl TcpAcceptor {
|
|||
/// ```
|
||||
#[experimental]
|
||||
pub fn close_accept(&mut self) -> IoResult<()> {
|
||||
self.obj.close_accept().map_err(IoError::from_rtio_error)
|
||||
self.inner.close_accept()
|
||||
}
|
||||
}
|
||||
|
||||
impl Acceptor<TcpStream> for TcpAcceptor {
|
||||
fn accept(&mut self) -> IoResult<TcpStream> {
|
||||
match self.obj.accept(){
|
||||
Ok(s) => Ok(TcpStream::new(s)),
|
||||
Err(e) => Err(IoError::from_rtio_error(e)),
|
||||
}
|
||||
self.inner.accept().map(TcpStream::new)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -473,7 +448,7 @@ impl Clone for TcpAcceptor {
|
|||
/// This function is useful for creating a handle to invoke `close_accept`
|
||||
/// on to wake up any other task blocked in `accept`.
|
||||
fn clone(&self) -> TcpAcceptor {
|
||||
TcpAcceptor { obj: self.obj.clone() }
|
||||
TcpAcceptor { inner: self.inner.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1112,8 +1087,6 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn shutdown_smoke() {
|
||||
use rt::rtio::RtioTcpStream;
|
||||
|
||||
let addr = next_test_ip4();
|
||||
let a = TcpListener::bind(addr).unwrap().listen();
|
||||
spawn(proc() {
|
||||
|
@ -1124,7 +1097,7 @@ mod test {
|
|||
});
|
||||
|
||||
let mut s = TcpStream::connect(addr).unwrap();
|
||||
assert!(s.obj.close_write().is_ok());
|
||||
assert!(s.inner.close_write().is_ok());
|
||||
assert!(s.write([1]).is_err());
|
||||
assert_eq!(s.read_to_end(), Ok(vec!(1)));
|
||||
}
|
||||
|
|
|
@ -17,13 +17,10 @@
|
|||
|
||||
use clone::Clone;
|
||||
use io::net::ip::{SocketAddr, IpAddr, ToSocketAddr};
|
||||
use io::{Reader, Writer, IoResult, IoError};
|
||||
use kinds::Send;
|
||||
use boxed::Box;
|
||||
use io::{Reader, Writer, IoResult};
|
||||
use option::Option;
|
||||
use result::{Ok, Err};
|
||||
use rt::rtio::{RtioSocket, RtioUdpSocket, IoFactory};
|
||||
use rt::rtio;
|
||||
use sys::udp::UdpSocket as UdpSocketImp;
|
||||
|
||||
/// A User Datagram Protocol socket.
|
||||
///
|
||||
|
@ -60,7 +57,7 @@ use rt::rtio;
|
|||
/// }
|
||||
/// ```
|
||||
pub struct UdpSocket {
|
||||
obj: Box<RtioUdpSocket + Send>,
|
||||
inner: UdpSocketImp,
|
||||
}
|
||||
|
||||
impl UdpSocket {
|
||||
|
@ -69,18 +66,15 @@ impl UdpSocket {
|
|||
/// Address type can be any implementor of `ToSocketAddr` trait. See its
|
||||
/// documentation for concrete examples.
|
||||
pub fn bind<A: ToSocketAddr>(addr: A) -> IoResult<UdpSocket> {
|
||||
super::with_addresses_io(addr, |io, addr| io.udp_bind(addr).map(|s| UdpSocket { obj: s }))
|
||||
super::with_addresses(addr, |addr| {
|
||||
UdpSocketImp::bind(addr).map(|s| UdpSocket { inner: s })
|
||||
})
|
||||
}
|
||||
|
||||
/// Receives data from the socket. On success, returns the number of bytes
|
||||
/// read and the address from whence the data came.
|
||||
pub fn recv_from(&mut self, buf: &mut [u8]) -> IoResult<(uint, SocketAddr)> {
|
||||
match self.obj.recv_from(buf) {
|
||||
Ok((amt, rtio::SocketAddr { ip, port })) => {
|
||||
Ok((amt, SocketAddr { ip: super::from_rtio(ip), port: port }))
|
||||
}
|
||||
Err(e) => Err(IoError::from_rtio_error(e)),
|
||||
}
|
||||
self.inner.recv_from(buf)
|
||||
}
|
||||
|
||||
/// Sends data on the socket to the given address. Returns nothing on
|
||||
|
@ -89,10 +83,7 @@ impl UdpSocket {
|
|||
/// Address type can be any implementor of `ToSocketAddr` trait. See its
|
||||
/// documentation for concrete examples.
|
||||
pub fn send_to<A: ToSocketAddr>(&mut self, buf: &[u8], addr: A) -> IoResult<()> {
|
||||
super::with_addresses(addr, |addr| self.obj.send_to(buf, rtio::SocketAddr {
|
||||
ip: super::to_rtio(addr.ip),
|
||||
port: addr.port,
|
||||
}).map_err(IoError::from_rtio_error))
|
||||
super::with_addresses(addr, |addr| self.inner.send_to(buf, addr))
|
||||
}
|
||||
|
||||
/// Creates a `UdpStream`, which allows use of the `Reader` and `Writer`
|
||||
|
@ -112,24 +103,19 @@ impl UdpSocket {
|
|||
|
||||
/// Returns the socket address that this socket was created from.
|
||||
pub fn socket_name(&mut self) -> IoResult<SocketAddr> {
|
||||
match self.obj.socket_name() {
|
||||
Ok(a) => Ok(SocketAddr { ip: super::from_rtio(a.ip), port: a.port }),
|
||||
Err(e) => Err(IoError::from_rtio_error(e))
|
||||
}
|
||||
self.inner.socket_name()
|
||||
}
|
||||
|
||||
/// Joins a multicast IP address (becomes a member of it)
|
||||
#[experimental]
|
||||
pub fn join_multicast(&mut self, multi: IpAddr) -> IoResult<()> {
|
||||
let e = self.obj.join_multicast(super::to_rtio(multi));
|
||||
e.map_err(IoError::from_rtio_error)
|
||||
self.inner.join_multicast(multi)
|
||||
}
|
||||
|
||||
/// Leaves a multicast IP address (drops membership from it)
|
||||
#[experimental]
|
||||
pub fn leave_multicast(&mut self, multi: IpAddr) -> IoResult<()> {
|
||||
let e = self.obj.leave_multicast(super::to_rtio(multi));
|
||||
e.map_err(IoError::from_rtio_error)
|
||||
self.inner.leave_multicast(multi)
|
||||
}
|
||||
|
||||
/// Set the multicast loop flag to the specified value
|
||||
|
@ -137,33 +123,25 @@ impl UdpSocket {
|
|||
/// This lets multicast packets loop back to local sockets (if enabled)
|
||||
#[experimental]
|
||||
pub fn set_multicast_loop(&mut self, on: bool) -> IoResult<()> {
|
||||
if on {
|
||||
self.obj.loop_multicast_locally()
|
||||
} else {
|
||||
self.obj.dont_loop_multicast_locally()
|
||||
}.map_err(IoError::from_rtio_error)
|
||||
self.inner.set_multicast_loop(on)
|
||||
}
|
||||
|
||||
/// Sets the multicast TTL
|
||||
#[experimental]
|
||||
pub fn set_multicast_ttl(&mut self, ttl: int) -> IoResult<()> {
|
||||
self.obj.multicast_time_to_live(ttl).map_err(IoError::from_rtio_error)
|
||||
self.inner.multicast_time_to_live(ttl)
|
||||
}
|
||||
|
||||
/// Sets this socket's TTL
|
||||
#[experimental]
|
||||
pub fn set_ttl(&mut self, ttl: int) -> IoResult<()> {
|
||||
self.obj.time_to_live(ttl).map_err(IoError::from_rtio_error)
|
||||
self.inner.time_to_live(ttl)
|
||||
}
|
||||
|
||||
/// Sets the broadcast flag on or off
|
||||
#[experimental]
|
||||
pub fn set_broadcast(&mut self, broadcast: bool) -> IoResult<()> {
|
||||
if broadcast {
|
||||
self.obj.hear_broadcasts()
|
||||
} else {
|
||||
self.obj.ignore_broadcasts()
|
||||
}.map_err(IoError::from_rtio_error)
|
||||
self.inner.set_broadcast(broadcast)
|
||||
}
|
||||
|
||||
/// Sets the read/write timeout for this socket.
|
||||
|
@ -171,7 +149,7 @@ impl UdpSocket {
|
|||
/// For more information, see `TcpStream::set_timeout`
|
||||
#[experimental = "the timeout argument may change in type and value"]
|
||||
pub fn set_timeout(&mut self, timeout_ms: Option<u64>) {
|
||||
self.obj.set_timeout(timeout_ms)
|
||||
self.inner.set_timeout(timeout_ms)
|
||||
}
|
||||
|
||||
/// Sets the read timeout for this socket.
|
||||
|
@ -179,7 +157,7 @@ impl UdpSocket {
|
|||
/// For more information, see `TcpStream::set_timeout`
|
||||
#[experimental = "the timeout argument may change in type and value"]
|
||||
pub fn set_read_timeout(&mut self, timeout_ms: Option<u64>) {
|
||||
self.obj.set_read_timeout(timeout_ms)
|
||||
self.inner.set_read_timeout(timeout_ms)
|
||||
}
|
||||
|
||||
/// Sets the write timeout for this socket.
|
||||
|
@ -187,7 +165,7 @@ impl UdpSocket {
|
|||
/// For more information, see `TcpStream::set_timeout`
|
||||
#[experimental = "the timeout argument may change in type and value"]
|
||||
pub fn set_write_timeout(&mut self, timeout_ms: Option<u64>) {
|
||||
self.obj.set_write_timeout(timeout_ms)
|
||||
self.inner.set_write_timeout(timeout_ms)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -201,7 +179,7 @@ impl Clone for UdpSocket {
|
|||
/// received, and the second read will receive the second packet.
|
||||
fn clone(&self) -> UdpSocket {
|
||||
UdpSocket {
|
||||
obj: self.obj.clone(),
|
||||
inner: self.inner.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,15 +17,17 @@
|
|||
|
||||
use prelude::*;
|
||||
|
||||
use io::{IoResult, IoError};
|
||||
use io::IoResult;
|
||||
use libc;
|
||||
use os;
|
||||
use rt::rtio::{RtioPipe, LocalIo};
|
||||
use sync::Arc;
|
||||
|
||||
use sys_common;
|
||||
use sys;
|
||||
use sys::fs::FileDesc as FileDesc;
|
||||
|
||||
/// A synchronous, in-memory pipe.
|
||||
pub struct PipeStream {
|
||||
/// The internal, opaque runtime pipe object.
|
||||
obj: Box<RtioPipe + Send>,
|
||||
inner: Arc<FileDesc>
|
||||
}
|
||||
|
||||
pub struct PipePair {
|
||||
|
@ -55,14 +57,14 @@ impl PipeStream {
|
|||
/// }
|
||||
/// ```
|
||||
pub fn open(fd: libc::c_int) -> IoResult<PipeStream> {
|
||||
LocalIo::maybe_raise(|io| {
|
||||
io.pipe_open(fd).map(|obj| PipeStream { obj: obj })
|
||||
}).map_err(IoError::from_rtio_error)
|
||||
Ok(PipeStream::from_filedesc(FileDesc::new(fd, true)))
|
||||
}
|
||||
|
||||
// FIXME: expose this some other way
|
||||
/// Wrap a FileDesc directly, taking ownership.
|
||||
#[doc(hidden)]
|
||||
pub fn new(inner: Box<RtioPipe + Send>) -> PipeStream {
|
||||
PipeStream { obj: inner }
|
||||
pub fn from_filedesc(fd: FileDesc) -> PipeStream {
|
||||
PipeStream { inner: Arc::new(fd) }
|
||||
}
|
||||
|
||||
/// Creates a pair of in-memory OS pipes for a unidirectional communication
|
||||
|
@ -76,43 +78,35 @@ impl PipeStream {
|
|||
/// This function can fail to succeed if the underlying OS has run out of
|
||||
/// available resources to allocate a new pipe.
|
||||
pub fn pair() -> IoResult<PipePair> {
|
||||
struct Closer { fd: libc::c_int }
|
||||
let (reader, writer) = try!(unsafe { sys::os::pipe() });
|
||||
Ok(PipePair {
|
||||
reader: PipeStream::from_filedesc(reader),
|
||||
writer: PipeStream::from_filedesc(writer),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let os::Pipe { reader, writer } = try!(unsafe { os::pipe() });
|
||||
let mut reader = Closer { fd: reader };
|
||||
let mut writer = Closer { fd: writer };
|
||||
|
||||
let io_reader = try!(PipeStream::open(reader.fd));
|
||||
reader.fd = -1;
|
||||
let io_writer = try!(PipeStream::open(writer.fd));
|
||||
writer.fd = -1;
|
||||
return Ok(PipePair { reader: io_reader, writer: io_writer });
|
||||
|
||||
impl Drop for Closer {
|
||||
fn drop(&mut self) {
|
||||
if self.fd != -1 {
|
||||
let _ = unsafe { libc::close(self.fd) };
|
||||
}
|
||||
}
|
||||
}
|
||||
impl sys_common::AsFileDesc for PipeStream {
|
||||
fn as_fd(&self) -> &sys::fs::FileDesc {
|
||||
&*self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for PipeStream {
|
||||
fn clone(&self) -> PipeStream {
|
||||
PipeStream { obj: self.obj.clone() }
|
||||
PipeStream { inner: self.inner.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Reader for PipeStream {
|
||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
|
||||
self.obj.read(buf).map_err(IoError::from_rtio_error)
|
||||
self.inner.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl Writer for PipeStream {
|
||||
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
||||
self.obj.write(buf).map_err(IoError::from_rtio_error)
|
||||
self.inner.write(buf)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,14 +20,17 @@ use os;
|
|||
use io::{IoResult, IoError};
|
||||
use io;
|
||||
use libc;
|
||||
use mem;
|
||||
use rt::rtio::{RtioProcess, ProcessConfig, IoFactory, LocalIo};
|
||||
use rt::rtio;
|
||||
use c_str::CString;
|
||||
use collections::HashMap;
|
||||
use hash::Hash;
|
||||
#[cfg(windows)]
|
||||
use std::hash::sip::SipState;
|
||||
use io::pipe::{PipeStream, PipePair};
|
||||
use path::BytesContainer;
|
||||
|
||||
use sys;
|
||||
use sys::fs::FileDesc;
|
||||
use sys::process::Process as ProcessImp;
|
||||
|
||||
/// Signal a process to exit, without forcibly killing it. Corresponds to
|
||||
/// SIGTERM on unix platforms.
|
||||
|
@ -62,24 +65,29 @@ use std::hash::sip::SipState;
|
|||
/// assert!(child.wait().unwrap().success());
|
||||
/// ```
|
||||
pub struct Process {
|
||||
handle: Box<RtioProcess + Send>,
|
||||
handle: ProcessImp,
|
||||
forget: bool,
|
||||
|
||||
/// None until wait() is called.
|
||||
exit_code: Option<ProcessExit>,
|
||||
|
||||
/// Manually delivered signal
|
||||
exit_signal: Option<int>,
|
||||
|
||||
/// Deadline after which wait() will return
|
||||
deadline: u64,
|
||||
|
||||
/// Handle to the child's stdin, if the `stdin` field of this process's
|
||||
/// `ProcessConfig` was `CreatePipe`. By default, this handle is `Some`.
|
||||
pub stdin: Option<io::PipeStream>,
|
||||
pub stdin: Option<PipeStream>,
|
||||
|
||||
/// Handle to the child's stdout, if the `stdout` field of this process's
|
||||
/// `ProcessConfig` was `CreatePipe`. By default, this handle is `Some`.
|
||||
pub stdout: Option<io::PipeStream>,
|
||||
pub stdout: Option<PipeStream>,
|
||||
|
||||
/// Handle to the child's stderr, if the `stderr` field of this process's
|
||||
/// `ProcessConfig` was `CreatePipe`. By default, this handle is `Some`.
|
||||
pub stderr: Option<io::PipeStream>,
|
||||
|
||||
/// Extra I/O handles as configured by the original `ProcessConfig` when
|
||||
/// this process was created. This is by default empty.
|
||||
pub extra_io: Vec<Option<io::PipeStream>>,
|
||||
pub stderr: Option<PipeStream>,
|
||||
}
|
||||
|
||||
/// A representation of environment variable name
|
||||
|
@ -130,6 +138,13 @@ impl PartialEq for EnvKey {
|
|||
}
|
||||
}
|
||||
|
||||
impl BytesContainer for EnvKey {
|
||||
fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
|
||||
let &EnvKey(ref k) = self;
|
||||
k.container_as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
/// A HashMap representation of environment variables.
|
||||
pub type EnvMap = HashMap<EnvKey, CString>;
|
||||
|
||||
|
@ -160,7 +175,6 @@ pub struct Command {
|
|||
stdin: StdioContainer,
|
||||
stdout: StdioContainer,
|
||||
stderr: StdioContainer,
|
||||
extra_io: Vec<StdioContainer>,
|
||||
uid: Option<uint>,
|
||||
gid: Option<uint>,
|
||||
detach: bool,
|
||||
|
@ -194,7 +208,6 @@ impl Command {
|
|||
stdin: CreatePipe(true, false),
|
||||
stdout: CreatePipe(false, true),
|
||||
stderr: CreatePipe(false, true),
|
||||
extra_io: Vec::new(),
|
||||
uid: None,
|
||||
gid: None,
|
||||
detach: false,
|
||||
|
@ -281,14 +294,6 @@ impl Command {
|
|||
self.stderr = cfg;
|
||||
self
|
||||
}
|
||||
/// Attaches a stream/file descriptor/pipe to the child process. Inherited
|
||||
/// file descriptors are numbered consecutively, starting at 3; the first
|
||||
/// three file descriptors (stdin/stdout/stderr) are configured with the
|
||||
/// `stdin`, `stdout`, and `stderr` methods.
|
||||
pub fn extra_io<'a>(&'a mut self, cfg: StdioContainer) -> &'a mut Command {
|
||||
self.extra_io.push(cfg);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the child process's user id. This translates to a `setuid` call in
|
||||
/// the child process. Setting this value on windows will cause the spawn to
|
||||
|
@ -315,50 +320,23 @@ impl Command {
|
|||
|
||||
/// Executes the command as a child process, which is returned.
|
||||
pub fn spawn(&self) -> IoResult<Process> {
|
||||
fn to_rtio(p: StdioContainer) -> rtio::StdioContainer {
|
||||
match p {
|
||||
Ignored => rtio::Ignored,
|
||||
InheritFd(fd) => rtio::InheritFd(fd),
|
||||
CreatePipe(a, b) => rtio::CreatePipe(a, b),
|
||||
}
|
||||
}
|
||||
let extra_io: Vec<rtio::StdioContainer> =
|
||||
self.extra_io.iter().map(|x| to_rtio(*x)).collect();
|
||||
LocalIo::maybe_raise(|io| {
|
||||
let env = match self.env {
|
||||
None => None,
|
||||
Some(ref env_map) =>
|
||||
Some(env_map.iter()
|
||||
.map(|(&EnvKey(ref key), val)| (key, val))
|
||||
.collect::<Vec<_>>())
|
||||
};
|
||||
let cfg = ProcessConfig {
|
||||
program: &self.program,
|
||||
args: self.args.as_slice(),
|
||||
env: env.as_ref().map(|e| e.as_slice()),
|
||||
cwd: self.cwd.as_ref(),
|
||||
stdin: to_rtio(self.stdin),
|
||||
stdout: to_rtio(self.stdout),
|
||||
stderr: to_rtio(self.stderr),
|
||||
extra_io: extra_io.as_slice(),
|
||||
uid: self.uid,
|
||||
gid: self.gid,
|
||||
detach: self.detach,
|
||||
};
|
||||
io.spawn(cfg).map(|(p, io)| {
|
||||
let mut io = io.into_iter().map(|p| {
|
||||
p.map(|p| io::PipeStream::new(p))
|
||||
});
|
||||
Process {
|
||||
handle: p,
|
||||
forget: false,
|
||||
stdin: io.next().unwrap(),
|
||||
stdout: io.next().unwrap(),
|
||||
stderr: io.next().unwrap(),
|
||||
extra_io: io.collect(),
|
||||
}
|
||||
let (their_stdin, our_stdin) = try!(setup_io(self.stdin));
|
||||
let (their_stdout, our_stdout) = try!(setup_io(self.stdout));
|
||||
let (their_stderr, our_stderr) = try!(setup_io(self.stderr));
|
||||
|
||||
match ProcessImp::spawn(self, their_stdin, their_stdout, their_stderr) {
|
||||
Err(e) => Err(e),
|
||||
Ok(handle) => Ok(Process {
|
||||
handle: handle,
|
||||
forget: false,
|
||||
exit_code: None,
|
||||
exit_signal: None,
|
||||
deadline: 0,
|
||||
stdin: our_stdin,
|
||||
stdout: our_stdout,
|
||||
stderr: our_stderr,
|
||||
})
|
||||
}).map_err(IoError::from_rtio_error)
|
||||
}
|
||||
}
|
||||
|
||||
/// Executes the command as a child process, waiting for it to finish and
|
||||
|
@ -415,6 +393,58 @@ impl fmt::Show for Command {
|
|||
}
|
||||
}
|
||||
|
||||
fn setup_io(io: StdioContainer) -> IoResult<(Option<PipeStream>, Option<PipeStream>)> {
|
||||
let ours;
|
||||
let theirs;
|
||||
match io {
|
||||
Ignored => {
|
||||
theirs = None;
|
||||
ours = None;
|
||||
}
|
||||
InheritFd(fd) => {
|
||||
theirs = Some(PipeStream::from_filedesc(FileDesc::new(fd, false)));
|
||||
ours = None;
|
||||
}
|
||||
CreatePipe(readable, _writable) => {
|
||||
let PipePair { reader, writer } = try!(PipeStream::pair());
|
||||
if readable {
|
||||
theirs = Some(reader);
|
||||
ours = Some(writer);
|
||||
} else {
|
||||
theirs = Some(writer);
|
||||
ours = Some(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok((theirs, ours))
|
||||
}
|
||||
|
||||
// Allow the sys module to get access to the Command state
|
||||
impl sys::process::ProcessConfig<EnvKey, CString> for Command {
|
||||
fn program(&self) -> &CString {
|
||||
&self.program
|
||||
}
|
||||
fn args(&self) -> &[CString] {
|
||||
self.args.as_slice()
|
||||
}
|
||||
fn env(&self) -> Option<&EnvMap> {
|
||||
self.env.as_ref()
|
||||
}
|
||||
fn cwd(&self) -> Option<&CString> {
|
||||
self.cwd.as_ref()
|
||||
}
|
||||
fn uid(&self) -> Option<uint> {
|
||||
self.uid.clone()
|
||||
}
|
||||
fn gid(&self) -> Option<uint> {
|
||||
self.gid.clone()
|
||||
}
|
||||
fn detach(&self) -> bool {
|
||||
self.detach
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// The output of a finished process.
|
||||
#[deriving(PartialEq, Eq, Clone)]
|
||||
pub struct ProcessOutput {
|
||||
|
@ -494,9 +524,7 @@ impl Process {
|
|||
/// be successfully delivered if the child has exited, but not yet been
|
||||
/// reaped.
|
||||
pub fn kill(id: libc::pid_t, signal: int) -> IoResult<()> {
|
||||
LocalIo::maybe_raise(|io| {
|
||||
io.kill(id, signal)
|
||||
}).map_err(IoError::from_rtio_error)
|
||||
unsafe { ProcessImp::killpid(id, signal) }
|
||||
}
|
||||
|
||||
/// Returns the process id of this child process
|
||||
|
@ -518,7 +546,42 @@ impl Process {
|
|||
///
|
||||
/// If the signal delivery fails, the corresponding error is returned.
|
||||
pub fn signal(&mut self, signal: int) -> IoResult<()> {
|
||||
self.handle.kill(signal).map_err(IoError::from_rtio_error)
|
||||
#[cfg(unix)] fn collect_status(p: &mut Process) {
|
||||
// On Linux (and possibly other unices), a process that has exited will
|
||||
// continue to accept signals because it is "defunct". The delivery of
|
||||
// signals will only fail once the child has been reaped. For this
|
||||
// reason, if the process hasn't exited yet, then we attempt to collect
|
||||
// their status with WNOHANG.
|
||||
if p.exit_code.is_none() {
|
||||
match p.handle.try_wait() {
|
||||
Some(code) => { p.exit_code = Some(code); }
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(windows)] fn collect_status(_p: &mut Process) {}
|
||||
|
||||
collect_status(self);
|
||||
|
||||
// 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
|
||||
if self.exit_code.is_some() {
|
||||
return Err(IoError {
|
||||
kind: io::InvalidInput,
|
||||
desc: "invalid argument: can't kill an exited process",
|
||||
detail: None,
|
||||
})
|
||||
}
|
||||
|
||||
// A successfully delivered signal that isn't 0 (just a poll for being
|
||||
// alive) is recorded for windows (see wait())
|
||||
match unsafe { self.handle.kill(signal) } {
|
||||
Ok(()) if signal == 0 => Ok(()),
|
||||
Ok(()) => { self.exit_signal = Some(signal); Ok(()) }
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Sends a signal to this child requesting that it exits. This is
|
||||
|
@ -545,10 +608,21 @@ impl Process {
|
|||
/// `set_timeout` and the timeout expires before the child exits.
|
||||
pub fn wait(&mut self) -> IoResult<ProcessExit> {
|
||||
drop(self.stdin.take());
|
||||
match self.handle.wait() {
|
||||
Ok(rtio::ExitSignal(s)) => Ok(ExitSignal(s)),
|
||||
Ok(rtio::ExitStatus(s)) => Ok(ExitStatus(s)),
|
||||
Err(e) => Err(IoError::from_rtio_error(e)),
|
||||
match self.exit_code {
|
||||
Some(code) => Ok(code),
|
||||
None => {
|
||||
let code = try!(self.handle.wait(self.deadline));
|
||||
// On windows, waitpid will never return a signal. If a signal
|
||||
// was successfully delivered to the process, however, we can
|
||||
// consider it as having died via a signal.
|
||||
let code = match self.exit_signal {
|
||||
None => code,
|
||||
Some(signal) if cfg!(windows) => ExitSignal(signal),
|
||||
Some(..) => code,
|
||||
};
|
||||
self.exit_code = Some(code);
|
||||
Ok(code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -594,7 +668,7 @@ impl Process {
|
|||
/// ```
|
||||
#[experimental = "the type of the timeout is likely to change"]
|
||||
pub fn set_timeout(&mut self, timeout_ms: Option<u64>) {
|
||||
self.handle.set_timeout(timeout_ms)
|
||||
self.deadline = timeout_ms.map(|i| i + sys::timer::now()).unwrap_or(0);
|
||||
}
|
||||
|
||||
/// Simultaneously wait for the child to exit and collect all remaining
|
||||
|
@ -653,7 +727,6 @@ impl Drop for Process {
|
|||
drop(self.stdin.take());
|
||||
drop(self.stdout.take());
|
||||
drop(self.stderr.take());
|
||||
drop(mem::replace(&mut self.extra_io, Vec::new()));
|
||||
|
||||
self.set_timeout(None);
|
||||
let _ = self.wait().unwrap();
|
||||
|
@ -1109,8 +1182,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn dont_close_fd_on_command_spawn() {
|
||||
use std::rt::rtio::{Truncate, Write};
|
||||
use self::native::io::file;
|
||||
use sys::fs;
|
||||
|
||||
let path = if cfg!(windows) {
|
||||
Path::new("NUL")
|
||||
|
@ -1118,7 +1190,7 @@ mod tests {
|
|||
Path::new("/dev/null")
|
||||
};
|
||||
|
||||
let mut fdes = match file::open(&path.to_c_str(), Truncate, Write) {
|
||||
let mut fdes = match fs::open(&path, Truncate, Write) {
|
||||
Ok(f) => f,
|
||||
Err(_) => panic!("failed to open file descriptor"),
|
||||
};
|
||||
|
@ -1126,7 +1198,7 @@ mod tests {
|
|||
let mut cmd = pwd_cmd();
|
||||
let _ = cmd.stdout(InheritFd(fdes.fd()));
|
||||
assert!(cmd.status().unwrap().success());
|
||||
assert!(fdes.inner_write("extra write\n".as_bytes()).is_ok());
|
||||
assert!(fdes.write("extra write\n".as_bytes()).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -36,11 +36,11 @@ use kinds::Send;
|
|||
use libc;
|
||||
use option::{Option, Some, None};
|
||||
use boxed::Box;
|
||||
use sys::{fs, tty};
|
||||
use result::{Ok, Err};
|
||||
use rt;
|
||||
use rt::local::Local;
|
||||
use rt::task::Task;
|
||||
use rt::rtio::{DontClose, IoFactory, LocalIo, RtioFileStream, RtioTTY};
|
||||
use slice::SlicePrelude;
|
||||
use str::StrPrelude;
|
||||
use uint;
|
||||
|
@ -74,17 +74,15 @@ use uint;
|
|||
// tl;dr; TTY works on everything but when windows stdout is redirected, in that
|
||||
// case pipe also doesn't work, but magically file does!
|
||||
enum StdSource {
|
||||
TTY(Box<RtioTTY + Send>),
|
||||
File(Box<RtioFileStream + Send>),
|
||||
TTY(tty::TTY),
|
||||
File(fs::FileDesc),
|
||||
}
|
||||
|
||||
fn src<T>(fd: libc::c_int, readable: bool, f: |StdSource| -> T) -> T {
|
||||
LocalIo::maybe_raise(|io| {
|
||||
Ok(match io.tty_open(fd, readable) {
|
||||
Ok(tty) => f(TTY(tty)),
|
||||
Err(_) => f(File(io.fs_from_raw_fd(fd, DontClose))),
|
||||
})
|
||||
}).map_err(IoError::from_rtio_error).unwrap()
|
||||
fn src<T>(fd: libc::c_int, _readable: bool, f: |StdSource| -> T) -> T {
|
||||
match tty::TTY::new(fd) {
|
||||
Ok(tty) => f(TTY(tty)),
|
||||
Err(_) => f(File(fs::FileDesc::new(fd, false))),
|
||||
}
|
||||
}
|
||||
|
||||
local_data_key!(local_stdout: Box<Writer + Send>)
|
||||
|
@ -278,10 +276,10 @@ impl Reader for StdReader {
|
|||
// print!'d prompt not being shown until after the user hits
|
||||
// enter.
|
||||
flush();
|
||||
tty.read(buf)
|
||||
tty.read(buf).map(|i| i as uint)
|
||||
},
|
||||
File(ref mut file) => file.read(buf).map(|i| i as uint),
|
||||
}.map_err(IoError::from_rtio_error);
|
||||
};
|
||||
match ret {
|
||||
// When reading a piped stdin, libuv will return 0-length reads when
|
||||
// stdin reaches EOF. For pretty much all other streams it will
|
||||
|
@ -313,7 +311,7 @@ impl StdWriter {
|
|||
pub fn winsize(&mut self) -> IoResult<(int, int)> {
|
||||
match self.inner {
|
||||
TTY(ref mut tty) => {
|
||||
tty.get_winsize().map_err(IoError::from_rtio_error)
|
||||
tty.get_winsize()
|
||||
}
|
||||
File(..) => {
|
||||
Err(IoError {
|
||||
|
@ -335,7 +333,7 @@ impl StdWriter {
|
|||
pub fn set_raw(&mut self, raw: bool) -> IoResult<()> {
|
||||
match self.inner {
|
||||
TTY(ref mut tty) => {
|
||||
tty.set_raw(raw).map_err(IoError::from_rtio_error)
|
||||
tty.set_raw(raw)
|
||||
}
|
||||
File(..) => {
|
||||
Err(IoError {
|
||||
|
@ -374,7 +372,7 @@ impl Writer for StdWriter {
|
|||
try!(match self.inner {
|
||||
TTY(ref mut tty) => tty.write(chunk),
|
||||
File(ref mut file) => file.write(chunk),
|
||||
}.map_err(IoError::from_rtio_error))
|
||||
})
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -21,10 +21,9 @@ and create receivers which will receive notifications after a period of time.
|
|||
|
||||
use comm::{Receiver, Sender, channel};
|
||||
use time::Duration;
|
||||
use io::{IoResult, IoError};
|
||||
use kinds::Send;
|
||||
use boxed::Box;
|
||||
use rt::rtio::{IoFactory, LocalIo, RtioTimer, Callback};
|
||||
use io::IoResult;
|
||||
use sys::timer::Callback;
|
||||
use sys::timer::Timer as TimerImp;
|
||||
|
||||
/// A synchronous timer object
|
||||
///
|
||||
|
@ -69,7 +68,7 @@ use rt::rtio::{IoFactory, LocalIo, RtioTimer, Callback};
|
|||
/// # }
|
||||
/// ```
|
||||
pub struct Timer {
|
||||
obj: Box<RtioTimer + Send>,
|
||||
inner: TimerImp,
|
||||
}
|
||||
|
||||
struct TimerCallback { tx: Sender<()> }
|
||||
|
@ -90,9 +89,7 @@ impl Timer {
|
|||
/// for a number of milliseconds, or to possibly create channels which will
|
||||
/// get notified after an amount of time has passed.
|
||||
pub fn new() -> IoResult<Timer> {
|
||||
LocalIo::maybe_raise(|io| {
|
||||
io.timer_init().map(|t| Timer { obj: t })
|
||||
}).map_err(IoError::from_rtio_error)
|
||||
TimerImp::new().map(|t| Timer { inner: t })
|
||||
}
|
||||
|
||||
/// Blocks the current task for the specified duration.
|
||||
|
@ -106,7 +103,7 @@ impl Timer {
|
|||
// Short-circuit the timer backend for 0 duration
|
||||
let ms = in_ms_u64(duration);
|
||||
if ms == 0 { return }
|
||||
self.obj.sleep(ms);
|
||||
self.inner.sleep(ms);
|
||||
}
|
||||
|
||||
/// Creates a oneshot receiver which will have a notification sent when
|
||||
|
@ -152,7 +149,7 @@ impl Timer {
|
|||
let (tx, rx) = channel();
|
||||
// Short-circuit the timer backend for 0 duration
|
||||
if in_ms_u64(duration) != 0 {
|
||||
self.obj.oneshot(in_ms_u64(duration), box TimerCallback { tx: tx });
|
||||
self.inner.oneshot(in_ms_u64(duration), box TimerCallback { tx: tx });
|
||||
} else {
|
||||
tx.send(());
|
||||
}
|
||||
|
@ -213,7 +210,7 @@ impl Timer {
|
|||
// not clear what use a 0ms period is anyway...
|
||||
let ms = if ms == 0 { 1 } else { ms };
|
||||
let (tx, rx) = channel();
|
||||
self.obj.period(ms, box TimerCallback { tx: tx });
|
||||
self.inner.period(ms, box TimerCallback { tx: tx });
|
||||
return rx
|
||||
}
|
||||
}
|
||||
|
|
|
@ -242,6 +242,13 @@ pub mod io;
|
|||
pub mod path;
|
||||
pub mod fmt;
|
||||
|
||||
#[cfg(unix)]
|
||||
#[path = "sys/unix/mod.rs"] mod sys;
|
||||
#[cfg(windows)]
|
||||
#[path = "sys/windows/mod.rs"] mod sys;
|
||||
|
||||
#[path = "sys/common/mod.rs"] mod sys_common;
|
||||
|
||||
// FIXME #7809: This shouldn't be pub, and it should be reexported under 'unstable'
|
||||
// but name resolution doesn't work without it being pub.
|
||||
pub mod rt;
|
||||
|
|
190
src/libstd/os.rs
190
src/libstd/os.rs
|
@ -34,7 +34,7 @@
|
|||
use clone::Clone;
|
||||
use error::{FromError, Error};
|
||||
use fmt;
|
||||
use io::{IoResult, IoError};
|
||||
use io::IoResult;
|
||||
use iter::Iterator;
|
||||
use libc::{c_void, c_int};
|
||||
use libc;
|
||||
|
@ -43,6 +43,8 @@ use ops::Drop;
|
|||
use option::{Some, None, Option};
|
||||
use os;
|
||||
use path::{Path, GenericPath, BytesContainer};
|
||||
use sys;
|
||||
use sys::os as os_imp;
|
||||
use ptr::RawPtr;
|
||||
use ptr;
|
||||
use result::{Err, Ok, Result};
|
||||
|
@ -602,35 +604,11 @@ pub struct Pipe {
|
|||
/// descriptors to be closed, the file descriptors will leak. For safe handling
|
||||
/// of this scenario, use `std::io::PipeStream` instead.
|
||||
pub unsafe fn pipe() -> IoResult<Pipe> {
|
||||
return _pipe();
|
||||
|
||||
#[cfg(unix)]
|
||||
unsafe fn _pipe() -> IoResult<Pipe> {
|
||||
let mut fds = [0, ..2];
|
||||
match libc::pipe(fds.as_mut_ptr()) {
|
||||
0 => Ok(Pipe { reader: fds[0], writer: fds[1] }),
|
||||
_ => Err(IoError::last_error()),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
unsafe fn _pipe() -> IoResult<Pipe> {
|
||||
// Windows pipes work subtly differently than unix pipes, and their
|
||||
// inheritance has to be handled in a different way that I do not
|
||||
// fully understand. Here we explicitly make the pipe non-inheritable,
|
||||
// which means to pass it to a subprocess they need to be duplicated
|
||||
// first, as in std::run.
|
||||
let mut fds = [0, ..2];
|
||||
match libc::pipe(fds.as_mut_ptr(), 1024 as ::libc::c_uint,
|
||||
(libc::O_BINARY | libc::O_NOINHERIT) as c_int) {
|
||||
0 => {
|
||||
assert!(fds[0] != -1 && fds[0] != 0);
|
||||
assert!(fds[1] != -1 && fds[1] != 0);
|
||||
Ok(Pipe { reader: fds[0], writer: fds[1] })
|
||||
}
|
||||
_ => Err(IoError::last_error()),
|
||||
}
|
||||
}
|
||||
let (reader, writer) = try!(sys::os::pipe());
|
||||
Ok(Pipe {
|
||||
reader: reader.unwrap(),
|
||||
writer: writer.unwrap(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the proper dll filename for the given basename of a file
|
||||
|
@ -905,59 +883,9 @@ pub fn change_dir(p: &Path) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
/// Returns the platform-specific value of errno
|
||||
pub fn errno() -> int {
|
||||
#[cfg(any(target_os = "macos",
|
||||
target_os = "ios",
|
||||
target_os = "freebsd"))]
|
||||
fn errno_location() -> *const c_int {
|
||||
extern {
|
||||
fn __error() -> *const c_int;
|
||||
}
|
||||
unsafe {
|
||||
__error()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "dragonfly")]
|
||||
fn errno_location() -> *const c_int {
|
||||
extern {
|
||||
fn __dfly_error() -> *const c_int;
|
||||
}
|
||||
unsafe {
|
||||
__dfly_error()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn errno_location() -> *const c_int {
|
||||
extern {
|
||||
fn __errno_location() -> *const c_int;
|
||||
}
|
||||
unsafe {
|
||||
__errno_location()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
(*errno_location()) as int
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
/// Returns the platform-specific value of errno
|
||||
pub fn errno() -> uint {
|
||||
use libc::types::os::arch::extra::DWORD;
|
||||
|
||||
#[link_name = "kernel32"]
|
||||
extern "system" {
|
||||
fn GetLastError() -> DWORD;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
GetLastError() as uint
|
||||
}
|
||||
os_imp::errno() as uint
|
||||
}
|
||||
|
||||
/// Return the string corresponding to an `errno()` value of `errnum`.
|
||||
|
@ -969,105 +897,7 @@ pub fn errno() -> uint {
|
|||
/// println!("{}", os::error_string(os::errno() as uint));
|
||||
/// ```
|
||||
pub fn error_string(errnum: uint) -> String {
|
||||
return strerror(errnum);
|
||||
|
||||
#[cfg(unix)]
|
||||
fn strerror(errnum: uint) -> String {
|
||||
#[cfg(any(target_os = "macos",
|
||||
target_os = "ios",
|
||||
target_os = "android",
|
||||
target_os = "freebsd",
|
||||
target_os = "dragonfly"))]
|
||||
fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: libc::size_t)
|
||||
-> c_int {
|
||||
extern {
|
||||
fn strerror_r(errnum: c_int, buf: *mut c_char,
|
||||
buflen: libc::size_t) -> c_int;
|
||||
}
|
||||
unsafe {
|
||||
strerror_r(errnum, buf, buflen)
|
||||
}
|
||||
}
|
||||
|
||||
// GNU libc provides a non-compliant version of strerror_r by default
|
||||
// and requires macros to instead use the POSIX compliant variant.
|
||||
// So we just use __xpg_strerror_r which is always POSIX compliant
|
||||
#[cfg(target_os = "linux")]
|
||||
fn strerror_r(errnum: c_int, buf: *mut c_char,
|
||||
buflen: libc::size_t) -> c_int {
|
||||
extern {
|
||||
fn __xpg_strerror_r(errnum: c_int,
|
||||
buf: *mut c_char,
|
||||
buflen: libc::size_t)
|
||||
-> c_int;
|
||||
}
|
||||
unsafe {
|
||||
__xpg_strerror_r(errnum, buf, buflen)
|
||||
}
|
||||
}
|
||||
|
||||
let mut buf = [0 as c_char, ..TMPBUF_SZ];
|
||||
|
||||
let p = buf.as_mut_ptr();
|
||||
unsafe {
|
||||
if strerror_r(errnum as c_int, p, buf.len() as libc::size_t) < 0 {
|
||||
panic!("strerror_r failure");
|
||||
}
|
||||
|
||||
::string::raw::from_buf(p as *const u8)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn strerror(errnum: uint) -> String {
|
||||
use libc::types::os::arch::extra::DWORD;
|
||||
use libc::types::os::arch::extra::LPWSTR;
|
||||
use libc::types::os::arch::extra::LPVOID;
|
||||
use libc::types::os::arch::extra::WCHAR;
|
||||
|
||||
#[link_name = "kernel32"]
|
||||
extern "system" {
|
||||
fn FormatMessageW(flags: DWORD,
|
||||
lpSrc: LPVOID,
|
||||
msgId: DWORD,
|
||||
langId: DWORD,
|
||||
buf: LPWSTR,
|
||||
nsize: DWORD,
|
||||
args: *const c_void)
|
||||
-> DWORD;
|
||||
}
|
||||
|
||||
static FORMAT_MESSAGE_FROM_SYSTEM: DWORD = 0x00001000;
|
||||
static FORMAT_MESSAGE_IGNORE_INSERTS: DWORD = 0x00000200;
|
||||
|
||||
// This value is calculated from the macro
|
||||
// MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT)
|
||||
let langId = 0x0800 as DWORD;
|
||||
|
||||
let mut buf = [0 as WCHAR, ..TMPBUF_SZ];
|
||||
|
||||
unsafe {
|
||||
let res = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
ptr::null_mut(),
|
||||
errnum as DWORD,
|
||||
langId,
|
||||
buf.as_mut_ptr(),
|
||||
buf.len() as DWORD,
|
||||
ptr::null());
|
||||
if res == 0 {
|
||||
// Sometimes FormatMessageW can fail e.g. system doesn't like langId,
|
||||
let fm_err = errno();
|
||||
return format!("OS Error {} (FormatMessageW() returned error {})", errnum, fm_err);
|
||||
}
|
||||
|
||||
let msg = String::from_utf16(::str::truncate_utf16_at_nul(buf));
|
||||
match msg {
|
||||
Some(msg) => format!("OS Error {}: {}", errnum, msg),
|
||||
None => format!("OS Error {} (FormatMessageW() returned invalid UTF-16)", errnum),
|
||||
}
|
||||
}
|
||||
}
|
||||
return os_imp::error_string(errnum as i32);
|
||||
}
|
||||
|
||||
/// Get a string representing the platform-dependent last error
|
||||
|
|
|
@ -20,16 +20,15 @@
|
|||
//! can be created in the future and there must be no active timers at that
|
||||
//! time.
|
||||
|
||||
#![macro_escape]
|
||||
use mem;
|
||||
use rt::bookkeeping;
|
||||
use rt::mutex::StaticNativeMutex;
|
||||
use rt;
|
||||
use cell::UnsafeCell;
|
||||
use sys::helper_signal;
|
||||
use prelude::*;
|
||||
|
||||
use std::cell::UnsafeCell;
|
||||
use std::mem;
|
||||
use std::rt::bookkeeping;
|
||||
use std::rt::mutex::StaticNativeMutex;
|
||||
use std::rt;
|
||||
use std::task::TaskBuilder;
|
||||
|
||||
use NativeTaskBuilder;
|
||||
use task;
|
||||
|
||||
/// A structure for management of a helper thread.
|
||||
///
|
||||
|
@ -56,15 +55,6 @@ pub struct Helper<M> {
|
|||
pub initialized: UnsafeCell<bool>,
|
||||
}
|
||||
|
||||
macro_rules! helper_init( (static $name:ident: Helper<$m:ty>) => (
|
||||
static $name: Helper<$m> = Helper {
|
||||
lock: ::std::rt::mutex::NATIVE_MUTEX_INIT,
|
||||
chan: ::std::cell::UnsafeCell { value: 0 as *mut Sender<$m> },
|
||||
signal: ::std::cell::UnsafeCell { value: 0 },
|
||||
initialized: ::std::cell::UnsafeCell { value: false },
|
||||
};
|
||||
) )
|
||||
|
||||
impl<M: Send> Helper<M> {
|
||||
/// Lazily boots a helper thread, becoming a no-op if the helper has already
|
||||
/// been spawned.
|
||||
|
@ -77,17 +67,17 @@ impl<M: Send> Helper<M> {
|
|||
/// This function is safe to be called many times.
|
||||
pub fn boot<T: Send>(&'static self,
|
||||
f: || -> T,
|
||||
helper: fn(imp::signal, Receiver<M>, T)) {
|
||||
helper: fn(helper_signal::signal, Receiver<M>, T)) {
|
||||
unsafe {
|
||||
let _guard = self.lock.lock();
|
||||
if !*self.initialized.get() {
|
||||
let (tx, rx) = channel();
|
||||
*self.chan.get() = mem::transmute(box tx);
|
||||
let (receive, send) = imp::new();
|
||||
let (receive, send) = helper_signal::new();
|
||||
*self.signal.get() = send as uint;
|
||||
|
||||
let t = f();
|
||||
TaskBuilder::new().native().spawn(proc() {
|
||||
task::spawn(proc() {
|
||||
bookkeeping::decrement();
|
||||
helper(receive, rx, t);
|
||||
self.lock.lock().signal()
|
||||
|
@ -111,7 +101,7 @@ impl<M: Send> Helper<M> {
|
|||
// send the message.
|
||||
assert!(!self.chan.get().is_null());
|
||||
(**self.chan.get()).send(msg);
|
||||
imp::signal(*self.signal.get() as imp::signal);
|
||||
helper_signal::signal(*self.signal.get() as helper_signal::signal);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,7 +116,7 @@ impl<M: Send> Helper<M> {
|
|||
let chan: Box<Sender<M>> = mem::transmute(*self.chan.get());
|
||||
*self.chan.get() = 0 as *mut Sender<M>;
|
||||
drop(chan);
|
||||
imp::signal(*self.signal.get() as imp::signal);
|
||||
helper_signal::signal(*self.signal.get() as helper_signal::signal);
|
||||
|
||||
// Wait for the child to exit
|
||||
guard.wait();
|
||||
|
@ -134,64 +124,8 @@ impl<M: Send> Helper<M> {
|
|||
|
||||
// Clean up after ourselves
|
||||
self.lock.destroy();
|
||||
imp::close(*self.signal.get() as imp::signal);
|
||||
helper_signal::close(*self.signal.get() as helper_signal::signal);
|
||||
*self.signal.get() = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
mod imp {
|
||||
use libc;
|
||||
use std::os;
|
||||
|
||||
use io::file::FileDesc;
|
||||
|
||||
pub type signal = libc::c_int;
|
||||
|
||||
pub fn new() -> (signal, signal) {
|
||||
let os::Pipe { reader, writer } = unsafe { os::pipe().unwrap() };
|
||||
(reader, writer)
|
||||
}
|
||||
|
||||
pub fn signal(fd: libc::c_int) {
|
||||
FileDesc::new(fd, false).inner_write([0]).ok().unwrap();
|
||||
}
|
||||
|
||||
pub fn close(fd: libc::c_int) {
|
||||
let _fd = FileDesc::new(fd, true);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
mod imp {
|
||||
use libc::{BOOL, LPCSTR, HANDLE, LPSECURITY_ATTRIBUTES, CloseHandle};
|
||||
use std::ptr;
|
||||
use libc;
|
||||
|
||||
pub type signal = HANDLE;
|
||||
|
||||
pub fn new() -> (HANDLE, HANDLE) {
|
||||
unsafe {
|
||||
let handle = CreateEventA(ptr::null_mut(), libc::FALSE, libc::FALSE,
|
||||
ptr::null());
|
||||
(handle, handle)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn signal(handle: HANDLE) {
|
||||
assert!(unsafe { SetEvent(handle) != 0 });
|
||||
}
|
||||
|
||||
pub fn close(handle: HANDLE) {
|
||||
assert!(unsafe { CloseHandle(handle) != 0 });
|
||||
}
|
||||
|
||||
extern "system" {
|
||||
fn CreateEventA(lpSecurityAttributes: LPSECURITY_ATTRIBUTES,
|
||||
bManualReset: BOOL,
|
||||
bInitialState: BOOL,
|
||||
lpName: LPCSTR) -> HANDLE;
|
||||
fn SetEvent(hEvent: HANDLE) -> BOOL;
|
||||
}
|
||||
}
|
100
src/libstd/sys/common/mod.rs
Normal file
100
src/libstd/sys/common/mod.rs
Normal file
|
@ -0,0 +1,100 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
#![allow(missing_doc)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
use io::{mod, IoError, IoResult};
|
||||
use prelude::*;
|
||||
use num;
|
||||
use sys::{last_error, retry, fs};
|
||||
use c_str::CString;
|
||||
use path::BytesContainer;
|
||||
use collections;
|
||||
|
||||
pub mod net;
|
||||
pub mod helper_thread;
|
||||
|
||||
// common error constructors
|
||||
|
||||
pub fn eof() -> IoError {
|
||||
IoError {
|
||||
kind: io::EndOfFile,
|
||||
desc: "end of file",
|
||||
detail: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn timeout(desc: &'static str) -> IoError {
|
||||
IoError {
|
||||
kind: io::TimedOut,
|
||||
desc: desc,
|
||||
detail: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn short_write(n: uint, desc: &'static str) -> IoError {
|
||||
IoError {
|
||||
kind: if n == 0 { io::TimedOut } else { io::ShortWrite(n) },
|
||||
desc: desc,
|
||||
detail: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unimpl() -> IoError {
|
||||
IoError {
|
||||
kind: io::IoUnavailable,
|
||||
desc: "operations not yet supported",
|
||||
detail: None,
|
||||
}
|
||||
}
|
||||
|
||||
// unix has nonzero values as errors
|
||||
pub fn mkerr_libc<Int: num::Zero>(ret: Int) -> IoResult<()> {
|
||||
if !ret.is_zero() {
|
||||
Err(last_error())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn keep_going(data: &[u8], f: |*const u8, uint| -> i64) -> i64 {
|
||||
let origamt = data.len();
|
||||
let mut data = data.as_ptr();
|
||||
let mut amt = origamt;
|
||||
while amt > 0 {
|
||||
let ret = retry(|| f(data, amt));
|
||||
if ret == 0 {
|
||||
break
|
||||
} else if ret != -1 {
|
||||
amt -= ret as uint;
|
||||
data = unsafe { data.offset(ret as int) };
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return (origamt - amt) as i64;
|
||||
}
|
||||
|
||||
// traits for extracting representations from
|
||||
|
||||
pub trait AsFileDesc {
|
||||
fn as_fd(&self) -> &fs::FileDesc;
|
||||
}
|
||||
|
||||
pub trait ProcessConfig<K: BytesContainer, V: BytesContainer> {
|
||||
fn program(&self) -> &CString;
|
||||
fn args(&self) -> &[CString];
|
||||
fn env(&self) -> Option<&collections::HashMap<K, V>>;
|
||||
fn cwd(&self) -> Option<&CString>;
|
||||
fn uid(&self) -> Option<uint>;
|
||||
fn gid(&self) -> Option<uint>;
|
||||
fn detach(&self) -> bool;
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -11,6 +11,7 @@
|
|||
//! C definitions used by libnative that don't belong in liblibc
|
||||
|
||||
#![allow(dead_code)]
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
pub use self::select::fd_set;
|
||||
pub use self::signal::{sigaction, siginfo, sigset_t};
|
||||
|
@ -106,7 +107,7 @@ mod select {
|
|||
target_os = "dragonfly",
|
||||
target_os = "linux"))]
|
||||
mod select {
|
||||
use std::uint;
|
||||
use uint;
|
||||
use libc;
|
||||
|
||||
pub const FD_SETSIZE: uint = 1024;
|
392
src/libstd/sys/unix/fs.rs
Normal file
392
src/libstd/sys/unix/fs.rs
Normal file
|
@ -0,0 +1,392 @@
|
|||
// Copyright 2013-2014 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.
|
||||
|
||||
//! Blocking posix-based file I/O
|
||||
|
||||
use libc::{mod, c_int, c_void};
|
||||
use c_str::CString;
|
||||
use mem;
|
||||
use io;
|
||||
|
||||
use prelude::*;
|
||||
|
||||
use io::{FilePermission, Write, UnstableFileStat, Open, FileAccess, FileMode};
|
||||
use io::{IoResult, FileStat, SeekStyle, Reader};
|
||||
use io::{Read, Truncate, SeekCur, SeekSet, ReadWrite, SeekEnd, Append};
|
||||
use result::{Ok, Err};
|
||||
use sys::retry;
|
||||
use sys_common::{keep_going, eof, mkerr_libc};
|
||||
|
||||
pub use path::PosixPath as Path;
|
||||
|
||||
pub type fd_t = libc::c_int;
|
||||
|
||||
pub struct FileDesc {
|
||||
/// The underlying C file descriptor.
|
||||
fd: fd_t,
|
||||
|
||||
/// Whether to close the file descriptor on drop.
|
||||
close_on_drop: bool,
|
||||
}
|
||||
|
||||
impl FileDesc {
|
||||
pub fn new(fd: fd_t, close_on_drop: bool) -> FileDesc {
|
||||
FileDesc { fd: fd, close_on_drop: close_on_drop }
|
||||
}
|
||||
|
||||
pub fn read(&self, buf: &mut [u8]) -> IoResult<uint> {
|
||||
let ret = retry(|| unsafe {
|
||||
libc::read(self.fd(),
|
||||
buf.as_mut_ptr() as *mut libc::c_void,
|
||||
buf.len() as libc::size_t)
|
||||
});
|
||||
if ret == 0 {
|
||||
Err(eof())
|
||||
} else if ret < 0 {
|
||||
Err(super::last_error())
|
||||
} else {
|
||||
Ok(ret as uint)
|
||||
}
|
||||
}
|
||||
pub fn write(&self, buf: &[u8]) -> IoResult<()> {
|
||||
let ret = keep_going(buf, |buf, len| {
|
||||
unsafe {
|
||||
libc::write(self.fd(), buf as *const libc::c_void,
|
||||
len as libc::size_t) as i64
|
||||
}
|
||||
});
|
||||
if ret < 0 {
|
||||
Err(super::last_error())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fd(&self) -> fd_t { self.fd }
|
||||
|
||||
pub fn seek(&self, pos: i64, whence: SeekStyle) -> IoResult<u64> {
|
||||
let whence = match whence {
|
||||
SeekSet => libc::SEEK_SET,
|
||||
SeekEnd => libc::SEEK_END,
|
||||
SeekCur => libc::SEEK_CUR,
|
||||
};
|
||||
let n = unsafe { libc::lseek(self.fd(), pos as libc::off_t, whence) };
|
||||
if n < 0 {
|
||||
Err(super::last_error())
|
||||
} else {
|
||||
Ok(n as u64)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tell(&self) -> IoResult<u64> {
|
||||
let n = unsafe { libc::lseek(self.fd(), 0, libc::SEEK_CUR) };
|
||||
if n < 0 {
|
||||
Err(super::last_error())
|
||||
} else {
|
||||
Ok(n as u64)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fsync(&self) -> IoResult<()> {
|
||||
mkerr_libc(retry(|| unsafe { libc::fsync(self.fd()) }))
|
||||
}
|
||||
|
||||
pub fn datasync(&self) -> IoResult<()> {
|
||||
return mkerr_libc(os_datasync(self.fd()));
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
fn os_datasync(fd: c_int) -> c_int {
|
||||
unsafe { libc::fcntl(fd, libc::F_FULLFSYNC) }
|
||||
}
|
||||
#[cfg(target_os = "linux")]
|
||||
fn os_datasync(fd: c_int) -> c_int {
|
||||
retry(|| unsafe { libc::fdatasync(fd) })
|
||||
}
|
||||
#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "linux")))]
|
||||
fn os_datasync(fd: c_int) -> c_int {
|
||||
retry(|| unsafe { libc::fsync(fd) })
|
||||
}
|
||||
}
|
||||
|
||||
pub fn truncate(&self, offset: i64) -> IoResult<()> {
|
||||
mkerr_libc(retry(|| unsafe {
|
||||
libc::ftruncate(self.fd(), offset as libc::off_t)
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn fstat(&self) -> IoResult<FileStat> {
|
||||
let mut stat: libc::stat = unsafe { mem::zeroed() };
|
||||
match unsafe { libc::fstat(self.fd(), &mut stat) } {
|
||||
0 => Ok(mkstat(&stat)),
|
||||
_ => Err(super::last_error()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract the actual filedescriptor without closing it.
|
||||
pub fn unwrap(self) -> fd_t {
|
||||
let fd = self.fd;
|
||||
unsafe { mem::forget(self) };
|
||||
fd
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for FileDesc {
|
||||
fn drop(&mut self) {
|
||||
// closing stdio file handles makes no sense, so never do it. Also, note
|
||||
// that errors are ignored when closing a file descriptor. The reason
|
||||
// for this is that if an error occurs we don't actually know if the
|
||||
// file descriptor was closed or not, and if we retried (for something
|
||||
// like EINTR), we might close another valid file descriptor (opened
|
||||
// after we closed ours.
|
||||
if self.close_on_drop && self.fd > libc::STDERR_FILENO {
|
||||
let n = unsafe { libc::close(self.fd) };
|
||||
if n != 0 {
|
||||
println!("error {} when closing file descriptor {}", n, self.fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open(path: &Path, fm: FileMode, fa: FileAccess) -> IoResult<FileDesc> {
|
||||
let flags = match fm {
|
||||
Open => 0,
|
||||
Append => libc::O_APPEND,
|
||||
Truncate => libc::O_TRUNC,
|
||||
};
|
||||
// Opening with a write permission must silently create the file.
|
||||
let (flags, mode) = match fa {
|
||||
Read => (flags | libc::O_RDONLY, 0),
|
||||
Write => (flags | libc::O_WRONLY | libc::O_CREAT,
|
||||
libc::S_IRUSR | libc::S_IWUSR),
|
||||
ReadWrite => (flags | libc::O_RDWR | libc::O_CREAT,
|
||||
libc::S_IRUSR | libc::S_IWUSR),
|
||||
};
|
||||
|
||||
let path = path.to_c_str();
|
||||
match retry(|| unsafe { libc::open(path.as_ptr(), flags, mode) }) {
|
||||
-1 => Err(super::last_error()),
|
||||
fd => Ok(FileDesc::new(fd, true)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mkdir(p: &Path, mode: uint) -> IoResult<()> {
|
||||
let p = p.to_c_str();
|
||||
mkerr_libc(unsafe { libc::mkdir(p.as_ptr(), mode as libc::mode_t) })
|
||||
}
|
||||
|
||||
pub fn readdir(p: &Path) -> IoResult<Vec<Path>> {
|
||||
use libc::{dirent_t};
|
||||
use libc::{opendir, readdir_r, closedir};
|
||||
|
||||
fn prune(root: &CString, dirs: Vec<Path>) -> Vec<Path> {
|
||||
let root = unsafe { CString::new(root.as_ptr(), false) };
|
||||
let root = Path::new(root);
|
||||
|
||||
dirs.into_iter().filter(|path| {
|
||||
path.as_vec() != b"." && path.as_vec() != b".."
|
||||
}).map(|path| root.join(path)).collect()
|
||||
}
|
||||
|
||||
extern {
|
||||
fn rust_dirent_t_size() -> libc::c_int;
|
||||
fn rust_list_dir_val(ptr: *mut dirent_t) -> *const libc::c_char;
|
||||
}
|
||||
|
||||
let size = unsafe { rust_dirent_t_size() };
|
||||
let mut buf = Vec::<u8>::with_capacity(size as uint);
|
||||
let ptr = buf.as_mut_slice().as_mut_ptr() as *mut dirent_t;
|
||||
|
||||
let p = p.to_c_str();
|
||||
let dir_ptr = unsafe {opendir(p.as_ptr())};
|
||||
|
||||
if dir_ptr as uint != 0 {
|
||||
let mut paths = vec!();
|
||||
let mut entry_ptr = 0 as *mut dirent_t;
|
||||
while unsafe { readdir_r(dir_ptr, ptr, &mut entry_ptr) == 0 } {
|
||||
if entry_ptr.is_null() { break }
|
||||
let cstr = unsafe {
|
||||
CString::new(rust_list_dir_val(entry_ptr), false)
|
||||
};
|
||||
paths.push(Path::new(cstr));
|
||||
}
|
||||
assert_eq!(unsafe { closedir(dir_ptr) }, 0);
|
||||
Ok(prune(&p, paths))
|
||||
} else {
|
||||
Err(super::last_error())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unlink(p: &Path) -> IoResult<()> {
|
||||
let p = p.to_c_str();
|
||||
mkerr_libc(unsafe { libc::unlink(p.as_ptr()) })
|
||||
}
|
||||
|
||||
pub fn rename(old: &Path, new: &Path) -> IoResult<()> {
|
||||
let old = old.to_c_str();
|
||||
let new = new.to_c_str();
|
||||
mkerr_libc(unsafe {
|
||||
libc::rename(old.as_ptr(), new.as_ptr())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn chmod(p: &Path, mode: uint) -> IoResult<()> {
|
||||
let p = p.to_c_str();
|
||||
mkerr_libc(retry(|| unsafe {
|
||||
libc::chmod(p.as_ptr(), mode as libc::mode_t)
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn rmdir(p: &Path) -> IoResult<()> {
|
||||
let p = p.to_c_str();
|
||||
mkerr_libc(unsafe { libc::rmdir(p.as_ptr()) })
|
||||
}
|
||||
|
||||
pub fn chown(p: &Path, uid: int, gid: int) -> IoResult<()> {
|
||||
let p = p.to_c_str();
|
||||
mkerr_libc(retry(|| unsafe {
|
||||
libc::chown(p.as_ptr(), uid as libc::uid_t, gid as libc::gid_t)
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn readlink(p: &Path) -> IoResult<Path> {
|
||||
let c_path = p.to_c_str();
|
||||
let p = c_path.as_ptr();
|
||||
let mut len = unsafe { libc::pathconf(p as *mut _, libc::_PC_NAME_MAX) };
|
||||
if len == -1 {
|
||||
len = 1024; // FIXME: read PATH_MAX from C ffi?
|
||||
}
|
||||
let mut buf: Vec<u8> = Vec::with_capacity(len as uint);
|
||||
match unsafe {
|
||||
libc::readlink(p, buf.as_ptr() as *mut libc::c_char,
|
||||
len as libc::size_t) as libc::c_int
|
||||
} {
|
||||
-1 => Err(super::last_error()),
|
||||
n => {
|
||||
assert!(n > 0);
|
||||
unsafe { buf.set_len(n as uint); }
|
||||
Ok(Path::new(buf))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> {
|
||||
let src = src.to_c_str();
|
||||
let dst = dst.to_c_str();
|
||||
mkerr_libc(unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) })
|
||||
}
|
||||
|
||||
pub fn link(src: &Path, dst: &Path) -> IoResult<()> {
|
||||
let src = src.to_c_str();
|
||||
let dst = dst.to_c_str();
|
||||
mkerr_libc(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })
|
||||
}
|
||||
|
||||
fn mkstat(stat: &libc::stat) -> FileStat {
|
||||
// FileStat times are in milliseconds
|
||||
fn mktime(secs: u64, nsecs: u64) -> u64 { secs * 1000 + nsecs / 1000000 }
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
fn flags(stat: &libc::stat) -> u64 { stat.st_flags as u64 }
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn flags(_stat: &libc::stat) -> u64 { 0 }
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
fn gen(stat: &libc::stat) -> u64 { stat.st_gen as u64 }
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn gen(_stat: &libc::stat) -> u64 { 0 }
|
||||
|
||||
FileStat {
|
||||
size: stat.st_size as u64,
|
||||
kind: match (stat.st_mode as libc::mode_t) & libc::S_IFMT {
|
||||
libc::S_IFREG => io::TypeFile,
|
||||
libc::S_IFDIR => io::TypeDirectory,
|
||||
libc::S_IFIFO => io::TypeNamedPipe,
|
||||
libc::S_IFBLK => io::TypeBlockSpecial,
|
||||
libc::S_IFLNK => io::TypeSymlink,
|
||||
_ => io::TypeUnknown,
|
||||
},
|
||||
perm: FilePermission::from_bits_truncate(stat.st_mode as u32),
|
||||
created: mktime(stat.st_ctime as u64, stat.st_ctime_nsec as u64),
|
||||
modified: mktime(stat.st_mtime as u64, stat.st_mtime_nsec as u64),
|
||||
accessed: mktime(stat.st_atime as u64, stat.st_atime_nsec as u64),
|
||||
unstable: UnstableFileStat {
|
||||
device: stat.st_dev as u64,
|
||||
inode: stat.st_ino as u64,
|
||||
rdev: stat.st_rdev as u64,
|
||||
nlink: stat.st_nlink as u64,
|
||||
uid: stat.st_uid as u64,
|
||||
gid: stat.st_gid as u64,
|
||||
blksize: stat.st_blksize as u64,
|
||||
blocks: stat.st_blocks as u64,
|
||||
flags: flags(stat),
|
||||
gen: gen(stat),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stat(p: &Path) -> IoResult<FileStat> {
|
||||
let p = p.to_c_str();
|
||||
let mut stat: libc::stat = unsafe { mem::zeroed() };
|
||||
match unsafe { libc::stat(p.as_ptr(), &mut stat) } {
|
||||
0 => Ok(mkstat(&stat)),
|
||||
_ => Err(super::last_error()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lstat(p: &Path) -> IoResult<FileStat> {
|
||||
let p = p.to_c_str();
|
||||
let mut stat: libc::stat = unsafe { mem::zeroed() };
|
||||
match unsafe { libc::lstat(p.as_ptr(), &mut stat) } {
|
||||
0 => Ok(mkstat(&stat)),
|
||||
_ => Err(super::last_error()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn utime(p: &Path, atime: u64, mtime: u64) -> IoResult<()> {
|
||||
let p = p.to_c_str();
|
||||
let buf = libc::utimbuf {
|
||||
actime: (atime / 1000) as libc::time_t,
|
||||
modtime: (mtime / 1000) as libc::time_t,
|
||||
};
|
||||
mkerr_libc(unsafe { libc::utime(p.as_ptr(), &buf) })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::FileDesc;
|
||||
use libc;
|
||||
use os;
|
||||
use prelude::*;
|
||||
|
||||
#[cfg_attr(target_os = "freebsd", ignore)] // hmm, maybe pipes have a tiny buffer
|
||||
#[test]
|
||||
fn test_file_desc() {
|
||||
// Run this test with some pipes so we don't have to mess around with
|
||||
// opening or closing files.
|
||||
let os::Pipe { reader, writer } = unsafe { os::pipe().unwrap() };
|
||||
let mut reader = FileDesc::new(reader, true);
|
||||
let mut writer = FileDesc::new(writer, true);
|
||||
|
||||
writer.write(b"test").ok().unwrap();
|
||||
let mut buf = [0u8, ..4];
|
||||
match reader.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);
|
||||
assert_eq!(buf[3], 't' as u8);
|
||||
}
|
||||
r => panic!("invalid read: {}", r),
|
||||
}
|
||||
|
||||
assert!(writer.read(buf).is_err());
|
||||
assert!(reader.write(buf).is_err());
|
||||
}
|
||||
}
|
29
src/libstd/sys/unix/helper_signal.rs
Normal file
29
src/libstd/sys/unix/helper_signal.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Copyright 2014 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 os;
|
||||
|
||||
use sys::fs::FileDesc;
|
||||
|
||||
pub type signal = libc::c_int;
|
||||
|
||||
pub fn new() -> (signal, signal) {
|
||||
let os::Pipe { reader, writer } = unsafe { os::pipe().unwrap() };
|
||||
(reader, writer)
|
||||
}
|
||||
|
||||
pub fn signal(fd: libc::c_int) {
|
||||
FileDesc::new(fd, false).write([0]).ok().unwrap();
|
||||
}
|
||||
|
||||
pub fn close(fd: libc::c_int) {
|
||||
let _fd = FileDesc::new(fd, true);
|
||||
}
|
146
src/libstd/sys/unix/mod.rs
Normal file
146
src/libstd/sys/unix/mod.rs
Normal file
|
@ -0,0 +1,146 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
#![allow(missing_doc)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused_unsafe)]
|
||||
#![allow(unused_mut)]
|
||||
|
||||
extern crate libc;
|
||||
|
||||
use num;
|
||||
use prelude::*;
|
||||
use io::{mod, IoResult, IoError};
|
||||
use sys_common::mkerr_libc;
|
||||
|
||||
macro_rules! helper_init( (static $name:ident: Helper<$m:ty>) => (
|
||||
static $name: Helper<$m> = Helper {
|
||||
lock: ::rt::mutex::NATIVE_MUTEX_INIT,
|
||||
chan: ::cell::UnsafeCell { value: 0 as *mut Sender<$m> },
|
||||
signal: ::cell::UnsafeCell { value: 0 },
|
||||
initialized: ::cell::UnsafeCell { value: false },
|
||||
};
|
||||
) )
|
||||
|
||||
pub mod c;
|
||||
pub mod fs;
|
||||
pub mod os;
|
||||
pub mod tcp;
|
||||
pub mod udp;
|
||||
pub mod pipe;
|
||||
pub mod helper_signal;
|
||||
pub mod process;
|
||||
pub mod timer;
|
||||
pub mod tty;
|
||||
|
||||
pub mod addrinfo {
|
||||
pub use sys_common::net::get_host_addresses;
|
||||
}
|
||||
|
||||
// FIXME: move these to c module
|
||||
pub type sock_t = self::fs::fd_t;
|
||||
pub type wrlen = libc::size_t;
|
||||
pub type msglen_t = libc::size_t;
|
||||
pub unsafe fn close_sock(sock: sock_t) { let _ = libc::close(sock); }
|
||||
|
||||
pub fn last_error() -> IoError {
|
||||
decode_error_detailed(os::errno() as i32)
|
||||
}
|
||||
|
||||
pub fn last_net_error() -> IoError {
|
||||
last_error()
|
||||
}
|
||||
|
||||
extern "system" {
|
||||
fn gai_strerror(errcode: libc::c_int) -> *const libc::c_char;
|
||||
}
|
||||
|
||||
pub fn last_gai_error(s: libc::c_int) -> IoError {
|
||||
use c_str::CString;
|
||||
|
||||
let mut err = decode_error(s);
|
||||
err.detail = Some(unsafe {
|
||||
CString::new(gai_strerror(s), false).as_str().unwrap().to_string()
|
||||
});
|
||||
err
|
||||
}
|
||||
|
||||
/// Convert an `errno` value into a high-level error variant and description.
|
||||
pub fn decode_error(errno: i32) -> IoError {
|
||||
// FIXME: this should probably be a bit more descriptive...
|
||||
let (kind, desc) = match errno {
|
||||
libc::EOF => (io::EndOfFile, "end of file"),
|
||||
libc::ECONNREFUSED => (io::ConnectionRefused, "connection refused"),
|
||||
libc::ECONNRESET => (io::ConnectionReset, "connection reset"),
|
||||
libc::EPERM | libc::EACCES =>
|
||||
(io::PermissionDenied, "permission denied"),
|
||||
libc::EPIPE => (io::BrokenPipe, "broken pipe"),
|
||||
libc::ENOTCONN => (io::NotConnected, "not connected"),
|
||||
libc::ECONNABORTED => (io::ConnectionAborted, "connection aborted"),
|
||||
libc::EADDRNOTAVAIL => (io::ConnectionRefused, "address not available"),
|
||||
libc::EADDRINUSE => (io::ConnectionRefused, "address in use"),
|
||||
libc::ENOENT => (io::FileNotFound, "no such file or directory"),
|
||||
libc::EISDIR => (io::InvalidInput, "illegal operation on a directory"),
|
||||
libc::ENOSYS => (io::IoUnavailable, "function not implemented"),
|
||||
libc::EINVAL => (io::InvalidInput, "invalid argument"),
|
||||
libc::ENOTTY =>
|
||||
(io::MismatchedFileTypeForOperation,
|
||||
"file descriptor is not a TTY"),
|
||||
libc::ETIMEDOUT => (io::TimedOut, "operation timed out"),
|
||||
libc::ECANCELED => (io::TimedOut, "operation aborted"),
|
||||
|
||||
// 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")
|
||||
};
|
||||
IoError { kind: kind, desc: desc, detail: None }
|
||||
}
|
||||
|
||||
pub fn decode_error_detailed(errno: i32) -> IoError {
|
||||
let mut err = decode_error(errno);
|
||||
err.detail = Some(os::error_string(errno));
|
||||
err
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn retry<I: PartialEq + num::One + Neg<I>> (f: || -> I) -> I {
|
||||
let minus_one = -num::one::<I>();
|
||||
loop {
|
||||
let n = f();
|
||||
if n == minus_one && os::errno() == libc::EINTR as int { }
|
||||
else { return n }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ms_to_timeval(ms: u64) -> libc::timeval {
|
||||
libc::timeval {
|
||||
tv_sec: (ms / 1000) as libc::time_t,
|
||||
tv_usec: ((ms % 1000) * 1000) as libc::suseconds_t,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wouldblock() -> bool {
|
||||
let err = os::errno();
|
||||
err == libc::EWOULDBLOCK as int || err == libc::EAGAIN as int
|
||||
}
|
||||
|
||||
pub fn set_nonblocking(fd: sock_t, nb: bool) -> IoResult<()> {
|
||||
let set = nb as libc::c_int;
|
||||
mkerr_libc(retry(|| unsafe { c::ioctl(fd, c::FIONBIO, &set) }))
|
||||
}
|
||||
|
||||
// nothing needed on unix platforms
|
||||
pub fn init_net() {}
|
112
src/libstd/sys/unix/os.rs
Normal file
112
src/libstd/sys/unix/os.rs
Normal file
|
@ -0,0 +1,112 @@
|
|||
// Copyright 2014 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 libc::{c_int, c_char};
|
||||
use prelude::*;
|
||||
use io::IoResult;
|
||||
use sys::fs::FileDesc;
|
||||
|
||||
use os::TMPBUF_SZ;
|
||||
|
||||
/// Returns the platform-specific value of errno
|
||||
pub fn errno() -> int {
|
||||
#[cfg(any(target_os = "macos",
|
||||
target_os = "ios",
|
||||
target_os = "freebsd"))]
|
||||
fn errno_location() -> *const c_int {
|
||||
extern {
|
||||
fn __error() -> *const c_int;
|
||||
}
|
||||
unsafe {
|
||||
__error()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "dragonfly")]
|
||||
fn errno_location() -> *const c_int {
|
||||
extern {
|
||||
fn __dfly_error() -> *const c_int;
|
||||
}
|
||||
unsafe {
|
||||
__dfly_error()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn errno_location() -> *const c_int {
|
||||
extern {
|
||||
fn __errno_location() -> *const c_int;
|
||||
}
|
||||
unsafe {
|
||||
__errno_location()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
(*errno_location()) as int
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a detailed string description for the given error number
|
||||
pub fn error_string(errno: i32) -> String {
|
||||
#[cfg(any(target_os = "macos",
|
||||
target_os = "ios",
|
||||
target_os = "android",
|
||||
target_os = "freebsd",
|
||||
target_os = "dragonfly"))]
|
||||
fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: libc::size_t)
|
||||
-> c_int {
|
||||
extern {
|
||||
fn strerror_r(errnum: c_int, buf: *mut c_char,
|
||||
buflen: libc::size_t) -> c_int;
|
||||
}
|
||||
unsafe {
|
||||
strerror_r(errnum, buf, buflen)
|
||||
}
|
||||
}
|
||||
|
||||
// GNU libc provides a non-compliant version of strerror_r by default
|
||||
// and requires macros to instead use the POSIX compliant variant.
|
||||
// So we just use __xpg_strerror_r which is always POSIX compliant
|
||||
#[cfg(target_os = "linux")]
|
||||
fn strerror_r(errnum: c_int, buf: *mut c_char,
|
||||
buflen: libc::size_t) -> c_int {
|
||||
extern {
|
||||
fn __xpg_strerror_r(errnum: c_int,
|
||||
buf: *mut c_char,
|
||||
buflen: libc::size_t)
|
||||
-> c_int;
|
||||
}
|
||||
unsafe {
|
||||
__xpg_strerror_r(errnum, buf, buflen)
|
||||
}
|
||||
}
|
||||
|
||||
let mut buf = [0 as c_char, ..TMPBUF_SZ];
|
||||
|
||||
let p = buf.as_mut_ptr();
|
||||
unsafe {
|
||||
if strerror_r(errno as c_int, p, buf.len() as libc::size_t) < 0 {
|
||||
panic!("strerror_r failure");
|
||||
}
|
||||
|
||||
::string::raw::from_buf(p as *const u8)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn pipe() -> IoResult<(FileDesc, FileDesc)> {
|
||||
let mut fds = [0, ..2];
|
||||
if libc::pipe(fds.as_mut_ptr()) == 0 {
|
||||
Ok((FileDesc::new(fds[0], true), FileDesc::new(fds[1], true)))
|
||||
} else {
|
||||
Err(super::last_error())
|
||||
}
|
||||
}
|
|
@ -10,19 +10,17 @@
|
|||
|
||||
use alloc::arc::Arc;
|
||||
use libc;
|
||||
use std::c_str::CString;
|
||||
use std::mem;
|
||||
use std::rt::mutex;
|
||||
use std::rt::rtio;
|
||||
use std::rt::rtio::{IoResult, IoError};
|
||||
use std::sync::atomic;
|
||||
use c_str::CString;
|
||||
use mem;
|
||||
use rt::mutex;
|
||||
use sync::atomic;
|
||||
use io::{mod, IoResult, IoError};
|
||||
use prelude::*;
|
||||
|
||||
use super::retry;
|
||||
use super::net;
|
||||
use super::util;
|
||||
use super::c;
|
||||
use super::process;
|
||||
use super::file::{fd_t, FileDesc};
|
||||
use sys::{mod, timer, retry, c, set_nonblocking, wouldblock};
|
||||
use sys::fs::{fd_t, FileDesc};
|
||||
use sys_common::net::*;
|
||||
use sys_common::{eof, mkerr_libc};
|
||||
|
||||
fn unix_socket(ty: libc::c_int) -> IoResult<fd_t> {
|
||||
match unsafe { libc::socket(libc::AF_UNIX, ty, 0) } {
|
||||
|
@ -41,12 +39,10 @@ fn addr_to_sockaddr_un(addr: &CString,
|
|||
|
||||
let len = addr.len();
|
||||
if len > s.sun_path.len() - 1 {
|
||||
#[cfg(unix)] use libc::EINVAL as ERROR;
|
||||
#[cfg(windows)] use libc::WSAEINVAL as ERROR;
|
||||
return Err(IoError {
|
||||
code: ERROR as uint,
|
||||
extra: 0,
|
||||
detail: Some("path must be smaller than SUN_LEN".to_string()),
|
||||
kind: io::InvalidInput,
|
||||
desc: "invalid argument: path must be smaller than SUN_LEN",
|
||||
detail: None,
|
||||
})
|
||||
}
|
||||
s.sun_family = libc::AF_UNIX as libc::sa_family_t;
|
||||
|
@ -92,7 +88,7 @@ fn connect(addr: &CString, ty: libc::c_int,
|
|||
}
|
||||
}
|
||||
Some(timeout_ms) => {
|
||||
try!(util::connect_timeout(inner.fd, addrp, len, timeout_ms));
|
||||
try!(connect_timeout(inner.fd, addrp, len, timeout_ms));
|
||||
Ok(inner)
|
||||
}
|
||||
}
|
||||
|
@ -143,18 +139,16 @@ impl UnixStream {
|
|||
fn lock_nonblocking(&self) {}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
fn lock_nonblocking<'a>(&'a self) -> net::Guard<'a> {
|
||||
let ret = net::Guard {
|
||||
fn lock_nonblocking<'a>(&'a self) -> Guard<'a> {
|
||||
let ret = Guard {
|
||||
fd: self.fd(),
|
||||
guard: unsafe { self.inner.lock.lock() },
|
||||
};
|
||||
assert!(util::set_nonblocking(self.fd(), true).is_ok());
|
||||
assert!(set_nonblocking(self.fd(), true).is_ok());
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl rtio::RtioPipe for UnixStream {
|
||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
|
||||
pub fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
|
||||
let fd = self.fd();
|
||||
let dolock = || self.lock_nonblocking();
|
||||
let doread = |nb| unsafe {
|
||||
|
@ -164,10 +158,10 @@ impl rtio::RtioPipe for UnixStream {
|
|||
buf.len() as libc::size_t,
|
||||
flags) as libc::c_int
|
||||
};
|
||||
net::read(fd, self.read_deadline, dolock, doread)
|
||||
read(fd, self.read_deadline, dolock, doread)
|
||||
}
|
||||
|
||||
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
||||
pub fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
||||
let fd = self.fd();
|
||||
let dolock = || self.lock_nonblocking();
|
||||
let dowrite = |nb: bool, buf: *const u8, len: uint| unsafe {
|
||||
|
@ -177,32 +171,38 @@ impl rtio::RtioPipe for UnixStream {
|
|||
len as libc::size_t,
|
||||
flags) as i64
|
||||
};
|
||||
match net::write(fd, self.write_deadline, buf, true, dolock, dowrite) {
|
||||
match write(fd, self.write_deadline, buf, true, dolock, dowrite) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => Err(e)
|
||||
}
|
||||
}
|
||||
|
||||
fn clone(&self) -> Box<rtio::RtioPipe + Send> {
|
||||
box UnixStream::new(self.inner.clone()) as Box<rtio::RtioPipe + Send>
|
||||
pub fn close_write(&mut self) -> IoResult<()> {
|
||||
mkerr_libc(unsafe { libc::shutdown(self.fd(), libc::SHUT_WR) })
|
||||
}
|
||||
|
||||
fn close_write(&mut self) -> IoResult<()> {
|
||||
super::mkerr_libc(unsafe { libc::shutdown(self.fd(), libc::SHUT_WR) })
|
||||
pub fn close_read(&mut self) -> IoResult<()> {
|
||||
mkerr_libc(unsafe { libc::shutdown(self.fd(), libc::SHUT_RD) })
|
||||
}
|
||||
fn close_read(&mut self) -> IoResult<()> {
|
||||
super::mkerr_libc(unsafe { libc::shutdown(self.fd(), libc::SHUT_RD) })
|
||||
}
|
||||
fn set_timeout(&mut self, timeout: Option<u64>) {
|
||||
let deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0);
|
||||
|
||||
pub fn set_timeout(&mut self, timeout: Option<u64>) {
|
||||
let deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
|
||||
self.read_deadline = deadline;
|
||||
self.write_deadline = deadline;
|
||||
}
|
||||
fn set_read_timeout(&mut self, timeout: Option<u64>) {
|
||||
self.read_deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0);
|
||||
|
||||
pub fn set_read_timeout(&mut self, timeout: Option<u64>) {
|
||||
self.read_deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
|
||||
}
|
||||
fn set_write_timeout(&mut self, timeout: Option<u64>) {
|
||||
self.write_deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0);
|
||||
|
||||
pub fn set_write_timeout(&mut self, timeout: Option<u64>) {
|
||||
self.write_deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for UnixStream {
|
||||
fn clone(&self) -> UnixStream {
|
||||
UnixStream::new(self.inner.clone())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -224,16 +224,15 @@ impl UnixListener {
|
|||
|
||||
fn fd(&self) -> fd_t { self.inner.fd }
|
||||
|
||||
pub fn native_listen(self, backlog: int) -> IoResult<UnixAcceptor> {
|
||||
match unsafe { libc::listen(self.fd(), backlog as libc::c_int) } {
|
||||
pub fn listen(self) -> IoResult<UnixAcceptor> {
|
||||
match unsafe { libc::listen(self.fd(), 128) } {
|
||||
-1 => Err(super::last_error()),
|
||||
|
||||
#[cfg(unix)]
|
||||
_ => {
|
||||
let (reader, writer) = try!(process::pipe());
|
||||
try!(util::set_nonblocking(reader.fd(), true));
|
||||
try!(util::set_nonblocking(writer.fd(), true));
|
||||
try!(util::set_nonblocking(self.fd(), true));
|
||||
let (reader, writer) = try!(unsafe { sys::os::pipe() });
|
||||
try!(set_nonblocking(reader.fd(), true));
|
||||
try!(set_nonblocking(writer.fd(), true));
|
||||
try!(set_nonblocking(self.fd(), true));
|
||||
Ok(UnixAcceptor {
|
||||
inner: Arc::new(AcceptorInner {
|
||||
listener: self,
|
||||
|
@ -248,21 +247,11 @@ impl UnixListener {
|
|||
}
|
||||
}
|
||||
|
||||
impl rtio::RtioUnixListener for UnixListener {
|
||||
fn listen(self: Box<UnixListener>)
|
||||
-> IoResult<Box<rtio::RtioUnixAcceptor + Send>> {
|
||||
self.native_listen(128).map(|a| {
|
||||
box a as Box<rtio::RtioUnixAcceptor + Send>
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UnixAcceptor {
|
||||
inner: Arc<AcceptorInner>,
|
||||
deadline: u64,
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
struct AcceptorInner {
|
||||
listener: UnixListener,
|
||||
reader: FileDesc,
|
||||
|
@ -273,7 +262,7 @@ struct AcceptorInner {
|
|||
impl UnixAcceptor {
|
||||
fn fd(&self) -> fd_t { self.inner.listener.fd() }
|
||||
|
||||
pub fn native_accept(&mut self) -> IoResult<UnixStream> {
|
||||
pub fn accept(&mut self) -> IoResult<UnixStream> {
|
||||
let deadline = if self.deadline == 0 {None} else {Some(self.deadline)};
|
||||
|
||||
while !self.inner.closed.load(atomic::SeqCst) {
|
||||
|
@ -287,43 +276,36 @@ impl UnixAcceptor {
|
|||
storagep as *mut libc::sockaddr,
|
||||
&mut size as *mut libc::socklen_t) as libc::c_int
|
||||
}) {
|
||||
-1 if util::wouldblock() => {}
|
||||
-1 if wouldblock() => {}
|
||||
-1 => return Err(super::last_error()),
|
||||
fd => return Ok(UnixStream::new(Arc::new(Inner::new(fd)))),
|
||||
}
|
||||
}
|
||||
try!(util::await([self.fd(), self.inner.reader.fd()],
|
||||
deadline, util::Readable));
|
||||
try!(await([self.fd(), self.inner.reader.fd()],
|
||||
deadline, Readable));
|
||||
}
|
||||
|
||||
Err(util::eof())
|
||||
Err(eof())
|
||||
}
|
||||
|
||||
pub fn set_timeout(&mut self, timeout: Option<u64>) {
|
||||
self.deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
|
||||
}
|
||||
|
||||
pub fn close_accept(&mut self) -> IoResult<()> {
|
||||
self.inner.closed.store(true, atomic::SeqCst);
|
||||
let fd = FileDesc::new(self.inner.writer.fd(), false);
|
||||
match fd.write([0]) {
|
||||
Ok(..) => Ok(()),
|
||||
Err(..) if wouldblock() => Ok(()),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl rtio::RtioUnixAcceptor for UnixAcceptor {
|
||||
fn accept(&mut self) -> IoResult<Box<rtio::RtioPipe + Send>> {
|
||||
self.native_accept().map(|s| box s as Box<rtio::RtioPipe + Send>)
|
||||
}
|
||||
fn set_timeout(&mut self, timeout: Option<u64>) {
|
||||
self.deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0);
|
||||
}
|
||||
|
||||
fn clone(&self) -> Box<rtio::RtioUnixAcceptor + Send> {
|
||||
box UnixAcceptor {
|
||||
inner: self.inner.clone(),
|
||||
deadline: 0,
|
||||
} as Box<rtio::RtioUnixAcceptor + Send>
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn close_accept(&mut self) -> IoResult<()> {
|
||||
self.inner.closed.store(true, atomic::SeqCst);
|
||||
let mut fd = FileDesc::new(self.inner.writer.fd(), false);
|
||||
match fd.inner_write([0]) {
|
||||
Ok(..) => Ok(()),
|
||||
Err(..) if util::wouldblock() => Ok(()),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
impl Clone for UnixAcceptor {
|
||||
fn clone(&self) -> UnixAcceptor {
|
||||
UnixAcceptor { inner: self.inner.clone(), deadline: 0 }
|
||||
}
|
||||
}
|
||||
|
587
src/libstd/sys/unix/process.rs
Normal file
587
src/libstd/sys/unix/process.rs
Normal file
|
@ -0,0 +1,587 @@
|
|||
// Copyright 2014 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::{mod, pid_t, c_void, c_int};
|
||||
use c_str::CString;
|
||||
use io::{mod, IoResult, IoError};
|
||||
use mem;
|
||||
use os;
|
||||
use ptr;
|
||||
use prelude::*;
|
||||
use io::process::{ProcessExit, ExitStatus, ExitSignal};
|
||||
use collections;
|
||||
use path::BytesContainer;
|
||||
use hash::Hash;
|
||||
|
||||
use sys::{mod, retry, c, wouldblock, set_nonblocking, ms_to_timeval};
|
||||
use sys::fs::FileDesc;
|
||||
use sys_common::helper_thread::Helper;
|
||||
use sys_common::{AsFileDesc, mkerr_libc, timeout};
|
||||
|
||||
pub use sys_common::ProcessConfig;
|
||||
|
||||
helper_init!(static HELPER: Helper<Req>)
|
||||
|
||||
/// The unique id of the process (this should never be negative).
|
||||
pub struct Process {
|
||||
pub pid: pid_t
|
||||
}
|
||||
|
||||
enum Req {
|
||||
NewChild(libc::pid_t, Sender<ProcessExit>, u64),
|
||||
}
|
||||
|
||||
impl Process {
|
||||
pub fn id(&self) -> pid_t {
|
||||
self.pid
|
||||
}
|
||||
|
||||
pub unsafe fn kill(&self, signal: int) -> IoResult<()> {
|
||||
Process::killpid(self.pid, signal)
|
||||
}
|
||||
|
||||
pub unsafe fn killpid(pid: pid_t, signal: int) -> IoResult<()> {
|
||||
let r = libc::funcs::posix88::signal::kill(pid, signal as c_int);
|
||||
mkerr_libc(r)
|
||||
}
|
||||
|
||||
pub fn spawn<K, V, C, P>(cfg: &C, in_fd: Option<P>,
|
||||
out_fd: Option<P>, err_fd: Option<P>)
|
||||
-> IoResult<Process>
|
||||
where C: ProcessConfig<K, V>, P: AsFileDesc,
|
||||
K: BytesContainer + Eq + Hash, V: BytesContainer
|
||||
{
|
||||
use libc::funcs::posix88::unistd::{fork, dup2, close, chdir, execvp};
|
||||
use libc::funcs::bsd44::getdtablesize;
|
||||
|
||||
mod rustrt {
|
||||
extern {
|
||||
pub fn rust_unset_sigprocmask();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
unsafe fn set_environ(envp: *const c_void) {
|
||||
extern { fn _NSGetEnviron() -> *mut *const c_void; }
|
||||
|
||||
*_NSGetEnviron() = envp;
|
||||
}
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
unsafe fn set_environ(envp: *const c_void) {
|
||||
extern { static mut environ: *const c_void; }
|
||||
environ = envp;
|
||||
}
|
||||
|
||||
unsafe fn set_cloexec(fd: c_int) {
|
||||
let ret = c::ioctl(fd, c::FIOCLEX);
|
||||
assert_eq!(ret, 0);
|
||||
}
|
||||
|
||||
let dirp = cfg.cwd().map(|c| c.as_ptr()).unwrap_or(ptr::null());
|
||||
|
||||
// temporary until unboxed closures land
|
||||
let cfg = unsafe {
|
||||
mem::transmute::<&ProcessConfig<K,V>,&'static ProcessConfig<K,V>>(cfg)
|
||||
};
|
||||
|
||||
with_envp(cfg.env(), proc(envp) {
|
||||
with_argv(cfg.program(), cfg.args(), proc(argv) unsafe {
|
||||
let (input, mut output) = try!(sys::os::pipe());
|
||||
|
||||
// We may use this in the child, so perform allocations before the
|
||||
// fork
|
||||
let devnull = "/dev/null".to_c_str();
|
||||
|
||||
set_cloexec(output.fd());
|
||||
|
||||
let pid = fork();
|
||||
if pid < 0 {
|
||||
return Err(super::last_error())
|
||||
} else if pid > 0 {
|
||||
drop(output);
|
||||
let mut bytes = [0, ..4];
|
||||
return match input.read(bytes) {
|
||||
Ok(4) => {
|
||||
let errno = (bytes[0] as i32 << 24) |
|
||||
(bytes[1] as i32 << 16) |
|
||||
(bytes[2] as i32 << 8) |
|
||||
(bytes[3] as i32 << 0);
|
||||
Err(super::decode_error(errno))
|
||||
}
|
||||
Err(..) => Ok(Process { pid: pid }),
|
||||
Ok(..) => panic!("short read on the cloexec pipe"),
|
||||
};
|
||||
}
|
||||
|
||||
// And at this point we've reached a special time in the life of the
|
||||
// child. The child must now be considered hamstrung and unable to
|
||||
// do anything other than syscalls really. Consider the following
|
||||
// scenario:
|
||||
//
|
||||
// 1. Thread A of process 1 grabs the malloc() mutex
|
||||
// 2. Thread B of process 1 forks(), creating thread C
|
||||
// 3. Thread C of process 2 then attempts to malloc()
|
||||
// 4. The memory of process 2 is the same as the memory of
|
||||
// process 1, so the mutex is locked.
|
||||
//
|
||||
// This situation looks a lot like deadlock, right? It turns out
|
||||
// that this is what pthread_atfork() takes care of, which is
|
||||
// presumably implemented across platforms. The first thing that
|
||||
// threads to *before* forking is to do things like grab the malloc
|
||||
// mutex, and then after the fork they unlock it.
|
||||
//
|
||||
// Despite this information, libnative's spawn has been witnessed to
|
||||
// deadlock on both OSX and FreeBSD. I'm not entirely sure why, but
|
||||
// all collected backtraces point at malloc/free traffic in the
|
||||
// child spawned process.
|
||||
//
|
||||
// For this reason, the block of code below should contain 0
|
||||
// invocations of either malloc of free (or their related friends).
|
||||
//
|
||||
// As an example of not having malloc/free traffic, we don't close
|
||||
// this file descriptor by dropping the FileDesc (which contains an
|
||||
// allocation). Instead we just close it manually. This will never
|
||||
// have the drop glue anyway because this code never returns (the
|
||||
// child will either exec() or invoke libc::exit)
|
||||
let _ = libc::close(input.fd());
|
||||
|
||||
fn fail(output: &mut FileDesc) -> ! {
|
||||
let errno = sys::os::errno();
|
||||
let bytes = [
|
||||
(errno >> 24) as u8,
|
||||
(errno >> 16) as u8,
|
||||
(errno >> 8) as u8,
|
||||
(errno >> 0) as u8,
|
||||
];
|
||||
assert!(output.write(bytes).is_ok());
|
||||
unsafe { libc::_exit(1) }
|
||||
}
|
||||
|
||||
rustrt::rust_unset_sigprocmask();
|
||||
|
||||
// If a stdio file descriptor is set to be ignored (via a -1 file
|
||||
// descriptor), then we don't actually close it, but rather open
|
||||
// up /dev/null into that file descriptor. Otherwise, the first file
|
||||
// descriptor opened up in the child would be numbered as one of the
|
||||
// stdio file descriptors, which is likely to wreak havoc.
|
||||
let setup = |src: Option<P>, dst: c_int| {
|
||||
let src = match src {
|
||||
None => {
|
||||
let flags = if dst == libc::STDIN_FILENO {
|
||||
libc::O_RDONLY
|
||||
} else {
|
||||
libc::O_RDWR
|
||||
};
|
||||
libc::open(devnull.as_ptr(), flags, 0)
|
||||
}
|
||||
Some(obj) => {
|
||||
let fd = obj.as_fd().fd();
|
||||
// Leak the memory and the file descriptor. We're in the
|
||||
// child now an all our resources are going to be
|
||||
// cleaned up very soon
|
||||
mem::forget(obj);
|
||||
fd
|
||||
}
|
||||
};
|
||||
src != -1 && retry(|| dup2(src, dst)) != -1
|
||||
};
|
||||
|
||||
if !setup(in_fd, libc::STDIN_FILENO) { fail(&mut output) }
|
||||
if !setup(out_fd, libc::STDOUT_FILENO) { fail(&mut output) }
|
||||
if !setup(err_fd, libc::STDERR_FILENO) { fail(&mut output) }
|
||||
|
||||
// close all other fds
|
||||
for fd in range(3, getdtablesize()).rev() {
|
||||
if fd != output.fd() {
|
||||
let _ = close(fd as c_int);
|
||||
}
|
||||
}
|
||||
|
||||
match cfg.gid() {
|
||||
Some(u) => {
|
||||
if libc::setgid(u as libc::gid_t) != 0 {
|
||||
fail(&mut output);
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
match cfg.uid() {
|
||||
Some(u) => {
|
||||
// When dropping privileges from root, the `setgroups` call
|
||||
// will remove any extraneous groups. If we don't call this,
|
||||
// then even though our uid has dropped, we may still have
|
||||
// groups that enable us to do super-user things. This will
|
||||
// fail if we aren't root, so don't bother checking the
|
||||
// return value, this is just done as an optimistic
|
||||
// privilege dropping function.
|
||||
extern {
|
||||
fn setgroups(ngroups: libc::c_int,
|
||||
ptr: *const libc::c_void) -> libc::c_int;
|
||||
}
|
||||
let _ = setgroups(0, 0 as *const libc::c_void);
|
||||
|
||||
if libc::setuid(u as libc::uid_t) != 0 {
|
||||
fail(&mut output);
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
if cfg.detach() {
|
||||
// Don't check the error of setsid because it fails if we're the
|
||||
// process leader already. We just forked so it shouldn't return
|
||||
// error, but ignore it anyway.
|
||||
let _ = libc::setsid();
|
||||
}
|
||||
if !dirp.is_null() && chdir(dirp) == -1 {
|
||||
fail(&mut output);
|
||||
}
|
||||
if !envp.is_null() {
|
||||
set_environ(envp);
|
||||
}
|
||||
let _ = execvp(*argv, argv as *mut _);
|
||||
fail(&mut output);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn wait(&self, deadline: u64) -> IoResult<ProcessExit> {
|
||||
use std::cmp;
|
||||
use std::comm;
|
||||
|
||||
static mut WRITE_FD: libc::c_int = 0;
|
||||
|
||||
let mut status = 0 as c_int;
|
||||
if deadline == 0 {
|
||||
return match retry(|| unsafe { c::waitpid(self.pid, &mut status, 0) }) {
|
||||
-1 => panic!("unknown waitpid error: {}", super::last_error()),
|
||||
_ => Ok(translate_status(status)),
|
||||
}
|
||||
}
|
||||
|
||||
// On unix, wait() and its friends have no timeout parameters, so there is
|
||||
// no way to time out a thread in wait(). From some googling and some
|
||||
// thinking, it appears that there are a few ways to handle timeouts in
|
||||
// wait(), but the only real reasonable one for a multi-threaded program is
|
||||
// to listen for SIGCHLD.
|
||||
//
|
||||
// With this in mind, the waiting mechanism with a timeout barely uses
|
||||
// waitpid() at all. There are a few times that waitpid() is invoked with
|
||||
// WNOHANG, but otherwise all the necessary blocking is done by waiting for
|
||||
// a SIGCHLD to arrive (and that blocking has a timeout). Note, however,
|
||||
// that waitpid() is still used to actually reap the child.
|
||||
//
|
||||
// Signal handling is super tricky in general, and this is no exception. Due
|
||||
// to the async nature of SIGCHLD, we use the self-pipe trick to transmit
|
||||
// data out of the signal handler to the rest of the application. The first
|
||||
// idea would be to have each thread waiting with a timeout to read this
|
||||
// output file descriptor, but a write() is akin to a signal(), not a
|
||||
// broadcast(), so it would only wake up one thread, and possibly the wrong
|
||||
// thread. Hence a helper thread is used.
|
||||
//
|
||||
// The helper thread here is responsible for farming requests for a
|
||||
// waitpid() with a timeout, and then processing all of the wait requests.
|
||||
// By guaranteeing that only this helper thread is reading half of the
|
||||
// self-pipe, we're sure that we'll never lose a SIGCHLD. This helper thread
|
||||
// is also responsible for select() to wait for incoming messages or
|
||||
// incoming SIGCHLD messages, along with passing an appropriate timeout to
|
||||
// select() to wake things up as necessary.
|
||||
//
|
||||
// The ordering of the following statements is also very purposeful. First,
|
||||
// we must be guaranteed that the helper thread is booted and available to
|
||||
// receive SIGCHLD signals, and then we must also ensure that we do a
|
||||
// nonblocking waitpid() at least once before we go ask the sigchld helper.
|
||||
// This prevents the race where the child exits, we boot the helper, and
|
||||
// then we ask for the child's exit status (never seeing a sigchld).
|
||||
//
|
||||
// The actual communication between the helper thread and this thread is
|
||||
// quite simple, just a channel moving data around.
|
||||
|
||||
unsafe { HELPER.boot(register_sigchld, waitpid_helper) }
|
||||
|
||||
match self.try_wait() {
|
||||
Some(ret) => return Ok(ret),
|
||||
None => {}
|
||||
}
|
||||
|
||||
let (tx, rx) = channel();
|
||||
unsafe { HELPER.send(NewChild(self.pid, tx, deadline)); }
|
||||
return match rx.recv_opt() {
|
||||
Ok(e) => Ok(e),
|
||||
Err(()) => Err(timeout("wait timed out")),
|
||||
};
|
||||
|
||||
// Register a new SIGCHLD handler, returning the reading half of the
|
||||
// self-pipe plus the old handler registered (return value of sigaction).
|
||||
//
|
||||
// Be sure to set up the self-pipe first because as soon as we register a
|
||||
// handler we're going to start receiving signals.
|
||||
fn register_sigchld() -> (libc::c_int, c::sigaction) {
|
||||
unsafe {
|
||||
let mut pipes = [0, ..2];
|
||||
assert_eq!(libc::pipe(pipes.as_mut_ptr()), 0);
|
||||
set_nonblocking(pipes[0], true).ok().unwrap();
|
||||
set_nonblocking(pipes[1], true).ok().unwrap();
|
||||
WRITE_FD = pipes[1];
|
||||
|
||||
let mut old: c::sigaction = mem::zeroed();
|
||||
let mut new: c::sigaction = mem::zeroed();
|
||||
new.sa_handler = sigchld_handler;
|
||||
new.sa_flags = c::SA_NOCLDSTOP;
|
||||
assert_eq!(c::sigaction(c::SIGCHLD, &new, &mut old), 0);
|
||||
(pipes[0], old)
|
||||
}
|
||||
}
|
||||
|
||||
// Helper thread for processing SIGCHLD messages
|
||||
fn waitpid_helper(input: libc::c_int,
|
||||
messages: Receiver<Req>,
|
||||
(read_fd, old): (libc::c_int, c::sigaction)) {
|
||||
set_nonblocking(input, true).ok().unwrap();
|
||||
let mut set: c::fd_set = unsafe { mem::zeroed() };
|
||||
let mut tv: libc::timeval;
|
||||
let mut active = Vec::<(libc::pid_t, Sender<ProcessExit>, u64)>::new();
|
||||
let max = cmp::max(input, read_fd) + 1;
|
||||
|
||||
'outer: loop {
|
||||
// Figure out the timeout of our syscall-to-happen. If we're waiting
|
||||
// for some processes, then they'll have a timeout, otherwise we
|
||||
// wait indefinitely for a message to arrive.
|
||||
//
|
||||
// FIXME: sure would be nice to not have to scan the entire array
|
||||
let min = active.iter().map(|a| *a.ref2()).enumerate().min_by(|p| {
|
||||
p.val1()
|
||||
});
|
||||
let (p, idx) = match min {
|
||||
Some((idx, deadline)) => {
|
||||
let now = sys::timer::now();
|
||||
let ms = if now < deadline {deadline - now} else {0};
|
||||
tv = ms_to_timeval(ms);
|
||||
(&mut tv as *mut _, idx)
|
||||
}
|
||||
None => (ptr::null_mut(), -1),
|
||||
};
|
||||
|
||||
// Wait for something to happen
|
||||
c::fd_set(&mut set, input);
|
||||
c::fd_set(&mut set, read_fd);
|
||||
match unsafe { c::select(max, &mut set, ptr::null_mut(),
|
||||
ptr::null_mut(), p) } {
|
||||
// interrupted, retry
|
||||
-1 if os::errno() == libc::EINTR as uint => continue,
|
||||
|
||||
// We read something, break out and process
|
||||
1 | 2 => {}
|
||||
|
||||
// Timeout, the pending request is removed
|
||||
0 => {
|
||||
drop(active.remove(idx));
|
||||
continue
|
||||
}
|
||||
|
||||
n => panic!("error in select {} ({})", os::errno(), n),
|
||||
}
|
||||
|
||||
// Process any pending messages
|
||||
if drain(input) {
|
||||
loop {
|
||||
match messages.try_recv() {
|
||||
Ok(NewChild(pid, tx, deadline)) => {
|
||||
active.push((pid, tx, deadline));
|
||||
}
|
||||
Err(comm::Disconnected) => {
|
||||
assert!(active.len() == 0);
|
||||
break 'outer;
|
||||
}
|
||||
Err(comm::Empty) => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If a child exited (somehow received SIGCHLD), then poll all
|
||||
// children to see if any of them exited.
|
||||
//
|
||||
// We also attempt to be responsible netizens when dealing with
|
||||
// SIGCHLD by invoking any previous SIGCHLD handler instead of just
|
||||
// ignoring any previous SIGCHLD handler. Note that we don't provide
|
||||
// a 1:1 mapping of our handler invocations to the previous handler
|
||||
// invocations because we drain the `read_fd` entirely. This is
|
||||
// probably OK because the kernel is already allowed to coalesce
|
||||
// simultaneous signals, we're just doing some extra coalescing.
|
||||
//
|
||||
// Another point of note is that this likely runs the signal handler
|
||||
// on a different thread than the one that received the signal. I
|
||||
// *think* this is ok at this time.
|
||||
//
|
||||
// The main reason for doing this is to allow stdtest to run native
|
||||
// tests as well. Both libgreen and libnative are running around
|
||||
// with process timeouts, but libgreen should get there first
|
||||
// (currently libuv doesn't handle old signal handlers).
|
||||
if drain(read_fd) {
|
||||
let i: uint = unsafe { mem::transmute(old.sa_handler) };
|
||||
if i != 0 {
|
||||
assert!(old.sa_flags & c::SA_SIGINFO == 0);
|
||||
(old.sa_handler)(c::SIGCHLD);
|
||||
}
|
||||
|
||||
// FIXME: sure would be nice to not have to scan the entire
|
||||
// array...
|
||||
active.retain(|&(pid, ref tx, _)| {
|
||||
let pr = Process { pid: pid };
|
||||
match pr.try_wait() {
|
||||
Some(msg) => { tx.send(msg); false }
|
||||
None => true,
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Once this helper thread is done, we re-register the old sigchld
|
||||
// handler and close our intermediate file descriptors.
|
||||
unsafe {
|
||||
assert_eq!(c::sigaction(c::SIGCHLD, &old, ptr::null_mut()), 0);
|
||||
let _ = libc::close(read_fd);
|
||||
let _ = libc::close(WRITE_FD);
|
||||
WRITE_FD = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Drain all pending data from the file descriptor, returning if any data
|
||||
// could be drained. This requires that the file descriptor is in
|
||||
// nonblocking mode.
|
||||
fn drain(fd: libc::c_int) -> bool {
|
||||
let mut ret = false;
|
||||
loop {
|
||||
let mut buf = [0u8, ..1];
|
||||
match unsafe {
|
||||
libc::read(fd, buf.as_mut_ptr() as *mut libc::c_void,
|
||||
buf.len() as libc::size_t)
|
||||
} {
|
||||
n if n > 0 => { ret = true; }
|
||||
0 => return true,
|
||||
-1 if wouldblock() => return ret,
|
||||
n => panic!("bad read {} ({})", os::last_os_error(), n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Signal handler for SIGCHLD signals, must be async-signal-safe!
|
||||
//
|
||||
// This function will write to the writing half of the "self pipe" to wake
|
||||
// up the helper thread if it's waiting. Note that this write must be
|
||||
// nonblocking because if it blocks and the reader is the thread we
|
||||
// interrupted, then we'll deadlock.
|
||||
//
|
||||
// When writing, if the write returns EWOULDBLOCK then we choose to ignore
|
||||
// it. At that point we're guaranteed that there's something in the pipe
|
||||
// which will wake up the other end at some point, so we just allow this
|
||||
// signal to be coalesced with the pending signals on the pipe.
|
||||
extern fn sigchld_handler(_signum: libc::c_int) {
|
||||
let msg = 1i;
|
||||
match unsafe {
|
||||
libc::write(WRITE_FD, &msg as *const _ as *const libc::c_void, 1)
|
||||
} {
|
||||
1 => {}
|
||||
-1 if wouldblock() => {} // see above comments
|
||||
n => panic!("bad error on write fd: {} {}", n, os::errno()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_wait(&self) -> Option<ProcessExit> {
|
||||
let mut status = 0 as c_int;
|
||||
match retry(|| unsafe {
|
||||
c::waitpid(self.pid, &mut status, c::WNOHANG)
|
||||
}) {
|
||||
n if n == self.pid => Some(translate_status(status)),
|
||||
0 => None,
|
||||
n => panic!("unknown waitpid error `{}`: {}", n,
|
||||
super::last_error()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn with_argv<T>(prog: &CString, args: &[CString],
|
||||
cb: proc(*const *const libc::c_char) -> T) -> T {
|
||||
let mut ptrs: Vec<*const libc::c_char> = Vec::with_capacity(args.len()+1);
|
||||
|
||||
// Convert the CStrings into an array of pointers. Note: the
|
||||
// lifetime of the various CStrings involved is guaranteed to be
|
||||
// larger than the lifetime of our invocation of cb, but this is
|
||||
// technically unsafe as the callback could leak these pointers
|
||||
// out of our scope.
|
||||
ptrs.push(prog.as_ptr());
|
||||
ptrs.extend(args.iter().map(|tmp| tmp.as_ptr()));
|
||||
|
||||
// Add a terminating null pointer (required by libc).
|
||||
ptrs.push(ptr::null());
|
||||
|
||||
cb(ptrs.as_ptr())
|
||||
}
|
||||
|
||||
fn with_envp<K, V, T>(env: Option<&collections::HashMap<K, V>>,
|
||||
cb: proc(*const c_void) -> T) -> T
|
||||
where K: BytesContainer + Eq + Hash, V: BytesContainer
|
||||
{
|
||||
// On posixy systems we can pass a char** for envp, which is a
|
||||
// null-terminated array of "k=v\0" strings. Since we must create
|
||||
// these strings locally, yet expose a raw pointer to them, we
|
||||
// create a temporary vector to own the CStrings that outlives the
|
||||
// call to cb.
|
||||
match env {
|
||||
Some(env) => {
|
||||
let mut tmps = Vec::with_capacity(env.len());
|
||||
|
||||
for pair in env.iter() {
|
||||
let mut kv = Vec::new();
|
||||
kv.push_all(pair.ref0().container_as_bytes());
|
||||
kv.push('=' as u8);
|
||||
kv.push_all(pair.ref1().container_as_bytes());
|
||||
kv.push(0); // terminating null
|
||||
tmps.push(kv);
|
||||
}
|
||||
|
||||
// As with `with_argv`, this is unsafe, since cb could leak the pointers.
|
||||
let mut ptrs: Vec<*const libc::c_char> =
|
||||
tmps.iter()
|
||||
.map(|tmp| tmp.as_ptr() as *const libc::c_char)
|
||||
.collect();
|
||||
ptrs.push(ptr::null());
|
||||
|
||||
cb(ptrs.as_ptr() as *const c_void)
|
||||
}
|
||||
_ => cb(ptr::null())
|
||||
}
|
||||
}
|
||||
|
||||
fn translate_status(status: c_int) -> ProcessExit {
|
||||
#![allow(non_snake_case)]
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
mod imp {
|
||||
pub fn WIFEXITED(status: i32) -> bool { (status & 0xff) == 0 }
|
||||
pub fn WEXITSTATUS(status: i32) -> i32 { (status >> 8) & 0xff }
|
||||
pub fn WTERMSIG(status: i32) -> i32 { status & 0x7f }
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "macos",
|
||||
target_os = "ios",
|
||||
target_os = "freebsd",
|
||||
target_os = "dragonfly"))]
|
||||
mod imp {
|
||||
pub fn WIFEXITED(status: i32) -> bool { (status & 0x7f) == 0 }
|
||||
pub fn WEXITSTATUS(status: i32) -> i32 { status >> 8 }
|
||||
pub fn WTERMSIG(status: i32) -> i32 { status & 0o177 }
|
||||
}
|
||||
|
||||
if imp::WIFEXITED(status) {
|
||||
ExitStatus(imp::WEXITSTATUS(status) as int)
|
||||
} else {
|
||||
ExitSignal(imp::WTERMSIG(status) as int)
|
||||
}
|
||||
}
|
157
src/libstd/sys/unix/tcp.rs
Normal file
157
src/libstd/sys/unix/tcp.rs
Normal file
|
@ -0,0 +1,157 @@
|
|||
// Copyright 2014 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 io::net::ip;
|
||||
use io::IoResult;
|
||||
use libc;
|
||||
use mem;
|
||||
use ptr;
|
||||
use prelude::*;
|
||||
use super::{last_error, last_net_error, retry, sock_t};
|
||||
use sync::{Arc, atomic};
|
||||
use sys::fs::FileDesc;
|
||||
use sys::{set_nonblocking, wouldblock};
|
||||
use sys;
|
||||
use sys_common;
|
||||
use sys_common::net::*;
|
||||
|
||||
pub use sys_common::net::TcpStream;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TCP listeners
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub struct TcpListener {
|
||||
pub inner: FileDesc,
|
||||
}
|
||||
|
||||
impl TcpListener {
|
||||
pub fn bind(addr: ip::SocketAddr) -> IoResult<TcpListener> {
|
||||
let fd = try!(socket(addr, libc::SOCK_STREAM));
|
||||
let ret = TcpListener { inner: FileDesc::new(fd, true) };
|
||||
|
||||
let mut storage = unsafe { mem::zeroed() };
|
||||
let len = addr_to_sockaddr(addr, &mut storage);
|
||||
let addrp = &storage as *const _ as *const libc::sockaddr;
|
||||
|
||||
// On platforms with Berkeley-derived sockets, this allows
|
||||
// to quickly rebind a socket, without needing to wait for
|
||||
// the OS to clean up the previous one.
|
||||
try!(setsockopt(fd, libc::SOL_SOCKET, libc::SO_REUSEADDR, 1 as libc::c_int));
|
||||
|
||||
|
||||
match unsafe { libc::bind(fd, addrp, len) } {
|
||||
-1 => Err(last_error()),
|
||||
_ => Ok(ret),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fd(&self) -> sock_t { self.inner.fd() }
|
||||
|
||||
pub fn listen(self, backlog: int) -> IoResult<TcpAcceptor> {
|
||||
match unsafe { libc::listen(self.fd(), backlog as libc::c_int) } {
|
||||
-1 => Err(last_net_error()),
|
||||
_ => {
|
||||
let (reader, writer) = try!(unsafe { sys::os::pipe() });
|
||||
try!(set_nonblocking(reader.fd(), true));
|
||||
try!(set_nonblocking(writer.fd(), true));
|
||||
try!(set_nonblocking(self.fd(), true));
|
||||
Ok(TcpAcceptor {
|
||||
inner: Arc::new(AcceptorInner {
|
||||
listener: self,
|
||||
reader: reader,
|
||||
writer: writer,
|
||||
closed: atomic::AtomicBool::new(false),
|
||||
}),
|
||||
deadline: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn socket_name(&mut self) -> IoResult<ip::SocketAddr> {
|
||||
sockname(self.fd(), libc::getsockname)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TcpAcceptor {
|
||||
inner: Arc<AcceptorInner>,
|
||||
deadline: u64,
|
||||
}
|
||||
|
||||
struct AcceptorInner {
|
||||
listener: TcpListener,
|
||||
reader: FileDesc,
|
||||
writer: FileDesc,
|
||||
closed: atomic::AtomicBool,
|
||||
}
|
||||
|
||||
impl TcpAcceptor {
|
||||
pub fn fd(&self) -> sock_t { self.inner.listener.fd() }
|
||||
|
||||
pub fn accept(&mut self) -> IoResult<TcpStream> {
|
||||
// In implementing accept, the two main concerns are dealing with
|
||||
// close_accept() and timeouts. The unix implementation is based on a
|
||||
// nonblocking accept plus a call to select(). Windows ends up having
|
||||
// an entirely separate implementation than unix, which is explained
|
||||
// below.
|
||||
//
|
||||
// To implement timeouts, all blocking is done via select() instead of
|
||||
// accept() by putting the socket in non-blocking mode. Because
|
||||
// select() takes a timeout argument, we just pass through the timeout
|
||||
// to select().
|
||||
//
|
||||
// To implement close_accept(), we have a self-pipe to ourselves which
|
||||
// is passed to select() along with the socket being accepted on. The
|
||||
// self-pipe is never written to unless close_accept() is called.
|
||||
let deadline = if self.deadline == 0 {None} else {Some(self.deadline)};
|
||||
|
||||
while !self.inner.closed.load(atomic::SeqCst) {
|
||||
match retry(|| unsafe {
|
||||
libc::accept(self.fd(), ptr::null_mut(), ptr::null_mut())
|
||||
}) {
|
||||
-1 if wouldblock() => {}
|
||||
-1 => return Err(last_net_error()),
|
||||
fd => return Ok(TcpStream::new(fd as sock_t)),
|
||||
}
|
||||
try!(await([self.fd(), self.inner.reader.fd()],
|
||||
deadline, Readable));
|
||||
}
|
||||
|
||||
Err(sys_common::eof())
|
||||
}
|
||||
|
||||
pub fn socket_name(&mut self) -> IoResult<ip::SocketAddr> {
|
||||
sockname(self.fd(), libc::getsockname)
|
||||
}
|
||||
|
||||
pub fn set_timeout(&mut self, timeout: Option<u64>) {
|
||||
self.deadline = timeout.map(|a| sys::timer::now() + a).unwrap_or(0);
|
||||
}
|
||||
|
||||
pub fn close_accept(&mut self) -> IoResult<()> {
|
||||
self.inner.closed.store(true, atomic::SeqCst);
|
||||
let fd = FileDesc::new(self.inner.writer.fd(), false);
|
||||
match fd.write([0]) {
|
||||
Ok(..) => Ok(()),
|
||||
Err(..) if wouldblock() => Ok(()),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for TcpAcceptor {
|
||||
fn clone(&self) -> TcpAcceptor {
|
||||
TcpAcceptor {
|
||||
inner: self.inner.clone(),
|
||||
deadline: 0,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -47,27 +47,30 @@
|
|||
//! Note that all time units in this file are in *milliseconds*.
|
||||
|
||||
use libc;
|
||||
use std::mem;
|
||||
use std::os;
|
||||
use std::ptr;
|
||||
use std::rt::rtio;
|
||||
use std::rt::rtio::IoResult;
|
||||
use std::sync::atomic;
|
||||
use std::comm;
|
||||
|
||||
use io::c;
|
||||
use io::file::FileDesc;
|
||||
use io::helper_thread::Helper;
|
||||
use mem;
|
||||
use os;
|
||||
use ptr;
|
||||
use sync::atomic;
|
||||
use comm;
|
||||
use sys::c;
|
||||
use sys::fs::FileDesc;
|
||||
use sys_common::helper_thread::Helper;
|
||||
use prelude::*;
|
||||
use io::IoResult;
|
||||
|
||||
helper_init!(static HELPER: Helper<Req>)
|
||||
|
||||
pub trait Callback {
|
||||
fn call(&mut self);
|
||||
}
|
||||
|
||||
pub struct Timer {
|
||||
id: uint,
|
||||
inner: Option<Box<Inner>>,
|
||||
}
|
||||
|
||||
pub struct Inner {
|
||||
cb: Option<Box<rtio::Callback + Send>>,
|
||||
cb: Option<Box<Callback + Send>>,
|
||||
interval: u64,
|
||||
repeat: bool,
|
||||
target: u64,
|
||||
|
@ -190,11 +193,11 @@ fn helper(input: libc::c_int, messages: Receiver<Req>, _: ()) {
|
|||
|
||||
// drain the file descriptor
|
||||
let mut buf = [0];
|
||||
assert_eq!(fd.inner_read(buf).ok().unwrap(), 1);
|
||||
assert_eq!(fd.read(buf).ok().unwrap(), 1);
|
||||
}
|
||||
|
||||
-1 if os::errno() == libc::EINTR as int => {}
|
||||
n => panic!("helper thread panicked in select() with error: {} ({})",
|
||||
-1 if os::errno() == libc::EINTR as uint => {}
|
||||
n => panic!("helper thread failed in select() with error: {} ({})",
|
||||
n, os::last_os_error())
|
||||
}
|
||||
}
|
||||
|
@ -220,7 +223,11 @@ impl Timer {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn sleep(ms: u64) {
|
||||
pub fn sleep(&mut self, ms: u64) {
|
||||
let mut inner = self.inner();
|
||||
inner.cb = None; // cancel any previous request
|
||||
self.inner = Some(inner);
|
||||
|
||||
let mut to_sleep = libc::timespec {
|
||||
tv_sec: (ms / 1000) as libc::time_t,
|
||||
tv_nsec: ((ms % 1000) * 1000000) as libc::c_long,
|
||||
|
@ -232,6 +239,30 @@ impl Timer {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn oneshot(&mut self, msecs: u64, cb: Box<Callback + Send>) {
|
||||
let now = now();
|
||||
let mut inner = self.inner();
|
||||
|
||||
inner.repeat = false;
|
||||
inner.cb = Some(cb);
|
||||
inner.interval = msecs;
|
||||
inner.target = now + msecs;
|
||||
|
||||
HELPER.send(NewTimer(inner));
|
||||
}
|
||||
|
||||
pub fn period(&mut self, msecs: u64, cb: Box<Callback + Send>) {
|
||||
let now = now();
|
||||
let mut inner = self.inner();
|
||||
|
||||
inner.repeat = true;
|
||||
inner.cb = Some(cb);
|
||||
inner.interval = msecs;
|
||||
inner.target = now + msecs;
|
||||
|
||||
HELPER.send(NewTimer(inner));
|
||||
}
|
||||
|
||||
fn inner(&mut self) -> Box<Inner> {
|
||||
match self.inner.take() {
|
||||
Some(i) => i,
|
||||
|
@ -244,40 +275,6 @@ impl Timer {
|
|||
}
|
||||
}
|
||||
|
||||
impl rtio::RtioTimer for Timer {
|
||||
fn sleep(&mut self, msecs: u64) {
|
||||
let mut inner = self.inner();
|
||||
inner.cb = None; // cancel any previous request
|
||||
self.inner = Some(inner);
|
||||
|
||||
Timer::sleep(msecs);
|
||||
}
|
||||
|
||||
fn oneshot(&mut self, msecs: u64, cb: Box<rtio::Callback + Send>) {
|
||||
let now = now();
|
||||
let mut inner = self.inner();
|
||||
|
||||
inner.repeat = false;
|
||||
inner.cb = Some(cb);
|
||||
inner.interval = msecs;
|
||||
inner.target = now + msecs;
|
||||
|
||||
HELPER.send(NewTimer(inner));
|
||||
}
|
||||
|
||||
fn period(&mut self, msecs: u64, cb: Box<rtio::Callback + Send>) {
|
||||
let now = now();
|
||||
let mut inner = self.inner();
|
||||
|
||||
inner.repeat = true;
|
||||
inner.cb = Some(cb);
|
||||
inner.interval = msecs;
|
||||
inner.target = now + msecs;
|
||||
|
||||
HELPER.send(NewTimer(inner));
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Timer {
|
||||
fn drop(&mut self) {
|
||||
self.inner = Some(self.inner());
|
47
src/libstd/sys/unix/tty.rs
Normal file
47
src/libstd/sys/unix/tty.rs
Normal file
|
@ -0,0 +1,47 @@
|
|||
// Copyright 2014 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 sys::fs::FileDesc;
|
||||
use prelude::*;
|
||||
use libc::{mod, c_int};
|
||||
use io::{mod, IoResult, IoError};
|
||||
use sys_common;
|
||||
|
||||
pub struct TTY {
|
||||
pub fd: FileDesc,
|
||||
}
|
||||
|
||||
impl TTY {
|
||||
pub fn new(fd: c_int) -> IoResult<TTY> {
|
||||
if unsafe { libc::isatty(fd) } != 0 {
|
||||
Ok(TTY { fd: FileDesc::new(fd, true) })
|
||||
} else {
|
||||
Err(IoError {
|
||||
kind: io::MismatchedFileTypeForOperation,
|
||||
desc: "file descriptor is not a TTY",
|
||||
detail: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
|
||||
self.fd.read(buf)
|
||||
}
|
||||
pub fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
||||
self.fd.write(buf)
|
||||
}
|
||||
pub fn set_raw(&mut self, _raw: bool) -> IoResult<()> {
|
||||
Err(sys_common::unimpl())
|
||||
}
|
||||
pub fn get_winsize(&mut self) -> IoResult<(int, int)> {
|
||||
Err(sys_common::unimpl())
|
||||
}
|
||||
pub fn isatty(&self) -> bool { false }
|
||||
}
|
11
src/libstd/sys/unix/udp.rs
Normal file
11
src/libstd/sys/unix/udp.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
pub use sys_common::net::UdpSocket;
|
|
@ -11,8 +11,11 @@
|
|||
//! C definitions used by libnative that don't belong in liblibc
|
||||
|
||||
#![allow(overflowing_literals)]
|
||||
#![allow(dead_code)]
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
use libc;
|
||||
use prelude::*;
|
||||
|
||||
pub const WSADESCRIPTION_LEN: uint = 256;
|
||||
pub const WSASYS_STATUS_LEN: uint = 128;
|
||||
|
@ -127,9 +130,10 @@ extern "system" {
|
|||
}
|
||||
|
||||
pub mod compat {
|
||||
use std::intrinsics::{atomic_store_relaxed, transmute};
|
||||
use std::iter::Iterator;
|
||||
use intrinsics::{atomic_store_relaxed, transmute};
|
||||
use iter::Iterator;
|
||||
use libc::types::os::arch::extra::{LPCWSTR, HMODULE, LPCSTR, LPVOID};
|
||||
use prelude::*;
|
||||
|
||||
extern "system" {
|
||||
fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE;
|
||||
|
@ -174,17 +178,17 @@ pub mod compat {
|
|||
|
||||
extern "system" fn thunk($($argname: $argtype),*) -> $rettype {
|
||||
unsafe {
|
||||
::io::c::compat::store_func(&mut ptr as *mut _ as *mut uint,
|
||||
::sys::c::compat::store_func(&mut ptr as *mut _ as *mut uint,
|
||||
stringify!($module),
|
||||
stringify!($symbol),
|
||||
fallback as uint);
|
||||
::std::intrinsics::atomic_load_relaxed(&ptr)($($argname),*)
|
||||
::intrinsics::atomic_load_relaxed(&ptr)($($argname),*)
|
||||
}
|
||||
}
|
||||
|
||||
extern "system" fn fallback($($argname: $argtype),*) -> $rettype $fallback
|
||||
|
||||
::std::intrinsics::atomic_load_relaxed(&ptr)($($argname),*)
|
||||
::intrinsics::atomic_load_relaxed(&ptr)($($argname),*)
|
||||
}
|
||||
);
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
|
@ -12,42 +12,40 @@
|
|||
|
||||
use alloc::arc::Arc;
|
||||
use libc::{mod, c_int};
|
||||
use std::c_str::CString;
|
||||
use std::mem;
|
||||
use std::os::windows::fill_utf16_buf_and_decode;
|
||||
use std::ptr;
|
||||
use std::rt::rtio;
|
||||
use std::rt::rtio::{IoResult, IoError};
|
||||
use std::str;
|
||||
|
||||
use c_str::CString;
|
||||
use mem;
|
||||
use os::windows::fill_utf16_buf_and_decode;
|
||||
use path;
|
||||
use ptr;
|
||||
use str;
|
||||
use io;
|
||||
|
||||
use prelude::*;
|
||||
use sys;
|
||||
use sys_common::{keep_going, eof, mkerr_libc};
|
||||
|
||||
use io::{FilePermission, Write, UnstableFileStat, Open, FileAccess, FileMode};
|
||||
use io::{IoResult, IoError, FileStat, SeekStyle, Seek, Writer, Reader};
|
||||
use io::{Read, Truncate, SeekCur, SeekSet, ReadWrite, SeekEnd, Append};
|
||||
|
||||
pub use path::WindowsPath as Path;
|
||||
pub type fd_t = libc::c_int;
|
||||
|
||||
struct Inner {
|
||||
fd: fd_t,
|
||||
pub struct FileDesc {
|
||||
/// The underlying C file descriptor.
|
||||
pub fd: fd_t,
|
||||
|
||||
/// Whether to close the file descriptor on drop.
|
||||
close_on_drop: bool,
|
||||
}
|
||||
|
||||
pub struct FileDesc {
|
||||
inner: Arc<Inner>
|
||||
}
|
||||
|
||||
impl FileDesc {
|
||||
/// Create a `FileDesc` from an open C file descriptor.
|
||||
///
|
||||
/// The `FileDesc` will take ownership of the specified file descriptor and
|
||||
/// close it upon destruction if the `close_on_drop` flag is true, otherwise
|
||||
/// it will not close the file descriptor when this `FileDesc` is dropped.
|
||||
///
|
||||
/// Note that all I/O operations done on this object will be *blocking*, but
|
||||
/// they do not require the runtime to be active.
|
||||
pub fn new(fd: fd_t, close_on_drop: bool) -> FileDesc {
|
||||
FileDesc { inner: Arc::new(Inner {
|
||||
fd: fd,
|
||||
close_on_drop: close_on_drop
|
||||
}) }
|
||||
FileDesc { fd: fd, close_on_drop: close_on_drop }
|
||||
}
|
||||
|
||||
pub fn inner_read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
|
||||
pub fn read(&self, buf: &mut [u8]) -> IoResult<uint> {
|
||||
let mut read = 0;
|
||||
let ret = unsafe {
|
||||
libc::ReadFile(self.handle(), buf.as_ptr() as libc::LPVOID,
|
||||
|
@ -60,7 +58,8 @@ impl FileDesc {
|
|||
Err(super::last_error())
|
||||
}
|
||||
}
|
||||
pub fn inner_write(&mut self, buf: &[u8]) -> IoResult<()> {
|
||||
|
||||
pub fn write(&self, buf: &[u8]) -> IoResult<()> {
|
||||
let mut cur = buf.as_ptr();
|
||||
let mut remaining = buf.len();
|
||||
while remaining > 0 {
|
||||
|
@ -80,7 +79,7 @@ impl FileDesc {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn fd(&self) -> fd_t { self.inner.fd }
|
||||
pub fn fd(&self) -> fd_t { self.fd }
|
||||
|
||||
pub fn handle(&self) -> libc::HANDLE {
|
||||
unsafe { libc::get_osfhandle(self.fd()) as libc::HANDLE }
|
||||
|
@ -88,153 +87,67 @@ impl FileDesc {
|
|||
|
||||
// A version of seek that takes &self so that tell can call it
|
||||
// - the private seek should of course take &mut self.
|
||||
fn seek_common(&self, pos: i64, style: rtio::SeekStyle) -> IoResult<u64> {
|
||||
fn seek_common(&self, pos: i64, style: SeekStyle) -> IoResult<u64> {
|
||||
let whence = match style {
|
||||
rtio::SeekSet => libc::FILE_BEGIN,
|
||||
rtio::SeekEnd => libc::FILE_END,
|
||||
rtio::SeekCur => libc::FILE_CURRENT,
|
||||
SeekSet => libc::FILE_BEGIN,
|
||||
SeekEnd => libc::FILE_END,
|
||||
SeekCur => libc::FILE_CURRENT,
|
||||
};
|
||||
unsafe {
|
||||
let mut newpos = 0;
|
||||
match libc::SetFilePointerEx(self.handle(), pos, &mut newpos,
|
||||
whence) {
|
||||
match libc::SetFilePointerEx(self.handle(), pos, &mut newpos, whence) {
|
||||
0 => Err(super::last_error()),
|
||||
_ => Ok(newpos as u64),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl rtio::RtioFileStream for FileDesc {
|
||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<int> {
|
||||
self.inner_read(buf).map(|i| i as int)
|
||||
}
|
||||
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
||||
self.inner_write(buf)
|
||||
}
|
||||
|
||||
fn pread(&mut self, buf: &mut [u8], offset: u64) -> IoResult<int> {
|
||||
let mut read = 0;
|
||||
let mut overlap: libc::OVERLAPPED = unsafe { mem::zeroed() };
|
||||
overlap.Offset = offset as libc::DWORD;
|
||||
overlap.OffsetHigh = (offset >> 32) as libc::DWORD;
|
||||
let ret = unsafe {
|
||||
libc::ReadFile(self.handle(), buf.as_ptr() as libc::LPVOID,
|
||||
buf.len() as libc::DWORD, &mut read,
|
||||
&mut overlap)
|
||||
};
|
||||
if ret != 0 {
|
||||
Ok(read as int)
|
||||
} else {
|
||||
Err(super::last_error())
|
||||
}
|
||||
}
|
||||
fn pwrite(&mut self, buf: &[u8], mut offset: u64) -> IoResult<()> {
|
||||
let mut cur = buf.as_ptr();
|
||||
let mut remaining = buf.len();
|
||||
let mut overlap: libc::OVERLAPPED = unsafe { mem::zeroed() };
|
||||
while remaining > 0 {
|
||||
overlap.Offset = offset as libc::DWORD;
|
||||
overlap.OffsetHigh = (offset >> 32) as libc::DWORD;
|
||||
let mut amt = 0;
|
||||
let ret = unsafe {
|
||||
libc::WriteFile(self.handle(), cur as libc::LPVOID,
|
||||
remaining as libc::DWORD, &mut amt,
|
||||
&mut overlap)
|
||||
};
|
||||
if ret != 0 {
|
||||
remaining -= amt as uint;
|
||||
cur = unsafe { cur.offset(amt as int) };
|
||||
offset += amt as u64;
|
||||
} else {
|
||||
return Err(super::last_error())
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn seek(&mut self, pos: i64, style: rtio::SeekStyle) -> IoResult<u64> {
|
||||
pub fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<u64> {
|
||||
self.seek_common(pos, style)
|
||||
}
|
||||
|
||||
fn tell(&self) -> IoResult<u64> {
|
||||
self.seek_common(0, rtio::SeekCur)
|
||||
pub fn tell(&self) -> IoResult<u64> {
|
||||
self.seek_common(0, SeekCur)
|
||||
}
|
||||
|
||||
fn fsync(&mut self) -> IoResult<()> {
|
||||
pub fn fsync(&mut self) -> IoResult<()> {
|
||||
super::mkerr_winbool(unsafe {
|
||||
libc::FlushFileBuffers(self.handle())
|
||||
})
|
||||
}
|
||||
|
||||
fn datasync(&mut self) -> IoResult<()> { return self.fsync(); }
|
||||
pub fn datasync(&mut self) -> IoResult<()> { return self.fsync(); }
|
||||
|
||||
fn truncate(&mut self, offset: i64) -> IoResult<()> {
|
||||
pub fn truncate(&mut self, offset: i64) -> IoResult<()> {
|
||||
let orig_pos = try!(self.tell());
|
||||
let _ = try!(self.seek(offset, rtio::SeekSet));
|
||||
let _ = try!(self.seek(offset, SeekSet));
|
||||
let ret = unsafe {
|
||||
match libc::SetEndOfFile(self.handle()) {
|
||||
0 => Err(super::last_error()),
|
||||
_ => Ok(())
|
||||
}
|
||||
};
|
||||
let _ = self.seek(orig_pos as i64, rtio::SeekSet);
|
||||
let _ = self.seek(orig_pos as i64, SeekSet);
|
||||
return ret;
|
||||
}
|
||||
|
||||
fn fstat(&mut self) -> IoResult<rtio::FileStat> {
|
||||
pub fn fstat(&mut self) -> IoResult<io::FileStat> {
|
||||
let mut stat: libc::stat = unsafe { mem::zeroed() };
|
||||
match unsafe { libc::fstat(self.fd(), &mut stat) } {
|
||||
0 => Ok(mkstat(&stat)),
|
||||
_ => Err(super::last_error()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract the actual filedescriptor without closing it.
|
||||
pub fn unwrap(self) -> fd_t {
|
||||
let fd = self.fd;
|
||||
unsafe { mem::forget(self) };
|
||||
fd
|
||||
}
|
||||
}
|
||||
|
||||
impl rtio::RtioPipe for FileDesc {
|
||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
|
||||
self.inner_read(buf)
|
||||
}
|
||||
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
||||
self.inner_write(buf)
|
||||
}
|
||||
fn clone(&self) -> Box<rtio::RtioPipe + Send> {
|
||||
box FileDesc { inner: self.inner.clone() } as Box<rtio::RtioPipe + Send>
|
||||
}
|
||||
|
||||
// Only supported on named pipes currently. Note that this doesn't have an
|
||||
// impact on the std::io primitives, this is never called via
|
||||
// std::io::PipeStream. If the functionality is exposed in the future, then
|
||||
// these methods will need to be implemented.
|
||||
fn close_read(&mut self) -> IoResult<()> {
|
||||
Err(super::unimpl())
|
||||
}
|
||||
fn close_write(&mut self) -> IoResult<()> {
|
||||
Err(super::unimpl())
|
||||
}
|
||||
fn set_timeout(&mut self, _t: Option<u64>) {}
|
||||
fn set_read_timeout(&mut self, _t: Option<u64>) {}
|
||||
fn set_write_timeout(&mut self, _t: Option<u64>) {}
|
||||
}
|
||||
|
||||
impl rtio::RtioTTY for FileDesc {
|
||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
|
||||
self.inner_read(buf)
|
||||
}
|
||||
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
||||
self.inner_write(buf)
|
||||
}
|
||||
fn set_raw(&mut self, _raw: bool) -> IoResult<()> {
|
||||
Err(super::unimpl())
|
||||
}
|
||||
fn get_winsize(&mut self) -> IoResult<(int, int)> {
|
||||
Err(super::unimpl())
|
||||
}
|
||||
fn isatty(&self) -> bool { false }
|
||||
}
|
||||
|
||||
impl Drop for Inner {
|
||||
impl Drop for FileDesc {
|
||||
fn drop(&mut self) {
|
||||
// closing stdio file handles makes no sense, so never do it. Also, note
|
||||
// that errors are ignored when closing a file descriptor. The reason
|
||||
|
@ -251,39 +164,26 @@ impl Drop for Inner {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn to_utf16(s: &CString) -> IoResult<Vec<u16>> {
|
||||
match s.as_str() {
|
||||
Some(s) => Ok({
|
||||
let mut s = s.utf16_units().collect::<Vec<u16>>();
|
||||
s.push(0);
|
||||
s
|
||||
}),
|
||||
None => Err(IoError {
|
||||
code: libc::ERROR_INVALID_NAME as uint,
|
||||
extra: 0,
|
||||
detail: Some("valid unicode input required".to_string()),
|
||||
})
|
||||
}
|
||||
pub fn to_utf16(s: &Path) -> IoResult<Vec<u16>> {
|
||||
sys::to_utf16(s.as_str())
|
||||
}
|
||||
|
||||
pub fn open(path: &CString, fm: rtio::FileMode, fa: rtio::FileAccess)
|
||||
-> IoResult<FileDesc> {
|
||||
pub fn open(path: &Path, fm: FileMode, fa: FileAccess) -> IoResult<FileDesc> {
|
||||
// Flags passed to open_osfhandle
|
||||
let flags = match fm {
|
||||
rtio::Open => 0,
|
||||
rtio::Append => libc::O_APPEND,
|
||||
rtio::Truncate => libc::O_TRUNC,
|
||||
Open => 0,
|
||||
Append => libc::O_APPEND,
|
||||
Truncate => libc::O_TRUNC,
|
||||
};
|
||||
let flags = match fa {
|
||||
rtio::Read => flags | libc::O_RDONLY,
|
||||
rtio::Write => flags | libc::O_WRONLY | libc::O_CREAT,
|
||||
rtio::ReadWrite => flags | libc::O_RDWR | libc::O_CREAT,
|
||||
Read => flags | libc::O_RDONLY,
|
||||
Write => flags | libc::O_WRONLY | libc::O_CREAT,
|
||||
ReadWrite => flags | libc::O_RDWR | libc::O_CREAT,
|
||||
};
|
||||
|
||||
let mut dwDesiredAccess = match fa {
|
||||
rtio::Read => libc::FILE_GENERIC_READ,
|
||||
rtio::Write => libc::FILE_GENERIC_WRITE,
|
||||
rtio::ReadWrite => libc::FILE_GENERIC_READ | libc::FILE_GENERIC_WRITE
|
||||
Read => libc::FILE_GENERIC_READ,
|
||||
Write => libc::FILE_GENERIC_WRITE,
|
||||
ReadWrite => libc::FILE_GENERIC_READ | libc::FILE_GENERIC_WRITE
|
||||
};
|
||||
|
||||
// libuv has a good comment about this, but the basic idea is what we try to
|
||||
|
@ -293,15 +193,15 @@ pub fn open(path: &CString, fm: rtio::FileMode, fa: rtio::FileAccess)
|
|||
libc::FILE_SHARE_DELETE;
|
||||
|
||||
let dwCreationDisposition = match (fm, fa) {
|
||||
(rtio::Truncate, rtio::Read) => libc::TRUNCATE_EXISTING,
|
||||
(rtio::Truncate, _) => libc::CREATE_ALWAYS,
|
||||
(rtio::Open, rtio::Read) => libc::OPEN_EXISTING,
|
||||
(rtio::Open, _) => libc::OPEN_ALWAYS,
|
||||
(rtio::Append, rtio::Read) => {
|
||||
(Truncate, Read) => libc::TRUNCATE_EXISTING,
|
||||
(Truncate, _) => libc::CREATE_ALWAYS,
|
||||
(Open, Read) => libc::OPEN_EXISTING,
|
||||
(Open, _) => libc::OPEN_ALWAYS,
|
||||
(Append, Read) => {
|
||||
dwDesiredAccess |= libc::FILE_APPEND_DATA;
|
||||
libc::OPEN_EXISTING
|
||||
}
|
||||
(rtio::Append, _) => {
|
||||
(Append, _) => {
|
||||
dwDesiredAccess &= !libc::FILE_WRITE_DATA;
|
||||
dwDesiredAccess |= libc::FILE_APPEND_DATA;
|
||||
libc::OPEN_ALWAYS
|
||||
|
@ -337,7 +237,7 @@ pub fn open(path: &CString, fm: rtio::FileMode, fa: rtio::FileAccess)
|
|||
}
|
||||
}
|
||||
|
||||
pub fn mkdir(p: &CString, _mode: uint) -> IoResult<()> {
|
||||
pub fn mkdir(p: &Path, _mode: uint) -> IoResult<()> {
|
||||
let p = try!(to_utf16(p));
|
||||
super::mkerr_winbool(unsafe {
|
||||
// FIXME: turn mode into something useful? #2623
|
||||
|
@ -345,20 +245,15 @@ pub fn mkdir(p: &CString, _mode: uint) -> IoResult<()> {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn readdir(p: &CString) -> IoResult<Vec<CString>> {
|
||||
fn prune(root: &CString, dirs: Vec<Path>) -> Vec<CString> {
|
||||
let root = unsafe { CString::new(root.as_ptr(), false) };
|
||||
let root = Path::new(root);
|
||||
|
||||
pub fn readdir(p: &Path) -> IoResult<Vec<Path>> {
|
||||
fn prune(root: &Path, dirs: Vec<Path>) -> Vec<Path> {
|
||||
dirs.into_iter().filter(|path| {
|
||||
path.as_vec() != b"." && path.as_vec() != b".."
|
||||
}).map(|path| root.join(path).to_c_str()).collect()
|
||||
}).map(|path| root.join(path)).collect()
|
||||
}
|
||||
|
||||
let star = Path::new(unsafe {
|
||||
CString::new(p.as_ptr(), false)
|
||||
}).join("*");
|
||||
let path = try!(to_utf16(&star.to_c_str()));
|
||||
let star = p.join("*");
|
||||
let path = try!(to_utf16(&star));
|
||||
|
||||
unsafe {
|
||||
let mut wfd = mem::zeroed();
|
||||
|
@ -374,8 +269,8 @@ pub fn readdir(p: &CString) -> IoResult<Vec<CString>> {
|
|||
None => {
|
||||
assert!(libc::FindClose(find_handle) != 0);
|
||||
return Err(IoError {
|
||||
code: super::c::ERROR_ILLEGAL_CHARACTER as uint,
|
||||
extra: 0,
|
||||
kind: io::InvalidInput,
|
||||
desc: "path was not valid UTF-16",
|
||||
detail: Some(format!("path was not valid UTF-16: {}", filename)),
|
||||
})
|
||||
}, // FIXME #12056: Convert the UCS-2 to invalid utf-8 instead of erroring
|
||||
|
@ -391,42 +286,74 @@ pub fn readdir(p: &CString) -> IoResult<Vec<CString>> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn unlink(p: &CString) -> IoResult<()> {
|
||||
let p = try!(to_utf16(p));
|
||||
super::mkerr_winbool(unsafe {
|
||||
libc::DeleteFileW(p.as_ptr())
|
||||
})
|
||||
pub fn unlink(p: &Path) -> IoResult<()> {
|
||||
fn do_unlink(p_utf16: &Vec<u16>) -> IoResult<()> {
|
||||
super::mkerr_winbool(unsafe { libc::DeleteFileW(p_utf16.as_ptr()) })
|
||||
}
|
||||
|
||||
let p_utf16 = try!(to_utf16(p));
|
||||
let res = do_unlink(&p_utf16);
|
||||
match res {
|
||||
Ok(()) => Ok(()),
|
||||
Err(e) => {
|
||||
// FIXME: change the code below to use more direct calls
|
||||
// than `stat` and `chmod`, to avoid re-conversion to
|
||||
// utf16 etc.
|
||||
|
||||
// On unix, a readonly file can be successfully removed. On windows,
|
||||
// however, it cannot. To keep the two platforms in line with
|
||||
// respect to their behavior, catch this case on windows, attempt to
|
||||
// change it to read-write, and then remove the file.
|
||||
if e.kind == io::PermissionDenied {
|
||||
let stat = match stat(p) {
|
||||
Ok(stat) => stat,
|
||||
Err(..) => return Err(e),
|
||||
};
|
||||
if stat.perm.intersects(io::USER_WRITE) { return Err(e) }
|
||||
|
||||
match chmod(p, (stat.perm | io::USER_WRITE).bits() as uint) {
|
||||
Ok(()) => do_unlink(&p_utf16),
|
||||
Err(..) => {
|
||||
// Try to put it back as we found it
|
||||
let _ = chmod(p, stat.perm.bits() as uint);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rename(old: &CString, new: &CString) -> IoResult<()> {
|
||||
pub fn rename(old: &Path, new: &Path) -> IoResult<()> {
|
||||
let old = try!(to_utf16(old));
|
||||
let new = try!(to_utf16(new));
|
||||
super::mkerr_winbool(unsafe {
|
||||
libc::MoveFileExW(old.as_ptr(), new.as_ptr(),
|
||||
libc::MOVEFILE_REPLACE_EXISTING)
|
||||
libc::MoveFileExW(old.as_ptr(), new.as_ptr(), libc::MOVEFILE_REPLACE_EXISTING)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn chmod(p: &CString, mode: uint) -> IoResult<()> {
|
||||
pub fn chmod(p: &Path, mode: uint) -> IoResult<()> {
|
||||
let p = try!(to_utf16(p));
|
||||
super::mkerr_libc(unsafe {
|
||||
mkerr_libc(unsafe {
|
||||
libc::wchmod(p.as_ptr(), mode as libc::c_int)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn rmdir(p: &CString) -> IoResult<()> {
|
||||
pub fn rmdir(p: &Path) -> IoResult<()> {
|
||||
let p = try!(to_utf16(p));
|
||||
super::mkerr_libc(unsafe { libc::wrmdir(p.as_ptr()) })
|
||||
mkerr_libc(unsafe { libc::wrmdir(p.as_ptr()) })
|
||||
}
|
||||
|
||||
pub fn chown(_p: &CString, _uid: int, _gid: int) -> IoResult<()> {
|
||||
pub fn chown(_p: &Path, _uid: int, _gid: int) -> IoResult<()> {
|
||||
// libuv has this as a no-op, so seems like this should as well?
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn readlink(p: &CString) -> IoResult<CString> {
|
||||
pub fn readlink(p: &Path) -> IoResult<Path> {
|
||||
// FIXME: I have a feeling that this reads intermediate symlinks as well.
|
||||
use io::c::compat::kernel32::GetFinalPathNameByHandleW;
|
||||
use sys::c::compat::kernel32::GetFinalPathNameByHandleW;
|
||||
let p = try!(to_utf16(p));
|
||||
let handle = unsafe {
|
||||
libc::CreateFileW(p.as_ptr(),
|
||||
|
@ -449,18 +376,18 @@ pub fn readlink(p: &CString) -> IoResult<CString> {
|
|||
libc::VOLUME_NAME_DOS)
|
||||
});
|
||||
let ret = match ret {
|
||||
Some(ref s) if s.as_slice().starts_with(r"\\?\") => {
|
||||
Ok(Path::new(s.as_slice().slice_from(4)).to_c_str())
|
||||
Some(ref s) if s.as_slice().starts_with(r"\\?\") => { // "
|
||||
Ok(Path::new(s.as_slice().slice_from(4)))
|
||||
}
|
||||
Some(s) => Ok(Path::new(s).to_c_str()),
|
||||
Some(s) => Ok(Path::new(s)),
|
||||
None => Err(super::last_error()),
|
||||
};
|
||||
assert!(unsafe { libc::CloseHandle(handle) } != 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pub fn symlink(src: &CString, dst: &CString) -> IoResult<()> {
|
||||
use io::c::compat::kernel32::CreateSymbolicLinkW;
|
||||
pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> {
|
||||
use sys::c::compat::kernel32::CreateSymbolicLinkW;
|
||||
let src = try!(to_utf16(src));
|
||||
let dst = try!(to_utf16(dst));
|
||||
super::mkerr_winbool(unsafe {
|
||||
|
@ -468,7 +395,7 @@ pub fn symlink(src: &CString, dst: &CString) -> IoResult<()> {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn link(src: &CString, dst: &CString) -> IoResult<()> {
|
||||
pub fn link(src: &Path, dst: &Path) -> IoResult<()> {
|
||||
let src = try!(to_utf16(src));
|
||||
let dst = try!(to_utf16(dst));
|
||||
super::mkerr_winbool(unsafe {
|
||||
|
@ -476,28 +403,37 @@ pub fn link(src: &CString, dst: &CString) -> IoResult<()> {
|
|||
})
|
||||
}
|
||||
|
||||
fn mkstat(stat: &libc::stat) -> rtio::FileStat {
|
||||
rtio::FileStat {
|
||||
fn mkstat(stat: &libc::stat) -> FileStat {
|
||||
FileStat {
|
||||
size: stat.st_size as u64,
|
||||
kind: stat.st_mode as u64,
|
||||
perm: stat.st_mode as u64,
|
||||
kind: match (stat.st_mode as libc::c_int) & libc::S_IFMT {
|
||||
libc::S_IFREG => io::TypeFile,
|
||||
libc::S_IFDIR => io::TypeDirectory,
|
||||
libc::S_IFIFO => io::TypeNamedPipe,
|
||||
libc::S_IFBLK => io::TypeBlockSpecial,
|
||||
libc::S_IFLNK => io::TypeSymlink,
|
||||
_ => io::TypeUnknown,
|
||||
},
|
||||
perm: FilePermission::from_bits_truncate(stat.st_mode as u32),
|
||||
created: stat.st_ctime as u64,
|
||||
modified: stat.st_mtime as u64,
|
||||
accessed: stat.st_atime as u64,
|
||||
device: stat.st_dev as u64,
|
||||
inode: stat.st_ino as u64,
|
||||
rdev: stat.st_rdev as u64,
|
||||
nlink: stat.st_nlink as u64,
|
||||
uid: stat.st_uid as u64,
|
||||
gid: stat.st_gid as u64,
|
||||
blksize: 0,
|
||||
blocks: 0,
|
||||
flags: 0,
|
||||
gen: 0,
|
||||
unstable: UnstableFileStat {
|
||||
device: stat.st_dev as u64,
|
||||
inode: stat.st_ino as u64,
|
||||
rdev: stat.st_rdev as u64,
|
||||
nlink: stat.st_nlink as u64,
|
||||
uid: stat.st_uid as u64,
|
||||
gid: stat.st_gid as u64,
|
||||
blksize:0,
|
||||
blocks: 0,
|
||||
flags: 0,
|
||||
gen: 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stat(p: &CString) -> IoResult<rtio::FileStat> {
|
||||
pub fn stat(p: &Path) -> IoResult<FileStat> {
|
||||
let mut stat: libc::stat = unsafe { mem::zeroed() };
|
||||
let p = try!(to_utf16(p));
|
||||
match unsafe { libc::wstat(p.as_ptr(), &mut stat) } {
|
||||
|
@ -506,18 +442,19 @@ pub fn stat(p: &CString) -> IoResult<rtio::FileStat> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn lstat(_p: &CString) -> IoResult<rtio::FileStat> {
|
||||
// FIXME: move this to platform-specific modules (for now)?
|
||||
pub fn lstat(_p: &Path) -> IoResult<FileStat> {
|
||||
// FIXME: implementation is missing
|
||||
Err(super::unimpl())
|
||||
}
|
||||
|
||||
pub fn utime(p: &CString, atime: u64, mtime: u64) -> IoResult<()> {
|
||||
pub fn utime(p: &Path, atime: u64, mtime: u64) -> IoResult<()> {
|
||||
let mut buf = libc::utimbuf {
|
||||
actime: atime as libc::time64_t,
|
||||
modtime: mtime as libc::time64_t,
|
||||
};
|
||||
let p = try!(to_utf16(p));
|
||||
super::mkerr_libc(unsafe {
|
||||
mkerr_libc(unsafe {
|
||||
libc::wutime(p.as_ptr(), &mut buf)
|
||||
})
|
||||
}
|
38
src/libstd/sys/windows/helper_signal.rs
Normal file
38
src/libstd/sys/windows/helper_signal.rs
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Copyright 2014 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::{mod, BOOL, LPCSTR, HANDLE, LPSECURITY_ATTRIBUTES, CloseHandle};
|
||||
use ptr;
|
||||
|
||||
pub type signal = HANDLE;
|
||||
|
||||
pub fn new() -> (HANDLE, HANDLE) {
|
||||
unsafe {
|
||||
let handle = CreateEventA(ptr::null_mut(), libc::FALSE, libc::FALSE,
|
||||
ptr::null());
|
||||
(handle, handle)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn signal(handle: HANDLE) {
|
||||
assert!(unsafe { SetEvent(handle) != 0 });
|
||||
}
|
||||
|
||||
pub fn close(handle: HANDLE) {
|
||||
assert!(unsafe { CloseHandle(handle) != 0 });
|
||||
}
|
||||
|
||||
extern "system" {
|
||||
fn CreateEventA(lpSecurityAttributes: LPSECURITY_ATTRIBUTES,
|
||||
bManualReset: BOOL,
|
||||
bInitialState: BOOL,
|
||||
lpName: LPCSTR) -> HANDLE;
|
||||
fn SetEvent(hEvent: HANDLE) -> BOOL;
|
||||
}
|
190
src/libstd/sys/windows/mod.rs
Normal file
190
src/libstd/sys/windows/mod.rs
Normal file
|
@ -0,0 +1,190 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
#![allow(missing_doc)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused_unsafe)]
|
||||
#![allow(unused_mut)]
|
||||
|
||||
extern crate libc;
|
||||
|
||||
use num;
|
||||
use mem;
|
||||
use prelude::*;
|
||||
use io::{mod, IoResult, IoError};
|
||||
use sync::{Once, ONCE_INIT};
|
||||
|
||||
macro_rules! helper_init( (static $name:ident: Helper<$m:ty>) => (
|
||||
static $name: Helper<$m> = Helper {
|
||||
lock: ::rt::mutex::NATIVE_MUTEX_INIT,
|
||||
chan: ::cell::UnsafeCell { value: 0 as *mut Sender<$m> },
|
||||
signal: ::cell::UnsafeCell { value: 0 },
|
||||
initialized: ::cell::UnsafeCell { value: false },
|
||||
};
|
||||
) )
|
||||
|
||||
pub mod c;
|
||||
pub mod fs;
|
||||
pub mod os;
|
||||
pub mod tcp;
|
||||
pub mod udp;
|
||||
pub mod pipe;
|
||||
pub mod helper_signal;
|
||||
pub mod process;
|
||||
pub mod timer;
|
||||
pub mod tty;
|
||||
|
||||
pub mod addrinfo {
|
||||
pub use sys_common::net::get_host_addresses;
|
||||
}
|
||||
|
||||
// FIXME: move these to c module
|
||||
pub type sock_t = libc::SOCKET;
|
||||
pub type wrlen = libc::c_int;
|
||||
pub type msglen_t = libc::c_int;
|
||||
pub unsafe fn close_sock(sock: sock_t) { let _ = libc::closesocket(sock); }
|
||||
|
||||
// windows has zero values as errors
|
||||
fn mkerr_winbool(ret: libc::c_int) -> IoResult<()> {
|
||||
if ret == 0 {
|
||||
Err(last_error())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn last_error() -> IoError {
|
||||
let errno = os::errno() as i32;
|
||||
let mut err = decode_error(errno);
|
||||
err.detail = Some(os::error_string(errno));
|
||||
err
|
||||
}
|
||||
|
||||
pub fn last_net_error() -> IoError {
|
||||
let errno = unsafe { c::WSAGetLastError() as i32 };
|
||||
let mut err = decode_error(errno);
|
||||
err.detail = Some(os::error_string(errno));
|
||||
err
|
||||
}
|
||||
|
||||
pub fn last_gai_error(_errno: i32) -> IoError {
|
||||
last_net_error()
|
||||
}
|
||||
|
||||
/// Convert an `errno` value into a high-level error variant and description.
|
||||
pub fn decode_error(errno: i32) -> IoError {
|
||||
let (kind, desc) = match errno {
|
||||
libc::EOF => (io::EndOfFile, "end of file"),
|
||||
libc::ERROR_NO_DATA => (io::BrokenPipe, "the pipe is being closed"),
|
||||
libc::ERROR_FILE_NOT_FOUND => (io::FileNotFound, "file not found"),
|
||||
libc::ERROR_INVALID_NAME => (io::InvalidInput, "invalid file name"),
|
||||
libc::WSAECONNREFUSED => (io::ConnectionRefused, "connection refused"),
|
||||
libc::WSAECONNRESET => (io::ConnectionReset, "connection reset"),
|
||||
libc::ERROR_ACCESS_DENIED | libc::WSAEACCES =>
|
||||
(io::PermissionDenied, "permission denied"),
|
||||
libc::WSAEWOULDBLOCK => {
|
||||
(io::ResourceUnavailable, "resource temporarily unavailable")
|
||||
}
|
||||
libc::WSAENOTCONN => (io::NotConnected, "not connected"),
|
||||
libc::WSAECONNABORTED => (io::ConnectionAborted, "connection aborted"),
|
||||
libc::WSAEADDRNOTAVAIL => (io::ConnectionRefused, "address not available"),
|
||||
libc::WSAEADDRINUSE => (io::ConnectionRefused, "address in use"),
|
||||
libc::ERROR_BROKEN_PIPE => (io::EndOfFile, "the pipe has ended"),
|
||||
libc::ERROR_OPERATION_ABORTED =>
|
||||
(io::TimedOut, "operation timed out"),
|
||||
libc::WSAEINVAL => (io::InvalidInput, "invalid argument"),
|
||||
libc::ERROR_CALL_NOT_IMPLEMENTED =>
|
||||
(io::IoUnavailable, "function not implemented"),
|
||||
libc::ERROR_INVALID_HANDLE =>
|
||||
(io::MismatchedFileTypeForOperation,
|
||||
"invalid handle provided to function"),
|
||||
libc::ERROR_NOTHING_TO_TERMINATE =>
|
||||
(io::InvalidInput, "no process to kill"),
|
||||
|
||||
// libuv maps this error code to EISDIR. we do too. if it is found
|
||||
// to be incorrect, we can add in some more machinery to only
|
||||
// return this message when ERROR_INVALID_FUNCTION after certain
|
||||
// Windows calls.
|
||||
libc::ERROR_INVALID_FUNCTION => (io::InvalidInput,
|
||||
"illegal operation on a directory"),
|
||||
|
||||
_ => (io::OtherIoError, "unknown error")
|
||||
};
|
||||
IoError { kind: kind, desc: desc, detail: None }
|
||||
}
|
||||
|
||||
pub fn decode_error_detailed(errno: i32) -> IoError {
|
||||
let mut err = decode_error(errno);
|
||||
err.detail = Some(os::error_string(errno));
|
||||
err
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn retry<I> (f: || -> I) -> I { f() } // PR rust-lang/rust/#17020
|
||||
|
||||
pub fn ms_to_timeval(ms: u64) -> libc::timeval {
|
||||
libc::timeval {
|
||||
tv_sec: (ms / 1000) as libc::c_long,
|
||||
tv_usec: ((ms % 1000) * 1000) as libc::c_long,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wouldblock() -> bool {
|
||||
let err = os::errno();
|
||||
err == libc::WSAEWOULDBLOCK as uint
|
||||
}
|
||||
|
||||
pub fn set_nonblocking(fd: sock_t, nb: bool) -> IoResult<()> {
|
||||
let mut set = nb as libc::c_ulong;
|
||||
if unsafe { c::ioctlsocket(fd, c::FIONBIO, &mut set) != 0 } {
|
||||
Err(last_error())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_net() {
|
||||
unsafe {
|
||||
static START: Once = ONCE_INIT;
|
||||
|
||||
START.doit(|| {
|
||||
let mut data: c::WSADATA = mem::zeroed();
|
||||
let ret = c::WSAStartup(0x202, // version 2.2
|
||||
&mut data);
|
||||
assert_eq!(ret, 0);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unimpl() -> IoError {
|
||||
IoError {
|
||||
kind: io::IoUnavailable,
|
||||
desc: "operation is not implemented",
|
||||
detail: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_utf16(s: Option<&str>) -> IoResult<Vec<u16>> {
|
||||
match s {
|
||||
Some(s) => Ok({
|
||||
let mut s = s.utf16_units().collect::<Vec<u16>>();
|
||||
s.push(0);
|
||||
s
|
||||
}),
|
||||
None => Err(IoError {
|
||||
kind: io::InvalidInput,
|
||||
desc: "valid unicode input required",
|
||||
detail: None
|
||||
})
|
||||
}
|
||||
}
|
103
src/libstd/sys/windows/os.rs
Normal file
103
src/libstd/sys/windows/os.rs
Normal file
|
@ -0,0 +1,103 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
// FIXME: move various extern bindings from here into liblibc or
|
||||
// something similar
|
||||
|
||||
use libc;
|
||||
use libc::{c_int, c_char, c_void};
|
||||
use prelude::*;
|
||||
use io::{IoResult, IoError};
|
||||
use sys::fs::FileDesc;
|
||||
use ptr;
|
||||
|
||||
use os::TMPBUF_SZ;
|
||||
|
||||
pub fn errno() -> uint {
|
||||
use libc::types::os::arch::extra::DWORD;
|
||||
|
||||
#[link_name = "kernel32"]
|
||||
extern "system" {
|
||||
fn GetLastError() -> DWORD;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
GetLastError() as uint
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a detailed string description for the given error number
|
||||
pub fn error_string(errnum: i32) -> String {
|
||||
use libc::types::os::arch::extra::DWORD;
|
||||
use libc::types::os::arch::extra::LPWSTR;
|
||||
use libc::types::os::arch::extra::LPVOID;
|
||||
use libc::types::os::arch::extra::WCHAR;
|
||||
|
||||
#[link_name = "kernel32"]
|
||||
extern "system" {
|
||||
fn FormatMessageW(flags: DWORD,
|
||||
lpSrc: LPVOID,
|
||||
msgId: DWORD,
|
||||
langId: DWORD,
|
||||
buf: LPWSTR,
|
||||
nsize: DWORD,
|
||||
args: *const c_void)
|
||||
-> DWORD;
|
||||
}
|
||||
|
||||
static FORMAT_MESSAGE_FROM_SYSTEM: DWORD = 0x00001000;
|
||||
static FORMAT_MESSAGE_IGNORE_INSERTS: DWORD = 0x00000200;
|
||||
|
||||
// This value is calculated from the macro
|
||||
// MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT)
|
||||
let langId = 0x0800 as DWORD;
|
||||
|
||||
let mut buf = [0 as WCHAR, ..TMPBUF_SZ];
|
||||
|
||||
unsafe {
|
||||
let res = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
ptr::null_mut(),
|
||||
errnum as DWORD,
|
||||
langId,
|
||||
buf.as_mut_ptr(),
|
||||
buf.len() as DWORD,
|
||||
ptr::null());
|
||||
if res == 0 {
|
||||
// Sometimes FormatMessageW can fail e.g. system doesn't like langId,
|
||||
let fm_err = errno();
|
||||
return format!("OS Error {} (FormatMessageW() returned error {})", errnum, fm_err);
|
||||
}
|
||||
|
||||
let msg = String::from_utf16(::str::truncate_utf16_at_nul(buf));
|
||||
match msg {
|
||||
Some(msg) => format!("OS Error {}: {}", errnum, msg),
|
||||
None => format!("OS Error {} (FormatMessageW() returned invalid UTF-16)", errnum),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn pipe() -> IoResult<(FileDesc, FileDesc)> {
|
||||
// Windows pipes work subtly differently than unix pipes, and their
|
||||
// inheritance has to be handled in a different way that I do not
|
||||
// fully understand. Here we explicitly make the pipe non-inheritable,
|
||||
// which means to pass it to a subprocess they need to be duplicated
|
||||
// first, as in std::run.
|
||||
let mut fds = [0, ..2];
|
||||
match libc::pipe(fds.as_mut_ptr(), 1024 as ::libc::c_uint,
|
||||
(libc::O_BINARY | libc::O_NOINHERIT) as c_int) {
|
||||
0 => {
|
||||
assert!(fds[0] != -1 && fds[0] != 0);
|
||||
assert!(fds[1] != -1 && fds[1] != 0);
|
||||
Ok((FileDesc::new(fds[0], true), FileDesc::new(fds[1], true)))
|
||||
}
|
||||
_ => Err(IoError::last_error()),
|
||||
}
|
||||
}
|
|
@ -86,18 +86,17 @@
|
|||
|
||||
use alloc::arc::Arc;
|
||||
use libc;
|
||||
use std::c_str::CString;
|
||||
use std::mem;
|
||||
use std::os;
|
||||
use std::ptr;
|
||||
use std::rt::rtio;
|
||||
use std::rt::rtio::{IoResult, IoError};
|
||||
use std::sync::atomic;
|
||||
use std::rt::mutex;
|
||||
use c_str::CString;
|
||||
use mem;
|
||||
use ptr;
|
||||
use sync::atomic;
|
||||
use rt::mutex;
|
||||
use io::{mod, IoError, IoResult};
|
||||
use prelude::*;
|
||||
|
||||
use super::c;
|
||||
use super::util;
|
||||
use super::file::to_utf16;
|
||||
use sys_common::{mod, eof};
|
||||
|
||||
use super::{c, os, timer, to_utf16, decode_error_detailed};
|
||||
|
||||
struct Event(libc::HANDLE);
|
||||
|
||||
|
@ -177,7 +176,7 @@ pub fn await(handle: libc::HANDLE, deadline: u64,
|
|||
let ms = if deadline == 0 {
|
||||
libc::INFINITE as u64
|
||||
} else {
|
||||
let now = ::io::timer::now();
|
||||
let now = timer::now();
|
||||
if deadline < now {0} else {deadline - now}
|
||||
};
|
||||
let ret = unsafe {
|
||||
|
@ -190,7 +189,7 @@ pub fn await(handle: libc::HANDLE, deadline: u64,
|
|||
WAIT_FAILED => Err(super::last_error()),
|
||||
WAIT_TIMEOUT => unsafe {
|
||||
let _ = c::CancelIo(handle);
|
||||
Err(util::timeout("operation timed out"))
|
||||
Err(sys_common::timeout("operation timed out"))
|
||||
},
|
||||
n => Ok((n - WAIT_OBJECT_0) as uint)
|
||||
}
|
||||
|
@ -198,8 +197,8 @@ pub fn await(handle: libc::HANDLE, deadline: u64,
|
|||
|
||||
fn epipe() -> IoError {
|
||||
IoError {
|
||||
code: libc::ERROR_BROKEN_PIPE as uint,
|
||||
extra: 0,
|
||||
kind: io::EndOfFile,
|
||||
desc: "the pipe has ended",
|
||||
detail: None,
|
||||
}
|
||||
}
|
||||
|
@ -268,8 +267,8 @@ impl UnixStream {
|
|||
}
|
||||
|
||||
pub fn connect(addr: &CString, timeout: Option<u64>) -> IoResult<UnixStream> {
|
||||
let addr = try!(to_utf16(addr));
|
||||
let start = ::io::timer::now();
|
||||
let addr = try!(to_utf16(addr.as_str()));
|
||||
let start = timer::now();
|
||||
loop {
|
||||
match UnixStream::try_connect(addr.as_ptr()) {
|
||||
Some(handle) => {
|
||||
|
@ -308,13 +307,13 @@ impl UnixStream {
|
|||
|
||||
match timeout {
|
||||
Some(timeout) => {
|
||||
let now = ::io::timer::now();
|
||||
let now = timer::now();
|
||||
let timed_out = (now - start) >= timeout || unsafe {
|
||||
let ms = (timeout - (now - start)) as libc::DWORD;
|
||||
libc::WaitNamedPipeW(addr.as_ptr(), ms) == 0
|
||||
};
|
||||
if timed_out {
|
||||
return Err(util::timeout("connect timed out"))
|
||||
return Err(sys_common::timeout("connect timed out"))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -349,10 +348,8 @@ impl UnixStream {
|
|||
_ => Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl rtio::RtioPipe for UnixStream {
|
||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
|
||||
pub fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
|
||||
if self.read.is_none() {
|
||||
self.read = Some(try!(Event::new(true, false)));
|
||||
}
|
||||
|
@ -368,7 +365,7 @@ impl rtio::RtioPipe for UnixStream {
|
|||
// See comments in close_read() about why this lock is necessary.
|
||||
let guard = unsafe { self.inner.lock.lock() };
|
||||
if self.read_closed() {
|
||||
return Err(util::eof())
|
||||
return Err(eof())
|
||||
}
|
||||
|
||||
// Issue a nonblocking requests, succeeding quickly if it happened to
|
||||
|
@ -416,15 +413,15 @@ impl rtio::RtioPipe for UnixStream {
|
|||
// If the reading half is now closed, then we're done. If we woke up
|
||||
// because the writing half was closed, keep trying.
|
||||
if wait_succeeded.is_err() {
|
||||
return Err(util::timeout("read timed out"))
|
||||
return Err(sys_common::timeout("read timed out"))
|
||||
}
|
||||
if self.read_closed() {
|
||||
return Err(util::eof())
|
||||
return Err(eof())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
||||
pub fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
||||
if self.write.is_none() {
|
||||
self.write = Some(try!(Event::new(true, false)));
|
||||
}
|
||||
|
@ -458,11 +455,7 @@ impl rtio::RtioPipe for UnixStream {
|
|||
|
||||
if ret == 0 {
|
||||
if err != libc::ERROR_IO_PENDING as uint {
|
||||
return Err(IoError {
|
||||
code: err as uint,
|
||||
extra: 0,
|
||||
detail: Some(os::error_string(err as uint)),
|
||||
})
|
||||
return Err(decode_error_detailed(err as i32))
|
||||
}
|
||||
// Process a timeout if one is pending
|
||||
let wait_succeeded = await(self.handle(), self.write_deadline,
|
||||
|
@ -484,12 +477,12 @@ impl rtio::RtioPipe for UnixStream {
|
|||
let amt = offset + bytes_written as uint;
|
||||
return if amt > 0 {
|
||||
Err(IoError {
|
||||
code: libc::ERROR_OPERATION_ABORTED as uint,
|
||||
extra: amt,
|
||||
detail: Some("short write during write".to_string()),
|
||||
kind: io::ShortWrite(amt),
|
||||
desc: "short write during write",
|
||||
detail: None,
|
||||
})
|
||||
} else {
|
||||
Err(util::timeout("write timed out"))
|
||||
Err(sys_common::timeout("write timed out"))
|
||||
}
|
||||
}
|
||||
if self.write_closed() {
|
||||
|
@ -503,17 +496,7 @@ impl rtio::RtioPipe for UnixStream {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn clone(&self) -> Box<rtio::RtioPipe + Send> {
|
||||
box UnixStream {
|
||||
inner: self.inner.clone(),
|
||||
read: None,
|
||||
write: None,
|
||||
read_deadline: 0,
|
||||
write_deadline: 0,
|
||||
} as Box<rtio::RtioPipe + Send>
|
||||
}
|
||||
|
||||
fn close_read(&mut self) -> IoResult<()> {
|
||||
pub fn close_read(&mut self) -> IoResult<()> {
|
||||
// On windows, there's no actual shutdown() method for pipes, so we're
|
||||
// forced to emulate the behavior manually at the application level. To
|
||||
// do this, we need to both cancel any pending requests, as well as
|
||||
|
@ -536,23 +519,35 @@ impl rtio::RtioPipe for UnixStream {
|
|||
self.cancel_io()
|
||||
}
|
||||
|
||||
fn close_write(&mut self) -> IoResult<()> {
|
||||
pub fn close_write(&mut self) -> IoResult<()> {
|
||||
// see comments in close_read() for why this lock is necessary
|
||||
let _guard = unsafe { self.inner.lock.lock() };
|
||||
self.inner.write_closed.store(true, atomic::SeqCst);
|
||||
self.cancel_io()
|
||||
}
|
||||
|
||||
fn set_timeout(&mut self, timeout: Option<u64>) {
|
||||
let deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0);
|
||||
pub fn set_timeout(&mut self, timeout: Option<u64>) {
|
||||
let deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
|
||||
self.read_deadline = deadline;
|
||||
self.write_deadline = deadline;
|
||||
}
|
||||
fn set_read_timeout(&mut self, timeout: Option<u64>) {
|
||||
self.read_deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0);
|
||||
pub fn set_read_timeout(&mut self, timeout: Option<u64>) {
|
||||
self.read_deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
|
||||
}
|
||||
fn set_write_timeout(&mut self, timeout: Option<u64>) {
|
||||
self.write_deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0);
|
||||
pub fn set_write_timeout(&mut self, timeout: Option<u64>) {
|
||||
self.write_deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for UnixStream {
|
||||
fn clone(&self) -> UnixStream {
|
||||
UnixStream {
|
||||
inner: self.inner.clone(),
|
||||
read: None,
|
||||
write: None,
|
||||
read_deadline: 0,
|
||||
write_deadline: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -570,7 +565,7 @@ impl UnixListener {
|
|||
// Although we technically don't need the pipe until much later, we
|
||||
// create the initial handle up front to test the validity of the name
|
||||
// and such.
|
||||
let addr_v = try!(to_utf16(addr));
|
||||
let addr_v = try!(to_utf16(addr.as_str()));
|
||||
let ret = unsafe { pipe(addr_v.as_ptr(), true) };
|
||||
if ret == libc::INVALID_HANDLE_VALUE {
|
||||
Err(super::last_error())
|
||||
|
@ -579,7 +574,7 @@ impl UnixListener {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn native_listen(self) -> IoResult<UnixAcceptor> {
|
||||
pub fn listen(self) -> IoResult<UnixAcceptor> {
|
||||
Ok(UnixAcceptor {
|
||||
listener: self,
|
||||
event: try!(Event::new(true, false)),
|
||||
|
@ -598,15 +593,6 @@ impl Drop for UnixListener {
|
|||
}
|
||||
}
|
||||
|
||||
impl rtio::RtioUnixListener for UnixListener {
|
||||
fn listen(self: Box<UnixListener>)
|
||||
-> IoResult<Box<rtio::RtioUnixAcceptor + Send>> {
|
||||
self.native_listen().map(|a| {
|
||||
box a as Box<rtio::RtioUnixAcceptor + Send>
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UnixAcceptor {
|
||||
inner: Arc<AcceptorState>,
|
||||
listener: UnixListener,
|
||||
|
@ -620,7 +606,7 @@ struct AcceptorState {
|
|||
}
|
||||
|
||||
impl UnixAcceptor {
|
||||
pub fn native_accept(&mut self) -> IoResult<UnixStream> {
|
||||
pub fn accept(&mut self) -> IoResult<UnixStream> {
|
||||
// This function has some funky implementation details when working with
|
||||
// unix pipes. On windows, each server named pipe handle can be
|
||||
// connected to a one or zero clients. To the best of my knowledge, a
|
||||
|
@ -657,9 +643,9 @@ impl UnixAcceptor {
|
|||
|
||||
// If we've had an artificial call to close_accept, be sure to never
|
||||
// proceed in accepting new clients in the future
|
||||
if self.inner.closed.load(atomic::SeqCst) { return Err(util::eof()) }
|
||||
if self.inner.closed.load(atomic::SeqCst) { return Err(eof()) }
|
||||
|
||||
let name = try!(to_utf16(&self.listener.name));
|
||||
let name = try!(to_utf16(self.listener.name.as_str()));
|
||||
|
||||
// Once we've got a "server handle", we need to wait for a client to
|
||||
// connect. The ConnectNamedPipe function will block this thread until
|
||||
|
@ -691,7 +677,7 @@ impl UnixAcceptor {
|
|||
if wait_succeeded.is_ok() {
|
||||
err = unsafe { libc::GetLastError() };
|
||||
} else {
|
||||
return Err(util::timeout("accept timed out"))
|
||||
return Err(sys_common::timeout("accept timed out"))
|
||||
}
|
||||
} else {
|
||||
// we succeeded, bypass the check below
|
||||
|
@ -727,34 +713,12 @@ impl UnixAcceptor {
|
|||
write_deadline: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl rtio::RtioUnixAcceptor for UnixAcceptor {
|
||||
fn accept(&mut self) -> IoResult<Box<rtio::RtioPipe + Send>> {
|
||||
self.native_accept().map(|s| box s as Box<rtio::RtioPipe + Send>)
|
||||
}
|
||||
fn set_timeout(&mut self, timeout: Option<u64>) {
|
||||
self.deadline = timeout.map(|i| i + ::io::timer::now()).unwrap_or(0);
|
||||
pub fn set_timeout(&mut self, timeout: Option<u64>) {
|
||||
self.deadline = timeout.map(|i| i + timer::now()).unwrap_or(0);
|
||||
}
|
||||
|
||||
fn clone(&self) -> Box<rtio::RtioUnixAcceptor + Send> {
|
||||
let name = to_utf16(&self.listener.name).ok().unwrap();
|
||||
box UnixAcceptor {
|
||||
inner: self.inner.clone(),
|
||||
event: Event::new(true, false).ok().unwrap(),
|
||||
deadline: 0,
|
||||
listener: UnixListener {
|
||||
name: self.listener.name.clone(),
|
||||
handle: unsafe {
|
||||
let p = pipe(name.as_ptr(), false) ;
|
||||
assert!(p != libc::INVALID_HANDLE_VALUE as libc::HANDLE);
|
||||
p
|
||||
},
|
||||
},
|
||||
} as Box<rtio::RtioUnixAcceptor + Send>
|
||||
}
|
||||
|
||||
fn close_accept(&mut self) -> IoResult<()> {
|
||||
pub fn close_accept(&mut self) -> IoResult<()> {
|
||||
self.inner.closed.store(true, atomic::SeqCst);
|
||||
let ret = unsafe {
|
||||
c::SetEvent(self.inner.abort.handle())
|
||||
|
@ -767,3 +731,21 @@ impl rtio::RtioUnixAcceptor for UnixAcceptor {
|
|||
}
|
||||
}
|
||||
|
||||
impl Clone for UnixAcceptor {
|
||||
fn clone(&self) -> UnixAcceptor {
|
||||
let name = to_utf16(self.listener.name.as_str()).ok().unwrap();
|
||||
UnixAcceptor {
|
||||
inner: self.inner.clone(),
|
||||
event: Event::new(true, false).ok().unwrap(),
|
||||
deadline: 0,
|
||||
listener: UnixListener {
|
||||
name: self.listener.name.clone(),
|
||||
handle: unsafe {
|
||||
let p = pipe(name.as_ptr(), false) ;
|
||||
assert!(p != libc::INVALID_HANDLE_VALUE as libc::HANDLE);
|
||||
p
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
511
src/libstd/sys/windows/process.rs
Normal file
511
src/libstd/sys/windows/process.rs
Normal file
|
@ -0,0 +1,511 @@
|
|||
// Copyright 2012-2014 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::{pid_t, c_void, c_int};
|
||||
use libc;
|
||||
use c_str::CString;
|
||||
use io;
|
||||
use mem;
|
||||
use os;
|
||||
use ptr;
|
||||
use prelude::*;
|
||||
use io::process::{ProcessExit, ExitStatus, ExitSignal};
|
||||
use collections;
|
||||
use path::BytesContainer;
|
||||
use hash::Hash;
|
||||
use io::{IoResult, IoError};
|
||||
|
||||
use sys::fs;
|
||||
use sys::{mod, retry, c, wouldblock, set_nonblocking, ms_to_timeval, timer};
|
||||
use sys::fs::FileDesc;
|
||||
use sys_common::helper_thread::Helper;
|
||||
use sys_common::{AsFileDesc, mkerr_libc, timeout};
|
||||
|
||||
use io::fs::PathExtensions;
|
||||
use string::String;
|
||||
|
||||
pub use sys_common::ProcessConfig;
|
||||
|
||||
/**
|
||||
* A value representing a child process.
|
||||
*
|
||||
* The lifetime of this value is linked to the lifetime of the actual
|
||||
* process - the Process destructor calls self.finish() which waits
|
||||
* for the process to terminate.
|
||||
*/
|
||||
pub struct Process {
|
||||
/// The unique id of the process (this should never be negative).
|
||||
pid: pid_t,
|
||||
|
||||
/// A HANDLE to the process, which will prevent the pid being
|
||||
/// re-used until the handle is closed.
|
||||
handle: *mut (),
|
||||
}
|
||||
|
||||
impl Drop for Process {
|
||||
fn drop(&mut self) {
|
||||
free_handle(self.handle);
|
||||
}
|
||||
}
|
||||
|
||||
impl Process {
|
||||
pub fn id(&self) -> pid_t {
|
||||
self.pid
|
||||
}
|
||||
|
||||
pub unsafe fn kill(&self, signal: int) -> IoResult<()> {
|
||||
Process::killpid(self.pid, signal)
|
||||
}
|
||||
|
||||
pub unsafe fn killpid(pid: pid_t, signal: int) -> IoResult<()> {
|
||||
let handle = libc::OpenProcess(libc::PROCESS_TERMINATE |
|
||||
libc::PROCESS_QUERY_INFORMATION,
|
||||
libc::FALSE, pid as libc::DWORD);
|
||||
if handle.is_null() {
|
||||
return Err(super::last_error())
|
||||
}
|
||||
let ret = match signal {
|
||||
// test for existence on signal 0
|
||||
0 => {
|
||||
let mut status = 0;
|
||||
let ret = libc::GetExitCodeProcess(handle, &mut status);
|
||||
if ret == 0 {
|
||||
Err(super::last_error())
|
||||
} else if status != libc::STILL_ACTIVE {
|
||||
Err(IoError {
|
||||
kind: io::InvalidInput,
|
||||
desc: "no process to kill",
|
||||
detail: None,
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
15 | 9 => { // sigterm or sigkill
|
||||
let ret = libc::TerminateProcess(handle, 1);
|
||||
super::mkerr_winbool(ret)
|
||||
}
|
||||
_ => Err(IoError {
|
||||
kind: io::IoUnavailable,
|
||||
desc: "unsupported signal on windows",
|
||||
detail: None,
|
||||
})
|
||||
};
|
||||
let _ = libc::CloseHandle(handle);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pub fn spawn<K, V, C, P>(cfg: &C, in_fd: Option<P>,
|
||||
out_fd: Option<P>, err_fd: Option<P>)
|
||||
-> IoResult<Process>
|
||||
where C: ProcessConfig<K, V>, P: AsFileDesc,
|
||||
K: BytesContainer + Eq + Hash, V: BytesContainer
|
||||
{
|
||||
use libc::types::os::arch::extra::{DWORD, HANDLE, STARTUPINFO};
|
||||
use libc::consts::os::extra::{
|
||||
TRUE, FALSE,
|
||||
STARTF_USESTDHANDLES,
|
||||
INVALID_HANDLE_VALUE,
|
||||
DUPLICATE_SAME_ACCESS
|
||||
};
|
||||
use libc::funcs::extra::kernel32::{
|
||||
GetCurrentProcess,
|
||||
DuplicateHandle,
|
||||
CloseHandle,
|
||||
CreateProcessW
|
||||
};
|
||||
use libc::funcs::extra::msvcrt::get_osfhandle;
|
||||
|
||||
use mem;
|
||||
use iter::Iterator;
|
||||
use str::StrPrelude;
|
||||
|
||||
if cfg.gid().is_some() || cfg.uid().is_some() {
|
||||
return Err(IoError {
|
||||
kind: io::IoUnavailable,
|
||||
desc: "unsupported gid/uid requested on windows",
|
||||
detail: None,
|
||||
})
|
||||
}
|
||||
|
||||
// To have the spawning semantics of unix/windows stay the same, we need to
|
||||
// read the *child's* PATH if one is provided. See #15149 for more details.
|
||||
let program = cfg.env().and_then(|env| {
|
||||
for (key, v) in env.iter() {
|
||||
if b"PATH" != key.container_as_bytes() { continue }
|
||||
|
||||
// Split the value and test each path to see if the
|
||||
// program exists.
|
||||
for path in os::split_paths(v.container_as_bytes()).into_iter() {
|
||||
let path = path.join(cfg.program().as_bytes_no_nul())
|
||||
.with_extension(os::consts::EXE_EXTENSION);
|
||||
if path.exists() {
|
||||
return Some(path.to_c_str())
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
None
|
||||
});
|
||||
|
||||
unsafe {
|
||||
let mut si = zeroed_startupinfo();
|
||||
si.cb = mem::size_of::<STARTUPINFO>() as DWORD;
|
||||
si.dwFlags = STARTF_USESTDHANDLES;
|
||||
|
||||
let cur_proc = GetCurrentProcess();
|
||||
|
||||
// Similarly to unix, we don't actually leave holes for the stdio file
|
||||
// descriptors, but rather open up /dev/null equivalents. These
|
||||
// equivalents are drawn from libuv's windows process spawning.
|
||||
let set_fd = |fd: &Option<P>, slot: &mut HANDLE,
|
||||
is_stdin: bool| {
|
||||
match *fd {
|
||||
None => {
|
||||
let access = if is_stdin {
|
||||
libc::FILE_GENERIC_READ
|
||||
} else {
|
||||
libc::FILE_GENERIC_WRITE | libc::FILE_READ_ATTRIBUTES
|
||||
};
|
||||
let size = mem::size_of::<libc::SECURITY_ATTRIBUTES>();
|
||||
let mut sa = libc::SECURITY_ATTRIBUTES {
|
||||
nLength: size as libc::DWORD,
|
||||
lpSecurityDescriptor: ptr::null_mut(),
|
||||
bInheritHandle: 1,
|
||||
};
|
||||
let mut filename: Vec<u16> = "NUL".utf16_units().collect();
|
||||
filename.push(0);
|
||||
*slot = libc::CreateFileW(filename.as_ptr(),
|
||||
access,
|
||||
libc::FILE_SHARE_READ |
|
||||
libc::FILE_SHARE_WRITE,
|
||||
&mut sa,
|
||||
libc::OPEN_EXISTING,
|
||||
0,
|
||||
ptr::null_mut());
|
||||
if *slot == INVALID_HANDLE_VALUE {
|
||||
return Err(super::last_error())
|
||||
}
|
||||
}
|
||||
Some(ref fd) => {
|
||||
let orig = get_osfhandle(fd.as_fd().fd()) as HANDLE;
|
||||
if orig == INVALID_HANDLE_VALUE {
|
||||
return Err(super::last_error())
|
||||
}
|
||||
if DuplicateHandle(cur_proc, orig, cur_proc, slot,
|
||||
0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE {
|
||||
return Err(super::last_error())
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
};
|
||||
|
||||
try!(set_fd(&in_fd, &mut si.hStdInput, true));
|
||||
try!(set_fd(&out_fd, &mut si.hStdOutput, false));
|
||||
try!(set_fd(&err_fd, &mut si.hStdError, false));
|
||||
|
||||
let cmd_str = make_command_line(program.as_ref().unwrap_or(cfg.program()),
|
||||
cfg.args());
|
||||
let mut pi = zeroed_process_information();
|
||||
let mut create_err = None;
|
||||
|
||||
// stolen from the libuv code.
|
||||
let mut flags = libc::CREATE_UNICODE_ENVIRONMENT;
|
||||
if cfg.detach() {
|
||||
flags |= libc::DETACHED_PROCESS | libc::CREATE_NEW_PROCESS_GROUP;
|
||||
}
|
||||
|
||||
with_envp(cfg.env(), |envp| {
|
||||
with_dirp(cfg.cwd(), |dirp| {
|
||||
let mut cmd_str: Vec<u16> = cmd_str.as_slice().utf16_units().collect();
|
||||
cmd_str.push(0);
|
||||
let created = CreateProcessW(ptr::null(),
|
||||
cmd_str.as_mut_ptr(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
TRUE,
|
||||
flags, envp, dirp,
|
||||
&mut si, &mut pi);
|
||||
if created == FALSE {
|
||||
create_err = Some(super::last_error());
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
assert!(CloseHandle(si.hStdInput) != 0);
|
||||
assert!(CloseHandle(si.hStdOutput) != 0);
|
||||
assert!(CloseHandle(si.hStdError) != 0);
|
||||
|
||||
match create_err {
|
||||
Some(err) => return Err(err),
|
||||
None => {}
|
||||
}
|
||||
|
||||
// We close the thread handle because we don't care about keeping the
|
||||
// thread id valid, and we aren't keeping the thread handle around to be
|
||||
// able to close it later. We don't close the process handle however
|
||||
// because std::we want the process id to stay valid at least until the
|
||||
// calling code closes the process handle.
|
||||
assert!(CloseHandle(pi.hThread) != 0);
|
||||
|
||||
Ok(Process {
|
||||
pid: pi.dwProcessId as pid_t,
|
||||
handle: pi.hProcess as *mut ()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for a process to exit and returns the exit code, failing
|
||||
* if there is no process with the specified id.
|
||||
*
|
||||
* Note that this is private to avoid race conditions on unix where if
|
||||
* a user calls waitpid(some_process.get_id()) then some_process.finish()
|
||||
* and some_process.destroy() and some_process.finalize() will then either
|
||||
* operate on a none-existent process or, even worse, on a newer process
|
||||
* with the same id.
|
||||
*/
|
||||
pub fn wait(&self, deadline: u64) -> IoResult<ProcessExit> {
|
||||
use libc::types::os::arch::extra::DWORD;
|
||||
use libc::consts::os::extra::{
|
||||
SYNCHRONIZE,
|
||||
PROCESS_QUERY_INFORMATION,
|
||||
FALSE,
|
||||
STILL_ACTIVE,
|
||||
INFINITE,
|
||||
WAIT_TIMEOUT,
|
||||
WAIT_OBJECT_0,
|
||||
};
|
||||
use libc::funcs::extra::kernel32::{
|
||||
OpenProcess,
|
||||
GetExitCodeProcess,
|
||||
CloseHandle,
|
||||
WaitForSingleObject,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let process = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION,
|
||||
FALSE,
|
||||
self.pid as DWORD);
|
||||
if process.is_null() {
|
||||
return Err(super::last_error())
|
||||
}
|
||||
|
||||
loop {
|
||||
let mut status = 0;
|
||||
if GetExitCodeProcess(process, &mut status) == FALSE {
|
||||
let err = Err(super::last_error());
|
||||
assert!(CloseHandle(process) != 0);
|
||||
return err;
|
||||
}
|
||||
if status != STILL_ACTIVE {
|
||||
assert!(CloseHandle(process) != 0);
|
||||
return Ok(ExitStatus(status as int));
|
||||
}
|
||||
let interval = if deadline == 0 {
|
||||
INFINITE
|
||||
} else {
|
||||
let now = timer::now();
|
||||
if deadline < now {0} else {(deadline - now) as u32}
|
||||
};
|
||||
match WaitForSingleObject(process, interval) {
|
||||
WAIT_OBJECT_0 => {}
|
||||
WAIT_TIMEOUT => {
|
||||
assert!(CloseHandle(process) != 0);
|
||||
return Err(timeout("process wait timed out"))
|
||||
}
|
||||
_ => {
|
||||
let err = Err(super::last_error());
|
||||
assert!(CloseHandle(process) != 0);
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn zeroed_startupinfo() -> libc::types::os::arch::extra::STARTUPINFO {
|
||||
libc::types::os::arch::extra::STARTUPINFO {
|
||||
cb: 0,
|
||||
lpReserved: ptr::null_mut(),
|
||||
lpDesktop: ptr::null_mut(),
|
||||
lpTitle: ptr::null_mut(),
|
||||
dwX: 0,
|
||||
dwY: 0,
|
||||
dwXSize: 0,
|
||||
dwYSize: 0,
|
||||
dwXCountChars: 0,
|
||||
dwYCountCharts: 0,
|
||||
dwFillAttribute: 0,
|
||||
dwFlags: 0,
|
||||
wShowWindow: 0,
|
||||
cbReserved2: 0,
|
||||
lpReserved2: ptr::null_mut(),
|
||||
hStdInput: libc::INVALID_HANDLE_VALUE,
|
||||
hStdOutput: libc::INVALID_HANDLE_VALUE,
|
||||
hStdError: libc::INVALID_HANDLE_VALUE,
|
||||
}
|
||||
}
|
||||
|
||||
fn zeroed_process_information() -> libc::types::os::arch::extra::PROCESS_INFORMATION {
|
||||
libc::types::os::arch::extra::PROCESS_INFORMATION {
|
||||
hProcess: ptr::null_mut(),
|
||||
hThread: ptr::null_mut(),
|
||||
dwProcessId: 0,
|
||||
dwThreadId: 0
|
||||
}
|
||||
}
|
||||
|
||||
fn make_command_line(prog: &CString, args: &[CString]) -> String {
|
||||
let mut cmd = String::new();
|
||||
append_arg(&mut cmd, prog.as_str()
|
||||
.expect("expected program name to be utf-8 encoded"));
|
||||
for arg in args.iter() {
|
||||
cmd.push(' ');
|
||||
append_arg(&mut cmd, arg.as_str()
|
||||
.expect("expected argument to be utf-8 encoded"));
|
||||
}
|
||||
return cmd;
|
||||
|
||||
fn append_arg(cmd: &mut String, arg: &str) {
|
||||
// If an argument has 0 characters then we need to quote it to ensure
|
||||
// that it actually gets passed through on the command line or otherwise
|
||||
// it will be dropped entirely when parsed on the other end.
|
||||
let quote = arg.chars().any(|c| c == ' ' || c == '\t') || arg.len() == 0;
|
||||
if quote {
|
||||
cmd.push('"');
|
||||
}
|
||||
let argvec: Vec<char> = arg.chars().collect();
|
||||
for i in range(0u, argvec.len()) {
|
||||
append_char_at(cmd, argvec.as_slice(), i);
|
||||
}
|
||||
if quote {
|
||||
cmd.push('"');
|
||||
}
|
||||
}
|
||||
|
||||
fn append_char_at(cmd: &mut String, arg: &[char], i: uint) {
|
||||
match arg[i] {
|
||||
'"' => {
|
||||
// Escape quotes.
|
||||
cmd.push_str("\\\"");
|
||||
}
|
||||
'\\' => {
|
||||
if backslash_run_ends_in_quote(arg, i) {
|
||||
// Double all backslashes that are in runs before quotes.
|
||||
cmd.push_str("\\\\");
|
||||
} else {
|
||||
// Pass other backslashes through unescaped.
|
||||
cmd.push('\\');
|
||||
}
|
||||
}
|
||||
c => {
|
||||
cmd.push(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn backslash_run_ends_in_quote(s: &[char], mut i: uint) -> bool {
|
||||
while i < s.len() && s[i] == '\\' {
|
||||
i += 1;
|
||||
}
|
||||
return i < s.len() && s[i] == '"';
|
||||
}
|
||||
}
|
||||
|
||||
fn with_envp<K, V, T>(env: Option<&collections::HashMap<K, V>>,
|
||||
cb: |*mut c_void| -> T) -> T
|
||||
where K: BytesContainer + Eq + Hash, V: BytesContainer
|
||||
{
|
||||
// On Windows we pass an "environment block" which is not a char**, but
|
||||
// rather a concatenation of null-terminated k=v\0 sequences, with a final
|
||||
// \0 to terminate.
|
||||
match env {
|
||||
Some(env) => {
|
||||
let mut blk = Vec::new();
|
||||
|
||||
for pair in env.iter() {
|
||||
let kv = format!("{}={}",
|
||||
pair.ref0().container_as_str().unwrap(),
|
||||
pair.ref1().container_as_str().unwrap());
|
||||
blk.extend(kv.as_slice().utf16_units());
|
||||
blk.push(0);
|
||||
}
|
||||
|
||||
blk.push(0);
|
||||
|
||||
cb(blk.as_mut_ptr() as *mut c_void)
|
||||
}
|
||||
_ => cb(ptr::null_mut())
|
||||
}
|
||||
}
|
||||
|
||||
fn with_dirp<T>(d: Option<&CString>, cb: |*const u16| -> T) -> T {
|
||||
match d {
|
||||
Some(dir) => {
|
||||
let dir_str = dir.as_str()
|
||||
.expect("expected workingdirectory to be utf-8 encoded");
|
||||
let mut dir_str: Vec<u16> = dir_str.utf16_units().collect();
|
||||
dir_str.push(0);
|
||||
cb(dir_str.as_ptr())
|
||||
},
|
||||
None => cb(ptr::null())
|
||||
}
|
||||
}
|
||||
|
||||
fn free_handle(handle: *mut ()) {
|
||||
assert!(unsafe {
|
||||
libc::CloseHandle(mem::transmute(handle)) != 0
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_make_command_line() {
|
||||
use prelude::*;
|
||||
use str;
|
||||
use c_str::CString;
|
||||
use super::make_command_line;
|
||||
|
||||
fn test_wrapper(prog: &str, args: &[&str]) -> String {
|
||||
make_command_line(&prog.to_c_str(),
|
||||
args.iter()
|
||||
.map(|a| a.to_c_str())
|
||||
.collect::<Vec<CString>>()
|
||||
.as_slice())
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
test_wrapper("prog", ["aaa", "bbb", "ccc"]),
|
||||
"prog aaa bbb ccc".to_string()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
test_wrapper("C:\\Program Files\\blah\\blah.exe", ["aaa"]),
|
||||
"\"C:\\Program Files\\blah\\blah.exe\" aaa".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
test_wrapper("C:\\Program Files\\test", ["aa\"bb"]),
|
||||
"\"C:\\Program Files\\test\" aa\\\"bb".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
test_wrapper("echo", ["a b c"]),
|
||||
"echo \"a b c\"".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
test_wrapper("\u03c0\u042f\u97f3\u00e6\u221e", []),
|
||||
"\u03c0\u042f\u97f3\u00e6\u221e".to_string()
|
||||
);
|
||||
}
|
||||
}
|
219
src/libstd/sys/windows/tcp.rs
Normal file
219
src/libstd/sys/windows/tcp.rs
Normal file
|
@ -0,0 +1,219 @@
|
|||
// Copyright 2014 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 io::net::ip;
|
||||
use io::IoResult;
|
||||
use libc;
|
||||
use mem;
|
||||
use ptr;
|
||||
use prelude::*;
|
||||
use super::{last_error, last_net_error, retry, sock_t};
|
||||
use sync::{Arc, atomic};
|
||||
use sys::fs::FileDesc;
|
||||
use sys::{mod, c, set_nonblocking, wouldblock, timer};
|
||||
use sys_common::{mod, timeout, eof};
|
||||
use sys_common::net::*;
|
||||
|
||||
pub use sys_common::net::TcpStream;
|
||||
|
||||
pub struct Event(c::WSAEVENT);
|
||||
|
||||
impl Event {
|
||||
pub fn new() -> IoResult<Event> {
|
||||
let event = unsafe { c::WSACreateEvent() };
|
||||
if event == c::WSA_INVALID_EVENT {
|
||||
Err(super::last_error())
|
||||
} else {
|
||||
Ok(Event(event))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle(&self) -> c::WSAEVENT { let Event(handle) = *self; handle }
|
||||
}
|
||||
|
||||
impl Drop for Event {
|
||||
fn drop(&mut self) {
|
||||
unsafe { let _ = c::WSACloseEvent(self.handle()); }
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TCP listeners
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub struct TcpListener {
|
||||
inner: FileDesc,
|
||||
}
|
||||
|
||||
impl TcpListener {
|
||||
pub fn bind(addr: ip::SocketAddr) -> IoResult<TcpListener> {
|
||||
sys::init_net();
|
||||
|
||||
let fd = try!(socket(addr, libc::SOCK_STREAM));
|
||||
let ret = TcpListener { inner: FileDesc::new(fd as libc::c_int, true) };
|
||||
|
||||
let mut storage = unsafe { mem::zeroed() };
|
||||
let len = addr_to_sockaddr(addr, &mut storage);
|
||||
let addrp = &storage as *const _ as *const libc::sockaddr;
|
||||
|
||||
match unsafe { libc::bind(fd, addrp, len) } {
|
||||
-1 => Err(last_net_error()),
|
||||
_ => Ok(ret),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fd(&self) -> sock_t { self.inner.fd as sock_t }
|
||||
|
||||
pub fn listen(self, backlog: int) -> IoResult<TcpAcceptor> {
|
||||
match unsafe { libc::listen(self.fd(), backlog as libc::c_int) } {
|
||||
-1 => Err(last_net_error()),
|
||||
|
||||
_ => {
|
||||
let accept = try!(Event::new());
|
||||
let ret = unsafe {
|
||||
c::WSAEventSelect(self.fd(), accept.handle(), c::FD_ACCEPT)
|
||||
};
|
||||
if ret != 0 {
|
||||
return Err(last_net_error())
|
||||
}
|
||||
Ok(TcpAcceptor {
|
||||
inner: Arc::new(AcceptorInner {
|
||||
listener: self,
|
||||
abort: try!(Event::new()),
|
||||
accept: accept,
|
||||
closed: atomic::AtomicBool::new(false),
|
||||
}),
|
||||
deadline: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn socket_name(&mut self) -> IoResult<ip::SocketAddr> {
|
||||
sockname(self.fd(), libc::getsockname)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TcpAcceptor {
|
||||
inner: Arc<AcceptorInner>,
|
||||
deadline: u64,
|
||||
}
|
||||
|
||||
struct AcceptorInner {
|
||||
listener: TcpListener,
|
||||
abort: Event,
|
||||
accept: Event,
|
||||
closed: atomic::AtomicBool,
|
||||
}
|
||||
|
||||
impl TcpAcceptor {
|
||||
pub fn fd(&self) -> sock_t { self.inner.listener.fd() }
|
||||
|
||||
pub fn accept(&mut self) -> IoResult<TcpStream> {
|
||||
// Unlink unix, windows cannot invoke `select` on arbitrary file
|
||||
// descriptors like pipes, only sockets. Consequently, windows cannot
|
||||
// use the same implementation as unix for accept() when close_accept()
|
||||
// is considered.
|
||||
//
|
||||
// In order to implement close_accept() and timeouts, windows uses
|
||||
// event handles. An acceptor-specific abort event is created which
|
||||
// will only get set in close_accept(), and it will never be un-set.
|
||||
// Additionally, another acceptor-specific event is associated with the
|
||||
// FD_ACCEPT network event.
|
||||
//
|
||||
// These two events are then passed to WaitForMultipleEvents to see
|
||||
// which one triggers first, and the timeout passed to this function is
|
||||
// the local timeout for the acceptor.
|
||||
//
|
||||
// If the wait times out, then the accept timed out. If the wait
|
||||
// succeeds with the abort event, then we were closed, and if the wait
|
||||
// succeeds otherwise, then we do a nonblocking poll via `accept` to
|
||||
// see if we can accept a connection. The connection is candidate to be
|
||||
// stolen, so we do all of this in a loop as well.
|
||||
let events = [self.inner.abort.handle(), self.inner.accept.handle()];
|
||||
|
||||
while !self.inner.closed.load(atomic::SeqCst) {
|
||||
let ms = if self.deadline == 0 {
|
||||
c::WSA_INFINITE as u64
|
||||
} else {
|
||||
let now = timer::now();
|
||||
if self.deadline < now {0} else {self.deadline - now}
|
||||
};
|
||||
let ret = unsafe {
|
||||
c::WSAWaitForMultipleEvents(2, events.as_ptr(), libc::FALSE,
|
||||
ms as libc::DWORD, libc::FALSE)
|
||||
};
|
||||
match ret {
|
||||
c::WSA_WAIT_TIMEOUT => {
|
||||
return Err(timeout("accept timed out"))
|
||||
}
|
||||
c::WSA_WAIT_FAILED => return Err(last_net_error()),
|
||||
c::WSA_WAIT_EVENT_0 => break,
|
||||
n => assert_eq!(n, c::WSA_WAIT_EVENT_0 + 1),
|
||||
}
|
||||
|
||||
let mut wsaevents: c::WSANETWORKEVENTS = unsafe { mem::zeroed() };
|
||||
let ret = unsafe {
|
||||
c::WSAEnumNetworkEvents(self.fd(), events[1], &mut wsaevents)
|
||||
};
|
||||
if ret != 0 { return Err(last_net_error()) }
|
||||
|
||||
if wsaevents.lNetworkEvents & c::FD_ACCEPT == 0 { continue }
|
||||
match unsafe {
|
||||
libc::accept(self.fd(), ptr::null_mut(), ptr::null_mut())
|
||||
} {
|
||||
-1 if wouldblock() => {}
|
||||
-1 => return Err(last_net_error()),
|
||||
|
||||
// Accepted sockets inherit the same properties as the caller,
|
||||
// so we need to deregister our event and switch the socket back
|
||||
// to blocking mode
|
||||
fd => {
|
||||
let stream = TcpStream::new(fd);
|
||||
let ret = unsafe {
|
||||
c::WSAEventSelect(fd, events[1], 0)
|
||||
};
|
||||
if ret != 0 { return Err(last_net_error()) }
|
||||
try!(set_nonblocking(fd, false));
|
||||
return Ok(stream)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(eof())
|
||||
}
|
||||
|
||||
pub fn socket_name(&mut self) -> IoResult<ip::SocketAddr> {
|
||||
sockname(self.fd(), libc::getsockname)
|
||||
}
|
||||
|
||||
pub fn set_timeout(&mut self, timeout: Option<u64>) {
|
||||
self.deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
|
||||
}
|
||||
|
||||
pub fn close_accept(&mut self) -> IoResult<()> {
|
||||
self.inner.closed.store(true, atomic::SeqCst);
|
||||
let ret = unsafe { c::WSASetEvent(self.inner.abort.handle()) };
|
||||
if ret == libc::TRUE {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(last_net_error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for TcpAcceptor {
|
||||
fn clone(&self) -> TcpAcceptor {
|
||||
TcpAcceptor {
|
||||
inner: self.inner.clone(),
|
||||
deadline: 0,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,15 +21,21 @@
|
|||
//! the other two implementations of timers with nothing *that* new showing up.
|
||||
|
||||
use libc;
|
||||
use std::ptr;
|
||||
use std::rt::rtio;
|
||||
use std::rt::rtio::{IoResult, Callback};
|
||||
use std::comm;
|
||||
use ptr;
|
||||
use comm;
|
||||
|
||||
use io::helper_thread::Helper;
|
||||
use sys::c;
|
||||
use sys::fs::FileDesc;
|
||||
use sys_common::helper_thread::Helper;
|
||||
use prelude::*;
|
||||
use io::IoResult;
|
||||
|
||||
helper_init!(static HELPER: Helper<Req>)
|
||||
|
||||
pub trait Callback {
|
||||
fn call(&mut self);
|
||||
}
|
||||
|
||||
pub struct Timer {
|
||||
obj: libc::HANDLE,
|
||||
on_worker: bool,
|
||||
|
@ -116,12 +122,6 @@ impl Timer {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn sleep(ms: u64) {
|
||||
use std::rt::rtio::RtioTimer;
|
||||
let mut t = Timer::new().ok().expect("must allocate a timer!");
|
||||
t.sleep(ms);
|
||||
}
|
||||
|
||||
fn remove(&mut self) {
|
||||
if !self.on_worker { return }
|
||||
|
||||
|
@ -131,10 +131,8 @@ impl Timer {
|
|||
|
||||
self.on_worker = false;
|
||||
}
|
||||
}
|
||||
|
||||
impl rtio::RtioTimer for Timer {
|
||||
fn sleep(&mut self, msecs: u64) {
|
||||
pub fn sleep(&mut self, msecs: u64) {
|
||||
self.remove();
|
||||
|
||||
// there are 10^6 nanoseconds in a millisecond, and the parameter is in
|
||||
|
@ -148,7 +146,7 @@ impl rtio::RtioTimer for Timer {
|
|||
let _ = unsafe { imp::WaitForSingleObject(self.obj, libc::INFINITE) };
|
||||
}
|
||||
|
||||
fn oneshot(&mut self, msecs: u64, cb: Box<Callback + Send>) {
|
||||
pub fn oneshot(&mut self, msecs: u64, cb: Box<Callback + Send>) {
|
||||
self.remove();
|
||||
|
||||
// see above for the calculation
|
||||
|
@ -162,7 +160,7 @@ impl rtio::RtioTimer for Timer {
|
|||
self.on_worker = true;
|
||||
}
|
||||
|
||||
fn period(&mut self, msecs: u64, cb: Box<Callback + Send>) {
|
||||
pub fn period(&mut self, msecs: u64, cb: Box<Callback + Send>) {
|
||||
self.remove();
|
||||
|
||||
// see above for the calculation
|
|
@ -33,16 +33,16 @@ use super::c::{ENABLE_PROCESSED_INPUT, ENABLE_QUICK_EDIT_MODE};
|
|||
use libc::{c_int, HANDLE, LPDWORD, DWORD, LPVOID};
|
||||
use libc::{get_osfhandle, CloseHandle};
|
||||
use libc::types::os::arch::extra::LPCVOID;
|
||||
use std::io::MemReader;
|
||||
use std::ptr;
|
||||
use std::rt::rtio::{IoResult, IoError, RtioTTY};
|
||||
use std::str::from_utf8;
|
||||
use io::{mod, IoError, IoResult, MemReader};
|
||||
use prelude::*;
|
||||
use ptr;
|
||||
use str::from_utf8;
|
||||
|
||||
fn invalid_encoding() -> IoError {
|
||||
IoError {
|
||||
code: ERROR_ILLEGAL_CHARACTER as uint,
|
||||
extra: 0,
|
||||
detail: Some("text was not valid unicode".to_string()),
|
||||
kind: io::InvalidInput,
|
||||
desc: "text was not valid unicode",
|
||||
detail: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,40 +56,37 @@ pub fn is_tty(fd: c_int) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct WindowsTTY {
|
||||
pub struct TTY {
|
||||
closeme: bool,
|
||||
handle: HANDLE,
|
||||
utf8: MemReader,
|
||||
}
|
||||
|
||||
impl WindowsTTY {
|
||||
pub fn new(fd: c_int) -> WindowsTTY {
|
||||
// If the file descriptor is one of stdin, stderr, or stdout
|
||||
// then it should not be closed by us
|
||||
let closeme = match fd {
|
||||
0...2 => false,
|
||||
_ => true,
|
||||
};
|
||||
let handle = unsafe { get_osfhandle(fd) as HANDLE };
|
||||
WindowsTTY {
|
||||
handle: handle,
|
||||
utf8: MemReader::new(Vec::new()),
|
||||
closeme: closeme,
|
||||
impl TTY {
|
||||
pub fn new(fd: c_int) -> IoResult<TTY> {
|
||||
if is_tty(fd) {
|
||||
// If the file descriptor is one of stdin, stderr, or stdout
|
||||
// then it should not be closed by us
|
||||
let closeme = match fd {
|
||||
0...2 => false,
|
||||
_ => true,
|
||||
};
|
||||
let handle = unsafe { get_osfhandle(fd) as HANDLE };
|
||||
Ok(TTY {
|
||||
handle: handle,
|
||||
utf8: MemReader::new(Vec::new()),
|
||||
closeme: closeme,
|
||||
})
|
||||
} else {
|
||||
Err(IoError {
|
||||
kind: io::MismatchedFileTypeForOperation,
|
||||
desc: "invalid handle provided to function",
|
||||
detail: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for WindowsTTY {
|
||||
fn drop(&mut self) {
|
||||
if self.closeme {
|
||||
// Nobody cares about the return value
|
||||
let _ = unsafe { CloseHandle(self.handle) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RtioTTY for WindowsTTY {
|
||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
|
||||
pub fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
|
||||
// Read more if the buffer is empty
|
||||
if self.utf8.eof() {
|
||||
let mut utf16 = Vec::from_elem(0x1000, 0u16);
|
||||
|
@ -113,7 +110,7 @@ impl RtioTTY for WindowsTTY {
|
|||
Ok(self.utf8.read(buf).unwrap())
|
||||
}
|
||||
|
||||
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
||||
pub fn write(&mut self, buf: &[u8]) -> IoResult<()> {
|
||||
let utf16 = match from_utf8(buf) {
|
||||
Some(utf8) => {
|
||||
utf8.as_slice().utf16_units().collect::<Vec<u16>>()
|
||||
|
@ -131,7 +128,7 @@ impl RtioTTY for WindowsTTY {
|
|||
}
|
||||
}
|
||||
|
||||
fn set_raw(&mut self, raw: bool) -> IoResult<()> {
|
||||
pub fn set_raw(&mut self, raw: bool) -> IoResult<()> {
|
||||
// FIXME
|
||||
// Somebody needs to decide on which of these flags we want
|
||||
match unsafe { SetConsoleMode(self.handle,
|
||||
|
@ -146,7 +143,7 @@ impl RtioTTY for WindowsTTY {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_winsize(&mut self) -> IoResult<(int, int)> {
|
||||
pub fn get_winsize(&mut self) -> IoResult<(int, int)> {
|
||||
// FIXME
|
||||
// Get console buffer via CreateFile with CONOUT$
|
||||
// Make a CONSOLE_SCREEN_BUFFER_INFO
|
||||
|
@ -156,5 +153,14 @@ impl RtioTTY for WindowsTTY {
|
|||
}
|
||||
|
||||
// Let us magically declare this as a TTY
|
||||
fn isatty(&self) -> bool { true }
|
||||
pub fn isatty(&self) -> bool { true }
|
||||
}
|
||||
|
||||
impl Drop for TTY {
|
||||
fn drop(&mut self) {
|
||||
if self.closeme {
|
||||
// Nobody cares about the return value
|
||||
let _ = unsafe { CloseHandle(self.handle) };
|
||||
}
|
||||
}
|
||||
}
|
11
src/libstd/sys/windows/udp.rs
Normal file
11
src/libstd/sys/windows/udp.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
pub use sys_common::net::UdpSocket;
|
|
@ -11,6 +11,7 @@
|
|||
// Check that we can use `-C lto` when linking against libraries that were
|
||||
// separately compiled.
|
||||
|
||||
// ignore-android linker weridness (see #18800)
|
||||
// aux-build:sepcomp_lib.rs
|
||||
// compile-flags: -C lto
|
||||
// no-prefer-dynamic
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
// ignore-windows
|
||||
// ignore-macos
|
||||
|
||||
#![feature(macro_rules)]
|
||||
|
||||
extern crate native;
|
||||
extern crate rustrt;
|
||||
extern crate libc;
|
||||
use libc::{c_char, c_int};
|
||||
use native::io::process;
|
||||
use rustrt::rtio;
|
||||
use rustrt::c_str;
|
||||
|
||||
macro_rules! c_string {
|
||||
($s:expr) => { {
|
||||
let ptr = concat!($s, "\0").as_ptr() as *const i8;
|
||||
unsafe { &c_str::CString::new(ptr, false) }
|
||||
} }
|
||||
}
|
||||
|
||||
static EXPECTED_ERRNO: c_int = 0x778899aa;
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn chdir(_: *const c_char) -> c_int {
|
||||
// copied from std::os::errno()
|
||||
#[cfg(any(target_os = "macos",
|
||||
target_os = "ios",
|
||||
target_os = "freebsd"))]
|
||||
fn errno_location() -> *mut c_int {
|
||||
extern {
|
||||
fn __error() -> *mut c_int;
|
||||
}
|
||||
unsafe {
|
||||
__error()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "dragonfly")]
|
||||
fn errno_location() -> *mut c_int {
|
||||
extern {
|
||||
fn __dfly_error() -> *mut c_int;
|
||||
}
|
||||
unsafe {
|
||||
__dfly_error()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn errno_location() -> *mut c_int {
|
||||
extern {
|
||||
fn __errno_location() -> *mut c_int;
|
||||
}
|
||||
unsafe {
|
||||
__errno_location()
|
||||
}
|
||||
}
|
||||
|
||||
*errno_location() = EXPECTED_ERRNO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let program = c_string!("true");
|
||||
let cwd = c_string!("whatever");
|
||||
let cfg = rtio::ProcessConfig {
|
||||
program: program,
|
||||
args: &[],
|
||||
env: None,
|
||||
cwd: Some(cwd),
|
||||
stdin: rtio::Ignored,
|
||||
stdout: rtio::Ignored,
|
||||
stderr: rtio::Ignored,
|
||||
extra_io: &[],
|
||||
uid: None,
|
||||
gid: None,
|
||||
detach: false
|
||||
};
|
||||
|
||||
match process::Process::spawn(cfg) {
|
||||
Ok(_) => { panic!("spawn() should have panicked"); }
|
||||
Err(rtio::IoError { code: err, ..}) => {
|
||||
assert_eq!(err as c_int, EXPECTED_ERRNO);
|
||||
}
|
||||
};
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue