mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 06:44:35 +00:00
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:
parent
15966c3c1f
commit
002643dcf0
@ -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
|
||||
|
@ -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 |
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
1288
src/librustuv/net.rs
1288
src/librustuv/net.rs
File diff suppressed because it is too large
Load Diff
@ -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!()
|
||||
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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.
|
||||
}
|
||||
}
|
||||
}
|
@ -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() }
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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) };
|
||||
}
|
||||
}
|
@ -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() }
|
||||
}
|
||||
}
|
@ -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()
|
||||
};
|
||||
}
|
||||
}
|
@ -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_();
|
||||
}
|
||||
}
|
||||
}
|
@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
@ -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 {}
|
Loading…
Reference in New Issue
Block a user