diff --git a/src/libstd/rt/uv/file.rs b/src/libstd/rt/uv/file.rs index 2d1450550975..cab27ee29c1c 100644 --- a/src/libstd/rt/uv/file.rs +++ b/src/libstd/rt/uv/file.rs @@ -11,30 +11,87 @@ use prelude::*; use ptr::null; use libc::c_void; -use rt::uv::{Request, NativeHandle, Loop, FsCallback}; +use rt::uv::{Request, NativeHandle, Loop, FsCallback, + status_to_maybe_uv_error_with_loop}; use rt::uv::uvll; use rt::uv::uvll::*; +use path::Path; +use cast::transmute; +use libc::{c_int}; pub struct FsRequest(*uvll::uv_fs_t); impl Request for FsRequest; +#[allow(non_camel_case_types)] +pub enum UvFileFlag { + O_RDONLY, + O_WRONLY, + O_RDWR, + O_CREAT, + O_TRUNC +} +pub fn map_flag(v: UvFileFlag) -> int { + unsafe { + match v { + O_RDONLY => uvll::get_O_RDONLY() as int, + O_WRONLY => uvll::get_O_WRONLY() as int, + O_RDWR => uvll::get_O_RDWR() as int, + O_CREAT => uvll::get_O_CREAT() as int, + O_TRUNC => uvll::get_O_TRUNC() as int + } + } +} + +pub struct RequestData { + complete_cb: Option +} + impl FsRequest { - fn new() -> FsRequest { + pub fn new(cb: Option) -> FsRequest { let fs_req = unsafe { malloc_req(UV_FS) }; assert!(fs_req.is_not_null()); - let fs_req = fs_req as *uvll::uv_write_t; - unsafe { uvll::set_data_for_req(fs_req, null::<()>()); } - NativeHandle::from_native_handle(fs_req) + let fs_req: FsRequest = NativeHandle::from_native_handle(fs_req); + fs_req.install_req_data(cb); + fs_req } - fn delete(self) { - unsafe { free_req(self.native_handle() as *c_void) } + pub fn install_req_data(&self, cb: Option) { + let fs_req = (self.native_handle()) as *uvll::uv_write_t; + let data = ~RequestData { + complete_cb: cb + }; + unsafe { + let data = transmute::<~RequestData, *c_void>(data); + uvll::set_data_for_req(fs_req, data); + } } - fn open(&mut self, _loop_: &Loop, _cb: FsCallback) { + fn get_req_data<'r>(&'r mut self) -> &'r mut RequestData { + unsafe { + let data = uvll::get_data_for_req((self.native_handle())); + let data = transmute::<&*c_void, &mut ~RequestData>(&data); + return &mut **data; + } } - fn close(&mut self, _loop_: &Loop, _cb: FsCallback) { + pub fn get_result(&mut self) -> c_int { + unsafe { + uvll::get_result_from_fs_req(self.native_handle()) + } + } + + pub fn get_loop(&self) -> Loop { + unsafe { Loop{handle:uvll::get_loop_from_fs_req(self.native_handle())} } + } + + fn cleanup_and_delete(self) { + unsafe { + uvll::fs_req_cleanup(self.native_handle()); + let data = uvll::get_data_for_uv_handle(self.native_handle()); + let _data = transmute::<*c_void, ~RequestData>(data); + uvll::set_data_for_uv_handle(self.native_handle(), null::<()>()); + free_req(self.native_handle() as *c_void) + } } } @@ -46,3 +103,97 @@ impl NativeHandle<*uvll::uv_fs_t> for FsRequest { match self { &FsRequest(ptr) => ptr } } } + +pub struct FileDescriptor(c_int); +impl FileDescriptor { + fn new(fd: c_int) -> FileDescriptor { + FileDescriptor(fd) + } + + pub fn from_open_req(req: &mut FsRequest) -> FileDescriptor { + FileDescriptor::new(req.get_result()) + } + + pub fn open(loop_: Loop, path: Path, flags: int, mode: int, + cb: FsCallback) -> int { + let req = FsRequest::new(Some(cb)); + path.to_str().to_c_str().with_ref(|p| unsafe { + uvll::fs_open(loop_.native_handle(), + req.native_handle(), p, flags, mode, complete_cb) as int + }) + + } + + fn close(self, loop_: Loop, cb: FsCallback) -> int { + let req = FsRequest::new(Some(cb)); + unsafe { + uvll::fs_close(loop_.native_handle(), req.native_handle(), + self.native_handle(), complete_cb) as int + } + } +} +extern fn complete_cb(req: *uv_fs_t) { + let mut req: FsRequest = NativeHandle::from_native_handle(req); + let loop_ = req.get_loop(); + // pull the user cb out of the req data + let cb = { + let data = req.get_req_data(); + assert!(data.complete_cb.is_some()); + // option dance, option dance. oooooh yeah. + data.complete_cb.take_unwrap() + }; + // in uv_fs_open calls, the result will be the fd in the + // case of success, otherwise it's -1 indicating an error + let result = req.get_result(); + let status = status_to_maybe_uv_error_with_loop( + loop_.native_handle(), result); + // we have a req and status, call the user cb.. + // only giving the user a ref to the FsRequest, as we + // have to clean it up, afterwards (and they aren't really + // reusable, anyways + cb(&mut req, status); + // clean up the req (and its data!) after calling the user cb + req.cleanup_and_delete(); +} + +impl NativeHandle for FileDescriptor { + fn from_native_handle(handle: c_int) -> FileDescriptor { + FileDescriptor(handle) + } + fn native_handle(&self) -> c_int { + match self { &FileDescriptor(ptr) => ptr } + } +} + +mod test { + use super::*; + //use rt::test::*; + use unstable::run_in_bare_thread; + use path::Path; + use rt::uv::Loop; + + // this is equiv to touch, i guess? + fn file_test_touch_impl() { + debug!("hello?") + do run_in_bare_thread { + debug!("In bare thread") + let loop_ = Loop::new(); + let flags = map_flag(O_RDWR) | + map_flag(O_CREAT) | map_flag(O_TRUNC); + do FileDescriptor::open(loop_, Path("./foo.txt"), flags, 0644) + |req, uverr| { + let loop_ = req.get_loop(); + assert!(uverr.is_none()); + let fd = FileDescriptor::from_open_req(req); + do fd.close(loop_) |_, uverr| { + assert!(uverr.is_none()); + }; + }; + } + } + + #[test] + fn file_test_touch() { + file_test_touch_impl(); + } +} diff --git a/src/libstd/rt/uv/mod.rs b/src/libstd/rt/uv/mod.rs index 9312efbf03e9..75b9a5ac553e 100644 --- a/src/libstd/rt/uv/mod.rs +++ b/src/libstd/rt/uv/mod.rs @@ -53,7 +53,7 @@ use rt::io::IoError; //#[cfg(test)] use unstable::run_in_bare_thread; -pub use self::file::FsRequest; +pub use self::file::{FsRequest}; pub use self::net::{StreamWatcher, TcpWatcher, UdpWatcher}; pub use self::idle::IdleWatcher; pub use self::timer::TimerWatcher; @@ -125,7 +125,7 @@ pub type ReadCallback = ~fn(StreamWatcher, int, Buf, Option); pub type NullCallback = ~fn(); pub type IdleCallback = ~fn(IdleWatcher, Option); pub type ConnectionCallback = ~fn(StreamWatcher, Option); -pub type FsCallback = ~fn(FsRequest, Option); +pub type FsCallback = ~fn(&mut FsRequest, Option); pub type TimerCallback = ~fn(TimerWatcher, Option); pub type AsyncCallback = ~fn(AsyncWatcher, Option); pub type UdpReceiveCallback = ~fn(UdpWatcher, int, Buf, SocketAddr, uint, Option); @@ -281,6 +281,20 @@ pub fn uv_error_to_io_error(uverr: UvError) -> IoError { } } +/// Given a uv handle, convert a callback status to a UvError +pub fn status_to_maybe_uv_error_with_loop( + loop_: *uvll::uv_loop_t, + status: c_int) -> Option { + if status != -1 { + None + } else { + unsafe { + rtdebug!("loop: %x", loop_ as uint); + let err = uvll::last_error(loop_); + Some(UvError(err)) + } + } +} /// Given a uv handle, convert a callback status to a UvError pub fn status_to_maybe_uv_error>(handle: U, status: c_int) -> Option { @@ -290,9 +304,7 @@ pub fn status_to_maybe_uv_error>(handle: U, unsafe { rtdebug!("handle: %x", handle.native_handle() as uint); let loop_ = uvll::get_loop_for_uv_handle(handle.native_handle()); - rtdebug!("loop: %x", loop_ as uint); - let err = uvll::last_error(loop_); - Some(UvError(err)) + status_to_maybe_uv_error_with_loop(loop_, status) } } } diff --git a/src/libstd/rt/uv/uvll.rs b/src/libstd/rt/uv/uvll.rs index 71387b09a8d0..b6cffa754c14 100644 --- a/src/libstd/rt/uv/uvll.rs +++ b/src/libstd/rt/uv/uvll.rs @@ -617,7 +617,40 @@ pub unsafe fn ip6_port(addr: *sockaddr_in6) -> c_uint { return rust_uv_ip6_port(addr); } +pub unsafe fn fs_open(loop_ptr: *uv_loop_t, req: *uv_fs_t, path: *c_char, flags: int, mode: int, + cb: *u8) -> c_int { + rust_uv_fs_open(loop_ptr, req, path, flags as c_int, mode as c_int, cb) +} +pub unsafe fn fs_close(loop_ptr: *uv_loop_t, req: *uv_fs_t, fd: c_int, + cb: *u8) -> c_int { + rust_uv_fs_close(loop_ptr, req, fd, cb) +} +pub unsafe fn fs_req_cleanup(req: *uv_fs_t) { + rust_uv_fs_req_cleanup(req); +} + // data access helpers +pub unsafe fn get_O_RDONLY() -> c_int { + rust_uv_get_O_RDONLY() +} +pub unsafe fn get_O_WRONLY() -> c_int { + rust_uv_get_O_WRONLY() +} +pub unsafe fn get_O_RDWR() -> c_int { + rust_uv_get_O_RDWR() +} +pub unsafe fn get_O_CREAT() -> c_int { + rust_uv_get_O_CREAT() +} +pub unsafe fn get_O_TRUNC() -> c_int { + rust_uv_get_O_TRUNC() +} +pub unsafe fn get_result_from_fs_req(req: *uv_fs_t) -> c_int { + rust_uv_get_result_from_fs_req(req) +} +pub unsafe fn get_loop_from_fs_req(req: *uv_fs_t) -> *uv_loop_t { + rust_uv_get_loop_from_fs_req(req) +} pub unsafe fn get_loop_for_uv_handle(handle: *T) -> *c_void { #[fixed_stack_segment]; #[inline(never)]; @@ -784,6 +817,18 @@ extern { fn rust_uv_timer_start(timer_handle: *uv_timer_t, cb: uv_timer_cb, timeout: libc::uint64_t, repeat: libc::uint64_t) -> c_int; fn rust_uv_timer_stop(handle: *uv_timer_t) -> c_int; + fn rust_uv_fs_open(loop_ptr: *c_void, req: *uv_fs_t, path: *c_char, + flags: c_int, mode: c_int, cb: *u8) -> c_int; + fn rust_uv_fs_close(loop_ptr: *c_void, req: *uv_fs_t, fd: c_int, + cb: *u8) -> c_int; + fn rust_uv_fs_req_cleanup(req: *uv_fs_t); + fn rust_uv_get_O_RDONLY() -> c_int; + fn rust_uv_get_O_WRONLY() -> c_int; + fn rust_uv_get_O_RDWR() -> c_int; + fn rust_uv_get_O_CREAT() -> c_int; + fn rust_uv_get_O_TRUNC() -> c_int; + fn rust_uv_get_result_from_fs_req(req: *uv_fs_t) -> c_int; + fn rust_uv_get_loop_from_fs_req(req: *uv_fs_t) -> *uv_loop_t; fn rust_uv_get_stream_handle_from_connect_req(connect_req: *uv_connect_t) -> *uv_stream_t; fn rust_uv_get_stream_handle_from_write_req(write_req: *uv_write_t) -> *uv_stream_t; diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index ecf22d72b128..0e9eefd3018c 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -108,6 +108,16 @@ rust_uv_idle_delete rust_uv_idle_init rust_uv_idle_start rust_uv_idle_stop +rust_uv_fs_open +rust_uv_fs_close +rust_uv_get_O_RDONLY +rust_uv_get_O_WRONLY +rust_uv_get_O_RDWR +rust_uv_get_O_CREAT +rust_uv_get_O_TRUNC +rust_uv_get_result_from_fs_req +rust_uv_get_loop_from_fs_req +rust_uv_fs_req_cleanup rust_dbg_lock_create rust_dbg_lock_destroy rust_dbg_lock_lock