Remove librustuv

This commit removes the `librustuv` crate.

See the [runtime removal
RFC](https://github.com/rust-lang/rfcs/pull/230) for more context.

See [green-rs](https://github.com/alexcrichton/green-rs/) for a possible
migration path if you wish to continue using green-threaded I/O. The
library provides its own I/O API surface.

[breaking-change]
This commit is contained in:
Aaron Turon 2014-09-30 22:25:22 -07:00
parent 15966c3c1f
commit 002643dcf0
22 changed files with 2 additions and 6468 deletions

View File

@ -49,7 +49,7 @@
# automatically generated for all stage/host/target combinations.
################################################################################
TARGET_CRATES := libc std green rustuv native flate arena glob term semver \
TARGET_CRATES := libc std green native flate arena glob term semver \
uuid serialize sync getopts collections num test time rand \
url log regex graphviz core rbml rlibc alloc debug rustrt \
unicode
@ -69,7 +69,6 @@ DEPS_std := core libc rand alloc collections rustrt sync unicode \
native:rust_builtin native:backtrace
DEPS_graphviz := std
DEPS_green := std native:context_switch
DEPS_rustuv := std native:uv native:uv_support
DEPS_native := std
DEPS_syntax := std term serialize log fmt_macros debug arena libc
DEPS_rustc := syntax flate arena serialize getopts rbml \
@ -102,7 +101,7 @@ DEPS_regex := std
DEPS_regex_macros = rustc syntax std regex
DEPS_fmt_macros = std
TOOL_DEPS_compiletest := test green rustuv getopts
TOOL_DEPS_compiletest := test getopts
TOOL_DEPS_rustdoc := rustdoc native
TOOL_DEPS_rustc := rustc native
TOOL_SOURCE_compiletest := $(S)src/compiletest/compiletest.rs

View File

@ -57,8 +57,6 @@ Source layout:
| `test/auxiliary` | - Dependencies of tests |
| ------------------- | --------------------------------------------------------- |
| `librustdoc/` | The Rust API documentation tool |
| `libuv/` | The libuv submodule |
| `librustuv/` | Rust libuv support code |
| ------------------- | --------------------------------------------------------- |
| `llvm/` | The LLVM submodule |
| `rustllvm/` | LLVM support code |

View File

@ -1,173 +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.
/// An exclusive access primitive
///
/// This primitive is used to gain exclusive access to read() and write() in uv.
/// It is assumed that all invocations of this struct happen on the same thread
/// (the uv event loop).
use alloc::arc::Arc;
use std::mem;
use std::rt::local::Local;
use std::rt::task::{BlockedTask, Task};
use std::cell::UnsafeCell;
use homing::HomingMissile;
pub struct Access<T> {
inner: Arc<UnsafeCell<Inner<T>>>,
}
pub struct Guard<'a, T:'static> {
access: &'a mut Access<T>,
missile: Option<HomingMissile>,
}
struct Inner<T> {
queue: Vec<(BlockedTask, uint)>,
held: bool,
closed: bool,
data: T,
}
impl<T: Send> Access<T> {
pub fn new(data: T) -> Access<T> {
Access {
inner: Arc::new(UnsafeCell::new(Inner {
queue: vec![],
held: false,
closed: false,
data: data,
}))
}
}
pub fn grant<'a>(&'a mut self, token: uint,
missile: HomingMissile) -> Guard<'a, T> {
// This unsafety is actually OK because the homing missile argument
// guarantees that we're on the same event loop as all the other objects
// attempting to get access granted.
let inner = unsafe { &mut *self.inner.get() };
if inner.held {
let t: Box<Task> = Local::take();
t.deschedule(1, |task| {
inner.queue.push((task, token));
Ok(())
});
assert!(inner.held);
} else {
inner.held = true;
}
Guard { access: self, missile: Some(missile) }
}
pub fn unsafe_get(&self) -> *mut T {
unsafe { &mut (*self.inner.get()).data as *mut _ }
}
// Safe version which requires proof that you are on the home scheduler.
pub fn get_mut<'a>(&'a mut self, _missile: &HomingMissile) -> &'a mut T {
unsafe { &mut *self.unsafe_get() }
}
pub fn close(&self, _missile: &HomingMissile) {
// This unsafety is OK because with a homing missile we're guaranteed to
// be the only task looking at the `closed` flag (and are therefore
// allowed to modify it). Additionally, no atomics are necessary because
// everyone's running on the same thread and has already done the
// necessary synchronization to be running on this thread.
unsafe { (*self.inner.get()).closed = true; }
}
// Dequeue a blocked task with a specified token. This is unsafe because it
// is only safe to invoke while on the home event loop, and there is no
// guarantee that this i being invoked on the home event loop.
pub unsafe fn dequeue(&mut self, token: uint) -> Option<BlockedTask> {
let inner = &mut *self.inner.get();
match inner.queue.iter().position(|&(_, t)| t == token) {
Some(i) => Some(inner.queue.remove(i).unwrap().val0()),
None => None,
}
}
/// Test whether this access is closed, using a homing missile to prove
/// that it's safe
pub fn is_closed(&self, _missile: &HomingMissile) -> bool {
unsafe { (*self.inner.get()).closed }
}
}
impl<T: Send> Clone for Access<T> {
fn clone(&self) -> Access<T> {
Access { inner: self.inner.clone() }
}
}
impl<'a, T: Send> Guard<'a, T> {
pub fn is_closed(&self) -> bool {
// See above for why this unsafety is ok, it just applies to the read
// instead of the write.
unsafe { (*self.access.inner.get()).closed }
}
}
impl<'a, T: Send> Deref<T> for Guard<'a, T> {
fn deref<'a>(&'a self) -> &'a T {
// A guard represents exclusive access to a piece of data, so it's safe
// to hand out shared and mutable references
unsafe { &(*self.access.inner.get()).data }
}
}
impl<'a, T: Send> DerefMut<T> for Guard<'a, T> {
fn deref_mut<'a>(&'a mut self) -> &'a mut T {
unsafe { &mut (*self.access.inner.get()).data }
}
}
#[unsafe_destructor]
impl<'a, T:Send> Drop for Guard<'a, T> {
fn drop(&mut self) {
// This guard's homing missile is still armed, so we're guaranteed to be
// on the same I/O event loop, so this unsafety should be ok.
assert!(self.missile.is_some());
let inner: &mut Inner<T> = unsafe {
mem::transmute(self.access.inner.get())
};
match inner.queue.remove(0) {
// Here we have found a task that was waiting for access, and we
// current have the "access lock" we need to relinquish access to
// this sleeping task.
//
// To do so, we first drop out homing missile and we then reawaken
// the task. In reawakening the task, it will be immediately
// scheduled on this scheduler. Because we might be woken up on some
// other scheduler, we drop our homing missile before we reawaken
// the task.
Some((task, _)) => {
drop(self.missile.take());
task.reawaken();
}
None => { inner.held = false; }
}
}
}
#[unsafe_destructor]
impl<T> Drop for Inner<T> {
fn drop(&mut self) {
assert!(!self.held);
assert_eq!(self.queue.len(), 0);
}
}

View File

@ -1,143 +0,0 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use libc::c_int;
use libc;
use std::mem;
use std::ptr::{null, null_mut};
use std::rt::task::BlockedTask;
use std::rt::rtio;
use net;
use super::{Loop, UvError, Request, wait_until_woken_after, wakeup};
use uvll;
pub struct Addrinfo {
handle: *const libc::addrinfo,
}
struct Ctx {
slot: Option<BlockedTask>,
status: c_int,
addrinfo: Option<Addrinfo>,
}
pub struct GetAddrInfoRequest;
impl GetAddrInfoRequest {
pub fn run(loop_: &Loop, node: Option<&str>, service: Option<&str>,
hints: Option<rtio::AddrinfoHint>)
-> Result<Vec<rtio::AddrinfoInfo>, UvError>
{
assert!(node.is_some() || service.is_some());
let (_c_node, c_node_ptr) = match node {
Some(n) => {
let c_node = n.to_c_str();
let c_node_ptr = c_node.as_ptr();
(Some(c_node), c_node_ptr)
}
None => (None, null())
};
let (_c_service, c_service_ptr) = match service {
Some(s) => {
let c_service = s.to_c_str();
let c_service_ptr = c_service.as_ptr();
(Some(c_service), c_service_ptr)
}
None => (None, null())
};
let hint = hints.map(|hint| {
libc::addrinfo {
ai_flags: 0,
ai_family: hint.family as c_int,
ai_socktype: 0,
ai_protocol: 0,
ai_addrlen: 0,
ai_canonname: null_mut(),
ai_addr: null_mut(),
ai_next: null_mut(),
}
});
let hint_ptr = hint.as_ref().map_or(null(), |x| {
x as *const libc::addrinfo
});
let mut req = Request::new(uvll::UV_GETADDRINFO);
return match unsafe {
uvll::uv_getaddrinfo(loop_.handle, req.handle,
getaddrinfo_cb, c_node_ptr, c_service_ptr,
hint_ptr)
} {
0 => {
req.defuse(); // uv callback now owns this request
let mut cx = Ctx { slot: None, status: 0, addrinfo: None };
wait_until_woken_after(&mut cx.slot, loop_, || {
req.set_data(&mut cx);
});
match cx.status {
0 => Ok(accum_addrinfo(cx.addrinfo.as_ref().unwrap())),
n => Err(UvError(n))
}
}
n => Err(UvError(n))
};
extern fn getaddrinfo_cb(req: *mut uvll::uv_getaddrinfo_t,
status: c_int,
res: *const libc::addrinfo) {
let req = Request::wrap(req);
assert!(status != uvll::ECANCELED);
let cx: &mut Ctx = unsafe { req.get_data() };
cx.status = status;
cx.addrinfo = Some(Addrinfo { handle: res });
wakeup(&mut cx.slot);
}
}
}
impl Drop for Addrinfo {
fn drop(&mut self) {
unsafe { uvll::uv_freeaddrinfo(self.handle as *mut _) }
}
}
// Traverse the addrinfo linked list, producing a vector of Rust socket addresses
pub fn accum_addrinfo(addr: &Addrinfo) -> Vec<rtio::AddrinfoInfo> {
unsafe {
let mut addr = addr.handle;
let mut addrs = Vec::new();
loop {
let rustaddr = net::sockaddr_to_addr(mem::transmute((*addr).ai_addr),
(*addr).ai_addrlen as uint);
addrs.push(rtio::AddrinfoInfo {
address: rustaddr,
family: (*addr).ai_family as uint,
socktype: 0,
protocol: 0,
flags: 0,
});
if (*addr).ai_next.is_not_null() {
addr = (*addr).ai_next as *const _;
} else {
break;
}
}
addrs
}
}

View File

@ -1,156 +0,0 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use alloc::arc::Arc;
use std::mem;
use std::rt::exclusive::Exclusive;
use std::rt::rtio::{Callback, RemoteCallback};
use uvll;
use super::{Loop, UvHandle};
// The entire point of async is to call into a loop from other threads so it
// does not need to home.
pub struct AsyncWatcher {
handle: *mut uvll::uv_async_t,
// A flag to tell the callback to exit, set from the dtor. This is
// almost never contested - only in rare races with the dtor.
exit_flag: Arc<Exclusive<bool>>,
}
struct Payload {
callback: Box<Callback + Send>,
exit_flag: Arc<Exclusive<bool>>,
}
impl AsyncWatcher {
pub fn new(loop_: &mut Loop, cb: Box<Callback + Send>) -> AsyncWatcher {
let handle = UvHandle::alloc(None::<AsyncWatcher>, uvll::UV_ASYNC);
assert_eq!(unsafe {
uvll::uv_async_init(loop_.handle, handle, async_cb)
}, 0);
let flag = Arc::new(Exclusive::new(false));
let payload = box Payload { callback: cb, exit_flag: flag.clone() };
unsafe {
let payload: *mut u8 = mem::transmute(payload);
uvll::set_data_for_uv_handle(handle, payload);
}
return AsyncWatcher { handle: handle, exit_flag: flag, };
}
}
impl UvHandle<uvll::uv_async_t> for AsyncWatcher {
fn uv_handle(&self) -> *mut uvll::uv_async_t { self.handle }
unsafe fn from_uv_handle<'a>(_: &'a *mut uvll::uv_async_t) -> &'a mut AsyncWatcher {
fail!("async watchers can't be built from their handles");
}
}
extern fn async_cb(handle: *mut uvll::uv_async_t) {
let payload: &mut Payload = unsafe {
mem::transmute(uvll::get_data_for_uv_handle(handle))
};
// The synchronization logic here is subtle. To review,
// the uv async handle type promises that, after it is
// triggered the remote callback is definitely called at
// least once. UvRemoteCallback needs to maintain those
// semantics while also shutting down cleanly from the
// dtor. In our case that means that, when the
// UvRemoteCallback dtor calls `async.send()`, here `f` is
// always called later.
// In the dtor both the exit flag is set and the async
// callback fired under a lock. Here, before calling `f`,
// we take the lock and check the flag. Because we are
// checking the flag before calling `f`, and the flag is
// set under the same lock as the send, then if the flag
// is set then we're guaranteed to call `f` after the
// final send.
// If the check was done after `f()` then there would be a
// period between that call and the check where the dtor
// could be called in the other thread, missing the final
// callback while still destroying the handle.
let should_exit = unsafe { *payload.exit_flag.lock() };
payload.callback.call();
if should_exit {
unsafe { uvll::uv_close(handle, close_cb) }
}
}
extern fn close_cb(handle: *mut uvll::uv_handle_t) {
// drop the payload
let _payload: Box<Payload> = unsafe {
mem::transmute(uvll::get_data_for_uv_handle(handle))
};
// and then free the handle
unsafe { uvll::free_handle(handle) }
}
impl RemoteCallback for AsyncWatcher {
fn fire(&mut self) {
unsafe { uvll::uv_async_send(self.handle) }
}
}
impl Drop for AsyncWatcher {
fn drop(&mut self) {
let mut should_exit = unsafe { self.exit_flag.lock() };
// NB: These two things need to happen atomically. Otherwise
// the event handler could wake up due to a *previous*
// signal and see the exit flag, destroying the handle
// before the final send.
*should_exit = true;
unsafe { uvll::uv_async_send(self.handle) }
}
}
#[cfg(test)]
mod test_remote {
use std::rt::rtio::{Callback, RemoteCallback};
use std::rt::thread::Thread;
use super::AsyncWatcher;
use super::super::local_loop;
// Make sure that we can fire watchers in remote threads and that they
// actually trigger what they say they will.
#[test]
fn smoke_test() {
struct MyCallback(Option<Sender<int>>);
impl Callback for MyCallback {
fn call(&mut self) {
// this can get called more than once, but we only want to send
// once
let MyCallback(ref mut s) = *self;
if s.is_some() {
s.take().unwrap().send(1);
}
}
}
let (tx, rx) = channel();
let cb = box MyCallback(Some(tx));
let watcher = AsyncWatcher::new(&mut local_loop().loop_, cb);
let thread = Thread::start(proc() {
let mut watcher = watcher;
watcher.fire();
});
assert_eq!(rx.recv(), 1);
thread.join();
}
}

View File

@ -1,581 +0,0 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use libc::{c_int, c_char, c_void, ssize_t};
use libc;
use std::c_str::CString;
use std::c_str;
use std::mem;
use std::os;
use std::rt::rtio::{IoResult, IoError};
use std::rt::rtio;
use std::rt::task::BlockedTask;
use homing::{HomingIO, HomeHandle};
use super::{Loop, UvError, uv_error_to_io_error, wait_until_woken_after, wakeup};
use uvio::UvIoFactory;
use uvll;
pub struct FsRequest {
req: *mut uvll::uv_fs_t,
fired: bool,
}
pub struct FileWatcher {
loop_: Loop,
fd: c_int,
close: rtio::CloseBehavior,
home: HomeHandle,
}
impl FsRequest {
pub fn open(io: &mut UvIoFactory, path: &CString, flags: int, mode: int)
-> Result<FileWatcher, UvError>
{
execute(|req, cb| unsafe {
uvll::uv_fs_open(io.uv_loop(),
req, path.as_ptr(), flags as c_int,
mode as c_int, cb)
}).map(|req|
FileWatcher::new(io, req.get_result() as c_int,
rtio::CloseSynchronously)
)
}
pub fn unlink(loop_: &Loop, path: &CString) -> Result<(), UvError> {
execute_nop(|req, cb| unsafe {
uvll::uv_fs_unlink(loop_.handle, req, path.as_ptr(),
cb)
})
}
pub fn lstat(loop_: &Loop, path: &CString)
-> Result<rtio::FileStat, UvError>
{
execute(|req, cb| unsafe {
uvll::uv_fs_lstat(loop_.handle, req, path.as_ptr(),
cb)
}).map(|req| req.mkstat())
}
pub fn stat(loop_: &Loop, path: &CString) -> Result<rtio::FileStat, UvError> {
execute(|req, cb| unsafe {
uvll::uv_fs_stat(loop_.handle, req, path.as_ptr(),
cb)
}).map(|req| req.mkstat())
}
pub fn fstat(loop_: &Loop, fd: c_int) -> Result<rtio::FileStat, UvError> {
execute(|req, cb| unsafe {
uvll::uv_fs_fstat(loop_.handle, req, fd, cb)
}).map(|req| req.mkstat())
}
pub fn write(loop_: &Loop, fd: c_int, buf: &[u8], offset: i64)
-> Result<(), UvError>
{
// In libuv, uv_fs_write is basically just shelling out to a write()
// syscall at some point, with very little fluff around it. This means
// that write() could actually be a short write, so we need to be sure
// to call it continuously if we get a short write back. This method is
// expected to write the full data if it returns success.
let mut written = 0;
while written < buf.len() {
let offset = if offset == -1 {
offset
} else {
offset + written as i64
};
let uvbuf = uvll::uv_buf_t {
base: buf.slice_from(written as uint).as_ptr() as *mut _,
len: (buf.len() - written) as uvll::uv_buf_len_t,
};
match execute(|req, cb| unsafe {
uvll::uv_fs_write(loop_.handle, req, fd, &uvbuf, 1, offset, cb)
}).map(|req| req.get_result()) {
Err(e) => return Err(e),
Ok(n) => { written += n as uint; }
}
}
Ok(())
}
pub fn read(loop_: &Loop, fd: c_int, buf: &mut [u8], offset: i64)
-> Result<int, UvError>
{
execute(|req, cb| unsafe {
let mut uvbuf = uvll::uv_buf_t {
base: buf.as_mut_ptr(),
len: buf.len() as uvll::uv_buf_len_t,
};
uvll::uv_fs_read(loop_.handle, req, fd, &mut uvbuf, 1, offset, cb)
}).map(|req| {
req.get_result() as int
})
}
pub fn mkdir(loop_: &Loop, path: &CString, mode: c_int)
-> Result<(), UvError>
{
execute_nop(|req, cb| unsafe {
uvll::uv_fs_mkdir(loop_.handle, req, path.as_ptr(),
mode, cb)
})
}
pub fn rmdir(loop_: &Loop, path: &CString) -> Result<(), UvError> {
execute_nop(|req, cb| unsafe {
uvll::uv_fs_rmdir(loop_.handle, req, path.as_ptr(),
cb)
})
}
pub fn rename(loop_: &Loop, path: &CString, to: &CString)
-> Result<(), UvError>
{
execute_nop(|req, cb| unsafe {
uvll::uv_fs_rename(loop_.handle,
req,
path.as_ptr(),
to.as_ptr(),
cb)
})
}
pub fn chmod(loop_: &Loop, path: &CString, mode: c_int)
-> Result<(), UvError>
{
execute_nop(|req, cb| unsafe {
uvll::uv_fs_chmod(loop_.handle, req, path.as_ptr(),
mode, cb)
})
}
pub fn readdir(loop_: &Loop, path: &CString, flags: c_int)
-> Result<Vec<CString>, UvError>
{
execute(|req, cb| unsafe {
uvll::uv_fs_readdir(loop_.handle,
req, path.as_ptr(), flags, cb)
}).map(|req| unsafe {
let mut paths = vec!();
let path = CString::new(path.as_ptr(), false);
let parent = Path::new(path);
let _ = c_str::from_c_multistring(req.get_ptr() as *const libc::c_char,
Some(req.get_result() as uint),
|rel| {
let p = rel.as_bytes();
paths.push(parent.join(p.slice_to(rel.len())).to_c_str());
});
paths
})
}
pub fn readlink(loop_: &Loop, path: &CString) -> Result<CString, UvError> {
execute(|req, cb| unsafe {
uvll::uv_fs_readlink(loop_.handle, req,
path.as_ptr(), cb)
}).map(|req| {
// Be sure to clone the cstring so we get an independently owned
// allocation to work with and return.
unsafe {
CString::new(req.get_ptr() as *const libc::c_char, false).clone()
}
})
}
pub fn chown(loop_: &Loop, path: &CString, uid: int, gid: int)
-> Result<(), UvError>
{
execute_nop(|req, cb| unsafe {
uvll::uv_fs_chown(loop_.handle,
req, path.as_ptr(),
uid as uvll::uv_uid_t,
gid as uvll::uv_gid_t,
cb)
})
}
pub fn truncate(loop_: &Loop, file: c_int, offset: i64)
-> Result<(), UvError>
{
execute_nop(|req, cb| unsafe {
uvll::uv_fs_ftruncate(loop_.handle, req, file, offset, cb)
})
}
pub fn link(loop_: &Loop, src: &CString, dst: &CString)
-> Result<(), UvError>
{
execute_nop(|req, cb| unsafe {
uvll::uv_fs_link(loop_.handle, req,
src.as_ptr(),
dst.as_ptr(),
cb)
})
}
pub fn symlink(loop_: &Loop, src: &CString, dst: &CString)
-> Result<(), UvError>
{
execute_nop(|req, cb| unsafe {
uvll::uv_fs_symlink(loop_.handle, req,
src.as_ptr(),
dst.as_ptr(),
0, cb)
})
}
pub fn fsync(loop_: &Loop, fd: c_int) -> Result<(), UvError> {
execute_nop(|req, cb| unsafe {
uvll::uv_fs_fsync(loop_.handle, req, fd, cb)
})
}
pub fn datasync(loop_: &Loop, fd: c_int) -> Result<(), UvError> {
execute_nop(|req, cb| unsafe {
uvll::uv_fs_fdatasync(loop_.handle, req, fd, cb)
})
}
pub fn utime(loop_: &Loop, path: &CString, atime: u64, mtime: u64)
-> Result<(), UvError>
{
// libuv takes seconds
let atime = atime as libc::c_double / 1000.0;
let mtime = mtime as libc::c_double / 1000.0;
execute_nop(|req, cb| unsafe {
uvll::uv_fs_utime(loop_.handle, req, path.as_ptr(),
atime, mtime, cb)
})
}
pub fn get_result(&self) -> ssize_t {
unsafe { uvll::get_result_from_fs_req(self.req) }
}
pub fn get_stat(&self) -> uvll::uv_stat_t {
let mut stat = uvll::uv_stat_t::new();
unsafe { uvll::populate_stat(self.req, &mut stat); }
stat
}
pub fn get_ptr(&self) -> *mut libc::c_void {
unsafe { uvll::get_ptr_from_fs_req(self.req) }
}
pub fn mkstat(&self) -> rtio::FileStat {
let stat = self.get_stat();
fn to_msec(stat: uvll::uv_timespec_t) -> u64 {
// Be sure to cast to u64 first to prevent overflowing if the tv_sec
// field is a 32-bit integer.
(stat.tv_sec as u64) * 1000 + (stat.tv_nsec as u64) / 1000000
}
rtio::FileStat {
size: stat.st_size as u64,
kind: stat.st_mode as u64,
perm: stat.st_mode as u64,
created: to_msec(stat.st_birthtim),
modified: to_msec(stat.st_mtim),
accessed: to_msec(stat.st_atim),
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: stat.st_flags as u64,
gen: stat.st_gen as u64,
}
}
}
impl Drop for FsRequest {
fn drop(&mut self) {
unsafe {
if self.fired {
uvll::uv_fs_req_cleanup(self.req);
}
uvll::free_req(self.req);
}
}
}
fn execute(f: |*mut uvll::uv_fs_t, uvll::uv_fs_cb| -> c_int)
-> Result<FsRequest, UvError>
{
let mut req = FsRequest {
fired: false,
req: unsafe { uvll::malloc_req(uvll::UV_FS) }
};
return match f(req.req, fs_cb) {
0 => {
req.fired = true;
let mut slot = None;
let loop_ = unsafe { uvll::get_loop_from_fs_req(req.req) };
wait_until_woken_after(&mut slot, &Loop::wrap(loop_), || {
unsafe { uvll::set_data_for_req(req.req, &mut slot) }
});
match req.get_result() {
n if n < 0 => Err(UvError(n as i32)),
_ => Ok(req),
}
}
n => Err(UvError(n))
};
extern fn fs_cb(req: *mut uvll::uv_fs_t) {
let slot: &mut Option<BlockedTask> = unsafe {
mem::transmute(uvll::get_data_for_req(req))
};
wakeup(slot);
}
}
fn execute_nop(f: |*mut uvll::uv_fs_t, uvll::uv_fs_cb| -> c_int)
-> Result<(), UvError> {
execute(f).map(|_| {})
}
impl HomingIO for FileWatcher {
fn home<'r>(&'r mut self) -> &'r mut HomeHandle { &mut self.home }
}
impl FileWatcher {
pub fn new(io: &mut UvIoFactory, fd: c_int,
close: rtio::CloseBehavior) -> FileWatcher {
FileWatcher {
loop_: Loop::wrap(io.uv_loop()),
fd: fd,
close: close,
home: io.make_handle(),
}
}
fn base_read(&mut self, buf: &mut [u8], offset: i64) -> IoResult<int> {
let _m = self.fire_homing_missile();
let r = FsRequest::read(&self.loop_, self.fd, buf, offset);
r.map_err(uv_error_to_io_error)
}
fn base_write(&mut self, buf: &[u8], offset: i64) -> IoResult<()> {
let _m = self.fire_homing_missile();
let r = FsRequest::write(&self.loop_, self.fd, buf, offset);
r.map_err(uv_error_to_io_error)
}
fn seek_common(&self, pos: i64, whence: c_int) -> IoResult<u64>{
match unsafe { libc::lseek(self.fd, pos as libc::off_t, whence) } {
-1 => {
Err(IoError {
code: os::errno() as uint,
extra: 0,
detail: None,
})
},
n => Ok(n as u64)
}
}
}
impl Drop for FileWatcher {
fn drop(&mut self) {
let _m = self.fire_homing_missile();
match self.close {
rtio::DontClose => {}
rtio::CloseAsynchronously => {
unsafe {
let req = uvll::malloc_req(uvll::UV_FS);
assert_eq!(uvll::uv_fs_close(self.loop_.handle, req,
self.fd, close_cb), 0);
}
extern fn close_cb(req: *mut uvll::uv_fs_t) {
unsafe {
uvll::uv_fs_req_cleanup(req);
uvll::free_req(req);
}
}
}
rtio::CloseSynchronously => {
let _ = execute_nop(|req, cb| unsafe {
uvll::uv_fs_close(self.loop_.handle, req, self.fd, cb)
});
}
}
}
}
impl rtio::RtioFileStream for FileWatcher {
fn read(&mut self, buf: &mut [u8]) -> IoResult<int> {
self.base_read(buf, -1)
}
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
self.base_write(buf, -1)
}
fn pread(&mut self, buf: &mut [u8], offset: u64) -> IoResult<int> {
self.base_read(buf, offset as i64)
}
fn pwrite(&mut self, buf: &[u8], offset: u64) -> IoResult<()> {
self.base_write(buf, offset as i64)
}
fn seek(&mut self, pos: i64, whence: rtio::SeekStyle) -> IoResult<u64> {
use libc::{SEEK_SET, SEEK_CUR, SEEK_END};
let whence = match whence {
rtio::SeekSet => SEEK_SET,
rtio::SeekCur => SEEK_CUR,
rtio::SeekEnd => SEEK_END
};
self.seek_common(pos, whence)
}
fn tell(&self) -> IoResult<u64> {
use libc::SEEK_CUR;
self.seek_common(0, SEEK_CUR)
}
fn fsync(&mut self) -> IoResult<()> {
let _m = self.fire_homing_missile();
FsRequest::fsync(&self.loop_, self.fd).map_err(uv_error_to_io_error)
}
fn datasync(&mut self) -> IoResult<()> {
let _m = self.fire_homing_missile();
FsRequest::datasync(&self.loop_, self.fd).map_err(uv_error_to_io_error)
}
fn truncate(&mut self, offset: i64) -> IoResult<()> {
let _m = self.fire_homing_missile();
let r = FsRequest::truncate(&self.loop_, self.fd, offset);
r.map_err(uv_error_to_io_error)
}
fn fstat(&mut self) -> IoResult<rtio::FileStat> {
let _m = self.fire_homing_missile();
FsRequest::fstat(&self.loop_, self.fd).map_err(uv_error_to_io_error)
}
}
#[cfg(test)]
mod test {
use libc::c_int;
use libc::{O_CREAT, O_RDWR, O_RDONLY, S_IWUSR, S_IRUSR};
use std::str;
use super::FsRequest;
use super::super::Loop;
use super::super::local_loop;
fn l() -> &'static mut Loop { &mut local_loop().loop_ }
#[test]
fn file_test_full_simple_sync() {
let create_flags = O_RDWR | O_CREAT;
let read_flags = O_RDONLY;
let mode = S_IWUSR | S_IRUSR;
let path_str = "./tmp/file_full_simple_sync.txt";
{
// open/create
let result = FsRequest::open(local_loop(), &path_str.to_c_str(),
create_flags as int, mode as int);
assert!(result.is_ok());
let result = result.unwrap();
let fd = result.fd;
// write
let result = FsRequest::write(l(), fd, "hello".as_bytes(), -1);
assert!(result.is_ok());
}
{
// re-open
let result = FsRequest::open(local_loop(), &path_str.to_c_str(),
read_flags as int, 0);
assert!(result.is_ok());
let result = result.unwrap();
let fd = result.fd;
// read
let mut read_mem = Vec::from_elem(1000, 0u8);
let result = FsRequest::read(l(), fd, read_mem.as_mut_slice(), 0);
assert!(result.is_ok());
let nread = result.unwrap();
assert!(nread > 0);
let read_str = str::from_utf8(read_mem.slice_to(nread as uint)).unwrap();
assert_eq!(read_str, "hello");
}
// unlink
let result = FsRequest::unlink(l(), &path_str.to_c_str());
assert!(result.is_ok());
}
#[test]
fn file_test_stat() {
let path = &"./tmp/file_test_stat_simple".to_c_str();
let create_flags = (O_RDWR | O_CREAT) as int;
let mode = (S_IWUSR | S_IRUSR) as int;
let result = FsRequest::open(local_loop(), path, create_flags, mode);
assert!(result.is_ok());
let file = result.unwrap();
let result = FsRequest::write(l(), file.fd, "hello".as_bytes(), 0);
assert!(result.is_ok());
let result = FsRequest::stat(l(), path);
assert!(result.is_ok());
assert_eq!(result.unwrap().size, 5);
let result = FsRequest::fstat(l(), file.fd);
assert!(result.is_ok());
assert_eq!(result.unwrap().size, 5);
fn free<T>(_: T) {}
free(file);
let result = FsRequest::unlink(l(), path);
assert!(result.is_ok());
}
#[test]
fn file_test_mk_rm_dir() {
let path = &"./tmp/mk_rm_dir".to_c_str();
let mode = S_IWUSR | S_IRUSR;
let result = FsRequest::mkdir(l(), path, mode as c_int);
assert!(result.is_ok());
let result = FsRequest::rmdir(l(), path);
assert!(result.is_ok());
let result = FsRequest::stat(l(), path);
assert!(result.is_err());
}
#[test]
fn file_test_mkdir_chokes_on_double_create() {
let path = &"./tmp/double_create_dir".to_c_str();
let mode = S_IWUSR | S_IRUSR;
let result = FsRequest::stat(l(), path);
assert!(result.is_err(), "{:?}", result);
let result = FsRequest::mkdir(l(), path, mode as c_int);
assert!(result.is_ok(), "{:?}", result);
let result = FsRequest::mkdir(l(), path, mode as c_int);
assert!(result.is_err(), "{:?}", result);
let result = FsRequest::rmdir(l(), path);
assert!(result.is_ok(), "{:?}", result);
}
#[test]
fn file_test_rmdir_chokes_on_nonexistant_path() {
let path = &"./tmp/never_existed_dir".to_c_str();
let result = FsRequest::rmdir(l(), path);
assert!(result.is_err());
}
}

View File

@ -1,214 +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.
//! Homing I/O implementation
//!
//! In libuv, whenever a handle is created on an I/O loop it is illegal to use
//! that handle outside of that I/O loop. We use libuv I/O with our green
//! scheduler, and each green scheduler corresponds to a different I/O loop on a
//! different OS thread. Green tasks are also free to roam among schedulers,
//! which implies that it is possible to create an I/O handle on one event loop
//! and then attempt to use it on another.
//!
//! In order to solve this problem, this module implements the notion of a
//! "homing operation" which will transplant a task from its currently running
//! scheduler back onto the original I/O loop. This is accomplished entirely at
//! the librustuv layer with very little cooperation from the scheduler (which
//! we don't even know exists technically).
//!
//! These homing operations are completed by first realizing that we're on the
//! wrong I/O loop, then descheduling ourselves, sending ourselves to the
//! correct I/O loop, and then waking up the I/O loop in order to process its
//! local queue of tasks which need to run.
//!
//! This enqueueing is done with a concurrent queue from libstd, and the
//! signalling is achieved with an async handle.
#![allow(dead_code)]
use std::mem;
use std::rt::local::Local;
use std::rt::rtio::LocalIo;
use std::rt::task::{Task, BlockedTask};
use ForbidUnwind;
use queue::{Queue, QueuePool};
/// A handle to a remote libuv event loop. This handle will keep the event loop
/// alive while active in order to ensure that a homing operation can always be
/// completed.
///
/// Handles are clone-able in order to derive new handles from existing handles
/// (very useful for when accepting a socket from a server).
pub struct HomeHandle {
queue: Queue,
id: uint,
}
impl HomeHandle {
pub fn new(id: uint, pool: &mut QueuePool) -> HomeHandle {
HomeHandle { queue: pool.queue(), id: id }
}
fn send(&mut self, task: BlockedTask) {
self.queue.push(task);
}
}
impl Clone for HomeHandle {
fn clone(&self) -> HomeHandle {
HomeHandle {
queue: self.queue.clone(),
id: self.id,
}
}
}
pub fn local_id() -> uint {
use std::raw::TraitObject;
let mut io = match LocalIo::borrow() {
Some(io) => io, None => return 0,
};
let io = io.get();
unsafe {
let obj: TraitObject = mem::transmute(io);
return mem::transmute(obj.data);
}
}
#[doc(hidden)]
pub trait HomingIO {
fn home<'r>(&'r mut self) -> &'r mut HomeHandle;
/// This function will move tasks to run on their home I/O scheduler. Note
/// that this function does *not* pin the task to the I/O scheduler, but
/// rather it simply moves it to running on the I/O scheduler.
fn go_to_io_home(&mut self) -> uint {
let _f = ForbidUnwind::new("going home");
let cur_loop_id = local_id();
let destination = self.home().id;
// Try at all costs to avoid the homing operation because it is quite
// expensive. Hence, we only deschedule/send if we're not on the correct
// event loop. If we're already on the home event loop, then we're good
// to go (remember we have no preemption, so we're guaranteed to stay on
// this event loop as long as we avoid the scheduler).
if cur_loop_id != destination {
let cur_task: Box<Task> = Local::take();
cur_task.deschedule(1, |task| {
self.home().send(task);
Ok(())
});
// Once we wake up, assert that we're in the right location
assert_eq!(local_id(), destination);
}
return destination;
}
/// Fires a single homing missile, returning another missile targeted back
/// at the original home of this task. In other words, this function will
/// move the local task to its I/O scheduler and then return an RAII wrapper
/// which will return the task home.
fn fire_homing_missile(&mut self) -> HomingMissile {
HomingMissile { io_home: self.go_to_io_home() }
}
}
/// After a homing operation has been completed, this will return the current
/// task back to its appropriate home (if applicable). The field is used to
/// assert that we are where we think we are.
pub struct HomingMissile {
io_home: uint,
}
impl HomingMissile {
/// Check at runtime that the task has *not* transplanted itself to a
/// different I/O loop while executing.
pub fn check(&self, msg: &'static str) {
assert!(local_id() == self.io_home, "{}", msg);
}
}
impl Drop for HomingMissile {
fn drop(&mut self) {
let _f = ForbidUnwind::new("leaving home");
// It would truly be a sad day if we had moved off the home I/O
// scheduler while we were doing I/O.
self.check("task moved away from the home scheduler");
}
}
#[cfg(test)]
mod test {
use green::sched;
use green::{SchedPool, PoolConfig};
use std::rt::rtio::RtioUdpSocket;
use std::rt::task::TaskOpts;
use net::UdpWatcher;
use super::super::local_loop;
// On one thread, create a udp socket. Then send that socket to another
// thread and destroy the socket on the remote thread. This should make sure
// that homing kicks in for the socket to go back home to the original
// thread, close itself, and then come back to the last thread.
#[test]
fn test_homing_closes_correctly() {
let (tx, rx) = channel();
let mut pool = SchedPool::new(PoolConfig {
threads: 1,
event_loop_factory: ::event_loop,
});
pool.spawn(TaskOpts::new(), proc() {
let listener = UdpWatcher::bind(local_loop(), ::next_test_ip4());
tx.send(listener.unwrap());
});
let task = pool.task(TaskOpts::new(), proc() {
drop(rx.recv());
});
pool.spawn_sched().send(sched::TaskFromFriend(task));
pool.shutdown();
}
#[test]
fn test_homing_read() {
let (tx, rx) = channel();
let mut pool = SchedPool::new(PoolConfig {
threads: 1,
event_loop_factory: ::event_loop,
});
pool.spawn(TaskOpts::new(), proc() {
let addr1 = ::next_test_ip4();
let addr2 = ::next_test_ip4();
let listener = UdpWatcher::bind(local_loop(), addr2);
tx.send((listener.unwrap(), addr1));
let mut listener = UdpWatcher::bind(local_loop(), addr1).unwrap();
listener.send_to([1, 2, 3, 4], addr2).ok().unwrap();
});
let task = pool.task(TaskOpts::new(), proc() {
let (mut watcher, addr) = rx.recv();
let mut buf = [0, ..10];
assert!(watcher.recv_from(buf).ok().unwrap() == (4, addr));
});
pool.spawn_sched().send(sched::TaskFromFriend(task));
pool.shutdown();
}
}

View File

@ -1,206 +0,0 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use libc::c_void;
use std::mem;
use uvll;
use super::{Loop, UvHandle};
use std::rt::rtio::{Callback, PausableIdleCallback};
pub struct IdleWatcher {
handle: *mut uvll::uv_idle_t,
idle_flag: bool,
callback: Box<Callback + Send>,
}
impl IdleWatcher {
pub fn new(loop_: &mut Loop, cb: Box<Callback + Send>) -> Box<IdleWatcher> {
let handle = UvHandle::alloc(None::<IdleWatcher>, uvll::UV_IDLE);
assert_eq!(unsafe {
uvll::uv_idle_init(loop_.handle, handle)
}, 0);
let me = box IdleWatcher {
handle: handle,
idle_flag: false,
callback: cb,
};
return me.install();
}
pub fn onetime(loop_: &mut Loop, f: proc()) {
let handle = UvHandle::alloc(None::<IdleWatcher>, uvll::UV_IDLE);
unsafe {
assert_eq!(uvll::uv_idle_init(loop_.handle, handle), 0);
let data: *mut c_void = mem::transmute(box f);
uvll::set_data_for_uv_handle(handle, data);
assert_eq!(uvll::uv_idle_start(handle, onetime_cb), 0)
}
extern fn onetime_cb(handle: *mut uvll::uv_idle_t) {
unsafe {
let data = uvll::get_data_for_uv_handle(handle);
let f: Box<proc()> = mem::transmute(data);
(*f)();
assert_eq!(uvll::uv_idle_stop(handle), 0);
uvll::uv_close(handle, close_cb);
}
}
extern fn close_cb(handle: *mut uvll::uv_handle_t) {
unsafe { uvll::free_handle(handle) }
}
}
}
impl PausableIdleCallback for IdleWatcher {
fn pause(&mut self) {
if self.idle_flag == true {
assert_eq!(unsafe {uvll::uv_idle_stop(self.handle) }, 0);
self.idle_flag = false;
}
}
fn resume(&mut self) {
if self.idle_flag == false {
assert_eq!(unsafe { uvll::uv_idle_start(self.handle, idle_cb) }, 0)
self.idle_flag = true;
}
}
}
impl UvHandle<uvll::uv_idle_t> for IdleWatcher {
fn uv_handle(&self) -> *mut uvll::uv_idle_t { self.handle }
}
extern fn idle_cb(handle: *mut uvll::uv_idle_t) {
let idle: &mut IdleWatcher = unsafe { UvHandle::from_uv_handle(&handle) };
idle.callback.call();
}
impl Drop for IdleWatcher {
fn drop(&mut self) {
self.pause();
self.close_async_();
}
}
#[cfg(test)]
mod test {
use std::mem;
use std::cell::RefCell;
use std::rc::Rc;
use std::rt::rtio::{Callback, PausableIdleCallback};
use std::rt::task::{BlockedTask, Task};
use std::rt::local::Local;
use super::IdleWatcher;
use super::super::local_loop;
type Chan = Rc<RefCell<(Option<BlockedTask>, uint)>>;
struct MyCallback(Rc<RefCell<(Option<BlockedTask>, uint)>>, uint);
impl Callback for MyCallback {
fn call(&mut self) {
let task = match *self {
MyCallback(ref rc, n) => {
match *rc.borrow_mut().deref_mut() {
(ref mut task, ref mut val) => {
*val = n;
match task.take() {
Some(t) => t,
None => return
}
}
}
}
};
let _ = task.wake().map(|t| t.reawaken());
}
}
fn mk(v: uint) -> (Box<IdleWatcher>, Chan) {
let rc = Rc::new(RefCell::new((None, 0)));
let cb = box MyCallback(rc.clone(), v);
let cb = cb as Box<Callback>;
let cb = unsafe { mem::transmute(cb) };
(IdleWatcher::new(&mut local_loop().loop_, cb), rc)
}
fn sleep(chan: &Chan) -> uint {
let task: Box<Task> = Local::take();
task.deschedule(1, |task| {
match *chan.borrow_mut().deref_mut() {
(ref mut slot, _) => {
assert!(slot.is_none());
*slot = Some(task);
}
}
Ok(())
});
match *chan.borrow() { (_, n) => n }
}
#[test]
fn not_used() {
let (_idle, _chan) = mk(1);
}
#[test]
fn smoke_test() {
let (mut idle, chan) = mk(1);
idle.resume();
assert_eq!(sleep(&chan), 1);
}
#[test] #[should_fail]
fn smoke_fail() {
// By default, the test harness is capturing our stderr output through a
// channel. This means that when we start failing and "print" our error
// message, we could be switched to running on another test. The
// IdleWatcher assumes that we're already running on the same task, so
// it can cause serious problems and internal race conditions.
//
// To fix this bug, we just set our stderr to a null writer which will
// never reschedule us, so we're guaranteed to stay on the same
// task/event loop.
use std::io;
drop(io::stdio::set_stderr(box io::util::NullWriter));
let (mut idle, _chan) = mk(1);
idle.resume();
fail!();
}
#[test]
fn fun_combinations_of_methods() {
let (mut idle, chan) = mk(1);
idle.resume();
assert_eq!(sleep(&chan), 1);
idle.pause();
idle.resume();
idle.resume();
assert_eq!(sleep(&chan), 1);
idle.pause();
idle.pause();
idle.resume();
assert_eq!(sleep(&chan), 1);
}
#[test]
fn pause_pauses() {
let (mut idle1, chan1) = mk(1);
let (mut idle2, chan2) = mk(2);
idle2.resume();
assert_eq!(sleep(&chan2), 2);
idle2.pause();
idle1.resume();
assert_eq!(sleep(&chan1), 1);
}
}

View File

@ -1,536 +0,0 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*!
Bindings to libuv, along with the default implementation of `std::rt::rtio`.
UV types consist of the event loop (Loop), Watchers, Requests and
Callbacks.
Watchers and Requests encapsulate pointers to uv *handles*, which have
subtyping relationships with each other. This subtyping is reflected
in the bindings with explicit or implicit coercions. For example, an
upcast from TcpWatcher to StreamWatcher is done with
`tcp_watcher.as_stream()`. In other cases a callback on a specific
type of watcher will be passed a watcher of a supertype.
Currently all use of Request types (connect/write requests) are
encapsulated in the bindings and don't need to be dealt with by the
caller.
# Safety note
Due to the complex lifecycle of uv handles, as well as compiler bugs,
this module is not memory safe and requires explicit memory management,
via `close` and `delete` methods.
*/
#![crate_name = "rustuv"]
#![experimental]
#![license = "MIT/ASL2"]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
html_root_url = "http://doc.rust-lang.org/master/",
html_playground_url = "http://play.rust-lang.org/")]
#![feature(macro_rules, unsafe_destructor)]
#![deny(unused_result, unused_must_use)]
#![reexport_test_harness_main = "test_main"]
#[cfg(test)] extern crate green;
#[cfg(test)] extern crate debug;
#[cfg(test)] extern crate "rustuv" as realrustuv;
extern crate libc;
extern crate alloc;
use libc::{c_int, c_void};
use std::fmt;
use std::mem;
use std::ptr;
use std::string;
use std::rt::local::Local;
use std::rt::rtio;
use std::rt::rtio::{IoResult, IoError};
use std::rt::task::{BlockedTask, Task};
use std::task;
pub use self::async::AsyncWatcher;
pub use self::file::{FsRequest, FileWatcher};
pub use self::idle::IdleWatcher;
pub use self::net::{TcpWatcher, TcpListener, TcpAcceptor, UdpWatcher};
pub use self::pipe::{PipeWatcher, PipeListener, PipeAcceptor};
pub use self::process::Process;
pub use self::signal::SignalWatcher;
pub use self::timer::TimerWatcher;
pub use self::tty::TtyWatcher;
// Run tests with libgreen instead of libnative.
#[cfg(test)] #[start]
fn start(argc: int, argv: *const *const u8) -> int {
green::start(argc, argv, event_loop, test_main)
}
mod macros;
mod access;
mod timeout;
mod homing;
mod queue;
mod rc;
pub mod uvio;
pub mod uvll;
pub mod file;
pub mod net;
pub mod idle;
pub mod timer;
pub mod async;
pub mod addrinfo;
pub mod process;
pub mod pipe;
pub mod tty;
pub mod signal;
pub mod stream;
/// Creates a new event loop which is powered by libuv
///
/// This function is used in tandem with libgreen's `PoolConfig` type as a value
/// for the `event_loop_factory` field. Using this function as the event loop
/// factory will power programs with libuv and enable green threading.
///
/// # Example
///
/// ```
/// extern crate rustuv;
/// extern crate green;
///
/// #[start]
/// fn start(argc: int, argv: *const *const u8) -> int {
/// green::start(argc, argv, rustuv::event_loop, main)
/// }
///
/// fn main() {
/// // this code is running inside of a green task powered by libuv
/// }
/// ```
pub fn event_loop() -> Box<rtio::EventLoop + Send> {
box uvio::UvEventLoop::new() as Box<rtio::EventLoop + Send>
}
/// A type that wraps a uv handle
pub trait UvHandle<T> {
fn uv_handle(&self) -> *mut T;
fn uv_loop(&self) -> Loop {
Loop::wrap(unsafe { uvll::get_loop_for_uv_handle(self.uv_handle()) })
}
// FIXME(#8888) dummy self
fn alloc(_: Option<Self>, ty: uvll::uv_handle_type) -> *mut T {
unsafe {
let handle = uvll::malloc_handle(ty);
assert!(!handle.is_null());
handle as *mut T
}
}
unsafe fn from_uv_handle<'a>(h: &'a *mut T) -> &'a mut Self {
mem::transmute(uvll::get_data_for_uv_handle(*h))
}
fn install(self: Box<Self>) -> Box<Self> {
unsafe {
let myptr = mem::transmute::<&Box<Self>, &*mut u8>(&self);
uvll::set_data_for_uv_handle(self.uv_handle(), *myptr);
}
self
}
fn close_async_(&mut self) {
// we used malloc to allocate all handles, so we must always have at
// least a callback to free all the handles we allocated.
extern fn close_cb(handle: *mut uvll::uv_handle_t) {
unsafe { uvll::free_handle(handle) }
}
unsafe {
uvll::set_data_for_uv_handle(self.uv_handle(), ptr::null_mut::<()>());
uvll::uv_close(self.uv_handle() as *mut uvll::uv_handle_t, close_cb)
}
}
fn close(&mut self) {
let mut slot = None;
unsafe {
uvll::uv_close(self.uv_handle() as *mut uvll::uv_handle_t, close_cb);
uvll::set_data_for_uv_handle(self.uv_handle(),
ptr::null_mut::<()>());
wait_until_woken_after(&mut slot, &self.uv_loop(), || {
uvll::set_data_for_uv_handle(self.uv_handle(), &mut slot);
})
}
extern fn close_cb(handle: *mut uvll::uv_handle_t) {
unsafe {
let data = uvll::get_data_for_uv_handle(handle);
uvll::free_handle(handle);
if data == ptr::null_mut() { return }
let slot: &mut Option<BlockedTask> = mem::transmute(data);
wakeup(slot);
}
}
}
}
pub struct ForbidSwitch {
msg: &'static str,
io: uint,
}
impl ForbidSwitch {
fn new(s: &'static str) -> ForbidSwitch {
ForbidSwitch {
msg: s,
io: homing::local_id(),
}
}
}
impl Drop for ForbidSwitch {
fn drop(&mut self) {
assert!(self.io == homing::local_id(),
"didn't want a scheduler switch: {}",
self.msg);
}
}
pub struct ForbidUnwind {
msg: &'static str,
failing_before: bool,
}
impl ForbidUnwind {
fn new(s: &'static str) -> ForbidUnwind {
ForbidUnwind {
msg: s, failing_before: task::failing(),
}
}
}
impl Drop for ForbidUnwind {
fn drop(&mut self) {
assert!(self.failing_before == task::failing(),
"didn't want an unwind during: {}", self.msg);
}
}
fn wait_until_woken_after(slot: *mut Option<BlockedTask>,
loop_: &Loop,
f: ||) {
let _f = ForbidUnwind::new("wait_until_woken_after");
unsafe {
assert!((*slot).is_none());
let task: Box<Task> = Local::take();
loop_.modify_blockers(1);
task.deschedule(1, |task| {
*slot = Some(task);
f();
Ok(())
});
loop_.modify_blockers(-1);
}
}
fn wakeup(slot: &mut Option<BlockedTask>) {
assert!(slot.is_some());
let _ = slot.take().unwrap().wake().map(|t| t.reawaken());
}
pub struct Request {
pub handle: *mut uvll::uv_req_t,
defused: bool,
}
impl Request {
pub fn new(ty: uvll::uv_req_type) -> Request {
unsafe {
let handle = uvll::malloc_req(ty);
uvll::set_data_for_req(handle, ptr::null_mut::<()>());
Request::wrap(handle)
}
}
pub fn wrap(handle: *mut uvll::uv_req_t) -> Request {
Request { handle: handle, defused: false }
}
pub fn set_data<T>(&self, t: *mut T) {
unsafe { uvll::set_data_for_req(self.handle, t) }
}
pub unsafe fn get_data<T>(&self) -> &'static mut T {
let data = uvll::get_data_for_req(self.handle);
assert!(data != ptr::null_mut());
mem::transmute(data)
}
// This function should be used when the request handle has been given to an
// underlying uv function, and the uv function has succeeded. This means
// that uv will at some point invoke the callback, and in the meantime we
// can't deallocate the handle because libuv could be using it.
//
// This is still a problem in blocking situations due to linked failure. In
// the connection callback the handle should be re-wrapped with the `wrap`
// function to ensure its destruction.
pub fn defuse(&mut self) {
self.defused = true;
}
}
impl Drop for Request {
fn drop(&mut self) {
if !self.defused {
unsafe { uvll::free_req(self.handle) }
}
}
}
/// FIXME: Loop(*handle) is buggy with destructors. Normal structs
/// with dtors may not be destructured, but tuple structs can,
/// but the results are not correct.
pub struct Loop {
handle: *mut uvll::uv_loop_t
}
impl Loop {
pub fn new() -> Loop {
let handle = unsafe { uvll::loop_new() };
assert!(handle.is_not_null());
unsafe { uvll::set_data_for_uv_loop(handle, 0 as *mut c_void) }
Loop::wrap(handle)
}
pub fn wrap(handle: *mut uvll::uv_loop_t) -> Loop { Loop { handle: handle } }
pub fn run(&mut self) {
assert_eq!(unsafe { uvll::uv_run(self.handle, uvll::RUN_DEFAULT) }, 0);
}
pub fn close(&mut self) {
unsafe { uvll::uv_loop_delete(self.handle) };
}
// The 'data' field of the uv_loop_t is used to count the number of tasks
// that are currently blocked waiting for I/O to complete.
fn modify_blockers(&self, amt: uint) {
unsafe {
let cur = uvll::get_data_for_uv_loop(self.handle) as uint;
uvll::set_data_for_uv_loop(self.handle, (cur + amt) as *mut c_void)
}
}
fn get_blockers(&self) -> uint {
unsafe { uvll::get_data_for_uv_loop(self.handle) as uint }
}
}
// FIXME: Need to define the error constants like EOF so they can be
// compared to the UvError type
pub struct UvError(c_int);
impl UvError {
pub fn name(&self) -> String {
unsafe {
let inner = match self { &UvError(a) => a };
let name_str = uvll::uv_err_name(inner);
assert!(name_str.is_not_null());
string::raw::from_buf(name_str as *const u8)
}
}
pub fn desc(&self) -> String {
unsafe {
let inner = match self { &UvError(a) => a };
let desc_str = uvll::uv_strerror(inner);
assert!(desc_str.is_not_null());
string::raw::from_buf(desc_str as *const u8)
}
}
pub fn is_eof(&self) -> bool {
let UvError(handle) = *self;
handle == uvll::EOF
}
}
impl fmt::Show for UvError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}: {}", self.name(), self.desc())
}
}
#[test]
fn error_smoke_test() {
let err: UvError = UvError(uvll::EOF);
assert_eq!(err.to_string(), "EOF: end of file".to_string());
}
#[cfg(unix)]
pub fn uv_error_to_io_error(uverr: UvError) -> IoError {
let UvError(errcode) = uverr;
IoError {
code: if errcode == uvll::EOF {libc::EOF as uint} else {-errcode as uint},
extra: 0,
detail: Some(uverr.desc()),
}
}
#[cfg(windows)]
pub fn uv_error_to_io_error(uverr: UvError) -> IoError {
let UvError(errcode) = uverr;
IoError {
code: match errcode {
uvll::EOF => libc::EOF,
uvll::EACCES => libc::ERROR_ACCESS_DENIED,
uvll::ECONNREFUSED => libc::WSAECONNREFUSED,
uvll::ECONNRESET => libc::WSAECONNRESET,
uvll::ENOTCONN => libc::WSAENOTCONN,
uvll::ENOENT => libc::ERROR_FILE_NOT_FOUND,
uvll::EPIPE => libc::ERROR_NO_DATA,
uvll::ECONNABORTED => libc::WSAECONNABORTED,
uvll::EADDRNOTAVAIL => libc::WSAEADDRNOTAVAIL,
uvll::ECANCELED => libc::ERROR_OPERATION_ABORTED,
uvll::EADDRINUSE => libc::WSAEADDRINUSE,
uvll::EPERM => libc::ERROR_ACCESS_DENIED,
err => {
uvdebug!("uverr.code {}", err as int);
// FIXME: Need to map remaining uv error types
-1
}
} as uint,
extra: 0,
detail: Some(uverr.desc()),
}
}
/// Given a uv error code, convert a callback status to a UvError
pub fn status_to_maybe_uv_error(status: c_int) -> Option<UvError> {
if status >= 0 {
None
} else {
Some(UvError(status))
}
}
pub fn status_to_io_result(status: c_int) -> IoResult<()> {
if status >= 0 {Ok(())} else {Err(uv_error_to_io_error(UvError(status)))}
}
/// The uv buffer type
pub type Buf = uvll::uv_buf_t;
pub fn empty_buf() -> Buf {
uvll::uv_buf_t {
base: ptr::null_mut(),
len: 0,
}
}
/// Borrow a slice to a Buf
pub fn slice_to_uv_buf(v: &[u8]) -> Buf {
let data = v.as_ptr();
uvll::uv_buf_t { base: data as *mut u8, len: v.len() as uvll::uv_buf_len_t }
}
// This function is full of lies!
#[cfg(test)]
fn local_loop() -> &'static mut uvio::UvIoFactory {
use std::raw::TraitObject;
unsafe {
mem::transmute({
let mut task = Local::borrow(None::<Task>);
let mut io = task.local_io().unwrap();
let obj: TraitObject =
mem::transmute(io.get());
obj.data
})
}
}
#[cfg(test)]
fn next_test_ip4() -> std::rt::rtio::SocketAddr {
use std::io;
use std::rt::rtio;
let io::net::ip::SocketAddr { ip, port } = io::test::next_test_ip4();
let ip = match ip {
io::net::ip::Ipv4Addr(a, b, c, d) => rtio::Ipv4Addr(a, b, c, d),
_ => unreachable!(),
};
rtio::SocketAddr { ip: ip, port: port }
}
#[cfg(test)]
fn next_test_ip6() -> std::rt::rtio::SocketAddr {
use std::io;
use std::rt::rtio;
let io::net::ip::SocketAddr { ip, port } = io::test::next_test_ip6();
let ip = match ip {
io::net::ip::Ipv6Addr(a, b, c, d, e, f, g, h) =>
rtio::Ipv6Addr(a, b, c, d, e, f, g, h),
_ => unreachable!(),
};
rtio::SocketAddr { ip: ip, port: port }
}
#[cfg(test)]
mod test {
use std::mem::transmute;
use std::rt::thread::Thread;
use super::{slice_to_uv_buf, Loop};
#[test]
fn test_slice_to_uv_buf() {
let slice = [0, .. 20];
let buf = slice_to_uv_buf(slice);
assert_eq!(buf.len, 20);
unsafe {
let base = transmute::<*mut u8, *mut u8>(buf.base);
(*base) = 1;
(*base.offset(1)) = 2;
}
assert!(slice[0] == 1);
assert!(slice[1] == 2);
}
#[test]
fn loop_smoke_test() {
Thread::start(proc() {
let mut loop_ = Loop::new();
loop_.run();
loop_.close();
}).join();
}
}

View File

@ -1,34 +0,0 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![macro_escape]
use std::fmt;
macro_rules! uverrln (
($($arg:tt)*) => ( {
format_args!(::macros::dumb_println, $($arg)*)
} )
)
// Some basic logging. Enabled by passing `--cfg uvdebug` to the libstd build.
macro_rules! uvdebug (
($($arg:tt)*) => ( {
if cfg!(uvdebug) {
uverrln!($($arg)*)
}
})
)
pub fn dumb_println(args: &fmt::Arguments) {
use std::rt;
let mut w = rt::Stderr;
let _ = writeln!(&mut w, "{}", args);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,436 +0,0 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use libc;
use std::c_str::CString;
use std::mem;
use std::rt::rtio;
use std::rt::rtio::IoResult;
use std::rt::task::BlockedTask;
use homing::{HomingIO, HomeHandle};
use net;
use rc::Refcount;
use stream::StreamWatcher;
use super::{Loop, UvError, UvHandle, uv_error_to_io_error};
use timeout::{AcceptTimeout, ConnectCtx, AccessTimeout};
use uvio::UvIoFactory;
use uvll;
pub struct PipeWatcher {
stream: StreamWatcher,
home: HomeHandle,
defused: bool,
refcount: Refcount,
// see comments in TcpWatcher for why these exist
write_access: AccessTimeout<()>,
read_access: AccessTimeout<()>,
}
pub struct PipeListener {
home: HomeHandle,
pipe: *mut uvll::uv_pipe_t,
}
pub struct PipeAcceptor {
home: HomeHandle,
handle: *mut uvll::uv_pipe_t,
access: AcceptTimeout<Box<rtio::RtioPipe + Send>>,
refcount: Refcount,
}
// PipeWatcher implementation and traits
impl PipeWatcher {
// Creates an uninitialized pipe watcher. The underlying uv pipe is ready to
// get bound to some other source (this is normally a helper method paired
// with another call).
pub fn new(io: &mut UvIoFactory, ipc: bool) -> PipeWatcher {
let home = io.make_handle();
PipeWatcher::new_home(&io.loop_, home, ipc)
}
pub fn new_home(loop_: &Loop, home: HomeHandle, ipc: bool) -> PipeWatcher {
let handle = unsafe {
let handle = uvll::malloc_handle(uvll::UV_NAMED_PIPE);
assert!(!handle.is_null());
let ipc = ipc as libc::c_int;
assert_eq!(uvll::uv_pipe_init(loop_.handle, handle, ipc), 0);
handle
};
PipeWatcher {
stream: StreamWatcher::new(handle, true),
home: home,
defused: false,
refcount: Refcount::new(),
read_access: AccessTimeout::new(()),
write_access: AccessTimeout::new(()),
}
}
pub fn open(io: &mut UvIoFactory, file: libc::c_int)
-> Result<PipeWatcher, UvError>
{
let pipe = PipeWatcher::new(io, false);
match unsafe { uvll::uv_pipe_open(pipe.handle(), file) } {
0 => Ok(pipe),
n => Err(UvError(n))
}
}
pub fn connect(io: &mut UvIoFactory, name: &CString, timeout: Option<u64>)
-> Result<PipeWatcher, UvError>
{
let pipe = PipeWatcher::new(io, false);
let cx = ConnectCtx { status: -1, task: None, timer: None };
cx.connect(pipe, timeout, io, |req, pipe, cb| {
unsafe {
uvll::uv_pipe_connect(req.handle, pipe.handle(),
name.as_ptr(), cb)
}
0
})
}
pub fn handle(&self) -> *mut uvll::uv_pipe_t { self.stream.handle }
// Unwraps the underlying uv pipe. This cancels destruction of the pipe and
// allows the pipe to get moved elsewhere
fn unwrap(mut self) -> *mut uvll::uv_pipe_t {
self.defused = true;
return self.stream.handle;
}
}
impl rtio::RtioPipe for PipeWatcher {
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
let m = self.fire_homing_missile();
let guard = try!(self.read_access.grant(m));
// see comments in close_read about this check
if guard.access.is_closed() {
return Err(uv_error_to_io_error(UvError(uvll::EOF)))
}
self.stream.read(buf).map_err(uv_error_to_io_error)
}
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
let m = self.fire_homing_missile();
let guard = try!(self.write_access.grant(m));
self.stream.write(buf, guard.can_timeout).map_err(uv_error_to_io_error)
}
fn clone(&self) -> Box<rtio::RtioPipe + Send> {
box PipeWatcher {
stream: StreamWatcher::new(self.stream.handle, false),
defused: false,
home: self.home.clone(),
refcount: self.refcount.clone(),
read_access: self.read_access.clone(),
write_access: self.write_access.clone(),
} as Box<rtio::RtioPipe + Send>
}
fn close_read(&mut self) -> IoResult<()> {
// The current uv_shutdown method only shuts the writing half of the
// connection, and no method is provided to shut down the reading half
// of the connection. With a lack of method, we emulate shutting down
// the reading half of the connection by manually returning early from
// all future calls to `read`.
//
// Note that we must be careful to ensure that *all* cloned handles see
// the closing of the read half, so we stored the "is closed" bit in the
// Access struct, not in our own personal watcher. Additionally, the
// homing missile is used as a locking mechanism to ensure there is no
// contention over this bit.
//
// To shutdown the read half, we must first flag the access as being
// closed, and then afterwards we cease any pending read. Note that this
// ordering is crucial because we could in theory be rescheduled during
// the uv_read_stop which means that another read invocation could leak
// in before we set the flag.
let task = {
let m = self.fire_homing_missile();
self.read_access.access.close(&m);
self.stream.cancel_read(uvll::EOF as libc::ssize_t)
};
let _ = task.map(|t| t.reawaken());
Ok(())
}
fn close_write(&mut self) -> IoResult<()> {
let _m = self.fire_homing_missile();
net::shutdown(self.stream.handle, &self.uv_loop())
}
fn set_timeout(&mut self, timeout: Option<u64>) {
self.set_read_timeout(timeout);
self.set_write_timeout(timeout);
}
fn set_read_timeout(&mut self, ms: Option<u64>) {
let _m = self.fire_homing_missile();
let loop_ = self.uv_loop();
self.read_access.set_timeout(ms, &self.home, &loop_, cancel_read,
&self.stream as *const _ as uint);
fn cancel_read(stream: uint) -> Option<BlockedTask> {
let stream: &mut StreamWatcher = unsafe { mem::transmute(stream) };
stream.cancel_read(uvll::ECANCELED as libc::ssize_t)
}
}
fn set_write_timeout(&mut self, ms: Option<u64>) {
let _m = self.fire_homing_missile();
let loop_ = self.uv_loop();
self.write_access.set_timeout(ms, &self.home, &loop_, cancel_write,
&self.stream as *const _ as uint);
fn cancel_write(stream: uint) -> Option<BlockedTask> {
let stream: &mut StreamWatcher = unsafe { mem::transmute(stream) };
stream.cancel_write()
}
}
}
impl HomingIO for PipeWatcher {
fn home<'a>(&'a mut self) -> &'a mut HomeHandle { &mut self.home }
}
impl UvHandle<uvll::uv_pipe_t> for PipeWatcher {
fn uv_handle(&self) -> *mut uvll::uv_pipe_t { self.stream.handle }
}
impl Drop for PipeWatcher {
fn drop(&mut self) {
let _m = self.fire_homing_missile();
if !self.defused && self.refcount.decrement() {
self.close();
}
}
}
// PipeListener implementation and traits
impl PipeListener {
pub fn bind(io: &mut UvIoFactory, name: &CString)
-> Result<Box<PipeListener>, UvError>
{
let pipe = PipeWatcher::new(io, false);
match unsafe {
uvll::uv_pipe_bind(pipe.handle(), name.as_ptr())
} {
0 => {
// If successful, unwrap the PipeWatcher because we control how
// we close the pipe differently. We can't rely on
// StreamWatcher's default close method.
let p = box PipeListener {
home: io.make_handle(),
pipe: pipe.unwrap(),
};
Ok(p.install())
}
n => Err(UvError(n))
}
}
}
impl rtio::RtioUnixListener for PipeListener {
fn listen(mut self: Box<PipeListener>)
-> IoResult<Box<rtio::RtioUnixAcceptor + Send>> {
let _m = self.fire_homing_missile();
// create the acceptor object from ourselves
let acceptor = (box PipeAcceptor {
handle: self.pipe,
home: self.home.clone(),
access: AcceptTimeout::new(),
refcount: Refcount::new(),
}).install();
self.pipe = 0 as *mut _;
// FIXME: the 128 backlog should be configurable
match unsafe { uvll::uv_listen(acceptor.handle, 128, listen_cb) } {
0 => Ok(acceptor as Box<rtio::RtioUnixAcceptor + Send>),
n => Err(uv_error_to_io_error(UvError(n))),
}
}
}
impl HomingIO for PipeListener {
fn home<'r>(&'r mut self) -> &'r mut HomeHandle { &mut self.home }
}
impl UvHandle<uvll::uv_pipe_t> for PipeListener {
fn uv_handle(&self) -> *mut uvll::uv_pipe_t { self.pipe }
}
extern fn listen_cb(server: *mut uvll::uv_stream_t, status: libc::c_int) {
assert!(status != uvll::ECANCELED);
let pipe: &mut PipeAcceptor = unsafe { UvHandle::from_uv_handle(&server) };
let msg = match status {
0 => {
let loop_ = Loop::wrap(unsafe {
uvll::get_loop_for_uv_handle(server)
});
let client = PipeWatcher::new_home(&loop_, pipe.home().clone(), false);
assert_eq!(unsafe { uvll::uv_accept(server, client.handle()) }, 0);
Ok(box client as Box<rtio::RtioPipe + Send>)
}
n => Err(uv_error_to_io_error(UvError(n)))
};
// If we're running then we have exclusive access, so the unsafe_get() is ok
unsafe { pipe.access.push(msg); }
}
impl Drop for PipeListener {
fn drop(&mut self) {
if self.pipe.is_null() { return }
let _m = self.fire_homing_missile();
self.close();
}
}
// PipeAcceptor implementation and traits
impl rtio::RtioUnixAcceptor for PipeAcceptor {
fn accept(&mut self) -> IoResult<Box<rtio::RtioPipe + Send>> {
let m = self.fire_homing_missile();
let loop_ = self.uv_loop();
self.access.accept(m, &loop_)
}
fn set_timeout(&mut self, ms: Option<u64>) {
let _m = self.fire_homing_missile();
let loop_ = self.uv_loop();
self.access.set_timeout(ms, &loop_, &self.home);
}
fn clone(&self) -> Box<rtio::RtioUnixAcceptor + Send> {
box PipeAcceptor {
refcount: self.refcount.clone(),
home: self.home.clone(),
handle: self.handle,
access: self.access.clone(),
} as Box<rtio::RtioUnixAcceptor + Send>
}
fn close_accept(&mut self) -> IoResult<()> {
let m = self.fire_homing_missile();
self.access.close(m);
Ok(())
}
}
impl HomingIO for PipeAcceptor {
fn home<'r>(&'r mut self) -> &'r mut HomeHandle { &mut self.home }
}
impl UvHandle<uvll::uv_pipe_t> for PipeAcceptor {
fn uv_handle(&self) -> *mut uvll::uv_pipe_t { self.handle }
}
impl Drop for PipeAcceptor {
fn drop(&mut self) {
let _m = self.fire_homing_missile();
if self.refcount.decrement() {
self.close();
}
}
}
#[cfg(test)]
mod tests {
use std::rt::rtio::{RtioUnixListener, RtioUnixAcceptor, RtioPipe};
use std::io::test::next_test_unix;
use super::{PipeWatcher, PipeListener};
use super::super::local_loop;
#[test]
fn connect_err() {
match PipeWatcher::connect(local_loop(), &"path/to/nowhere".to_c_str(),
None) {
Ok(..) => fail!(),
Err(..) => {}
}
}
#[test]
fn bind_err() {
match PipeListener::bind(local_loop(), &"path/to/nowhere".to_c_str()) {
Ok(..) => fail!(),
Err(e) => assert_eq!(e.name(), "EACCES".to_string()),
}
}
#[test]
fn bind() {
let p = next_test_unix().to_c_str();
match PipeListener::bind(local_loop(), &p) {
Ok(..) => {}
Err(..) => fail!(),
}
}
#[test] #[should_fail]
fn bind_fail() {
let p = next_test_unix().to_c_str();
let _w = PipeListener::bind(local_loop(), &p).unwrap();
fail!();
}
#[test]
fn connect() {
let path = next_test_unix();
let path2 = path.clone();
let (tx, rx) = channel();
spawn(proc() {
let p = PipeListener::bind(local_loop(), &path2.to_c_str()).unwrap();
let mut p = p.listen().ok().unwrap();
tx.send(());
let mut client = p.accept().ok().unwrap();
let mut buf = [0];
assert!(client.read(buf).ok().unwrap() == 1);
assert_eq!(buf[0], 1);
assert!(client.write([2]).is_ok());
});
rx.recv();
let mut c = PipeWatcher::connect(local_loop(), &path.to_c_str(), None).unwrap();
assert!(c.write([1]).is_ok());
let mut buf = [0];
assert!(c.read(buf).ok().unwrap() == 1);
assert_eq!(buf[0], 2);
}
#[test] #[should_fail]
fn connect_fail() {
let path = next_test_unix();
let path2 = path.clone();
let (tx, rx) = channel();
spawn(proc() {
let p = PipeListener::bind(local_loop(), &path2.to_c_str()).unwrap();
let mut p = p.listen().ok().unwrap();
tx.send(());
drop(p.accept().ok().unwrap());
});
rx.recv();
let _c = PipeWatcher::connect(local_loop(), &path.to_c_str(), None).unwrap();
fail!()
}
}

View File

@ -1,324 +0,0 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use libc::c_int;
use libc;
use std::ptr;
use std::c_str::CString;
use std::rt::rtio;
use std::rt::rtio::IoResult;
use std::rt::task::BlockedTask;
use homing::{HomingIO, HomeHandle};
use pipe::PipeWatcher;
use super::{UvHandle, UvError, uv_error_to_io_error,
wait_until_woken_after, wakeup, Loop};
use timer::TimerWatcher;
use uvio::UvIoFactory;
use uvll;
pub struct Process {
handle: *mut uvll::uv_process_t,
home: HomeHandle,
/// Task to wake up (may be null) for when the process exits
to_wake: Option<BlockedTask>,
/// Collected from the exit_cb
exit_status: Option<rtio::ProcessExit>,
/// Lazily initialized timeout timer
timer: Option<Box<TimerWatcher>>,
timeout_state: TimeoutState,
}
enum TimeoutState {
NoTimeout,
TimeoutPending,
TimeoutElapsed,
}
impl Process {
/// Spawn a new process inside the specified event loop.
///
/// Returns either the corresponding process object or an error which
/// occurred.
pub fn spawn(io_loop: &mut UvIoFactory, cfg: rtio::ProcessConfig)
-> Result<(Box<Process>, Vec<Option<PipeWatcher>>), UvError> {
let mut io = vec![cfg.stdin, cfg.stdout, cfg.stderr];
for slot in cfg.extra_io.iter() {
io.push(*slot);
}
let mut stdio = Vec::<uvll::uv_stdio_container_t>::with_capacity(io.len());
let mut ret_io = Vec::with_capacity(io.len());
unsafe {
stdio.set_len(io.len());
for (slot, other) in stdio.iter_mut().zip(io.iter()) {
let io = set_stdio(slot as *mut uvll::uv_stdio_container_t, other,
io_loop);
ret_io.push(io);
}
}
let ret = with_argv(cfg.program, cfg.args, |argv| {
with_env(cfg.env, |envp| {
let mut flags = 0;
if cfg.uid.is_some() {
flags |= uvll::PROCESS_SETUID;
}
if cfg.gid.is_some() {
flags |= uvll::PROCESS_SETGID;
}
if cfg.detach {
flags |= uvll::PROCESS_DETACHED;
}
let mut options = uvll::uv_process_options_t {
exit_cb: on_exit,
file: unsafe { *argv },
args: argv,
env: envp,
cwd: match cfg.cwd {
Some(cwd) => cwd.as_ptr(),
None => ptr::null(),
},
flags: flags as libc::c_uint,
stdio_count: stdio.len() as libc::c_int,
stdio: stdio.as_mut_ptr(),
uid: cfg.uid.unwrap_or(0) as uvll::uv_uid_t,
gid: cfg.gid.unwrap_or(0) as uvll::uv_gid_t,
};
let handle = UvHandle::alloc(None::<Process>, uvll::UV_PROCESS);
let process = box Process {
handle: handle,
home: io_loop.make_handle(),
to_wake: None,
exit_status: None,
timer: None,
timeout_state: NoTimeout,
};
match unsafe {
uvll::uv_spawn(io_loop.uv_loop(), handle, &mut options)
} {
0 => Ok(process.install()),
err => Err(UvError(err)),
}
})
});
match ret {
Ok(p) => Ok((p, ret_io)),
Err(e) => Err(e),
}
}
pub fn kill(pid: libc::pid_t, signum: int) -> Result<(), UvError> {
match unsafe {
uvll::uv_kill(pid as libc::c_int, signum as libc::c_int)
} {
0 => Ok(()),
n => Err(UvError(n))
}
}
}
extern fn on_exit(handle: *mut uvll::uv_process_t,
exit_status: i64,
term_signal: libc::c_int) {
let p: &mut Process = unsafe { UvHandle::from_uv_handle(&handle) };
assert!(p.exit_status.is_none());
p.exit_status = Some(match term_signal {
0 => rtio::ExitStatus(exit_status as int),
n => rtio::ExitSignal(n as int),
});
if p.to_wake.is_none() { return }
wakeup(&mut p.to_wake);
}
unsafe fn set_stdio(dst: *mut uvll::uv_stdio_container_t,
io: &rtio::StdioContainer,
io_loop: &mut UvIoFactory) -> Option<PipeWatcher> {
match *io {
rtio::Ignored => {
uvll::set_stdio_container_flags(dst, uvll::STDIO_IGNORE);
None
}
rtio::InheritFd(fd) => {
uvll::set_stdio_container_flags(dst, uvll::STDIO_INHERIT_FD);
uvll::set_stdio_container_fd(dst, fd);
None
}
rtio::CreatePipe(readable, writable) => {
let mut flags = uvll::STDIO_CREATE_PIPE as libc::c_int;
if readable {
flags |= uvll::STDIO_READABLE_PIPE as libc::c_int;
}
if writable {
flags |= uvll::STDIO_WRITABLE_PIPE as libc::c_int;
}
let pipe = PipeWatcher::new(io_loop, false);
uvll::set_stdio_container_flags(dst, flags);
uvll::set_stdio_container_stream(dst, pipe.handle());
Some(pipe)
}
}
}
/// Converts the program and arguments to the argv array expected by libuv.
fn with_argv<T>(prog: &CString, args: &[CString],
cb: |*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())
}
/// Converts the environment to the env array expected by libuv
fn with_env<T>(env: Option<&[(&CString, &CString)]>,
cb: |*const *const libc::c_char| -> T) -> T {
// 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().as_bytes_no_nul());
kv.push('=' as u8);
kv.push_all(pair.ref1().as_bytes()); // includes terminal \0
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())
}
_ => cb(ptr::null())
}
}
impl HomingIO for Process {
fn home<'r>(&'r mut self) -> &'r mut HomeHandle { &mut self.home }
}
impl UvHandle<uvll::uv_process_t> for Process {
fn uv_handle(&self) -> *mut uvll::uv_process_t { self.handle }
}
impl rtio::RtioProcess for Process {
fn id(&self) -> libc::pid_t {
unsafe { uvll::process_pid(self.handle) as libc::pid_t }
}
fn kill(&mut self, signal: int) -> IoResult<()> {
let _m = self.fire_homing_missile();
match unsafe {
uvll::uv_process_kill(self.handle, signal as libc::c_int)
} {
0 => Ok(()),
err => Err(uv_error_to_io_error(UvError(err)))
}
}
fn wait(&mut self) -> IoResult<rtio::ProcessExit> {
// Make sure (on the home scheduler) that we have an exit status listed
let _m = self.fire_homing_missile();
match self.exit_status {
Some(status) => return Ok(status),
None => {}
}
// If there's no exit code previously listed, then the process's exit
// callback has yet to be invoked. We just need to deschedule ourselves
// and wait to be reawoken.
match self.timeout_state {
NoTimeout | TimeoutPending => {
wait_until_woken_after(&mut self.to_wake, &self.uv_loop(), || {});
}
TimeoutElapsed => {}
}
// If there's still no exit status listed, then we timed out, and we
// need to return.
match self.exit_status {
Some(status) => Ok(status),
None => Err(uv_error_to_io_error(UvError(uvll::ECANCELED)))
}
}
fn set_timeout(&mut self, timeout: Option<u64>) {
let _m = self.fire_homing_missile();
self.timeout_state = NoTimeout;
let ms = match timeout {
Some(ms) => ms,
None => {
match self.timer {
Some(ref mut timer) => timer.stop(),
None => {}
}
return
}
};
if self.timer.is_none() {
let loop_ = Loop::wrap(unsafe {
uvll::get_loop_for_uv_handle(self.uv_handle())
});
let mut timer = box TimerWatcher::new_home(&loop_, self.home().clone());
unsafe {
timer.set_data(self as *mut _);
}
self.timer = Some(timer);
}
let timer = self.timer.as_mut().unwrap();
timer.stop();
timer.start(timer_cb, ms, 0);
self.timeout_state = TimeoutPending;
extern fn timer_cb(timer: *mut uvll::uv_timer_t) {
let p: &mut Process = unsafe {
&mut *(uvll::get_data_for_uv_handle(timer) as *mut Process)
};
p.timeout_state = TimeoutElapsed;
match p.to_wake.take() {
Some(task) => { let _t = task.wake().map(|t| t.reawaken()); }
None => {}
}
}
}
}
impl Drop for Process {
fn drop(&mut self) {
let _m = self.fire_homing_missile();
assert!(self.to_wake.is_none());
self.close();
}
}

View File

@ -1,185 +0,0 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! A concurrent queue used to signal remote event loops
//!
//! This queue implementation is used to send tasks among event loops. This is
//! backed by a multi-producer/single-consumer queue from libstd and uv_async_t
//! handles (to wake up a remote event loop).
//!
//! The uv_async_t is stored next to the event loop, so in order to not keep the
//! event loop alive we use uv_ref and uv_unref in order to control when the
//! async handle is active or not.
#![allow(dead_code)]
use alloc::arc::Arc;
use libc::c_void;
use std::mem;
use std::rt::mutex::NativeMutex;
use std::rt::task::BlockedTask;
use std::sync::mpsc_queue as mpsc;
use async::AsyncWatcher;
use super::{Loop, UvHandle};
use uvll;
enum Message {
Task(BlockedTask),
Increment,
Decrement,
}
struct State {
handle: *mut uvll::uv_async_t,
lock: NativeMutex, // see comments in async_cb for why this is needed
queue: mpsc::Queue<Message>,
}
/// This structure is intended to be stored next to the event loop, and it is
/// used to create new `Queue` structures.
pub struct QueuePool {
queue: Arc<State>,
refcnt: uint,
}
/// This type is used to send messages back to the original event loop.
pub struct Queue {
queue: Arc<State>,
}
extern fn async_cb(handle: *mut uvll::uv_async_t) {
let pool: &mut QueuePool = unsafe {
mem::transmute(uvll::get_data_for_uv_handle(handle))
};
let state: &State = &*pool.queue;
// Remember that there is no guarantee about how many times an async
// callback is called with relation to the number of sends, so process the
// entire queue in a loop.
loop {
match state.queue.pop() {
mpsc::Data(Task(task)) => {
let _ = task.wake().map(|t| t.reawaken());
}
mpsc::Data(Increment) => unsafe {
if pool.refcnt == 0 {
uvll::uv_ref(state.handle);
}
pool.refcnt += 1;
},
mpsc::Data(Decrement) => unsafe {
pool.refcnt -= 1;
if pool.refcnt == 0 {
uvll::uv_unref(state.handle);
}
},
mpsc::Empty | mpsc::Inconsistent => break
};
}
// If the refcount is now zero after processing the queue, then there is no
// longer a reference on the async handle and it is possible that this event
// loop can exit. What we're not guaranteed, however, is that a producer in
// the middle of dropping itself is yet done with the handle. It could be
// possible that we saw their Decrement message but they have yet to signal
// on the async handle. If we were to return immediately, the entire uv loop
// could be destroyed meaning the call to uv_async_send would abort()
//
// In order to fix this, an OS mutex is used to wait for the other end to
// finish before we continue. The drop block on a handle will acquire a
// mutex and then drop it after both the push and send have been completed.
// If we acquire the mutex here, then we are guaranteed that there are no
// longer any senders which are holding on to their handles, so we can
// safely allow the event loop to exit.
if pool.refcnt == 0 {
unsafe {
let _l = state.lock.lock();
}
}
}
impl QueuePool {
pub fn new(loop_: &mut Loop) -> Box<QueuePool> {
let handle = UvHandle::alloc(None::<AsyncWatcher>, uvll::UV_ASYNC);
let state = Arc::new(State {
handle: handle,
lock: unsafe {NativeMutex::new()},
queue: mpsc::Queue::new(),
});
let mut q = box QueuePool {
refcnt: 0,
queue: state,
};
unsafe {
assert_eq!(uvll::uv_async_init(loop_.handle, handle, async_cb), 0);
uvll::uv_unref(handle);
let data = &mut *q as *mut QueuePool as *mut c_void;
uvll::set_data_for_uv_handle(handle, data);
}
return q;
}
pub fn queue(&mut self) -> Queue {
unsafe {
if self.refcnt == 0 {
uvll::uv_ref(self.queue.handle);
}
self.refcnt += 1;
}
Queue { queue: self.queue.clone() }
}
pub fn handle(&self) -> *mut uvll::uv_async_t { self.queue.handle }
}
impl Queue {
pub fn push(&mut self, task: BlockedTask) {
self.queue.queue.push(Task(task));
unsafe { uvll::uv_async_send(self.queue.handle); }
}
}
impl Clone for Queue {
fn clone(&self) -> Queue {
// Push a request to increment on the queue, but there's no need to
// signal the event loop to process it at this time. We're guaranteed
// that the count is at least one (because we have a queue right here),
// and if the queue is dropped later on it'll see the increment for the
// decrement anyway.
self.queue.queue.push(Increment);
Queue { queue: self.queue.clone() }
}
}
impl Drop for Queue {
fn drop(&mut self) {
// See the comments in the async_cb function for why there is a lock
// that is acquired only on a drop.
unsafe {
let _l = self.queue.lock.lock();
self.queue.queue.push(Decrement);
uvll::uv_async_send(self.queue.handle);
}
}
}
impl Drop for State {
fn drop(&mut self) {
unsafe {
uvll::uv_close(self.handle, mem::transmute(0u));
// Note that this does *not* free the handle, that is the
// responsibility of the caller because the uv loop must be closed
// before we deallocate this uv handle.
}
}
}

View File

@ -1,50 +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.
/// Simple refcount structure for cloning handles
///
/// This is meant to be an unintrusive solution to cloning handles in rustuv.
/// The handles themselves shouldn't be sharing memory because there are bits of
/// state in the rust objects which shouldn't be shared across multiple users of
/// the same underlying uv object, hence Rc is not used and this simple counter
/// should suffice.
use alloc::arc::Arc;
use std::cell::UnsafeCell;
pub struct Refcount {
rc: Arc<UnsafeCell<uint>>,
}
impl Refcount {
/// Creates a new refcount of 1
pub fn new() -> Refcount {
Refcount { rc: Arc::new(UnsafeCell::new(1)) }
}
fn increment(&self) {
unsafe { *self.rc.get() += 1; }
}
/// Returns whether the refcount just hit 0 or not
pub fn decrement(&self) -> bool {
unsafe {
*self.rc.get() -= 1;
*self.rc.get() == 0
}
}
}
impl Clone for Refcount {
fn clone(&self) -> Refcount {
self.increment();
Refcount { rc: self.rc.clone() }
}
}

View File

@ -1,68 +0,0 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use libc::c_int;
use std::rt::rtio::{RtioSignal, Callback};
use homing::{HomingIO, HomeHandle};
use super::{UvError, UvHandle};
use uvll;
use uvio::UvIoFactory;
pub struct SignalWatcher {
handle: *mut uvll::uv_signal_t,
home: HomeHandle,
cb: Box<Callback + Send>,
}
impl SignalWatcher {
pub fn new(io: &mut UvIoFactory, signum: int, cb: Box<Callback + Send>)
-> Result<Box<SignalWatcher>, UvError> {
let s = box SignalWatcher {
handle: UvHandle::alloc(None::<SignalWatcher>, uvll::UV_SIGNAL),
home: io.make_handle(),
cb: cb,
};
assert_eq!(unsafe {
uvll::uv_signal_init(io.uv_loop(), s.handle)
}, 0);
match unsafe {
uvll::uv_signal_start(s.handle, signal_cb, signum as c_int)
} {
0 => Ok(s.install()),
n => Err(UvError(n)),
}
}
}
extern fn signal_cb(handle: *mut uvll::uv_signal_t, _signum: c_int) {
let s: &mut SignalWatcher = unsafe { UvHandle::from_uv_handle(&handle) };
let _ = s.cb.call();
}
impl HomingIO for SignalWatcher {
fn home<'r>(&'r mut self) -> &'r mut HomeHandle { &mut self.home }
}
impl UvHandle<uvll::uv_signal_t> for SignalWatcher {
fn uv_handle(&self) -> *mut uvll::uv_signal_t { self.handle }
}
impl RtioSignal for SignalWatcher {}
impl Drop for SignalWatcher {
fn drop(&mut self) {
let _m = self.fire_homing_missile();
self.close();
}
}

View File

@ -1,281 +0,0 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use libc::{c_int, size_t, ssize_t};
use std::mem;
use std::ptr;
use std::rt::task::BlockedTask;
use Loop;
use super::{UvError, Buf, slice_to_uv_buf, Request, wait_until_woken_after,
ForbidUnwind, wakeup};
use uvll;
// This is a helper structure which is intended to get embedded into other
// Watcher structures. This structure will retain a handle to the underlying
// uv_stream_t instance, and all I/O operations assume that it's already located
// on the appropriate scheduler.
pub struct StreamWatcher {
pub handle: *mut uvll::uv_stream_t,
// Cache the last used uv_write_t so we don't have to allocate a new one on
// every call to uv_write(). Ideally this would be a stack-allocated
// structure, but currently we don't have mappings for all the structures
// defined in libuv, so we're forced to malloc this.
last_write_req: Option<Request>,
blocked_writer: Option<BlockedTask>,
}
struct ReadContext {
buf: Option<Buf>,
result: ssize_t,
task: Option<BlockedTask>,
}
struct WriteContext {
result: c_int,
stream: *mut StreamWatcher,
data: Option<Vec<u8>>,
}
impl StreamWatcher {
// Creates a new helper structure which should be then embedded into another
// watcher. This provides the generic read/write methods on streams.
//
// This structure will *not* close the stream when it is dropped. It is up
// to the enclosure structure to be sure to call the close method (which
// will block the task). Note that this is also required to prevent memory
// leaks.
//
// It should also be noted that the `data` field of the underlying uv handle
// will be manipulated on each of the methods called on this watcher.
// Wrappers should ensure to always reset the field to an appropriate value
// if they rely on the field to perform an action.
pub fn new(stream: *mut uvll::uv_stream_t,
init: bool) -> StreamWatcher {
if init {
unsafe { uvll::set_data_for_uv_handle(stream, 0 as *mut int) }
}
StreamWatcher {
handle: stream,
last_write_req: None,
blocked_writer: None,
}
}
pub fn read(&mut self, buf: &mut [u8]) -> Result<uint, UvError> {
// This read operation needs to get canceled on an unwind via libuv's
// uv_read_stop function
let _f = ForbidUnwind::new("stream read");
let mut rcx = ReadContext {
buf: Some(slice_to_uv_buf(buf)),
// if the read is canceled, we'll see eof, otherwise this will get
// overwritten
result: 0,
task: None,
};
// When reading a TTY stream on windows, libuv will invoke alloc_cb
// immediately as part of the call to alloc_cb. What this means is that
// we must be ready for this to happen (by setting the data in the uv
// handle). In theory this otherwise doesn't need to happen until after
// the read is successfully started.
unsafe { uvll::set_data_for_uv_handle(self.handle, &mut rcx) }
// Send off the read request, but don't block until we're sure that the
// read request is queued.
let ret = match unsafe {
uvll::uv_read_start(self.handle, alloc_cb, read_cb)
} {
0 => {
let loop_ = unsafe { uvll::get_loop_for_uv_handle(self.handle) };
wait_until_woken_after(&mut rcx.task, &Loop::wrap(loop_), || {});
match rcx.result {
n if n < 0 => Err(UvError(n as c_int)),
n => Ok(n as uint),
}
}
n => Err(UvError(n))
};
// Make sure a read cancellation sees that there's no pending read
unsafe { uvll::set_data_for_uv_handle(self.handle, 0 as *mut int) }
return ret;
}
pub fn cancel_read(&mut self, reason: ssize_t) -> Option<BlockedTask> {
// When we invoke uv_read_stop, it cancels the read and alloc
// callbacks. We need to manually wake up a pending task (if one was
// present).
assert_eq!(unsafe { uvll::uv_read_stop(self.handle) }, 0);
let data = unsafe {
let data = uvll::get_data_for_uv_handle(self.handle);
if data.is_null() { return None }
uvll::set_data_for_uv_handle(self.handle, 0 as *mut int);
&mut *(data as *mut ReadContext)
};
data.result = reason;
data.task.take()
}
pub fn write(&mut self, buf: &[u8], may_timeout: bool) -> Result<(), UvError> {
// The ownership of the write request is dubious if this function
// unwinds. I believe that if the write_cb fails to re-schedule the task
// then the write request will be leaked.
let _f = ForbidUnwind::new("stream write");
// Prepare the write request, either using a cached one or allocating a
// new one
let mut req = match self.last_write_req.take() {
Some(req) => req, None => Request::new(uvll::UV_WRITE),
};
req.set_data(ptr::null_mut::<()>());
// And here's where timeouts get a little interesting. Currently, libuv
// does not support canceling an in-flight write request. Consequently,
// when a write timeout expires, there's not much we can do other than
// detach the sleeping task from the write request itself. Semantically,
// this means that the write request will complete asynchronously, but
// the calling task will return error (because the write timed out).
//
// There is special wording in the documentation of set_write_timeout()
// indicating that this is a plausible failure scenario, and this
// function is why that wording exists.
//
// Implementation-wise, we must be careful when passing a buffer down to
// libuv. Most of this implementation avoids allocations because of the
// blocking guarantee (all stack local variables are valid for the
// entire read/write request). If our write request can be timed out,
// however, we must heap allocate the data and pass that to the libuv
// functions instead. The reason for this is that if we time out and
// return, there's no guarantee that `buf` is a valid buffer any more.
//
// To do this, the write context has an optionally owned vector of
// bytes.
let data = if may_timeout {Some(buf.to_vec())} else {None};
let uv_buf = if may_timeout {
slice_to_uv_buf(data.as_ref().unwrap().as_slice())
} else {
slice_to_uv_buf(buf)
};
// Send off the request, but be careful to not block until we're sure
// that the write request is queued. If the request couldn't be queued,
// then we should return immediately with an error.
match unsafe {
uvll::uv_write(req.handle, self.handle, [uv_buf], write_cb)
} {
0 => {
let mut wcx = WriteContext {
result: uvll::ECANCELED,
stream: self as *mut _,
data: data,
};
req.defuse(); // uv callback now owns this request
let loop_ = unsafe { uvll::get_loop_for_uv_handle(self.handle) };
wait_until_woken_after(&mut self.blocked_writer,
&Loop::wrap(loop_), || {
req.set_data(&mut wcx);
});
if wcx.result != uvll::ECANCELED {
self.last_write_req = Some(Request::wrap(req.handle));
return match wcx.result {
0 => Ok(()),
n => Err(UvError(n)),
}
}
// This is the second case where canceling an in-flight write
// gets interesting. If we've been canceled (no one reset our
// result), then someone still needs to free the request, and
// someone still needs to free the allocate buffer.
//
// To take care of this, we swap out the stack-allocated write
// context for a heap-allocated context, transferring ownership
// of everything to the write_cb. Libuv guarantees that this
// callback will be invoked at some point, and the callback will
// be responsible for deallocating these resources.
//
// Note that we don't cache this write request back in the
// stream watcher because we no longer have ownership of it, and
// we never will.
let mut new_wcx = box WriteContext {
result: 0,
stream: 0 as *mut StreamWatcher,
data: wcx.data.take(),
};
unsafe {
req.set_data(&mut *new_wcx);
mem::forget(new_wcx);
}
Err(UvError(wcx.result))
}
n => Err(UvError(n)),
}
}
pub fn cancel_write(&mut self) -> Option<BlockedTask> {
self.blocked_writer.take()
}
}
// This allocation callback expects to be invoked once and only once. It will
// unwrap the buffer in the ReadContext stored in the stream and return it. This
// will fail if it is called more than once.
extern fn alloc_cb(stream: *mut uvll::uv_stream_t, _hint: size_t, buf: *mut Buf) {
uvdebug!("alloc_cb");
unsafe {
let rcx: &mut ReadContext =
mem::transmute(uvll::get_data_for_uv_handle(stream));
*buf = rcx.buf.take().expect("stream alloc_cb called more than once");
}
}
// When a stream has read some data, we will always forcibly stop reading and
// return all the data read (even if it didn't fill the whole buffer).
extern fn read_cb(handle: *mut uvll::uv_stream_t, nread: ssize_t,
_buf: *const Buf) {
uvdebug!("read_cb {}", nread);
assert!(nread != uvll::ECANCELED as ssize_t);
let rcx: &mut ReadContext = unsafe {
mem::transmute(uvll::get_data_for_uv_handle(handle))
};
// Stop reading so that no read callbacks are
// triggered before the user calls `read` again.
// FIXME: Is there a performance impact to calling
// stop here?
unsafe { assert_eq!(uvll::uv_read_stop(handle), 0); }
rcx.result = nread;
wakeup(&mut rcx.task);
}
// Unlike reading, the WriteContext is stored in the uv_write_t request. Like
// reading, however, all this does is wake up the blocked task after squirreling
// away the error code as a result.
extern fn write_cb(req: *mut uvll::uv_write_t, status: c_int) {
let mut req = Request::wrap(req);
// Remember to not free the request because it is re-used between writes on
// the same stream.
let wcx: &mut WriteContext = unsafe { req.get_data() };
wcx.result = status;
// If the stream is present, we haven't timed out, otherwise we acquire
// ownership of everything and then deallocate it all at once.
if wcx.stream as uint != 0 {
req.defuse();
let stream: &mut StreamWatcher = unsafe { &mut *wcx.stream };
wakeup(&mut stream.blocked_writer);
} else {
let _wcx: Box<WriteContext> = unsafe { mem::transmute(wcx) };
}
}

View File

@ -1,411 +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::c_int;
use std::mem;
use std::rt::task::BlockedTask;
use std::rt::rtio::IoResult;
use access;
use homing::{HomeHandle, HomingMissile};
use timer::TimerWatcher;
use uvll;
use uvio::UvIoFactory;
use {Loop, UvError, uv_error_to_io_error, Request, wakeup};
use {UvHandle, wait_until_woken_after};
/// Management of a timeout when gaining access to a portion of a duplex stream.
pub struct AccessTimeout<T> {
state: TimeoutState,
timer: Option<Box<TimerWatcher>>,
pub access: access::Access<T>,
}
pub struct Guard<'a, T:'static> {
state: &'a mut TimeoutState,
pub access: access::Guard<'a, T>,
pub can_timeout: bool,
}
#[deriving(PartialEq)]
enum TimeoutState {
NoTimeout,
TimeoutPending(ClientState),
TimedOut,
}
#[deriving(PartialEq)]
enum ClientState {
NoWaiter,
AccessPending,
RequestPending,
}
struct TimerContext {
timeout: *mut AccessTimeout<()>,
callback: fn(*mut AccessTimeout<()>, &TimerContext),
user_unblock: fn(uint) -> Option<BlockedTask>,
user_payload: uint,
}
impl<T: Send> AccessTimeout<T> {
pub fn new(data: T) -> AccessTimeout<T> {
AccessTimeout {
state: NoTimeout,
timer: None,
access: access::Access::new(data),
}
}
/// Grants access to half of a duplex stream, timing out if necessary.
///
/// On success, Ok(Guard) is returned and access has been granted to the
/// stream. If a timeout occurs, then Err is returned with an appropriate
/// error.
pub fn grant<'a>(&'a mut self, m: HomingMissile) -> IoResult<Guard<'a, T>> {
// First, flag that we're attempting to acquire access. This will allow
// us to cancel the pending grant if we timeout out while waiting for a
// grant.
match self.state {
NoTimeout => {},
TimeoutPending(ref mut client) => *client = AccessPending,
TimedOut => return Err(uv_error_to_io_error(UvError(uvll::ECANCELED)))
}
let access = self.access.grant(self as *mut _ as uint, m);
// After acquiring the grant, we need to flag ourselves as having a
// pending request so the timeout knows to cancel the request.
let can_timeout = match self.state {
NoTimeout => false,
TimeoutPending(ref mut client) => { *client = RequestPending; true }
TimedOut => return Err(uv_error_to_io_error(UvError(uvll::ECANCELED)))
};
Ok(Guard {
access: access,
state: &mut self.state,
can_timeout: can_timeout
})
}
pub fn timed_out(&self) -> bool {
match self.state {
TimedOut => true,
_ => false,
}
}
/// Sets the pending timeout to the value specified.
///
/// The home/loop variables are used to construct a timer if one has not
/// been previously constructed.
///
/// The callback will be invoked if the timeout elapses, and the data of
/// the time will be set to `data`.
pub fn set_timeout(&mut self, ms: Option<u64>,
home: &HomeHandle,
loop_: &Loop,
cb: fn(uint) -> Option<BlockedTask>,
data: uint) {
self.state = NoTimeout;
let ms = match ms {
Some(ms) => ms,
None => return match self.timer {
Some(ref mut t) => t.stop(),
None => {}
}
};
// If we have a timeout, lazily initialize the timer which will be used
// to fire when the timeout runs out.
if self.timer.is_none() {
let mut timer = box TimerWatcher::new_home(loop_, home.clone());
let mut cx = box TimerContext {
timeout: self as *mut _ as *mut AccessTimeout<()>,
callback: real_cb::<T>,
user_unblock: cb,
user_payload: data,
};
unsafe {
timer.set_data(&mut *cx);
mem::forget(cx);
}
self.timer = Some(timer);
}
let timer = self.timer.as_mut().unwrap();
unsafe {
let cx = uvll::get_data_for_uv_handle(timer.handle);
let cx = cx as *mut TimerContext;
(*cx).user_unblock = cb;
(*cx).user_payload = data;
}
timer.stop();
timer.start(timer_cb, ms, 0);
self.state = TimeoutPending(NoWaiter);
extern fn timer_cb(timer: *mut uvll::uv_timer_t) {
let cx: &TimerContext = unsafe {
&*(uvll::get_data_for_uv_handle(timer) as *const TimerContext)
};
(cx.callback)(cx.timeout, cx);
}
fn real_cb<T: Send>(timeout: *mut AccessTimeout<()>, cx: &TimerContext) {
let timeout = timeout as *mut AccessTimeout<T>;
let me = unsafe { &mut *timeout };
match mem::replace(&mut me.state, TimedOut) {
TimedOut | NoTimeout => unreachable!(),
TimeoutPending(NoWaiter) => {}
TimeoutPending(AccessPending) => {
match unsafe { me.access.dequeue(me as *mut _ as uint) } {
Some(task) => task.reawaken(),
None => unreachable!(),
}
}
TimeoutPending(RequestPending) => {
match (cx.user_unblock)(cx.user_payload) {
Some(task) => task.reawaken(),
None => unreachable!(),
}
}
}
}
}
}
impl<T: Send> Clone for AccessTimeout<T> {
fn clone(&self) -> AccessTimeout<T> {
AccessTimeout {
access: self.access.clone(),
state: NoTimeout,
timer: None,
}
}
}
#[unsafe_destructor]
impl<'a, T> Drop for Guard<'a, T> {
fn drop(&mut self) {
match *self.state {
TimeoutPending(NoWaiter) | TimeoutPending(AccessPending) =>
unreachable!(),
NoTimeout | TimedOut => {}
TimeoutPending(RequestPending) => {
*self.state = TimeoutPending(NoWaiter);
}
}
}
}
#[unsafe_destructor]
impl<T> Drop for AccessTimeout<T> {
fn drop(&mut self) {
match self.timer {
Some(ref timer) => unsafe {
let data = uvll::get_data_for_uv_handle(timer.handle);
let _data: Box<TimerContext> = mem::transmute(data);
},
None => {}
}
}
}
////////////////////////////////////////////////////////////////////////////////
// Connect timeouts
////////////////////////////////////////////////////////////////////////////////
pub struct ConnectCtx {
pub status: c_int,
pub task: Option<BlockedTask>,
pub timer: Option<Box<TimerWatcher>>,
}
impl ConnectCtx {
pub fn connect<T>(
mut self, obj: T, timeout: Option<u64>, io: &mut UvIoFactory,
f: |&Request, &T, uvll::uv_connect_cb| -> c_int
) -> Result<T, UvError> {
let mut req = Request::new(uvll::UV_CONNECT);
let r = f(&req, &obj, connect_cb);
return match r {
0 => {
req.defuse(); // uv callback now owns this request
match timeout {
Some(t) => {
let mut timer = TimerWatcher::new(io);
timer.start(timer_cb, t, 0);
self.timer = Some(timer);
}
None => {}
}
wait_until_woken_after(&mut self.task, &io.loop_, || {
let data = &self as *const _ as *mut ConnectCtx;
match self.timer {
Some(ref mut timer) => unsafe { timer.set_data(data) },
None => {}
}
req.set_data(data);
});
// Make sure an erroneously fired callback doesn't have access
// to the context any more.
req.set_data(0 as *mut int);
// If we failed because of a timeout, drop the TcpWatcher as
// soon as possible because it's data is now set to null and we
// want to cancel the callback ASAP.
match self.status {
0 => Ok(obj),
n => { drop(obj); Err(UvError(n)) }
}
}
n => Err(UvError(n))
};
extern fn timer_cb(handle: *mut uvll::uv_timer_t) {
// Don't close the corresponding tcp request, just wake up the task
// and let RAII take care of the pending watcher.
let cx: &mut ConnectCtx = unsafe {
&mut *(uvll::get_data_for_uv_handle(handle) as *mut ConnectCtx)
};
cx.status = uvll::ECANCELED;
wakeup(&mut cx.task);
}
extern fn connect_cb(req: *mut uvll::uv_connect_t, status: c_int) {
// This callback can be invoked with ECANCELED if the watcher is
// closed by the timeout callback. In that case we just want to free
// the request and be along our merry way.
let req = Request::wrap(req);
if status == uvll::ECANCELED { return }
// Apparently on windows when the handle is closed this callback may
// not be invoked with ECANCELED but rather another error code.
// Either ways, if the data is null, then our timeout has expired
// and there's nothing we can do.
let data = unsafe { uvll::get_data_for_req(req.handle) };
if data.is_null() { return }
let cx: &mut ConnectCtx = unsafe { &mut *(data as *mut ConnectCtx) };
cx.status = status;
match cx.timer {
Some(ref mut t) => t.stop(),
None => {}
}
// Note that the timer callback doesn't cancel the connect request
// (that's the job of uv_close()), so it's possible for this
// callback to get triggered after the timeout callback fires, but
// before the task wakes up. In that case, we did indeed
// successfully connect, but we don't need to wake someone up. We
// updated the status above (correctly so), and the task will pick
// up on this when it wakes up.
if cx.task.is_some() {
wakeup(&mut cx.task);
}
}
}
}
pub struct AcceptTimeout<T> {
access: AccessTimeout<AcceptorState<T>>,
}
struct AcceptorState<T> {
blocked_acceptor: Option<BlockedTask>,
pending: Vec<IoResult<T>>,
}
impl<T: Send> AcceptTimeout<T> {
pub fn new() -> AcceptTimeout<T> {
AcceptTimeout {
access: AccessTimeout::new(AcceptorState {
blocked_acceptor: None,
pending: Vec::new(),
})
}
}
pub fn accept(&mut self,
missile: HomingMissile,
loop_: &Loop) -> IoResult<T> {
// If we've timed out but we're not closed yet, poll the state of the
// queue to see if we can peel off a connection.
if self.access.timed_out() && !self.access.access.is_closed(&missile) {
let tmp = self.access.access.get_mut(&missile);
return match tmp.pending.remove(0) {
Some(msg) => msg,
None => Err(uv_error_to_io_error(UvError(uvll::ECANCELED)))
}
}
// Now that we're not polling, attempt to gain access and then peel off
// a connection. If we have no pending connections, then we need to go
// to sleep and wait for one.
//
// Note that if we're woken up for a pending connection then we're
// guaranteed that the check above will not steal our connection due to
// the single-threaded nature of the event loop.
let mut guard = try!(self.access.grant(missile));
if guard.access.is_closed() {
return Err(uv_error_to_io_error(UvError(uvll::EOF)))
}
match guard.access.pending.remove(0) {
Some(msg) => return msg,
None => {}
}
wait_until_woken_after(&mut guard.access.blocked_acceptor, loop_, || {});
match guard.access.pending.remove(0) {
_ if guard.access.is_closed() => {
Err(uv_error_to_io_error(UvError(uvll::EOF)))
}
Some(msg) => msg,
None => Err(uv_error_to_io_error(UvError(uvll::ECANCELED)))
}
}
pub unsafe fn push(&mut self, t: IoResult<T>) {
let state = self.access.access.unsafe_get();
(*state).pending.push(t);
let _ = (*state).blocked_acceptor.take().map(|t| t.reawaken());
}
pub fn set_timeout(&mut self,
ms: Option<u64>,
loop_: &Loop,
home: &HomeHandle) {
self.access.set_timeout(ms, home, loop_, cancel_accept::<T>,
self as *mut _ as uint);
fn cancel_accept<T: Send>(me: uint) -> Option<BlockedTask> {
unsafe {
let me: &mut AcceptTimeout<T> = mem::transmute(me);
(*me.access.access.unsafe_get()).blocked_acceptor.take()
}
}
}
pub fn close(&mut self, m: HomingMissile) {
self.access.access.close(&m);
let task = self.access.access.get_mut(&m).blocked_acceptor.take();
drop(m);
let _ = task.map(|t| t.reawaken());
}
}
impl<T: Send> Clone for AcceptTimeout<T> {
fn clone(&self) -> AcceptTimeout<T> {
AcceptTimeout { access: self.access.clone() }
}
}

View File

@ -1,173 +0,0 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::mem;
use std::rt::rtio::{RtioTimer, Callback};
use std::rt::task::BlockedTask;
use homing::{HomeHandle, HomingIO};
use super::{UvHandle, ForbidUnwind, ForbidSwitch, wait_until_woken_after, Loop};
use uvio::UvIoFactory;
use uvll;
pub struct TimerWatcher {
pub handle: *mut uvll::uv_timer_t,
home: HomeHandle,
action: Option<NextAction>,
blocker: Option<BlockedTask>,
id: uint, // see comments in timer_cb
}
pub enum NextAction {
WakeTask,
CallOnce(Box<Callback + Send>),
CallMany(Box<Callback + Send>, uint),
}
impl TimerWatcher {
pub fn new(io: &mut UvIoFactory) -> Box<TimerWatcher> {
let handle = io.make_handle();
let me = box TimerWatcher::new_home(&io.loop_, handle);
me.install()
}
pub fn new_home(loop_: &Loop, home: HomeHandle) -> TimerWatcher {
let handle = UvHandle::alloc(None::<TimerWatcher>, uvll::UV_TIMER);
assert_eq!(unsafe { uvll::uv_timer_init(loop_.handle, handle) }, 0);
TimerWatcher {
handle: handle,
action: None,
blocker: None,
home: home,
id: 0,
}
}
pub fn start(&mut self, f: uvll::uv_timer_cb, msecs: u64, period: u64) {
assert_eq!(unsafe {
uvll::uv_timer_start(self.handle, f, msecs, period)
}, 0)
}
pub fn stop(&mut self) {
assert_eq!(unsafe { uvll::uv_timer_stop(self.handle) }, 0)
}
pub unsafe fn set_data<T>(&mut self, data: *mut T) {
uvll::set_data_for_uv_handle(self.handle, data);
}
}
impl HomingIO for TimerWatcher {
fn home<'r>(&'r mut self) -> &'r mut HomeHandle { &mut self.home }
}
impl UvHandle<uvll::uv_timer_t> for TimerWatcher {
fn uv_handle(&self) -> *mut uvll::uv_timer_t { self.handle }
}
impl RtioTimer for TimerWatcher {
fn sleep(&mut self, msecs: u64) {
// As with all of the below functions, we must be extra careful when
// destroying the previous action. If the previous action was a channel,
// destroying it could invoke a context switch. For these situations,
// we must temporarily un-home ourselves, then destroy the action, and
// then re-home again.
let missile = self.fire_homing_missile();
self.id += 1;
self.stop();
let _missile = match mem::replace(&mut self.action, None) {
None => missile, // no need to do a homing dance
Some(action) => {
drop(missile); // un-home ourself
drop(action); // destroy the previous action
self.fire_homing_missile() // re-home ourself
}
};
// If the descheduling operation unwinds after the timer has been
// started, then we need to call stop on the timer.
let _f = ForbidUnwind::new("timer");
self.action = Some(WakeTask);
wait_until_woken_after(&mut self.blocker, &self.uv_loop(), || {
self.start(timer_cb, msecs, 0);
});
self.stop();
}
fn oneshot(&mut self, msecs: u64, cb: Box<Callback + Send>) {
// similarly to the destructor, we must drop the previous action outside
// of the homing missile
let _prev_action = {
let _m = self.fire_homing_missile();
self.id += 1;
self.stop();
self.start(timer_cb, msecs, 0);
mem::replace(&mut self.action, Some(CallOnce(cb)))
};
}
fn period(&mut self, msecs: u64, cb: Box<Callback + Send>) {
// similarly to the destructor, we must drop the previous action outside
// of the homing missile
let _prev_action = {
let _m = self.fire_homing_missile();
self.id += 1;
self.stop();
self.start(timer_cb, msecs, msecs);
mem::replace(&mut self.action, Some(CallMany(cb, self.id)))
};
}
}
extern fn timer_cb(handle: *mut uvll::uv_timer_t) {
let _f = ForbidSwitch::new("timer callback can't switch");
let timer: &mut TimerWatcher = unsafe { UvHandle::from_uv_handle(&handle) };
match timer.action.take().unwrap() {
WakeTask => {
let task = timer.blocker.take().unwrap();
let _ = task.wake().map(|t| t.reawaken());
}
CallOnce(mut cb) => { cb.call() }
CallMany(mut cb, id) => {
cb.call();
// Note that the above operation could have performed some form of
// scheduling. This means that the timer may have decided to insert
// some other action to happen. This 'id' keeps track of the updates
// to the timer, so we only reset the action back to sending on this
// channel if the id has remained the same. This is essentially a
// bug in that we have mutably aliasable memory, but that's libuv
// for you. We're guaranteed to all be running on the same thread,
// so there's no need for any synchronization here.
if timer.id == id {
timer.action = Some(CallMany(cb, id));
}
}
}
}
impl Drop for TimerWatcher {
fn drop(&mut self) {
// note that this drop is a little subtle. Dropping a channel which is
// held internally may invoke some scheduling operations. We can't take
// the channel unless we're on the home scheduler, but once we're on the
// home scheduler we should never move. Hence, we take the timer's
// action item and then move it outside of the homing block.
let _action = {
let _m = self.fire_homing_missile();
self.stop();
self.close();
self.action.take()
};
}
}

View File

@ -1,136 +0,0 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use libc;
use std::ptr;
use std::rt::rtio::{RtioTTY, IoResult};
use homing::{HomingIO, HomeHandle};
use stream::StreamWatcher;
use super::{UvError, UvHandle, uv_error_to_io_error};
use uvio::UvIoFactory;
use uvll;
pub struct TtyWatcher{
tty: *mut uvll::uv_tty_t,
stream: StreamWatcher,
home: HomeHandle,
fd: libc::c_int,
}
impl TtyWatcher {
pub fn new(io: &mut UvIoFactory, fd: libc::c_int, readable: bool)
-> Result<TtyWatcher, UvError>
{
// libuv may succeed in giving us a handle (via uv_tty_init), but if the
// handle isn't actually connected to a terminal there are frequently
// many problems in using it with libuv. To get around this, always
// return a failure if the specified file descriptor isn't actually a
// TTY.
//
// Related:
// - https://github.com/joyent/libuv/issues/982
// - https://github.com/joyent/libuv/issues/988
let guess = unsafe { uvll::guess_handle(fd) };
if guess != uvll::UV_TTY as libc::c_int {
return Err(UvError(uvll::EBADF));
}
// libuv was recently changed to not close the stdio file descriptors,
// but it did not change the behavior for windows. Until this issue is
// fixed, we need to dup the stdio file descriptors because otherwise
// uv_close will close them
let fd = if cfg!(windows) && fd <= libc::STDERR_FILENO {
unsafe { libc::dup(fd) }
} else { fd };
// If this file descriptor is indeed guessed to be a tty, then go ahead
// with attempting to open it as a tty.
let handle = UvHandle::alloc(None::<TtyWatcher>, uvll::UV_TTY);
let mut watcher = TtyWatcher {
tty: handle,
stream: StreamWatcher::new(handle, true),
home: io.make_handle(),
fd: fd,
};
match unsafe {
uvll::uv_tty_init(io.uv_loop(), handle, fd as libc::c_int,
readable as libc::c_int)
} {
0 => Ok(watcher),
n => {
// On windows, libuv returns errors before initializing the
// handle, so our only cleanup is to free the handle itself
if cfg!(windows) {
unsafe { uvll::free_handle(handle); }
watcher.tty = ptr::null_mut();
}
Err(UvError(n))
}
}
}
}
impl RtioTTY for TtyWatcher {
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
let _m = self.fire_homing_missile();
self.stream.read(buf).map_err(uv_error_to_io_error)
}
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
let _m = self.fire_homing_missile();
self.stream.write(buf, false).map_err(uv_error_to_io_error)
}
fn set_raw(&mut self, raw: bool) -> IoResult<()> {
let raw = raw as libc::c_int;
let _m = self.fire_homing_missile();
match unsafe { uvll::uv_tty_set_mode(self.tty, raw) } {
0 => Ok(()),
n => Err(uv_error_to_io_error(UvError(n)))
}
}
#[allow(unused_mut)]
fn get_winsize(&mut self) -> IoResult<(int, int)> {
let mut width: libc::c_int = 0;
let mut height: libc::c_int = 0;
let widthptr: *mut libc::c_int = &mut width;
let heightptr: *mut libc::c_int = &mut width;
let _m = self.fire_homing_missile();
match unsafe { uvll::uv_tty_get_winsize(self.tty,
widthptr, heightptr) } {
0 => Ok((width as int, height as int)),
n => Err(uv_error_to_io_error(UvError(n)))
}
}
fn isatty(&self) -> bool {
unsafe { uvll::guess_handle(self.fd) == uvll::UV_TTY as libc::c_int }
}
}
impl UvHandle<uvll::uv_tty_t> for TtyWatcher {
fn uv_handle(&self) -> *mut uvll::uv_tty_t { self.tty }
}
impl HomingIO for TtyWatcher {
fn home<'a>(&'a mut self) -> &'a mut HomeHandle { &mut self.home }
}
impl Drop for TtyWatcher {
fn drop(&mut self) {
if !self.tty.is_null() {
let _m = self.fire_homing_missile();
self.close_async_();
}
}
}

View File

@ -1,326 +0,0 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! The implementation of `rtio` for libuv
use std::c_str::CString;
use std::mem;
use libc::c_int;
use libc::{O_CREAT, O_APPEND, O_TRUNC, O_RDWR, O_RDONLY, O_WRONLY, S_IRUSR,
S_IWUSR};
use libc;
use std::rt::rtio;
use std::rt::rtio::{ProcessConfig, IoFactory, EventLoop, IoResult};
#[cfg(test)] use std::rt::thread::Thread;
use super::{uv_error_to_io_error, Loop};
use addrinfo::GetAddrInfoRequest;
use async::AsyncWatcher;
use file::{FsRequest, FileWatcher};
use queue::QueuePool;
use homing::HomeHandle;
use idle::IdleWatcher;
use net::{TcpWatcher, TcpListener, UdpWatcher};
use pipe::{PipeWatcher, PipeListener};
use process::Process;
use signal::SignalWatcher;
use timer::TimerWatcher;
use tty::TtyWatcher;
use uvll;
// Obviously an Event Loop is always home.
pub struct UvEventLoop {
uvio: UvIoFactory
}
impl UvEventLoop {
pub fn new() -> UvEventLoop {
let mut loop_ = Loop::new();
let handle_pool = QueuePool::new(&mut loop_);
UvEventLoop {
uvio: UvIoFactory {
loop_: loop_,
handle_pool: Some(handle_pool),
}
}
}
}
impl Drop for UvEventLoop {
fn drop(&mut self) {
// Must first destroy the pool of handles before we destroy the loop
// because otherwise the contained async handle will be destroyed after
// the loop is free'd (use-after-free). We also must free the uv handle
// after the loop has been closed because during the closing of the loop
// the handle is required to be used apparently.
//
// Lastly, after we've closed the pool of handles we pump the event loop
// one last time to run any closing callbacks to make sure the loop
// shuts down cleanly.
let handle = self.uvio.handle_pool.as_ref().unwrap().handle();
drop(self.uvio.handle_pool.take());
self.run();
self.uvio.loop_.close();
unsafe { uvll::free_handle(handle) }
}
}
impl EventLoop for UvEventLoop {
fn run(&mut self) {
self.uvio.loop_.run();
}
fn callback(&mut self, f: proc()) {
IdleWatcher::onetime(&mut self.uvio.loop_, f);
}
fn pausable_idle_callback(&mut self, cb: Box<rtio::Callback + Send>)
-> Box<rtio::PausableIdleCallback + Send> {
IdleWatcher::new(&mut self.uvio.loop_, cb)
as Box<rtio::PausableIdleCallback + Send>
}
fn remote_callback(&mut self, f: Box<rtio::Callback + Send>)
-> Box<rtio::RemoteCallback + Send> {
box AsyncWatcher::new(&mut self.uvio.loop_, f) as
Box<rtio::RemoteCallback + Send>
}
fn io<'a>(&'a mut self) -> Option<&'a mut rtio::IoFactory> {
let factory = &mut self.uvio as &mut rtio::IoFactory;
Some(factory)
}
fn has_active_io(&self) -> bool {
self.uvio.loop_.get_blockers() > 0
}
}
#[test]
fn test_callback_run_once() {
Thread::start(proc() {
let mut event_loop = UvEventLoop::new();
let mut count = 0;
let count_ptr: *mut int = &mut count;
event_loop.callback(proc() {
unsafe { *count_ptr += 1 }
});
event_loop.run();
assert_eq!(count, 1);
}).join();
}
pub struct UvIoFactory {
pub loop_: Loop,
handle_pool: Option<Box<QueuePool>>,
}
impl UvIoFactory {
pub fn uv_loop<'a>(&mut self) -> *mut uvll::uv_loop_t { self.loop_.handle }
pub fn make_handle(&mut self) -> HomeHandle {
// It's understood by the homing code that the "local id" is just the
// pointer of the local I/O factory cast to a uint.
let id: uint = unsafe { mem::transmute_copy(&self) };
HomeHandle::new(id, &mut **self.handle_pool.as_mut().unwrap())
}
}
impl IoFactory for UvIoFactory {
// Connect to an address and return a new stream
// NB: This blocks the task waiting on the connection.
// It would probably be better to return a future
fn tcp_connect(&mut self, addr: rtio::SocketAddr, timeout: Option<u64>)
-> IoResult<Box<rtio::RtioTcpStream + Send>> {
match TcpWatcher::connect(self, addr, timeout) {
Ok(t) => Ok(box t as Box<rtio::RtioTcpStream + Send>),
Err(e) => Err(uv_error_to_io_error(e)),
}
}
fn tcp_bind(&mut self, addr: rtio::SocketAddr)
-> IoResult<Box<rtio::RtioTcpListener + Send>> {
match TcpListener::bind(self, addr) {
Ok(t) => Ok(t as Box<rtio::RtioTcpListener + Send>),
Err(e) => Err(uv_error_to_io_error(e)),
}
}
fn udp_bind(&mut self, addr: rtio::SocketAddr)
-> IoResult<Box<rtio::RtioUdpSocket + Send>> {
match UdpWatcher::bind(self, addr) {
Ok(u) => Ok(box u as Box<rtio::RtioUdpSocket + Send>),
Err(e) => Err(uv_error_to_io_error(e)),
}
}
fn timer_init(&mut self) -> IoResult<Box<rtio::RtioTimer + Send>> {
Ok(TimerWatcher::new(self) as Box<rtio::RtioTimer + Send>)
}
fn get_host_addresses(&mut self, host: Option<&str>, servname: Option<&str>,
hint: Option<rtio::AddrinfoHint>)
-> IoResult<Vec<rtio::AddrinfoInfo>>
{
let r = GetAddrInfoRequest::run(&self.loop_, host, servname, hint);
r.map_err(uv_error_to_io_error)
}
fn fs_from_raw_fd(&mut self, fd: c_int, close: rtio::CloseBehavior)
-> Box<rtio::RtioFileStream + Send> {
box FileWatcher::new(self, 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>>
{
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 FsRequest::open(self, path, flags as int, mode as int) {
Ok(fs) => Ok(box fs as Box<rtio::RtioFileStream + Send>),
Err(e) => Err(uv_error_to_io_error(e))
}
}
fn fs_unlink(&mut self, path: &CString) -> IoResult<()> {
let r = FsRequest::unlink(&self.loop_, path);
r.map_err(uv_error_to_io_error)
}
fn fs_lstat(&mut self, path: &CString) -> IoResult<rtio::FileStat> {
let r = FsRequest::lstat(&self.loop_, path);
r.map_err(uv_error_to_io_error)
}
fn fs_stat(&mut self, path: &CString) -> IoResult<rtio::FileStat> {
let r = FsRequest::stat(&self.loop_, path);
r.map_err(uv_error_to_io_error)
}
fn fs_mkdir(&mut self, path: &CString, perm: uint) -> IoResult<()> {
let r = FsRequest::mkdir(&self.loop_, path, perm as c_int);
r.map_err(uv_error_to_io_error)
}
fn fs_rmdir(&mut self, path: &CString) -> IoResult<()> {
let r = FsRequest::rmdir(&self.loop_, path);
r.map_err(uv_error_to_io_error)
}
fn fs_rename(&mut self, path: &CString, to: &CString) -> IoResult<()> {
let r = FsRequest::rename(&self.loop_, path, to);
r.map_err(uv_error_to_io_error)
}
fn fs_chmod(&mut self, path: &CString, perm: uint) -> IoResult<()> {
let r = FsRequest::chmod(&self.loop_, path, perm as c_int);
r.map_err(uv_error_to_io_error)
}
fn fs_readdir(&mut self, path: &CString, flags: c_int)
-> IoResult<Vec<CString>>
{
let r = FsRequest::readdir(&self.loop_, path, flags);
r.map_err(uv_error_to_io_error)
}
fn fs_link(&mut self, src: &CString, dst: &CString) -> IoResult<()> {
let r = FsRequest::link(&self.loop_, src, dst);
r.map_err(uv_error_to_io_error)
}
fn fs_symlink(&mut self, src: &CString, dst: &CString) -> IoResult<()> {
let r = FsRequest::symlink(&self.loop_, src, dst);
r.map_err(uv_error_to_io_error)
}
fn fs_chown(&mut self, path: &CString, uid: int, gid: int) -> IoResult<()> {
let r = FsRequest::chown(&self.loop_, path, uid, gid);
r.map_err(uv_error_to_io_error)
}
fn fs_readlink(&mut self, path: &CString) -> IoResult<CString> {
let r = FsRequest::readlink(&self.loop_, path);
r.map_err(uv_error_to_io_error)
}
fn fs_utime(&mut self, path: &CString, atime: u64, mtime: u64)
-> IoResult<()>
{
let r = FsRequest::utime(&self.loop_, path, atime, mtime);
r.map_err(uv_error_to_io_error)
}
fn spawn(&mut self, cfg: ProcessConfig)
-> IoResult<(Box<rtio::RtioProcess + Send>,
Vec<Option<Box<rtio::RtioPipe + Send>>>)>
{
match Process::spawn(self, cfg) {
Ok((p, io)) => {
Ok((p as Box<rtio::RtioProcess + Send>,
io.into_iter().map(|i| i.map(|p| {
box p as Box<rtio::RtioPipe + Send>
})).collect()))
}
Err(e) => Err(uv_error_to_io_error(e)),
}
}
fn kill(&mut self, pid: libc::pid_t, signum: int) -> IoResult<()> {
Process::kill(pid, signum).map_err(uv_error_to_io_error)
}
fn unix_bind(&mut self, path: &CString)
-> IoResult<Box<rtio::RtioUnixListener + Send>> {
match PipeListener::bind(self, path) {
Ok(p) => Ok(p as Box<rtio::RtioUnixListener + Send>),
Err(e) => Err(uv_error_to_io_error(e)),
}
}
fn unix_connect(&mut self, path: &CString, timeout: Option<u64>)
-> IoResult<Box<rtio::RtioPipe + Send>> {
match PipeWatcher::connect(self, path, timeout) {
Ok(p) => Ok(box p as Box<rtio::RtioPipe + Send>),
Err(e) => Err(uv_error_to_io_error(e)),
}
}
fn tty_open(&mut self, fd: c_int, readable: bool)
-> IoResult<Box<rtio::RtioTTY + Send>> {
match TtyWatcher::new(self, fd, readable) {
Ok(tty) => Ok(box tty as Box<rtio::RtioTTY + Send>),
Err(e) => Err(uv_error_to_io_error(e))
}
}
fn pipe_open(&mut self, fd: c_int)
-> IoResult<Box<rtio::RtioPipe + Send>>
{
match PipeWatcher::open(self, fd) {
Ok(s) => Ok(box s as Box<rtio::RtioPipe + Send>),
Err(e) => Err(uv_error_to_io_error(e))
}
}
fn signal(&mut self, signum: int, cb: Box<rtio::Callback + Send>)
-> IoResult<Box<rtio::RtioSignal + Send>>
{
match SignalWatcher::new(self, signum, cb) {
Ok(s) => Ok(s as Box<rtio::RtioSignal + Send>),
Err(e) => Err(uv_error_to_io_error(e)),
}
}
}

View File

@ -1,742 +0,0 @@
// Copyright 2012 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.
/*!
* Low-level bindings to the libuv library.
*
* This module contains a set of direct, 'bare-metal' wrappers around
* the libuv C-API.
*
* We're not bothering yet to redefine uv's structs as Rust structs
* because they are quite large and change often between versions.
* The maintenance burden is just too high. Instead we use the uv's
* `uv_handle_size` and `uv_req_size` to find the correct size of the
* structs and allocate them on the heap. This can be revisited later.
*
* There are also a collection of helper functions to ease interacting
* with the low-level API.
*
* As new functionality, existent in uv.h, is added to the rust stdlib,
* the mappings should be added in this module.
*/
#![allow(non_camel_case_types)] // C types
use libc::{size_t, c_int, c_uint, c_void, c_char, c_double};
use libc::{ssize_t, sockaddr, free, addrinfo};
use libc;
use std::rt::libc_heap::malloc_raw;
#[cfg(test)]
use libc::uintptr_t;
pub use self::errors::{EACCES, ECONNREFUSED, ECONNRESET, EPIPE, ECONNABORTED,
ECANCELED, EBADF, ENOTCONN, ENOENT, EADDRNOTAVAIL,
EADDRINUSE, EPERM};
pub static OK: c_int = 0;
pub static EOF: c_int = -4095;
pub static UNKNOWN: c_int = -4094;
// uv-errno.h redefines error codes for windows, but not for unix...
// https://github.com/joyent/libuv/blob/master/include/uv-errno.h
#[cfg(windows)]
pub mod errors {
use libc::c_int;
pub static EACCES: c_int = -4092;
pub static ECONNREFUSED: c_int = -4078;
pub static ECONNRESET: c_int = -4077;
pub static ENOENT: c_int = -4058;
pub static ENOTCONN: c_int = -4053;
pub static EPIPE: c_int = -4047;
pub static ECONNABORTED: c_int = -4079;
pub static ECANCELED: c_int = -4081;
pub static EBADF: c_int = -4083;
pub static EADDRNOTAVAIL: c_int = -4090;
pub static EADDRINUSE: c_int = -4091;
pub static EPERM: c_int = -4048;
}
#[cfg(not(windows))]
pub mod errors {
use libc;
use libc::c_int;
pub static EACCES: c_int = -libc::EACCES;
pub static ECONNREFUSED: c_int = -libc::ECONNREFUSED;
pub static ECONNRESET: c_int = -libc::ECONNRESET;
pub static ENOENT: c_int = -libc::ENOENT;
pub static ENOTCONN: c_int = -libc::ENOTCONN;
pub static EPIPE: c_int = -libc::EPIPE;
pub static ECONNABORTED: c_int = -libc::ECONNABORTED;
pub static ECANCELED : c_int = -libc::ECANCELED;
pub static EBADF : c_int = -libc::EBADF;
pub static EADDRNOTAVAIL : c_int = -libc::EADDRNOTAVAIL;
pub static EADDRINUSE : c_int = -libc::EADDRINUSE;
pub static EPERM: c_int = -libc::EPERM;
}
pub static PROCESS_SETUID: c_int = 1 << 0;
pub static PROCESS_SETGID: c_int = 1 << 1;
pub static PROCESS_WINDOWS_VERBATIM_ARGUMENTS: c_int = 1 << 2;
pub static PROCESS_DETACHED: c_int = 1 << 3;
pub static PROCESS_WINDOWS_HIDE: c_int = 1 << 4;
pub static STDIO_IGNORE: c_int = 0x00;
pub static STDIO_CREATE_PIPE: c_int = 0x01;
pub static STDIO_INHERIT_FD: c_int = 0x02;
pub static STDIO_INHERIT_STREAM: c_int = 0x04;
pub static STDIO_READABLE_PIPE: c_int = 0x10;
pub static STDIO_WRITABLE_PIPE: c_int = 0x20;
#[cfg(unix)]
pub type uv_buf_len_t = libc::size_t;
#[cfg(windows)]
pub type uv_buf_len_t = libc::c_ulong;
// see libuv/include/uv-unix.h
#[repr(C)]
#[cfg(unix)]
pub struct uv_buf_t {
pub base: *mut u8,
pub len: uv_buf_len_t,
}
#[cfg(unix)]
pub type uv_os_socket_t = c_int;
// see libuv/include/uv-win.h
#[cfg(windows)]
#[repr(C)]
pub struct uv_buf_t {
pub len: uv_buf_len_t,
pub base: *mut u8,
}
#[cfg(windows)]
pub type uv_os_socket_t = libc::SOCKET;
#[repr(C)]
pub enum uv_run_mode {
RUN_DEFAULT = 0,
RUN_ONCE,
RUN_NOWAIT,
}
#[repr(C)]
pub enum uv_poll_event {
UV_READABLE = 1,
UV_WRITABLE = 2,
}
#[repr(C)]
pub struct uv_process_options_t {
pub exit_cb: uv_exit_cb,
pub file: *const libc::c_char,
pub args: *const *const libc::c_char,
pub env: *const *const libc::c_char,
pub cwd: *const libc::c_char,
pub flags: libc::c_uint,
pub stdio_count: libc::c_int,
pub stdio: *mut uv_stdio_container_t,
pub uid: uv_uid_t,
pub gid: uv_gid_t,
}
// These fields are private because they must be interfaced with through the
// functions below.
#[repr(C)]
pub struct uv_stdio_container_t {
flags: libc::c_int,
stream: *mut uv_stream_t,
}
pub type uv_handle_t = c_void;
pub type uv_req_t = c_void;
pub type uv_loop_t = c_void;
pub type uv_idle_t = c_void;
pub type uv_tcp_t = c_void;
pub type uv_udp_t = c_void;
pub type uv_poll_t = c_void;
pub type uv_connect_t = c_void;
pub type uv_connection_t = c_void;
pub type uv_write_t = c_void;
pub type uv_async_t = c_void;
pub type uv_timer_t = c_void;
pub type uv_stream_t = c_void;
pub type uv_fs_t = c_void;
pub type uv_udp_send_t = c_void;
pub type uv_getaddrinfo_t = c_void;
pub type uv_process_t = c_void;
pub type uv_pipe_t = c_void;
pub type uv_tty_t = c_void;
pub type uv_signal_t = c_void;
pub type uv_shutdown_t = c_void;
#[repr(C)]
pub struct uv_timespec_t {
pub tv_sec: libc::c_long,
pub tv_nsec: libc::c_long
}
#[repr(C)]
pub struct uv_stat_t {
pub st_dev: libc::uint64_t,
pub st_mode: libc::uint64_t,
pub st_nlink: libc::uint64_t,
pub st_uid: libc::uint64_t,
pub st_gid: libc::uint64_t,
pub st_rdev: libc::uint64_t,
pub st_ino: libc::uint64_t,
pub st_size: libc::uint64_t,
pub st_blksize: libc::uint64_t,
pub st_blocks: libc::uint64_t,
pub st_flags: libc::uint64_t,
pub st_gen: libc::uint64_t,
pub st_atim: uv_timespec_t,
pub st_mtim: uv_timespec_t,
pub st_ctim: uv_timespec_t,
pub st_birthtim: uv_timespec_t
}
impl uv_stat_t {
pub fn new() -> uv_stat_t {
uv_stat_t {
st_dev: 0,
st_mode: 0,
st_nlink: 0,
st_uid: 0,
st_gid: 0,
st_rdev: 0,
st_ino: 0,
st_size: 0,
st_blksize: 0,
st_blocks: 0,
st_flags: 0,
st_gen: 0,
st_atim: uv_timespec_t { tv_sec: 0, tv_nsec: 0 },
st_mtim: uv_timespec_t { tv_sec: 0, tv_nsec: 0 },
st_ctim: uv_timespec_t { tv_sec: 0, tv_nsec: 0 },
st_birthtim: uv_timespec_t { tv_sec: 0, tv_nsec: 0 }
}
}
pub fn is_file(&self) -> bool {
((self.st_mode) & libc::S_IFMT as libc::uint64_t) == libc::S_IFREG as libc::uint64_t
}
pub fn is_dir(&self) -> bool {
((self.st_mode) & libc::S_IFMT as libc::uint64_t) == libc::S_IFDIR as libc::uint64_t
}
}
pub type uv_idle_cb = extern "C" fn(handle: *mut uv_idle_t);
pub type uv_alloc_cb = extern "C" fn(stream: *mut uv_stream_t,
suggested_size: size_t,
buf: *mut uv_buf_t);
pub type uv_read_cb = extern "C" fn(stream: *mut uv_stream_t,
nread: ssize_t,
buf: *const uv_buf_t);
pub type uv_udp_send_cb = extern "C" fn(req: *mut uv_udp_send_t,
status: c_int);
pub type uv_udp_recv_cb = extern "C" fn(handle: *mut uv_udp_t,
nread: ssize_t,
buf: *const uv_buf_t,
addr: *const sockaddr,
flags: c_uint);
pub type uv_close_cb = extern "C" fn(handle: *mut uv_handle_t);
pub type uv_poll_cb = extern "C" fn(handle: *mut uv_poll_t,
status: c_int,
events: c_int);
pub type uv_walk_cb = extern "C" fn(handle: *mut uv_handle_t,
arg: *mut c_void);
pub type uv_async_cb = extern "C" fn(handle: *mut uv_async_t);
pub type uv_connect_cb = extern "C" fn(handle: *mut uv_connect_t,
status: c_int);
pub type uv_connection_cb = extern "C" fn(handle: *mut uv_connection_t,
status: c_int);
pub type uv_timer_cb = extern "C" fn(handle: *mut uv_timer_t);
pub type uv_write_cb = extern "C" fn(handle: *mut uv_write_t,
status: c_int);
pub type uv_getaddrinfo_cb = extern "C" fn(req: *mut uv_getaddrinfo_t,
status: c_int,
res: *const addrinfo);
pub type uv_exit_cb = extern "C" fn(handle: *mut uv_process_t,
exit_status: i64,
term_signal: c_int);
pub type uv_signal_cb = extern "C" fn(handle: *mut uv_signal_t,
signum: c_int);
pub type uv_fs_cb = extern "C" fn(req: *mut uv_fs_t);
pub type uv_shutdown_cb = extern "C" fn(req: *mut uv_shutdown_t, status: c_int);
#[cfg(unix)] pub type uv_uid_t = libc::types::os::arch::posix88::uid_t;
#[cfg(unix)] pub type uv_gid_t = libc::types::os::arch::posix88::gid_t;
#[cfg(windows)] pub type uv_uid_t = libc::c_uchar;
#[cfg(windows)] pub type uv_gid_t = libc::c_uchar;
#[repr(C)]
#[deriving(PartialEq)]
pub enum uv_handle_type {
UV_UNKNOWN_HANDLE,
UV_ASYNC,
UV_CHECK,
UV_FS_EVENT,
UV_FS_POLL,
UV_HANDLE,
UV_IDLE,
UV_NAMED_PIPE,
UV_POLL,
UV_PREPARE,
UV_PROCESS,
UV_STREAM,
UV_TCP,
UV_TIMER,
UV_TTY,
UV_UDP,
UV_SIGNAL,
UV_FILE,
UV_HANDLE_TYPE_MAX
}
#[repr(C)]
#[cfg(unix)]
#[deriving(PartialEq)]
pub enum uv_req_type {
UV_UNKNOWN_REQ,
UV_REQ,
UV_CONNECT,
UV_WRITE,
UV_SHUTDOWN,
UV_UDP_SEND,
UV_FS,
UV_WORK,
UV_GETADDRINFO,
UV_GETNAMEINFO,
UV_REQ_TYPE_MAX
}
// uv_req_type may have additional fields defined by UV_REQ_TYPE_PRIVATE.
// See UV_REQ_TYPE_PRIVATE at libuv/include/uv-win.h
#[repr(C)]
#[cfg(windows)]
#[deriving(PartialEq)]
pub enum uv_req_type {
UV_UNKNOWN_REQ,
UV_REQ,
UV_CONNECT,
UV_WRITE,
UV_SHUTDOWN,
UV_UDP_SEND,
UV_FS,
UV_WORK,
UV_GETNAMEINFO,
UV_GETADDRINFO,
UV_ACCEPT,
UV_FS_EVENT_REQ,
UV_POLL_REQ,
UV_PROCESS_EXIT,
UV_READ,
UV_UDP_RECV,
UV_WAKEUP,
UV_SIGNAL_REQ,
UV_REQ_TYPE_MAX
}
#[repr(C)]
#[deriving(PartialEq)]
pub enum uv_membership {
UV_LEAVE_GROUP,
UV_JOIN_GROUP
}
pub unsafe fn malloc_handle(handle: uv_handle_type) -> *mut c_void {
assert!(handle != UV_UNKNOWN_HANDLE && handle != UV_HANDLE_TYPE_MAX);
let size = uv_handle_size(handle);
malloc_raw(size as uint) as *mut c_void
}
pub unsafe fn free_handle(v: *mut c_void) {
free(v as *mut c_void)
}
pub unsafe fn malloc_req(req: uv_req_type) -> *mut c_void {
assert!(req != UV_UNKNOWN_REQ && req != UV_REQ_TYPE_MAX);
let size = uv_req_size(req);
malloc_raw(size as uint) as *mut c_void
}
pub unsafe fn free_req(v: *mut c_void) {
free(v as *mut c_void)
}
#[test]
fn handle_sanity_check() {
unsafe {
assert_eq!(UV_HANDLE_TYPE_MAX as libc::uintptr_t, rust_uv_handle_type_max());
}
}
#[test]
fn request_sanity_check() {
unsafe {
assert_eq!(UV_REQ_TYPE_MAX as libc::uintptr_t, rust_uv_req_type_max());
}
}
// FIXME Event loops ignore SIGPIPE by default.
pub unsafe fn loop_new() -> *mut c_void {
return rust_uv_loop_new();
}
pub unsafe fn uv_write(req: *mut uv_write_t,
stream: *mut uv_stream_t,
buf_in: &[uv_buf_t],
cb: uv_write_cb) -> c_int {
extern {
fn uv_write(req: *mut uv_write_t, stream: *mut uv_stream_t,
buf_in: *const uv_buf_t, buf_cnt: c_int,
cb: uv_write_cb) -> c_int;
}
let buf_ptr = buf_in.as_ptr();
let buf_cnt = buf_in.len() as i32;
return uv_write(req, stream, buf_ptr, buf_cnt, cb);
}
pub unsafe fn uv_udp_send(req: *mut uv_udp_send_t,
handle: *mut uv_udp_t,
buf_in: &[uv_buf_t],
addr: *const sockaddr,
cb: uv_udp_send_cb) -> c_int {
extern {
fn uv_udp_send(req: *mut uv_write_t, stream: *mut uv_stream_t,
buf_in: *const uv_buf_t, buf_cnt: c_int,
addr: *const sockaddr,
cb: uv_udp_send_cb) -> c_int;
}
let buf_ptr = buf_in.as_ptr();
let buf_cnt = buf_in.len() as i32;
return uv_udp_send(req, handle, buf_ptr, buf_cnt, addr, cb);
}
pub unsafe fn get_udp_handle_from_send_req(send_req: *mut uv_udp_send_t) -> *mut uv_udp_t {
return rust_uv_get_udp_handle_from_send_req(send_req);
}
pub unsafe fn process_pid(p: *mut uv_process_t) -> c_int {
return rust_uv_process_pid(p);
}
pub unsafe fn set_stdio_container_flags(c: *mut uv_stdio_container_t,
flags: libc::c_int) {
rust_set_stdio_container_flags(c, flags);
}
pub unsafe fn set_stdio_container_fd(c: *mut uv_stdio_container_t,
fd: libc::c_int) {
rust_set_stdio_container_fd(c, fd);
}
pub unsafe fn set_stdio_container_stream(c: *mut uv_stdio_container_t,
stream: *mut uv_stream_t) {
rust_set_stdio_container_stream(c, stream);
}
// data access helpers
pub unsafe fn get_result_from_fs_req(req: *mut uv_fs_t) -> ssize_t {
rust_uv_get_result_from_fs_req(req)
}
pub unsafe fn get_ptr_from_fs_req(req: *mut uv_fs_t) -> *mut libc::c_void {
rust_uv_get_ptr_from_fs_req(req)
}
pub unsafe fn get_path_from_fs_req(req: *mut uv_fs_t) -> *mut c_char {
rust_uv_get_path_from_fs_req(req)
}
pub unsafe fn get_loop_from_fs_req(req: *mut uv_fs_t) -> *mut uv_loop_t {
rust_uv_get_loop_from_fs_req(req)
}
pub unsafe fn get_loop_from_getaddrinfo_req(req: *mut uv_getaddrinfo_t) -> *mut uv_loop_t {
rust_uv_get_loop_from_getaddrinfo_req(req)
}
pub unsafe fn get_loop_for_uv_handle<T>(handle: *mut T) -> *mut c_void {
return rust_uv_get_loop_for_uv_handle(handle as *mut c_void);
}
pub unsafe fn get_stream_handle_from_connect_req(connect: *mut uv_connect_t) -> *mut uv_stream_t {
return rust_uv_get_stream_handle_from_connect_req(connect);
}
pub unsafe fn get_stream_handle_from_write_req(write_req: *mut uv_write_t) -> *mut uv_stream_t {
return rust_uv_get_stream_handle_from_write_req(write_req);
}
pub unsafe fn get_data_for_uv_loop(loop_ptr: *mut c_void) -> *mut c_void {
rust_uv_get_data_for_uv_loop(loop_ptr)
}
pub unsafe fn set_data_for_uv_loop(loop_ptr: *mut c_void, data: *mut c_void) {
rust_uv_set_data_for_uv_loop(loop_ptr, data);
}
pub unsafe fn get_data_for_uv_handle<T>(handle: *mut T) -> *mut c_void {
return rust_uv_get_data_for_uv_handle(handle as *mut c_void);
}
pub unsafe fn set_data_for_uv_handle<T, U>(handle: *mut T, data: *mut U) {
rust_uv_set_data_for_uv_handle(handle as *mut c_void, data as *mut c_void);
}
pub unsafe fn get_data_for_req<T>(req: *mut T) -> *mut c_void {
return rust_uv_get_data_for_req(req as *mut c_void);
}
pub unsafe fn set_data_for_req<T, U>(req: *mut T, data: *mut U) {
rust_uv_set_data_for_req(req as *mut c_void, data as *mut c_void);
}
pub unsafe fn populate_stat(req_in: *mut uv_fs_t, stat_out: *mut uv_stat_t) {
rust_uv_populate_uv_stat(req_in, stat_out)
}
pub unsafe fn guess_handle(handle: c_int) -> c_int {
rust_uv_guess_handle(handle)
}
// uv_support is the result of compiling rust_uv.cpp
//
// Note that this is in a cfg'd block so it doesn't get linked during testing.
// There's a bit of a conundrum when testing in that we're actually assuming
// that the tests are running in a uv loop, but they were created from the
// statically linked uv to the original rustuv crate. When we create the test
// executable, on some platforms if we re-link against uv, it actually creates
// second copies of everything. We obviously don't want this, so instead of
// dying horribly during testing, we allow all of the test rustuv's references
// to get resolved to the original rustuv crate.
#[cfg(not(test))]
#[link(name = "uv_support", kind = "static")]
#[link(name = "uv", kind = "static")]
extern {}
extern {
fn rust_uv_loop_new() -> *mut c_void;
#[cfg(test)]
fn rust_uv_handle_type_max() -> uintptr_t;
#[cfg(test)]
fn rust_uv_req_type_max() -> uintptr_t;
fn rust_uv_get_udp_handle_from_send_req(req: *mut uv_udp_send_t) -> *mut uv_udp_t;
fn rust_uv_populate_uv_stat(req_in: *mut uv_fs_t, stat_out: *mut uv_stat_t);
fn rust_uv_get_result_from_fs_req(req: *mut uv_fs_t) -> ssize_t;
fn rust_uv_get_ptr_from_fs_req(req: *mut uv_fs_t) -> *mut libc::c_void;
fn rust_uv_get_path_from_fs_req(req: *mut uv_fs_t) -> *mut c_char;
fn rust_uv_get_loop_from_fs_req(req: *mut uv_fs_t) -> *mut uv_loop_t;
fn rust_uv_get_loop_from_getaddrinfo_req(req: *mut uv_fs_t) -> *mut uv_loop_t;
fn rust_uv_get_stream_handle_from_connect_req(req: *mut uv_connect_t) -> *mut uv_stream_t;
fn rust_uv_get_stream_handle_from_write_req(req: *mut uv_write_t) -> *mut uv_stream_t;
fn rust_uv_get_loop_for_uv_handle(handle: *mut c_void) -> *mut c_void;
fn rust_uv_get_data_for_uv_loop(loop_ptr: *mut c_void) -> *mut c_void;
fn rust_uv_set_data_for_uv_loop(loop_ptr: *mut c_void, data: *mut c_void);
fn rust_uv_get_data_for_uv_handle(handle: *mut c_void) -> *mut c_void;
fn rust_uv_set_data_for_uv_handle(handle: *mut c_void, data: *mut c_void);
fn rust_uv_get_data_for_req(req: *mut c_void) -> *mut c_void;
fn rust_uv_set_data_for_req(req: *mut c_void, data: *mut c_void);
fn rust_set_stdio_container_flags(c: *mut uv_stdio_container_t, flags: c_int);
fn rust_set_stdio_container_fd(c: *mut uv_stdio_container_t, fd: c_int);
fn rust_set_stdio_container_stream(c: *mut uv_stdio_container_t,
stream: *mut uv_stream_t);
fn rust_uv_process_pid(p: *mut uv_process_t) -> c_int;
fn rust_uv_guess_handle(fd: c_int) -> c_int;
// generic uv functions
pub fn uv_loop_delete(l: *mut uv_loop_t);
pub fn uv_ref(t: *mut uv_handle_t);
pub fn uv_unref(t: *mut uv_handle_t);
pub fn uv_handle_size(ty: uv_handle_type) -> size_t;
pub fn uv_req_size(ty: uv_req_type) -> size_t;
pub fn uv_run(l: *mut uv_loop_t, mode: uv_run_mode) -> c_int;
pub fn uv_close(h: *mut uv_handle_t, cb: uv_close_cb);
pub fn uv_walk(l: *mut uv_loop_t, cb: uv_walk_cb, arg: *mut c_void);
pub fn uv_buf_init(base: *mut c_char, len: c_uint) -> uv_buf_t;
pub fn uv_strerror(err: c_int) -> *const c_char;
pub fn uv_err_name(err: c_int) -> *const c_char;
pub fn uv_listen(s: *mut uv_stream_t, backlog: c_int,
cb: uv_connection_cb) -> c_int;
pub fn uv_accept(server: *mut uv_stream_t, client: *mut uv_stream_t) -> c_int;
pub fn uv_read_start(stream: *mut uv_stream_t,
on_alloc: uv_alloc_cb,
on_read: uv_read_cb) -> c_int;
pub fn uv_read_stop(stream: *mut uv_stream_t) -> c_int;
pub fn uv_shutdown(req: *mut uv_shutdown_t, handle: *mut uv_stream_t,
cb: uv_shutdown_cb) -> c_int;
// idle bindings
pub fn uv_idle_init(l: *mut uv_loop_t, i: *mut uv_idle_t) -> c_int;
pub fn uv_idle_start(i: *mut uv_idle_t, cb: uv_idle_cb) -> c_int;
pub fn uv_idle_stop(i: *mut uv_idle_t) -> c_int;
// async bindings
pub fn uv_async_init(l: *mut uv_loop_t, a: *mut uv_async_t,
cb: uv_async_cb) -> c_int;
pub fn uv_async_send(a: *mut uv_async_t);
// tcp bindings
pub fn uv_tcp_init(l: *mut uv_loop_t, h: *mut uv_tcp_t) -> c_int;
pub fn uv_tcp_connect(c: *mut uv_connect_t, h: *mut uv_tcp_t,
addr: *const sockaddr, cb: uv_connect_cb) -> c_int;
pub fn uv_tcp_bind(t: *mut uv_tcp_t,
addr: *const sockaddr,
flags: c_uint) -> c_int;
pub fn uv_tcp_nodelay(h: *mut uv_tcp_t, enable: c_int) -> c_int;
pub fn uv_tcp_keepalive(h: *mut uv_tcp_t, enable: c_int,
delay: c_uint) -> c_int;
pub fn uv_tcp_simultaneous_accepts(h: *mut uv_tcp_t, enable: c_int) -> c_int;
pub fn uv_tcp_getsockname(h: *const uv_tcp_t, name: *mut sockaddr,
len: *mut c_int) -> c_int;
pub fn uv_tcp_getpeername(h: *const uv_tcp_t, name: *mut sockaddr,
len: *mut c_int) -> c_int;
// udp bindings
pub fn uv_udp_init(l: *mut uv_loop_t, h: *mut uv_udp_t) -> c_int;
pub fn uv_udp_bind(h: *mut uv_udp_t, addr: *const sockaddr,
flags: c_uint) -> c_int;
pub fn uv_udp_recv_start(server: *mut uv_udp_t,
on_alloc: uv_alloc_cb,
on_recv: uv_udp_recv_cb) -> c_int;
pub fn uv_udp_set_membership(handle: *mut uv_udp_t,
multicast_addr: *const c_char,
interface_addr: *const c_char,
membership: uv_membership) -> c_int;
pub fn uv_udp_recv_stop(server: *mut uv_udp_t) -> c_int;
pub fn uv_udp_set_multicast_loop(handle: *mut uv_udp_t, on: c_int) -> c_int;
pub fn uv_udp_set_multicast_ttl(handle: *mut uv_udp_t, ttl: c_int) -> c_int;
pub fn uv_udp_set_ttl(handle: *mut uv_udp_t, ttl: c_int) -> c_int;
pub fn uv_udp_set_broadcast(handle: *mut uv_udp_t, on: c_int) -> c_int;
pub fn uv_udp_getsockname(h: *const uv_udp_t, name: *mut sockaddr,
len: *mut c_int) -> c_int;
// timer bindings
pub fn uv_timer_init(l: *mut uv_loop_t, t: *mut uv_timer_t) -> c_int;
pub fn uv_timer_start(t: *mut uv_timer_t, cb: uv_timer_cb,
timeout: libc::uint64_t,
repeat: libc::uint64_t) -> c_int;
pub fn uv_timer_stop(handle: *mut uv_timer_t) -> c_int;
// fs operations
pub fn uv_fs_open(loop_ptr: *mut uv_loop_t, req: *mut uv_fs_t,
path: *const c_char, flags: c_int, mode: c_int,
cb: uv_fs_cb) -> c_int;
pub fn uv_fs_unlink(loop_ptr: *mut uv_loop_t, req: *mut uv_fs_t,
path: *const c_char, cb: uv_fs_cb) -> c_int;
pub fn uv_fs_write(l: *mut uv_loop_t, req: *mut uv_fs_t, fd: c_int,
bufs: *const uv_buf_t, nbufs: c_uint,
offset: i64, cb: uv_fs_cb) -> c_int;
pub fn uv_fs_read(l: *mut uv_loop_t, req: *mut uv_fs_t, fd: c_int,
bufs: *mut uv_buf_t, nbufs: c_uint,
offset: i64, cb: uv_fs_cb) -> c_int;
pub fn uv_fs_close(l: *mut uv_loop_t, req: *mut uv_fs_t, fd: c_int,
cb: uv_fs_cb) -> c_int;
pub fn uv_fs_stat(l: *mut uv_loop_t, req: *mut uv_fs_t, path: *const c_char,
cb: uv_fs_cb) -> c_int;
pub fn uv_fs_fstat(l: *mut uv_loop_t, req: *mut uv_fs_t, fd: c_int,
cb: uv_fs_cb) -> c_int;
pub fn uv_fs_mkdir(l: *mut uv_loop_t, req: *mut uv_fs_t, path: *const c_char,
mode: c_int, cb: uv_fs_cb) -> c_int;
pub fn uv_fs_rmdir(l: *mut uv_loop_t, req: *mut uv_fs_t, path: *const c_char,
cb: uv_fs_cb) -> c_int;
pub fn uv_fs_readdir(l: *mut uv_loop_t, req: *mut uv_fs_t,
path: *const c_char, flags: c_int,
cb: uv_fs_cb) -> c_int;
pub fn uv_fs_req_cleanup(req: *mut uv_fs_t);
pub fn uv_fs_fsync(handle: *mut uv_loop_t, req: *mut uv_fs_t, file: c_int,
cb: uv_fs_cb) -> c_int;
pub fn uv_fs_fdatasync(handle: *mut uv_loop_t, req: *mut uv_fs_t, file: c_int,
cb: uv_fs_cb) -> c_int;
pub fn uv_fs_ftruncate(handle: *mut uv_loop_t, req: *mut uv_fs_t, file: c_int,
offset: i64, cb: uv_fs_cb) -> c_int;
pub fn uv_fs_readlink(handle: *mut uv_loop_t, req: *mut uv_fs_t,
file: *const c_char, cb: uv_fs_cb) -> c_int;
pub fn uv_fs_symlink(handle: *mut uv_loop_t, req: *mut uv_fs_t,
src: *const c_char, dst: *const c_char, flags: c_int,
cb: uv_fs_cb) -> c_int;
pub fn uv_fs_rename(handle: *mut uv_loop_t, req: *mut uv_fs_t,
src: *const c_char, dst: *const c_char,
cb: uv_fs_cb) -> c_int;
pub fn uv_fs_utime(handle: *mut uv_loop_t, req: *mut uv_fs_t,
path: *const c_char, atime: c_double, mtime: c_double,
cb: uv_fs_cb) -> c_int;
pub fn uv_fs_link(handle: *mut uv_loop_t, req: *mut uv_fs_t,
src: *const c_char, dst: *const c_char,
cb: uv_fs_cb) -> c_int;
pub fn uv_fs_chown(handle: *mut uv_loop_t, req: *mut uv_fs_t, src: *const c_char,
uid: uv_uid_t, gid: uv_gid_t, cb: uv_fs_cb) -> c_int;
pub fn uv_fs_chmod(handle: *mut uv_loop_t, req: *mut uv_fs_t,
path: *const c_char, mode: c_int, cb: uv_fs_cb) -> c_int;
pub fn uv_fs_lstat(handle: *mut uv_loop_t, req: *mut uv_fs_t,
file: *const c_char, cb: uv_fs_cb) -> c_int;
// poll bindings
pub fn uv_poll_init_socket(l: *mut uv_loop_t, h: *mut uv_poll_t, s: uv_os_socket_t) -> c_int;
pub fn uv_poll_start(h: *mut uv_poll_t, events: c_int, cb: uv_poll_cb) -> c_int;
pub fn uv_poll_stop(h: *mut uv_poll_t) -> c_int;
// getaddrinfo
pub fn uv_getaddrinfo(loop_: *mut uv_loop_t, req: *mut uv_getaddrinfo_t,
getaddrinfo_cb: uv_getaddrinfo_cb,
node: *const c_char, service: *const c_char,
hints: *const addrinfo) -> c_int;
pub fn uv_freeaddrinfo(ai: *mut addrinfo);
// process spawning
pub fn uv_spawn(loop_ptr: *mut uv_loop_t, outptr: *mut uv_process_t,
options: *mut uv_process_options_t) -> c_int;
pub fn uv_process_kill(p: *mut uv_process_t, signum: c_int) -> c_int;
pub fn uv_kill(pid: c_int, signum: c_int) -> c_int;
// pipes
pub fn uv_pipe_init(l: *mut uv_loop_t, p: *mut uv_pipe_t,
ipc: c_int) -> c_int;
pub fn uv_pipe_open(pipe: *mut uv_pipe_t, file: c_int) -> c_int;
pub fn uv_pipe_bind(pipe: *mut uv_pipe_t, name: *const c_char) -> c_int;
pub fn uv_pipe_connect(req: *mut uv_connect_t, handle: *mut uv_pipe_t,
name: *const c_char, cb: uv_connect_cb);
// tty
pub fn uv_tty_init(l: *mut uv_loop_t, tty: *mut uv_tty_t, fd: c_int,
readable: c_int) -> c_int;
pub fn uv_tty_set_mode(tty: *mut uv_tty_t, mode: c_int) -> c_int;
pub fn uv_tty_get_winsize(tty: *mut uv_tty_t,
width: *mut c_int,
height: *mut c_int) -> c_int;
// signals
pub fn uv_signal_init(loop_: *mut uv_loop_t,
handle: *mut uv_signal_t) -> c_int;
pub fn uv_signal_start(h: *mut uv_signal_t, cb: uv_signal_cb,
signum: c_int) -> c_int;
pub fn uv_signal_stop(handle: *mut uv_signal_t) -> c_int;
}
// libuv requires other native libraries on various platforms. These are all
// listed here (for each platform)
// libuv doesn't use pthread on windows
// android libc (bionic) provides pthread, so no additional link is required
#[cfg(not(any(windows, target_os = "android")))]
#[link(name = "pthread")]
extern {}
#[cfg(any(target_os = "linux", target_os = "dragonfly"))]
#[link(name = "rt")]
extern {}
#[cfg(target_os = "windows")]
#[link(name = "ws2_32")]
#[link(name = "psapi")]
#[link(name = "iphlpapi")]
extern {}
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
#[link(name = "kvm")]
extern {}