From 002643dcf0693de43801f500a689b84580c1b94e Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Tue, 30 Sep 2014 22:25:22 -0700 Subject: [PATCH] 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] --- mk/crates.mk | 5 +- src/README.md | 2 - src/librustuv/access.rs | 173 ----- src/librustuv/addrinfo.rs | 143 ---- src/librustuv/async.rs | 156 ----- src/librustuv/file.rs | 581 ----------------- src/librustuv/homing.rs | 214 ------ src/librustuv/idle.rs | 206 ------ src/librustuv/lib.rs | 536 --------------- src/librustuv/macros.rs | 34 - src/librustuv/net.rs | 1288 ------------------------------------- src/librustuv/pipe.rs | 436 ------------- src/librustuv/process.rs | 324 ---------- src/librustuv/queue.rs | 185 ------ src/librustuv/rc.rs | 50 -- src/librustuv/signal.rs | 68 -- src/librustuv/stream.rs | 281 -------- src/librustuv/timeout.rs | 411 ------------ src/librustuv/timer.rs | 173 ----- src/librustuv/tty.rs | 136 ---- src/librustuv/uvio.rs | 326 ---------- src/librustuv/uvll.rs | 742 --------------------- 22 files changed, 2 insertions(+), 6468 deletions(-) delete mode 100644 src/librustuv/access.rs delete mode 100644 src/librustuv/addrinfo.rs delete mode 100644 src/librustuv/async.rs delete mode 100644 src/librustuv/file.rs delete mode 100644 src/librustuv/homing.rs delete mode 100644 src/librustuv/idle.rs delete mode 100644 src/librustuv/lib.rs delete mode 100644 src/librustuv/macros.rs delete mode 100644 src/librustuv/net.rs delete mode 100644 src/librustuv/pipe.rs delete mode 100644 src/librustuv/process.rs delete mode 100644 src/librustuv/queue.rs delete mode 100644 src/librustuv/rc.rs delete mode 100644 src/librustuv/signal.rs delete mode 100644 src/librustuv/stream.rs delete mode 100644 src/librustuv/timeout.rs delete mode 100644 src/librustuv/timer.rs delete mode 100644 src/librustuv/tty.rs delete mode 100644 src/librustuv/uvio.rs delete mode 100644 src/librustuv/uvll.rs diff --git a/mk/crates.mk b/mk/crates.mk index 9f01ff23c7f..1da124fb563 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -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 diff --git a/src/README.md b/src/README.md index 4c95ae94e0e..1bfa2641b4a 100644 --- a/src/README.md +++ b/src/README.md @@ -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 | diff --git a/src/librustuv/access.rs b/src/librustuv/access.rs deleted file mode 100644 index b7475c8c077..00000000000 --- a/src/librustuv/access.rs +++ /dev/null @@ -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 or the MIT license -// , 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 { - inner: Arc>>, -} - -pub struct Guard<'a, T:'static> { - access: &'a mut Access, - missile: Option, -} - -struct Inner { - queue: Vec<(BlockedTask, uint)>, - held: bool, - closed: bool, - data: T, -} - -impl Access { - pub fn new(data: T) -> Access { - 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 = 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 { - 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 Clone for Access { - fn clone(&self) -> Access { - 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 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 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 = 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 Drop for Inner { - fn drop(&mut self) { - assert!(!self.held); - assert_eq!(self.queue.len(), 0); - } -} diff --git a/src/librustuv/addrinfo.rs b/src/librustuv/addrinfo.rs deleted file mode 100644 index 609ce016d38..00000000000 --- a/src/librustuv/addrinfo.rs +++ /dev/null @@ -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 or the MIT license -// , 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, - status: c_int, - addrinfo: Option, -} - -pub struct GetAddrInfoRequest; - -impl GetAddrInfoRequest { - pub fn run(loop_: &Loop, node: Option<&str>, service: Option<&str>, - hints: Option) - -> Result, 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 { - 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 - } -} diff --git a/src/librustuv/async.rs b/src/librustuv/async.rs deleted file mode 100644 index 8bed4cfb811..00000000000 --- a/src/librustuv/async.rs +++ /dev/null @@ -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 or the MIT license -// , 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>, -} - -struct Payload { - callback: Box, - exit_flag: Arc>, -} - -impl AsyncWatcher { - pub fn new(loop_: &mut Loop, cb: Box) -> AsyncWatcher { - let handle = UvHandle::alloc(None::, 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 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 = 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>); - 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(); - } -} diff --git a/src/librustuv/file.rs b/src/librustuv/file.rs deleted file mode 100644 index f42f42d2111..00000000000 --- a/src/librustuv/file.rs +++ /dev/null @@ -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 or the MIT license -// , 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 - { - 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 - { - 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 { - 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 { - 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 - { - 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, 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 { - 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 -{ - 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 = 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 { - 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{ - 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 { - 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 { - 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 { - 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 { - 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 { - 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) {} - 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()); - } -} diff --git a/src/librustuv/homing.rs b/src/librustuv/homing.rs deleted file mode 100644 index 55d9811ccad..00000000000 --- a/src/librustuv/homing.rs +++ /dev/null @@ -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 or the MIT license -// , 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 = 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(); - } -} diff --git a/src/librustuv/idle.rs b/src/librustuv/idle.rs deleted file mode 100644 index 7b9a2fcf444..00000000000 --- a/src/librustuv/idle.rs +++ /dev/null @@ -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 or the MIT license -// , 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, -} - -impl IdleWatcher { - pub fn new(loop_: &mut Loop, cb: Box) -> Box { - let handle = UvHandle::alloc(None::, 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::, 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 = 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 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, uint)>>; - - struct MyCallback(Rc, 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, Chan) { - let rc = Rc::new(RefCell::new((None, 0))); - let cb = box MyCallback(rc.clone(), v); - let cb = cb as Box; - let cb = unsafe { mem::transmute(cb) }; - (IdleWatcher::new(&mut local_loop().loop_, cb), rc) - } - - fn sleep(chan: &Chan) -> uint { - let task: Box = 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); - } -} diff --git a/src/librustuv/lib.rs b/src/librustuv/lib.rs deleted file mode 100644 index 44cfafe3074..00000000000 --- a/src/librustuv/lib.rs +++ /dev/null @@ -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 or the MIT license -// , 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 { - box uvio::UvEventLoop::new() as Box -} - -/// A type that wraps a uv handle -pub trait UvHandle { - 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, 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) -> Box { - unsafe { - let myptr = mem::transmute::<&Box, &*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 = 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, - loop_: &Loop, - f: ||) { - let _f = ForbidUnwind::new("wait_until_woken_after"); - unsafe { - assert!((*slot).is_none()); - let task: Box = Local::take(); - loop_.modify_blockers(1); - task.deschedule(1, |task| { - *slot = Some(task); - f(); - Ok(()) - }); - loop_.modify_blockers(-1); - } -} - -fn wakeup(slot: &mut Option) { - 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(&self, t: *mut T) { - unsafe { uvll::set_data_for_req(self.handle, t) } - } - - pub unsafe fn get_data(&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 { - 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::); - 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(); - } -} diff --git a/src/librustuv/macros.rs b/src/librustuv/macros.rs deleted file mode 100644 index deb7036848f..00000000000 --- a/src/librustuv/macros.rs +++ /dev/null @@ -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 or the MIT license -// , 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); -} diff --git a/src/librustuv/net.rs b/src/librustuv/net.rs deleted file mode 100644 index 3531d162db0..00000000000 --- a/src/librustuv/net.rs +++ /dev/null @@ -1,1288 +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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use libc::{size_t, ssize_t, c_int, c_void, c_uint}; -use libc; -use std::mem; -use std::ptr; -use std::rt::rtio; -use std::rt::rtio::IoError; -use std::rt::task::BlockedTask; - -use homing::{HomingIO, HomeHandle}; -use rc::Refcount; -use stream::StreamWatcher; -use super::{Loop, Request, UvError, Buf, status_to_io_result, - uv_error_to_io_error, UvHandle, slice_to_uv_buf, - wait_until_woken_after, wakeup}; -use timeout::{AccessTimeout, ConnectCtx, AcceptTimeout}; -use uvio::UvIoFactory; -use uvll; - -//////////////////////////////////////////////////////////////////////////////// -/// Generic functions related to dealing with sockaddr things -//////////////////////////////////////////////////////////////////////////////// - -pub fn htons(u: u16) -> u16 { u.to_be() } -pub fn ntohs(u: u16) -> u16 { Int::from_be(u) } - -pub fn sockaddr_to_addr(storage: &libc::sockaddr_storage, - len: uint) -> rtio::SocketAddr { - match storage.ss_family as c_int { - libc::AF_INET => { - assert!(len as uint >= mem::size_of::()); - let storage: &libc::sockaddr_in = unsafe { - mem::transmute(storage) - }; - let ip = (storage.sin_addr.s_addr as u32).to_be(); - let a = (ip >> 24) as u8; - let b = (ip >> 16) as u8; - let c = (ip >> 8) as u8; - let d = (ip >> 0) as u8; - rtio::SocketAddr { - ip: rtio::Ipv4Addr(a, b, c, d), - port: ntohs(storage.sin_port), - } - } - libc::AF_INET6 => { - assert!(len as uint >= mem::size_of::()); - let storage: &libc::sockaddr_in6 = unsafe { - mem::transmute(storage) - }; - let a = ntohs(storage.sin6_addr.s6_addr[0]); - let b = ntohs(storage.sin6_addr.s6_addr[1]); - let c = ntohs(storage.sin6_addr.s6_addr[2]); - let d = ntohs(storage.sin6_addr.s6_addr[3]); - let e = ntohs(storage.sin6_addr.s6_addr[4]); - let f = ntohs(storage.sin6_addr.s6_addr[5]); - let g = ntohs(storage.sin6_addr.s6_addr[6]); - let h = ntohs(storage.sin6_addr.s6_addr[7]); - rtio::SocketAddr { - ip: rtio::Ipv6Addr(a, b, c, d, e, f, g, h), - port: ntohs(storage.sin6_port), - } - } - n => { - fail!("unknown family {}", n); - } - } -} - -fn addr_to_sockaddr(addr: rtio::SocketAddr, - storage: &mut libc::sockaddr_storage) - -> libc::socklen_t { - unsafe { - let len = match addr.ip { - rtio::Ipv4Addr(a, b, c, d) => { - let ip = (a as u32 << 24) | - (b as u32 << 16) | - (c as u32 << 8) | - (d as u32 << 0); - let storage = storage as *mut _ as *mut libc::sockaddr_in; - (*storage).sin_family = libc::AF_INET as libc::sa_family_t; - (*storage).sin_port = htons(addr.port); - (*storage).sin_addr = libc::in_addr { - s_addr: Int::from_be(ip), - - }; - mem::size_of::() - } - rtio::Ipv6Addr(a, b, c, d, e, f, g, h) => { - let storage = storage as *mut _ as *mut libc::sockaddr_in6; - (*storage).sin6_family = libc::AF_INET6 as libc::sa_family_t; - (*storage).sin6_port = htons(addr.port); - (*storage).sin6_addr = libc::in6_addr { - s6_addr: [ - htons(a), - htons(b), - htons(c), - htons(d), - htons(e), - htons(f), - htons(g), - htons(h), - ] - }; - mem::size_of::() - } - }; - return len as libc::socklen_t - } -} - -enum SocketNameKind { - TcpPeer, - Tcp, - Udp -} - -fn socket_name(sk: SocketNameKind, - handle: *mut c_void) -> Result { - let getsockname = match sk { - TcpPeer => uvll::uv_tcp_getpeername, - Tcp => uvll::uv_tcp_getsockname, - Udp => uvll::uv_udp_getsockname, - }; - - // Allocate a sockaddr_storage since we don't know if it's ipv4 or ipv6 - let mut sockaddr: libc::sockaddr_storage = unsafe { mem::zeroed() }; - let mut namelen = mem::size_of::() as c_int; - - let sockaddr_p = &mut sockaddr as *mut libc::sockaddr_storage; - match unsafe { - getsockname(&*handle, sockaddr_p as *mut libc::sockaddr, &mut namelen) - } { - 0 => Ok(sockaddr_to_addr(&sockaddr, namelen as uint)), - n => Err(uv_error_to_io_error(UvError(n))) - } -} - -//////////////////////////////////////////////////////////////////////////////// -/// TCP implementation -//////////////////////////////////////////////////////////////////////////////// - -pub struct TcpWatcher { - handle: *mut uvll::uv_tcp_t, - stream: StreamWatcher, - home: HomeHandle, - refcount: Refcount, - - // libuv can't support concurrent reads and concurrent writes of the same - // stream object, so we use these access guards in order to arbitrate among - // multiple concurrent reads and writes. Note that libuv *can* read and - // write simultaneously, it just can't read and read simultaneously. - read_access: AccessTimeout<()>, - write_access: AccessTimeout<()>, -} - -pub struct TcpListener { - home: HomeHandle, - handle: *mut uvll::uv_tcp_t, -} - -pub struct TcpAcceptor { - home: HomeHandle, - handle: *mut uvll::uv_tcp_t, - access: AcceptTimeout>, - refcount: Refcount, -} - -// TCP watchers (clients/streams) - -impl TcpWatcher { - pub fn new(io: &mut UvIoFactory) -> TcpWatcher { - let handle = io.make_handle(); - TcpWatcher::new_home(&io.loop_, handle) - } - - fn new_home(loop_: &Loop, home: HomeHandle) -> TcpWatcher { - let handle = unsafe { uvll::malloc_handle(uvll::UV_TCP) }; - assert_eq!(unsafe { - uvll::uv_tcp_init(loop_.handle, handle) - }, 0); - TcpWatcher { - home: home, - handle: handle, - stream: StreamWatcher::new(handle, true), - refcount: Refcount::new(), - read_access: AccessTimeout::new(()), - write_access: AccessTimeout::new(()), - } - } - - pub fn connect(io: &mut UvIoFactory, - address: rtio::SocketAddr, - timeout: Option) -> Result { - let tcp = TcpWatcher::new(io); - let cx = ConnectCtx { status: -1, task: None, timer: None }; - let mut storage = unsafe { mem::zeroed() }; - let _len = addr_to_sockaddr(address, &mut storage); - let addr_p = &storage as *const _ as *const libc::sockaddr; - cx.connect(tcp, timeout, io, |req, tcp, cb| { - unsafe { uvll::uv_tcp_connect(req.handle, tcp.handle, addr_p, cb) } - }) - } -} - -impl HomingIO for TcpWatcher { - fn home<'r>(&'r mut self) -> &'r mut HomeHandle { &mut self.home } -} - -impl rtio::RtioSocket for TcpWatcher { - fn socket_name(&mut self) -> Result { - let _m = self.fire_homing_missile(); - socket_name(Tcp, self.handle) - } -} - -impl rtio::RtioTcpStream for TcpWatcher { - fn read(&mut self, buf: &mut [u8]) -> Result { - 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]) -> Result<(), IoError> { - 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 peer_name(&mut self) -> Result { - let _m = self.fire_homing_missile(); - socket_name(TcpPeer, self.handle) - } - - fn control_congestion(&mut self) -> Result<(), IoError> { - let _m = self.fire_homing_missile(); - status_to_io_result(unsafe { - uvll::uv_tcp_nodelay(self.handle, 0 as c_int) - }) - } - - fn nodelay(&mut self) -> Result<(), IoError> { - let _m = self.fire_homing_missile(); - status_to_io_result(unsafe { - uvll::uv_tcp_nodelay(self.handle, 1 as c_int) - }) - } - - fn keepalive(&mut self, delay_in_seconds: uint) -> Result<(), IoError> { - let _m = self.fire_homing_missile(); - status_to_io_result(unsafe { - uvll::uv_tcp_keepalive(self.handle, 1 as c_int, - delay_in_seconds as c_uint) - }) - } - - fn letdie(&mut self) -> Result<(), IoError> { - let _m = self.fire_homing_missile(); - status_to_io_result(unsafe { - uvll::uv_tcp_keepalive(self.handle, 0 as c_int, 0 as c_uint) - }) - } - - fn clone(&self) -> Box { - box TcpWatcher { - handle: self.handle, - stream: StreamWatcher::new(self.handle, false), - home: self.home.clone(), - refcount: self.refcount.clone(), - read_access: self.read_access.clone(), - write_access: self.write_access.clone(), - } as Box - } - - fn close_read(&mut self) -> Result<(), IoError> { - // see comments in PipeWatcher::close_read - 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) -> Result<(), IoError> { - let _m = self.fire_homing_missile(); - shutdown(self.handle, &self.uv_loop()) - } - - fn set_timeout(&mut self, timeout: Option) { - self.set_read_timeout(timeout); - self.set_write_timeout(timeout); - } - - fn set_read_timeout(&mut self, ms: Option) { - 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 { - let stream: &mut StreamWatcher = unsafe { mem::transmute(stream) }; - stream.cancel_read(uvll::ECANCELED as ssize_t) - } - } - - fn set_write_timeout(&mut self, ms: Option) { - 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 { - let stream: &mut StreamWatcher = unsafe { mem::transmute(stream) }; - stream.cancel_write() - } - } -} - -impl UvHandle for TcpWatcher { - fn uv_handle(&self) -> *mut uvll::uv_tcp_t { self.stream.handle } -} - -impl Drop for TcpWatcher { - fn drop(&mut self) { - let _m = self.fire_homing_missile(); - if self.refcount.decrement() { - self.close(); - } - } -} - -// TCP listeners (unbound servers) - -impl TcpListener { - pub fn bind(io: &mut UvIoFactory, address: rtio::SocketAddr) - -> Result, UvError> { - let handle = unsafe { uvll::malloc_handle(uvll::UV_TCP) }; - assert_eq!(unsafe { - uvll::uv_tcp_init(io.uv_loop(), handle) - }, 0); - let l = box TcpListener { - home: io.make_handle(), - handle: handle, - }; - let mut storage = unsafe { mem::zeroed() }; - let _len = addr_to_sockaddr(address, &mut storage); - let res = unsafe { - let addr_p = &storage as *const _ as *const libc::sockaddr; - uvll::uv_tcp_bind(l.handle, addr_p, 0) - }; - return match res { - 0 => Ok(l.install()), - n => Err(UvError(n)) - }; - } -} - -impl HomingIO for TcpListener { - fn home<'r>(&'r mut self) -> &'r mut HomeHandle { &mut self.home } -} - -impl UvHandle for TcpListener { - fn uv_handle(&self) -> *mut uvll::uv_tcp_t { self.handle } -} - -impl rtio::RtioSocket for TcpListener { - fn socket_name(&mut self) -> Result { - let _m = self.fire_homing_missile(); - socket_name(Tcp, self.handle) - } -} - -impl rtio::RtioTcpListener for TcpListener { - fn listen(mut self: Box) - -> Result, IoError> { - let _m = self.fire_homing_missile(); - - // create the acceptor object from ourselves - let acceptor = (box TcpAcceptor { - handle: self.handle, - home: self.home.clone(), - access: AcceptTimeout::new(), - refcount: Refcount::new(), - }).install(); - self.handle = 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), - n => Err(uv_error_to_io_error(UvError(n))), - } - } -} - -extern fn listen_cb(server: *mut uvll::uv_stream_t, status: c_int) { - assert!(status != uvll::ECANCELED); - let tcp: &mut TcpAcceptor = 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 = TcpWatcher::new_home(&loop_, tcp.home().clone()); - assert_eq!(unsafe { uvll::uv_accept(server, client.handle) }, 0); - Ok(box client as Box) - } - 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 { tcp.access.push(msg); } -} - -impl Drop for TcpListener { - fn drop(&mut self) { - if self.handle.is_null() { return } - - let _m = self.fire_homing_missile(); - self.close(); - } -} - -// TCP acceptors (bound servers) - -impl HomingIO for TcpAcceptor { - fn home<'r>(&'r mut self) -> &'r mut HomeHandle { &mut self.home } -} - -impl rtio::RtioSocket for TcpAcceptor { - fn socket_name(&mut self) -> Result { - let _m = self.fire_homing_missile(); - socket_name(Tcp, self.handle) - } -} - -impl UvHandle for TcpAcceptor { - fn uv_handle(&self) -> *mut uvll::uv_tcp_t { self.handle } -} - -impl rtio::RtioTcpAcceptor for TcpAcceptor { - fn accept(&mut self) -> Result, IoError> { - let m = self.fire_homing_missile(); - let loop_ = self.uv_loop(); - self.access.accept(m, &loop_) - } - - fn accept_simultaneously(&mut self) -> Result<(), IoError> { - let _m = self.fire_homing_missile(); - status_to_io_result(unsafe { - uvll::uv_tcp_simultaneous_accepts(self.handle, 1) - }) - } - - fn dont_accept_simultaneously(&mut self) -> Result<(), IoError> { - let _m = self.fire_homing_missile(); - status_to_io_result(unsafe { - uvll::uv_tcp_simultaneous_accepts(self.handle, 0) - }) - } - - fn set_timeout(&mut self, ms: Option) { - let _m = self.fire_homing_missile(); - let loop_ = self.uv_loop(); - self.access.set_timeout(ms, &loop_, &self.home); - } - - fn clone(&self) -> Box { - box TcpAcceptor { - refcount: self.refcount.clone(), - home: self.home.clone(), - handle: self.handle, - access: self.access.clone(), - } as Box - } - - fn close_accept(&mut self) -> Result<(), IoError> { - let m = self.fire_homing_missile(); - self.access.close(m); - Ok(()) - } -} - -impl Drop for TcpAcceptor { - fn drop(&mut self) { - let _m = self.fire_homing_missile(); - if self.refcount.decrement() { - self.close(); - } - } -} - -//////////////////////////////////////////////////////////////////////////////// -/// UDP implementation -//////////////////////////////////////////////////////////////////////////////// - -pub struct UdpWatcher { - handle: *mut uvll::uv_udp_t, - home: HomeHandle, - - // See above for what these fields are - refcount: Refcount, - read_access: AccessTimeout<()>, - write_access: AccessTimeout<()>, - - blocked_sender: Option, -} - -struct UdpRecvCtx { - task: Option, - buf: Option, - result: Option<(ssize_t, Option)>, -} - -struct UdpSendCtx { - result: c_int, - data: Option>, - udp: *mut UdpWatcher, -} - -impl UdpWatcher { - pub fn bind(io: &mut UvIoFactory, address: rtio::SocketAddr) - -> Result { - let udp = UdpWatcher { - handle: unsafe { uvll::malloc_handle(uvll::UV_UDP) }, - home: io.make_handle(), - refcount: Refcount::new(), - read_access: AccessTimeout::new(()), - write_access: AccessTimeout::new(()), - blocked_sender: None, - }; - assert_eq!(unsafe { - uvll::uv_udp_init(io.uv_loop(), udp.handle) - }, 0); - let mut storage = unsafe { mem::zeroed() }; - let _len = addr_to_sockaddr(address, &mut storage); - let result = unsafe { - let addr_p = &storage as *const _ as *const libc::sockaddr; - uvll::uv_udp_bind(udp.handle, addr_p, 0u32) - }; - return match result { - 0 => Ok(udp), - n => Err(UvError(n)), - }; - } -} - -impl UvHandle for UdpWatcher { - fn uv_handle(&self) -> *mut uvll::uv_udp_t { self.handle } -} - -impl HomingIO for UdpWatcher { - fn home<'r>(&'r mut self) -> &'r mut HomeHandle { &mut self.home } -} - -impl rtio::RtioSocket for UdpWatcher { - fn socket_name(&mut self) -> Result { - let _m = self.fire_homing_missile(); - socket_name(Udp, self.handle) - } -} - -impl rtio::RtioUdpSocket for UdpWatcher { - fn recv_from(&mut self, buf: &mut [u8]) - -> Result<(uint, rtio::SocketAddr), IoError> - { - let loop_ = self.uv_loop(); - let m = self.fire_homing_missile(); - let _guard = try!(self.read_access.grant(m)); - - return match unsafe { - uvll::uv_udp_recv_start(self.handle, alloc_cb, recv_cb) - } { - 0 => { - let mut cx = UdpRecvCtx { - task: None, - buf: Some(slice_to_uv_buf(buf)), - result: None, - }; - let handle = self.handle; - wait_until_woken_after(&mut cx.task, &loop_, || { - unsafe { uvll::set_data_for_uv_handle(handle, &mut cx) } - }); - match cx.result.take().unwrap() { - (n, _) if n < 0 => - Err(uv_error_to_io_error(UvError(n as c_int))), - (n, addr) => Ok((n as uint, addr.unwrap())) - } - } - n => Err(uv_error_to_io_error(UvError(n))) - }; - - extern fn alloc_cb(handle: *mut uvll::uv_udp_t, - _suggested_size: size_t, - buf: *mut Buf) { - unsafe { - let cx = uvll::get_data_for_uv_handle(handle); - let cx = &mut *(cx as *mut UdpRecvCtx); - *buf = cx.buf.take().expect("recv alloc_cb called more than once") - } - } - - extern fn recv_cb(handle: *mut uvll::uv_udp_t, nread: ssize_t, - buf: *const Buf, - addr: *const libc::sockaddr, _flags: c_uint) { - assert!(nread != uvll::ECANCELED as ssize_t); - let cx = unsafe { - &mut *(uvll::get_data_for_uv_handle(handle) as *mut UdpRecvCtx) - }; - - // When there's no data to read the recv callback can be a no-op. - // This can happen if read returns EAGAIN/EWOULDBLOCK. By ignoring - // this we just drop back to kqueue and wait for the next callback. - if nread == 0 { - cx.buf = Some(unsafe { *buf }); - return - } - - unsafe { assert_eq!(uvll::uv_udp_recv_stop(handle), 0) } - let addr = if addr == ptr::null() { - None - } else { - let len = mem::size_of::(); - Some(sockaddr_to_addr(unsafe { mem::transmute(addr) }, len)) - }; - cx.result = Some((nread, addr)); - wakeup(&mut cx.task); - } - } - - fn send_to(&mut self, buf: &[u8], dst: rtio::SocketAddr) -> Result<(), IoError> { - let m = self.fire_homing_missile(); - let loop_ = self.uv_loop(); - let guard = try!(self.write_access.grant(m)); - - let mut req = Request::new(uvll::UV_UDP_SEND); - let mut storage = unsafe { mem::zeroed() }; - let _len = addr_to_sockaddr(dst, &mut storage); - let addr_p = &storage as *const _ as *const libc::sockaddr; - - // see comments in StreamWatcher::write for why we may allocate a buffer - // here. - let data = if guard.can_timeout {Some(buf.to_vec())} else {None}; - let uv_buf = if guard.can_timeout { - slice_to_uv_buf(data.as_ref().unwrap().as_slice()) - } else { - slice_to_uv_buf(buf) - }; - - return match unsafe { - uvll::uv_udp_send(req.handle, self.handle, [uv_buf], addr_p, send_cb) - } { - 0 => { - req.defuse(); // uv callback now owns this request - let mut cx = UdpSendCtx { - result: uvll::ECANCELED, data: data, udp: self as *mut _ - }; - wait_until_woken_after(&mut self.blocked_sender, &loop_, || { - req.set_data(&mut cx); - }); - - if cx.result != uvll::ECANCELED { - return match cx.result { - 0 => Ok(()), - n => Err(uv_error_to_io_error(UvError(n))) - } - } - let mut new_cx = box UdpSendCtx { - result: 0, - udp: 0 as *mut UdpWatcher, - data: cx.data.take(), - }; - unsafe { - req.set_data(&mut *new_cx); - mem::forget(new_cx); - } - Err(uv_error_to_io_error(UvError(cx.result))) - } - n => Err(uv_error_to_io_error(UvError(n))) - }; - - // This function is the same as stream::write_cb, but adapted for udp - // instead of streams. - extern fn send_cb(req: *mut uvll::uv_udp_send_t, status: c_int) { - let req = Request::wrap(req); - let cx: &mut UdpSendCtx = unsafe { req.get_data() }; - cx.result = status; - - if cx.udp as uint != 0 { - let udp: &mut UdpWatcher = unsafe { &mut *cx.udp }; - wakeup(&mut udp.blocked_sender); - } else { - let _cx: Box = unsafe { mem::transmute(cx) }; - } - } - } - - fn join_multicast(&mut self, multi: rtio::IpAddr) -> Result<(), IoError> { - let _m = self.fire_homing_missile(); - status_to_io_result(unsafe { - multi.to_string().with_c_str(|m_addr| { - uvll::uv_udp_set_membership(self.handle, - m_addr, ptr::null(), - uvll::UV_JOIN_GROUP) - }) - }) - } - - fn leave_multicast(&mut self, multi: rtio::IpAddr) -> Result<(), IoError> { - let _m = self.fire_homing_missile(); - status_to_io_result(unsafe { - multi.to_string().with_c_str(|m_addr| { - uvll::uv_udp_set_membership(self.handle, - m_addr, ptr::null(), - uvll::UV_LEAVE_GROUP) - }) - }) - } - - fn loop_multicast_locally(&mut self) -> Result<(), IoError> { - let _m = self.fire_homing_missile(); - status_to_io_result(unsafe { - uvll::uv_udp_set_multicast_loop(self.handle, - 1 as c_int) - }) - } - - fn dont_loop_multicast_locally(&mut self) -> Result<(), IoError> { - let _m = self.fire_homing_missile(); - status_to_io_result(unsafe { - uvll::uv_udp_set_multicast_loop(self.handle, - 0 as c_int) - }) - } - - fn multicast_time_to_live(&mut self, ttl: int) -> Result<(), IoError> { - let _m = self.fire_homing_missile(); - status_to_io_result(unsafe { - uvll::uv_udp_set_multicast_ttl(self.handle, - ttl as c_int) - }) - } - - fn time_to_live(&mut self, ttl: int) -> Result<(), IoError> { - let _m = self.fire_homing_missile(); - status_to_io_result(unsafe { - uvll::uv_udp_set_ttl(self.handle, ttl as c_int) - }) - } - - fn hear_broadcasts(&mut self) -> Result<(), IoError> { - let _m = self.fire_homing_missile(); - status_to_io_result(unsafe { - uvll::uv_udp_set_broadcast(self.handle, - 1 as c_int) - }) - } - - fn ignore_broadcasts(&mut self) -> Result<(), IoError> { - let _m = self.fire_homing_missile(); - status_to_io_result(unsafe { - uvll::uv_udp_set_broadcast(self.handle, - 0 as c_int) - }) - } - - fn clone(&self) -> Box { - box UdpWatcher { - handle: self.handle, - home: self.home.clone(), - refcount: self.refcount.clone(), - write_access: self.write_access.clone(), - read_access: self.read_access.clone(), - blocked_sender: None, - } as Box - } - - fn set_timeout(&mut self, timeout: Option) { - self.set_read_timeout(timeout); - self.set_write_timeout(timeout); - } - - fn set_read_timeout(&mut self, ms: Option) { - let _m = self.fire_homing_missile(); - let loop_ = self.uv_loop(); - self.read_access.set_timeout(ms, &self.home, &loop_, cancel_read, - self.handle as uint); - - fn cancel_read(stream: uint) -> Option { - // This method is quite similar to StreamWatcher::cancel_read, see - // there for more information - let handle = stream as *mut uvll::uv_udp_t; - assert_eq!(unsafe { uvll::uv_udp_recv_stop(handle) }, 0); - let data = unsafe { - let data = uvll::get_data_for_uv_handle(handle); - if data.is_null() { return None } - uvll::set_data_for_uv_handle(handle, 0 as *mut int); - &mut *(data as *mut UdpRecvCtx) - }; - data.result = Some((uvll::ECANCELED as ssize_t, None)); - data.task.take() - } - } - - fn set_write_timeout(&mut self, ms: Option) { - let _m = self.fire_homing_missile(); - let loop_ = self.uv_loop(); - self.write_access.set_timeout(ms, &self.home, &loop_, cancel_write, - self as *mut _ as uint); - - fn cancel_write(stream: uint) -> Option { - let stream: &mut UdpWatcher = unsafe { mem::transmute(stream) }; - stream.blocked_sender.take() - } - } -} - -impl Drop for UdpWatcher { - fn drop(&mut self) { - // Send ourselves home to close this handle (blocking while doing so). - let _m = self.fire_homing_missile(); - if self.refcount.decrement() { - self.close(); - } - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Shutdown helper -//////////////////////////////////////////////////////////////////////////////// - -pub fn shutdown(handle: *mut uvll::uv_stream_t, loop_: &Loop) -> Result<(), IoError> { - struct Ctx { - slot: Option, - status: c_int, - } - let mut req = Request::new(uvll::UV_SHUTDOWN); - - return match unsafe { uvll::uv_shutdown(req.handle, handle, shutdown_cb) } { - 0 => { - req.defuse(); // uv callback now owns this request - let mut cx = Ctx { slot: None, status: 0 }; - - wait_until_woken_after(&mut cx.slot, loop_, || { - req.set_data(&mut cx); - }); - - status_to_io_result(cx.status) - } - n => Err(uv_error_to_io_error(UvError(n))) - }; - - extern fn shutdown_cb(req: *mut uvll::uv_shutdown_t, status: libc::c_int) { - let req = Request::wrap(req); - assert!(status != uvll::ECANCELED); - let cx: &mut Ctx = unsafe { req.get_data() }; - cx.status = status; - wakeup(&mut cx.slot); - } -} - -#[cfg(test)] -mod test { - use std::rt::rtio::{RtioTcpStream, RtioTcpListener, RtioTcpAcceptor, - RtioUdpSocket}; - - use super::{UdpWatcher, TcpWatcher, TcpListener}; - use super::super::local_loop; - - #[test] - fn connect_close_ip4() { - match TcpWatcher::connect(local_loop(), ::next_test_ip4(), None) { - Ok(..) => fail!(), - Err(e) => assert_eq!(e.name(), "ECONNREFUSED".to_string()), - } - } - - #[test] - fn connect_close_ip6() { - match TcpWatcher::connect(local_loop(), ::next_test_ip6(), None) { - Ok(..) => fail!(), - Err(e) => assert_eq!(e.name(), "ECONNREFUSED".to_string()), - } - } - - #[test] - fn udp_bind_close_ip4() { - match UdpWatcher::bind(local_loop(), ::next_test_ip4()) { - Ok(..) => {} - Err(..) => fail!() - } - } - - #[test] - fn udp_bind_close_ip6() { - match UdpWatcher::bind(local_loop(), ::next_test_ip6()) { - Ok(..) => {} - Err(..) => fail!() - } - } - - #[test] - fn listen_ip4() { - let (tx, rx) = channel(); - let addr = ::next_test_ip4(); - - spawn(proc() { - let w = match TcpListener::bind(local_loop(), addr) { - Ok(w) => w, Err(e) => fail!("{:?}", e) - }; - let mut w = match w.listen() { - Ok(w) => w, Err(e) => fail!("{:?}", e), - }; - tx.send(()); - match w.accept() { - Ok(mut stream) => { - let mut buf = [0u8, ..10]; - match stream.read(buf) { - Ok(10) => {} e => fail!("{:?}", e), - } - for i in range(0, 10u8) { - assert_eq!(buf[i as uint], i + 1); - } - } - Err(e) => fail!("{:?}", e) - } - }); - - rx.recv(); - let mut w = match TcpWatcher::connect(local_loop(), addr, None) { - Ok(w) => w, Err(e) => fail!("{:?}", e) - }; - match w.write([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) { - Ok(()) => {}, Err(e) => fail!("{:?}", e) - } - } - - #[test] - fn listen_ip6() { - let (tx, rx) = channel(); - let addr = ::next_test_ip6(); - - spawn(proc() { - let w = match TcpListener::bind(local_loop(), addr) { - Ok(w) => w, Err(e) => fail!("{:?}", e) - }; - let mut w = match w.listen() { - Ok(w) => w, Err(e) => fail!("{:?}", e), - }; - tx.send(()); - match w.accept() { - Ok(mut stream) => { - let mut buf = [0u8, ..10]; - match stream.read(buf) { - Ok(10) => {} e => fail!("{:?}", e), - } - for i in range(0, 10u8) { - assert_eq!(buf[i as uint], i + 1); - } - } - Err(e) => fail!("{:?}", e) - } - }); - - rx.recv(); - let mut w = match TcpWatcher::connect(local_loop(), addr, None) { - Ok(w) => w, Err(e) => fail!("{:?}", e) - }; - match w.write([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) { - Ok(()) => {}, Err(e) => fail!("{:?}", e) - } - } - - #[test] - fn udp_recv_ip4() { - let (tx, rx) = channel(); - let client = ::next_test_ip4(); - let server = ::next_test_ip4(); - - spawn(proc() { - match UdpWatcher::bind(local_loop(), server) { - Ok(mut w) => { - tx.send(()); - let mut buf = [0u8, ..10]; - match w.recv_from(buf) { - Ok((10, addr)) => assert!(addr == client), - e => fail!("{:?}", e), - } - for i in range(0, 10u8) { - assert_eq!(buf[i as uint], i + 1); - } - } - Err(e) => fail!("{:?}", e) - } - }); - - rx.recv(); - let mut w = match UdpWatcher::bind(local_loop(), client) { - Ok(w) => w, Err(e) => fail!("{:?}", e) - }; - match w.send_to([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], server) { - Ok(()) => {}, Err(e) => fail!("{:?}", e) - } - } - - #[test] - fn udp_recv_ip6() { - let (tx, rx) = channel(); - let client = ::next_test_ip6(); - let server = ::next_test_ip6(); - - spawn(proc() { - match UdpWatcher::bind(local_loop(), server) { - Ok(mut w) => { - tx.send(()); - let mut buf = [0u8, ..10]; - match w.recv_from(buf) { - Ok((10, addr)) => assert!(addr == client), - e => fail!("{:?}", e), - } - for i in range(0, 10u8) { - assert_eq!(buf[i as uint], i + 1); - } - } - Err(e) => fail!("{:?}", e) - } - }); - - rx.recv(); - let mut w = match UdpWatcher::bind(local_loop(), client) { - Ok(w) => w, Err(e) => fail!("{:?}", e) - }; - match w.send_to([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], server) { - Ok(()) => {}, Err(e) => fail!("{:?}", e) - } - } - - #[test] - fn test_read_read_read() { - let addr = ::next_test_ip4(); - static MAX: uint = 5000; - let (tx, rx) = channel(); - - spawn(proc() { - let listener = TcpListener::bind(local_loop(), addr).unwrap(); - let mut acceptor = listener.listen().ok().unwrap(); - tx.send(()); - let mut stream = acceptor.accept().ok().unwrap(); - let buf = [1, .. 2048]; - let mut total_bytes_written = 0; - while total_bytes_written < MAX { - assert!(stream.write(buf).is_ok()); - uvdebug!("wrote bytes"); - total_bytes_written += buf.len(); - } - }); - - rx.recv(); - let mut stream = TcpWatcher::connect(local_loop(), addr, None).unwrap(); - let mut buf = [0, .. 2048]; - let mut total_bytes_read = 0; - while total_bytes_read < MAX { - let nread = stream.read(buf).ok().unwrap(); - total_bytes_read += nread; - for i in range(0u, nread) { - assert_eq!(buf[i], 1); - } - } - uvdebug!("read {} bytes total", total_bytes_read); - } - - #[test] - #[cfg_attr(windows, ignore)] // FIXME(#10102) server never sees second packet - fn test_udp_twice() { - let server_addr = ::next_test_ip4(); - let client_addr = ::next_test_ip4(); - let (tx, rx) = channel(); - - spawn(proc() { - let mut client = UdpWatcher::bind(local_loop(), client_addr).unwrap(); - rx.recv(); - assert!(client.send_to([1], server_addr).is_ok()); - assert!(client.send_to([2], server_addr).is_ok()); - }); - - let mut server = UdpWatcher::bind(local_loop(), server_addr).unwrap(); - tx.send(()); - let mut buf1 = [0]; - let mut buf2 = [0]; - let (nread1, src1) = server.recv_from(buf1).ok().unwrap(); - let (nread2, src2) = server.recv_from(buf2).ok().unwrap(); - assert_eq!(nread1, 1); - assert_eq!(nread2, 1); - assert!(src1 == client_addr); - assert!(src2 == client_addr); - assert_eq!(buf1[0], 1); - assert_eq!(buf2[0], 2); - } - - #[test] - fn test_udp_many_read() { - let server_out_addr = ::next_test_ip4(); - let server_in_addr = ::next_test_ip4(); - let client_out_addr = ::next_test_ip4(); - let client_in_addr = ::next_test_ip4(); - static MAX: uint = 500_000; - - let (tx1, rx1) = channel::<()>(); - let (tx2, rx2) = channel::<()>(); - - spawn(proc() { - let l = local_loop(); - let mut server_out = UdpWatcher::bind(l, server_out_addr).unwrap(); - let mut server_in = UdpWatcher::bind(l, server_in_addr).unwrap(); - let (tx, rx) = (tx2, rx1); - tx.send(()); - rx.recv(); - let msg = [1, .. 2048]; - let mut total_bytes_sent = 0; - let mut buf = [1]; - while buf[0] == 1 { - // send more data - assert!(server_out.send_to(msg, client_in_addr).is_ok()); - total_bytes_sent += msg.len(); - // check if the client has received enough - let res = server_in.recv_from(buf); - assert!(res.is_ok()); - let (nread, src) = res.ok().unwrap(); - assert_eq!(nread, 1); - assert!(src == client_out_addr); - } - assert!(total_bytes_sent >= MAX); - }); - - let l = local_loop(); - let mut client_out = UdpWatcher::bind(l, client_out_addr).unwrap(); - let mut client_in = UdpWatcher::bind(l, client_in_addr).unwrap(); - let (tx, rx) = (tx1, rx2); - rx.recv(); - tx.send(()); - let mut total_bytes_recv = 0; - let mut buf = [0, .. 2048]; - while total_bytes_recv < MAX { - // ask for more - assert!(client_out.send_to([1], server_in_addr).is_ok()); - // wait for data - let res = client_in.recv_from(buf); - assert!(res.is_ok()); - let (nread, src) = res.ok().unwrap(); - assert!(src == server_out_addr); - total_bytes_recv += nread; - for i in range(0u, nread) { - assert_eq!(buf[i], 1); - } - } - // tell the server we're done - assert!(client_out.send_to([0], server_in_addr).is_ok()); - } - - #[test] - fn test_read_and_block() { - let addr = ::next_test_ip4(); - let (tx, rx) = channel::>(); - - spawn(proc() { - let rx = rx.recv(); - let mut stream = TcpWatcher::connect(local_loop(), addr, None).unwrap(); - stream.write([0, 1, 2, 3, 4, 5, 6, 7]).ok().unwrap(); - stream.write([0, 1, 2, 3, 4, 5, 6, 7]).ok().unwrap(); - rx.recv(); - stream.write([0, 1, 2, 3, 4, 5, 6, 7]).ok().unwrap(); - stream.write([0, 1, 2, 3, 4, 5, 6, 7]).ok().unwrap(); - rx.recv(); - }); - - let listener = TcpListener::bind(local_loop(), addr).unwrap(); - let mut acceptor = listener.listen().ok().unwrap(); - let (tx2, rx2) = channel(); - tx.send(rx2); - let mut stream = acceptor.accept().ok().unwrap(); - let mut buf = [0, .. 2048]; - - let expected = 32; - let mut current = 0; - let mut reads = 0u; - - while current < expected { - let nread = stream.read(buf).ok().unwrap(); - for i in range(0u, nread) { - let val = buf[i] as uint; - assert_eq!(val, current % 8); - current += 1; - } - reads += 1; - - let _ = tx2.send_opt(()); - } - - // Make sure we had multiple reads - assert!(reads > 1); - } - - #[test] - fn test_simple_tcp_server_and_client_on_diff_threads() { - let addr = ::next_test_ip4(); - - spawn(proc() { - let listener = TcpListener::bind(local_loop(), addr).unwrap(); - let mut acceptor = listener.listen().ok().unwrap(); - let mut stream = acceptor.accept().ok().unwrap(); - let mut buf = [0, .. 2048]; - let nread = stream.read(buf).ok().unwrap(); - assert_eq!(nread, 8); - for i in range(0u, nread) { - assert_eq!(buf[i], i as u8); - } - }); - - let mut stream = TcpWatcher::connect(local_loop(), addr, None); - while stream.is_err() { - stream = TcpWatcher::connect(local_loop(), addr, None); - } - stream.unwrap().write([0, 1, 2, 3, 4, 5, 6, 7]).ok().unwrap(); - } - - #[should_fail] #[test] - fn tcp_listener_fail_cleanup() { - let addr = ::next_test_ip4(); - let w = TcpListener::bind(local_loop(), addr).unwrap(); - let _w = w.listen().ok().unwrap(); - fail!(); - } - - #[should_fail] #[test] - fn tcp_stream_fail_cleanup() { - let (tx, rx) = channel(); - let addr = ::next_test_ip4(); - - spawn(proc() { - let w = TcpListener::bind(local_loop(), addr).unwrap(); - let mut w = w.listen().ok().unwrap(); - tx.send(()); - drop(w.accept().ok().unwrap()); - }); - rx.recv(); - let _w = TcpWatcher::connect(local_loop(), addr, None).unwrap(); - fail!(); - } - - #[should_fail] #[test] - fn udp_listener_fail_cleanup() { - let addr = ::next_test_ip4(); - let _w = UdpWatcher::bind(local_loop(), addr).unwrap(); - fail!(); - } - - #[should_fail] #[test] - fn udp_fail_other_task() { - let addr = ::next_test_ip4(); - let (tx, rx) = channel(); - - // force the handle to be created on a different scheduler, failure in - // the original task will force a homing operation back to this - // scheduler. - spawn(proc() { - let w = UdpWatcher::bind(local_loop(), addr).unwrap(); - tx.send(w); - }); - - let _w = rx.recv(); - fail!(); - } -} diff --git a/src/librustuv/pipe.rs b/src/librustuv/pipe.rs deleted file mode 100644 index 9ece6525e1e..00000000000 --- a/src/librustuv/pipe.rs +++ /dev/null @@ -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 or the MIT license -// , 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>, - 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 - { - 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) - -> Result - { - 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 { - 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 { - 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 - } - - 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) { - self.set_read_timeout(timeout); - self.set_write_timeout(timeout); - } - - fn set_read_timeout(&mut self, ms: Option) { - 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 { - 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) { - 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 { - 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 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, 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) - -> IoResult> { - 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), - 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 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) - } - 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> { - let m = self.fire_homing_missile(); - let loop_ = self.uv_loop(); - self.access.accept(m, &loop_) - } - - fn set_timeout(&mut self, ms: Option) { - let _m = self.fire_homing_missile(); - let loop_ = self.uv_loop(); - self.access.set_timeout(ms, &loop_, &self.home); - } - - fn clone(&self) -> Box { - box PipeAcceptor { - refcount: self.refcount.clone(), - home: self.home.clone(), - handle: self.handle, - access: self.access.clone(), - } as Box - } - - 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 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!() - - } -} diff --git a/src/librustuv/process.rs b/src/librustuv/process.rs deleted file mode 100644 index 31c9fdde68c..00000000000 --- a/src/librustuv/process.rs +++ /dev/null @@ -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 or the MIT license -// , 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, - - /// Collected from the exit_cb - exit_status: Option, - - /// Lazily initialized timeout timer - timer: Option>, - 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, Vec>), 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::::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::, 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 { - 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(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(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 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 { - // 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) { - 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(); - } -} diff --git a/src/librustuv/queue.rs b/src/librustuv/queue.rs deleted file mode 100644 index 6a0f2a27b18..00000000000 --- a/src/librustuv/queue.rs +++ /dev/null @@ -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 or the MIT license -// , 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, -} - -/// 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, - refcnt: uint, -} - -/// This type is used to send messages back to the original event loop. -pub struct Queue { - queue: Arc, -} - -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 { - let handle = UvHandle::alloc(None::, 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. - } - } -} diff --git a/src/librustuv/rc.rs b/src/librustuv/rc.rs deleted file mode 100644 index 7016ece6427..00000000000 --- a/src/librustuv/rc.rs +++ /dev/null @@ -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 or the MIT license -// , 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>, -} - -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() } - } -} diff --git a/src/librustuv/signal.rs b/src/librustuv/signal.rs deleted file mode 100644 index 49ef4e9a24b..00000000000 --- a/src/librustuv/signal.rs +++ /dev/null @@ -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 or the MIT license -// , 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, -} - -impl SignalWatcher { - pub fn new(io: &mut UvIoFactory, signum: int, cb: Box) - -> Result, UvError> { - let s = box SignalWatcher { - handle: UvHandle::alloc(None::, 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 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(); - } -} diff --git a/src/librustuv/stream.rs b/src/librustuv/stream.rs deleted file mode 100644 index de49fd6cf09..00000000000 --- a/src/librustuv/stream.rs +++ /dev/null @@ -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 or the MIT license -// , 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, - - blocked_writer: Option, -} - -struct ReadContext { - buf: Option, - result: ssize_t, - task: Option, -} - -struct WriteContext { - result: c_int, - stream: *mut StreamWatcher, - data: Option>, -} - -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 { - // 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 { - // 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 { - 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 = unsafe { mem::transmute(wcx) }; - } -} diff --git a/src/librustuv/timeout.rs b/src/librustuv/timeout.rs deleted file mode 100644 index 88267c0ac45..00000000000 --- a/src/librustuv/timeout.rs +++ /dev/null @@ -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 or the MIT license -// , 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 { - state: TimeoutState, - timer: Option>, - pub access: access::Access, -} - -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, - user_payload: uint, -} - -impl AccessTimeout { - pub fn new(data: T) -> AccessTimeout { - 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> { - // 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, - home: &HomeHandle, - loop_: &Loop, - cb: fn(uint) -> Option, - 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::, - 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(timeout: *mut AccessTimeout<()>, cx: &TimerContext) { - let timeout = timeout as *mut AccessTimeout; - 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 Clone for AccessTimeout { - fn clone(&self) -> AccessTimeout { - 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 Drop for AccessTimeout { - fn drop(&mut self) { - match self.timer { - Some(ref timer) => unsafe { - let data = uvll::get_data_for_uv_handle(timer.handle); - let _data: Box = mem::transmute(data); - }, - None => {} - } - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Connect timeouts -//////////////////////////////////////////////////////////////////////////////// - -pub struct ConnectCtx { - pub status: c_int, - pub task: Option, - pub timer: Option>, -} - -impl ConnectCtx { - pub fn connect( - mut self, obj: T, timeout: Option, io: &mut UvIoFactory, - f: |&Request, &T, uvll::uv_connect_cb| -> c_int - ) -> Result { - 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 { - access: AccessTimeout>, -} - -struct AcceptorState { - blocked_acceptor: Option, - pending: Vec>, -} - -impl AcceptTimeout { - pub fn new() -> AcceptTimeout { - AcceptTimeout { - access: AccessTimeout::new(AcceptorState { - blocked_acceptor: None, - pending: Vec::new(), - }) - } - } - - pub fn accept(&mut self, - missile: HomingMissile, - loop_: &Loop) -> IoResult { - // 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) { - 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, - loop_: &Loop, - home: &HomeHandle) { - self.access.set_timeout(ms, home, loop_, cancel_accept::, - self as *mut _ as uint); - - fn cancel_accept(me: uint) -> Option { - unsafe { - let me: &mut AcceptTimeout = 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 Clone for AcceptTimeout { - fn clone(&self) -> AcceptTimeout { - AcceptTimeout { access: self.access.clone() } - } -} diff --git a/src/librustuv/timer.rs b/src/librustuv/timer.rs deleted file mode 100644 index 412506604c6..00000000000 --- a/src/librustuv/timer.rs +++ /dev/null @@ -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 or the MIT license -// , 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, - blocker: Option, - id: uint, // see comments in timer_cb -} - -pub enum NextAction { - WakeTask, - CallOnce(Box), - CallMany(Box, uint), -} - -impl TimerWatcher { - pub fn new(io: &mut UvIoFactory) -> Box { - 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::, 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(&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 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) { - // 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) { - // 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() - }; - } -} diff --git a/src/librustuv/tty.rs b/src/librustuv/tty.rs deleted file mode 100644 index 43973f2fb8e..00000000000 --- a/src/librustuv/tty.rs +++ /dev/null @@ -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 or the MIT license -// , 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 - { - // 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::, 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 { - 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 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_(); - } - } -} diff --git a/src/librustuv/uvio.rs b/src/librustuv/uvio.rs deleted file mode 100644 index f2a2b5d2f1d..00000000000 --- a/src/librustuv/uvio.rs +++ /dev/null @@ -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 or the MIT license -// , 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) - -> Box { - IdleWatcher::new(&mut self.uvio.loop_, cb) - as Box - } - - fn remote_callback(&mut self, f: Box) - -> Box { - box AsyncWatcher::new(&mut self.uvio.loop_, f) as - Box - } - - 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>, -} - -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) - -> IoResult> { - match TcpWatcher::connect(self, addr, timeout) { - Ok(t) => Ok(box t as Box), - Err(e) => Err(uv_error_to_io_error(e)), - } - } - - fn tcp_bind(&mut self, addr: rtio::SocketAddr) - -> IoResult> { - match TcpListener::bind(self, addr) { - Ok(t) => Ok(t as Box), - Err(e) => Err(uv_error_to_io_error(e)), - } - } - - fn udp_bind(&mut self, addr: rtio::SocketAddr) - -> IoResult> { - match UdpWatcher::bind(self, addr) { - Ok(u) => Ok(box u as Box), - Err(e) => Err(uv_error_to_io_error(e)), - } - } - - fn timer_init(&mut self) -> IoResult> { - Ok(TimerWatcher::new(self) as Box) - } - - fn get_host_addresses(&mut self, host: Option<&str>, servname: Option<&str>, - hint: Option) - -> IoResult> - { - 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 { - box FileWatcher::new(self, fd, close) as - Box - } - - fn fs_open(&mut self, path: &CString, fm: rtio::FileMode, - fa: rtio::FileAccess) - -> IoResult> - { - 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), - 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 { - let r = FsRequest::lstat(&self.loop_, path); - r.map_err(uv_error_to_io_error) - } - fn fs_stat(&mut self, path: &CString) -> IoResult { - 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> - { - 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 { - 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, - Vec>>)> - { - match Process::spawn(self, cfg) { - Ok((p, io)) => { - Ok((p as Box, - io.into_iter().map(|i| i.map(|p| { - box p as Box - })).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> { - match PipeListener::bind(self, path) { - Ok(p) => Ok(p as Box), - Err(e) => Err(uv_error_to_io_error(e)), - } - } - - fn unix_connect(&mut self, path: &CString, timeout: Option) - -> IoResult> { - match PipeWatcher::connect(self, path, timeout) { - Ok(p) => Ok(box p as Box), - Err(e) => Err(uv_error_to_io_error(e)), - } - } - - fn tty_open(&mut self, fd: c_int, readable: bool) - -> IoResult> { - match TtyWatcher::new(self, fd, readable) { - Ok(tty) => Ok(box tty as Box), - Err(e) => Err(uv_error_to_io_error(e)) - } - } - - fn pipe_open(&mut self, fd: c_int) - -> IoResult> - { - match PipeWatcher::open(self, fd) { - Ok(s) => Ok(box s as Box), - Err(e) => Err(uv_error_to_io_error(e)) - } - } - - fn signal(&mut self, signum: int, cb: Box) - -> IoResult> - { - match SignalWatcher::new(self, signum, cb) { - Ok(s) => Ok(s as Box), - Err(e) => Err(uv_error_to_io_error(e)), - } - } -} diff --git a/src/librustuv/uvll.rs b/src/librustuv/uvll.rs deleted file mode 100644 index 35bd20c0dd9..00000000000 --- a/src/librustuv/uvll.rs +++ /dev/null @@ -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 or the MIT license -// , 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(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(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(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(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(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 {}