* sanity while editing, filling-in-details and eliminating duplication) into
* definitions common-to-all (held in modules named c95, c99, posix88, posix01
* and posix08) and definitions that appear only on *some* platforms (named
-* 'extra'). This would be things like significant OSX foundation kit, or win32
+* 'extra'). This would be things like significant OSX foundation kit, or Windows
* library kernel32.dll, or various fancy glibc, linux or BSD extensions.
*
* In addition to the per-platform 'extra' modules, we define a module of
}
#[cfg(target_os = "windows")]
- #[cfg(stage0, target_os = "win32")] // NOTE: Remove after snapshot
pub mod os {
pub mod common {
pub mod posix01 {
use types::os::arch::extra::{int64, time64_t};
use types::os::arch::posix88::{dev_t, ino_t};
- // pub Note: this is the struct called stat64 in win32. Not stat,
+ // pub Note: this is the struct called stat64 in Windows. Not stat,
// nor stati64.
#[repr(C)]
pub struct stat {
pub st_ctime: time64_t,
}
- // note that this is called utimbuf64 in win32
+ // note that this is called utimbuf64 in Windows
#[repr(C)]
pub struct utimbuf {
pub actime: time64_t,
// into this module.
#[cfg(target_os = "windows")]
- #[cfg(stage0, target_os = "win32")] // NOTE: Remove after snapshot
pub mod os {
pub mod c95 {
use types::os::arch::c95::{c_int, c_uint};
// with the same POSIX functions and types as other platforms.
#[cfg(target_os = "windows")]
- #[cfg(stage0, target_os = "win32")] // NOTE: Remove after snapshot
pub mod posix88 {
pub mod stat_ {
use types::os::common::posix01::{stat, utimbuf};
}
#[cfg(target_os = "windows")]
- #[cfg(stage0, target_os = "win32")] // NOTE: Remove after snapshot
pub mod posix01 {
pub mod stat_ {
}
#[cfg(target_os = "windows")]
- #[cfg(stage0, target_os = "win32")] // NOTE: Remove after snapshot
#[cfg(target_os = "linux")]
#[cfg(target_os = "android")]
#[cfg(target_os = "macos")]
#[cfg(target_os = "windows")]
- #[cfg(stage0, target_os = "win32")] // NOTE: Remove after snapshot
pub mod bsd44 {
}
#[cfg(target_os = "windows")]
- #[cfg(stage0, target_os = "win32")] // NOTE: Remove after snapshot
pub mod extra {
pub mod kernel32 {
+++ /dev/null
-// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! C definitions used by libnative that don't belong in liblibc
-
-#![allow(type_overflow)]
-
-use libc;
-
-pub static WSADESCRIPTION_LEN: uint = 256;
-pub static WSASYS_STATUS_LEN: uint = 128;
-pub static FIONBIO: libc::c_long = 0x8004667e;
-static FD_SETSIZE: uint = 64;
-pub static MSG_DONTWAIT: libc::c_int = 0;
-pub static ERROR_ILLEGAL_CHARACTER: libc::c_int = 582;
-pub static ENABLE_ECHO_INPUT: libc::DWORD = 0x4;
-pub static ENABLE_EXTENDED_FLAGS: libc::DWORD = 0x80;
-pub static ENABLE_INSERT_MODE: libc::DWORD = 0x20;
-pub static ENABLE_LINE_INPUT: libc::DWORD = 0x2;
-pub static ENABLE_PROCESSED_INPUT: libc::DWORD = 0x1;
-pub static ENABLE_QUICK_EDIT_MODE: libc::DWORD = 0x40;
-
-#[repr(C)]
-#[cfg(target_arch = "x86")]
-pub struct WSADATA {
- pub wVersion: libc::WORD,
- pub wHighVersion: libc::WORD,
- pub szDescription: [u8, ..WSADESCRIPTION_LEN + 1],
- pub szSystemStatus: [u8, ..WSASYS_STATUS_LEN + 1],
- pub iMaxSockets: u16,
- pub iMaxUdpDg: u16,
- pub lpVendorInfo: *mut u8,
-}
-#[repr(C)]
-#[cfg(target_arch = "x86_64")]
-pub struct WSADATA {
- pub wVersion: libc::WORD,
- pub wHighVersion: libc::WORD,
- pub iMaxSockets: u16,
- pub iMaxUdpDg: u16,
- pub lpVendorInfo: *mut u8,
- pub szDescription: [u8, ..WSADESCRIPTION_LEN + 1],
- pub szSystemStatus: [u8, ..WSASYS_STATUS_LEN + 1],
-}
-
-pub type LPWSADATA = *mut WSADATA;
-
-#[repr(C)]
-pub struct fd_set {
- fd_count: libc::c_uint,
- fd_array: [libc::SOCKET, ..FD_SETSIZE],
-}
-
-pub fn fd_set(set: &mut fd_set, s: libc::SOCKET) {
- set.fd_array[set.fd_count as uint] = s;
- set.fd_count += 1;
-}
-
-#[link(name = "ws2_32")]
-extern "system" {
- pub fn WSAStartup(wVersionRequested: libc::WORD,
- lpWSAData: LPWSADATA) -> libc::c_int;
- pub fn WSAGetLastError() -> libc::c_int;
-
- pub fn ioctlsocket(s: libc::SOCKET, cmd: libc::c_long,
- argp: *mut libc::c_ulong) -> libc::c_int;
- pub fn select(nfds: libc::c_int,
- readfds: *mut fd_set,
- writefds: *mut fd_set,
- exceptfds: *mut fd_set,
- timeout: *mut libc::timeval) -> libc::c_int;
- pub fn getsockopt(sockfd: libc::SOCKET,
- level: libc::c_int,
- optname: libc::c_int,
- optval: *mut libc::c_char,
- optlen: *mut libc::c_int) -> libc::c_int;
-
- pub fn CancelIo(hFile: libc::HANDLE) -> libc::BOOL;
- pub fn CancelIoEx(hFile: libc::HANDLE,
- lpOverlapped: libc::LPOVERLAPPED) -> libc::BOOL;
-}
-
-pub mod compat {
- use std::intrinsics::{atomic_store_relaxed, transmute};
- use std::iter::Iterator;
- use libc::types::os::arch::extra::{LPCWSTR, HMODULE, LPCSTR, LPVOID};
-
- extern "system" {
- fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE;
- fn GetProcAddress(hModule: HMODULE, lpProcName: LPCSTR) -> LPVOID;
- }
-
- // store_func() is idempotent, so using relaxed ordering for the atomics
- // should be enough. This way, calling a function in this compatibility
- // layer (after it's loaded) shouldn't be any slower than a regular DLL
- // call.
- unsafe fn store_func(ptr: *mut uint, module: &str, symbol: &str, fallback: uint) {
- let module: Vec<u16> = module.utf16_units().collect();
- let module = module.append_one(0);
- symbol.with_c_str(|symbol| {
- let handle = GetModuleHandleW(module.as_ptr());
- let func: uint = transmute(GetProcAddress(handle, symbol));
- atomic_store_relaxed(ptr, if func == 0 {
- fallback
- } else {
- func
- })
- })
- }
-
- /// Macro for creating a compatibility fallback for a Windows function
- ///
- /// # Example
- /// ```
- /// compat_fn!(adll32::SomeFunctionW(_arg: LPCWSTR) {
- /// // Fallback implementation
- /// })
- /// ```
- ///
- /// Note that arguments unused by the fallback implementation should not be called `_` as
- /// they are used to be passed to the real function if available.
- macro_rules! compat_fn(
- ($module:ident::$symbol:ident($($argname:ident: $argtype:ty),*)
- -> $rettype:ty $fallback:block) => (
- #[inline(always)]
- pub unsafe fn $symbol($($argname: $argtype),*) -> $rettype {
- static mut ptr: extern "system" fn($($argname: $argtype),*) -> $rettype = thunk;
-
- extern "system" fn thunk($($argname: $argtype),*) -> $rettype {
- unsafe {
- ::io::c::compat::store_func(&mut ptr as *mut _ as *mut uint,
- stringify!($module),
- stringify!($symbol),
- fallback as uint);
- ::std::intrinsics::atomic_load_relaxed(&ptr)($($argname),*)
- }
- }
-
- extern "system" fn fallback($($argname: $argtype),*) -> $rettype $fallback
-
- ::std::intrinsics::atomic_load_relaxed(&ptr)($($argname),*)
- }
- );
-
- ($module:ident::$symbol:ident($($argname:ident: $argtype:ty),*) $fallback:block) => (
- compat_fn!($module::$symbol($($argname: $argtype),*) -> () $fallback)
- )
- )
-
- /// Compatibility layer for functions in `kernel32.dll`
- ///
- /// Latest versions of Windows this is needed for:
- ///
- /// * `CreateSymbolicLinkW`: Windows XP, Windows Server 2003
- /// * `GetFinalPathNameByHandleW`: Windows XP, Windows Server 2003
- pub mod kernel32 {
- use libc::types::os::arch::extra::{DWORD, LPCWSTR, BOOLEAN, HANDLE};
- use libc::consts::os::extra::ERROR_CALL_NOT_IMPLEMENTED;
-
- extern "system" {
- fn SetLastError(dwErrCode: DWORD);
- }
-
- compat_fn!(kernel32::CreateSymbolicLinkW(_lpSymlinkFileName: LPCWSTR,
- _lpTargetFileName: LPCWSTR,
- _dwFlags: DWORD) -> BOOLEAN {
- unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); }
- 0
- })
-
- compat_fn!(kernel32::GetFinalPathNameByHandleW(_hFile: HANDLE,
- _lpszFilePath: LPCWSTR,
- _cchFilePath: DWORD,
- _dwFlags: DWORD) -> DWORD {
- unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); }
- 0
- })
- }
-}
-
-extern "system" {
- // FIXME - pInputControl should be PCONSOLE_READCONSOLE_CONTROL
- pub fn ReadConsoleW(hConsoleInput: libc::HANDLE,
- lpBuffer: libc::LPVOID,
- nNumberOfCharsToRead: libc::DWORD,
- lpNumberOfCharsRead: libc::LPDWORD,
- pInputControl: libc::LPVOID) -> libc::BOOL;
-
- pub fn WriteConsoleW(hConsoleOutput: libc::HANDLE,
- lpBuffer: libc::types::os::arch::extra::LPCVOID,
- nNumberOfCharsToWrite: libc::DWORD,
- lpNumberOfCharsWritten: libc::LPDWORD,
- lpReserved: libc::LPVOID) -> libc::BOOL;
-
- pub fn GetConsoleMode(hConsoleHandle: libc::HANDLE,
- lpMode: libc::LPDWORD) -> libc::BOOL;
-
- pub fn SetConsoleMode(hConsoleHandle: libc::HANDLE,
- lpMode: libc::DWORD) -> libc::BOOL;
-}
--- /dev/null
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! C definitions used by libnative that don't belong in liblibc
+
+#![allow(type_overflow)]
+
+use libc;
+
+pub static WSADESCRIPTION_LEN: uint = 256;
+pub static WSASYS_STATUS_LEN: uint = 128;
+pub static FIONBIO: libc::c_long = 0x8004667e;
+static FD_SETSIZE: uint = 64;
+pub static MSG_DONTWAIT: libc::c_int = 0;
+pub static ERROR_ILLEGAL_CHARACTER: libc::c_int = 582;
+pub static ENABLE_ECHO_INPUT: libc::DWORD = 0x4;
+pub static ENABLE_EXTENDED_FLAGS: libc::DWORD = 0x80;
+pub static ENABLE_INSERT_MODE: libc::DWORD = 0x20;
+pub static ENABLE_LINE_INPUT: libc::DWORD = 0x2;
+pub static ENABLE_PROCESSED_INPUT: libc::DWORD = 0x1;
+pub static ENABLE_QUICK_EDIT_MODE: libc::DWORD = 0x40;
+
+#[repr(C)]
+#[cfg(target_arch = "x86")]
+pub struct WSADATA {
+ pub wVersion: libc::WORD,
+ pub wHighVersion: libc::WORD,
+ pub szDescription: [u8, ..WSADESCRIPTION_LEN + 1],
+ pub szSystemStatus: [u8, ..WSASYS_STATUS_LEN + 1],
+ pub iMaxSockets: u16,
+ pub iMaxUdpDg: u16,
+ pub lpVendorInfo: *mut u8,
+}
+#[repr(C)]
+#[cfg(target_arch = "x86_64")]
+pub struct WSADATA {
+ pub wVersion: libc::WORD,
+ pub wHighVersion: libc::WORD,
+ pub iMaxSockets: u16,
+ pub iMaxUdpDg: u16,
+ pub lpVendorInfo: *mut u8,
+ pub szDescription: [u8, ..WSADESCRIPTION_LEN + 1],
+ pub szSystemStatus: [u8, ..WSASYS_STATUS_LEN + 1],
+}
+
+pub type LPWSADATA = *mut WSADATA;
+
+#[repr(C)]
+pub struct fd_set {
+ fd_count: libc::c_uint,
+ fd_array: [libc::SOCKET, ..FD_SETSIZE],
+}
+
+pub fn fd_set(set: &mut fd_set, s: libc::SOCKET) {
+ set.fd_array[set.fd_count as uint] = s;
+ set.fd_count += 1;
+}
+
+#[link(name = "ws2_32")]
+extern "system" {
+ pub fn WSAStartup(wVersionRequested: libc::WORD,
+ lpWSAData: LPWSADATA) -> libc::c_int;
+ pub fn WSAGetLastError() -> libc::c_int;
+
+ pub fn ioctlsocket(s: libc::SOCKET, cmd: libc::c_long,
+ argp: *mut libc::c_ulong) -> libc::c_int;
+ pub fn select(nfds: libc::c_int,
+ readfds: *mut fd_set,
+ writefds: *mut fd_set,
+ exceptfds: *mut fd_set,
+ timeout: *mut libc::timeval) -> libc::c_int;
+ pub fn getsockopt(sockfd: libc::SOCKET,
+ level: libc::c_int,
+ optname: libc::c_int,
+ optval: *mut libc::c_char,
+ optlen: *mut libc::c_int) -> libc::c_int;
+
+ pub fn CancelIo(hFile: libc::HANDLE) -> libc::BOOL;
+ pub fn CancelIoEx(hFile: libc::HANDLE,
+ lpOverlapped: libc::LPOVERLAPPED) -> libc::BOOL;
+}
+
+pub mod compat {
+ use std::intrinsics::{atomic_store_relaxed, transmute};
+ use std::iter::Iterator;
+ use libc::types::os::arch::extra::{LPCWSTR, HMODULE, LPCSTR, LPVOID};
+
+ extern "system" {
+ fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE;
+ fn GetProcAddress(hModule: HMODULE, lpProcName: LPCSTR) -> LPVOID;
+ }
+
+ // store_func() is idempotent, so using relaxed ordering for the atomics
+ // should be enough. This way, calling a function in this compatibility
+ // layer (after it's loaded) shouldn't be any slower than a regular DLL
+ // call.
+ unsafe fn store_func(ptr: *mut uint, module: &str, symbol: &str, fallback: uint) {
+ let module: Vec<u16> = module.utf16_units().collect();
+ let module = module.append_one(0);
+ symbol.with_c_str(|symbol| {
+ let handle = GetModuleHandleW(module.as_ptr());
+ let func: uint = transmute(GetProcAddress(handle, symbol));
+ atomic_store_relaxed(ptr, if func == 0 {
+ fallback
+ } else {
+ func
+ })
+ })
+ }
+
+ /// Macro for creating a compatibility fallback for a Windows function
+ ///
+ /// # Example
+ /// ```
+ /// compat_fn!(adll32::SomeFunctionW(_arg: LPCWSTR) {
+ /// // Fallback implementation
+ /// })
+ /// ```
+ ///
+ /// Note that arguments unused by the fallback implementation should not be called `_` as
+ /// they are used to be passed to the real function if available.
+ macro_rules! compat_fn(
+ ($module:ident::$symbol:ident($($argname:ident: $argtype:ty),*)
+ -> $rettype:ty $fallback:block) => (
+ #[inline(always)]
+ pub unsafe fn $symbol($($argname: $argtype),*) -> $rettype {
+ static mut ptr: extern "system" fn($($argname: $argtype),*) -> $rettype = thunk;
+
+ extern "system" fn thunk($($argname: $argtype),*) -> $rettype {
+ unsafe {
+ ::io::c::compat::store_func(&mut ptr as *mut _ as *mut uint,
+ stringify!($module),
+ stringify!($symbol),
+ fallback as uint);
+ ::std::intrinsics::atomic_load_relaxed(&ptr)($($argname),*)
+ }
+ }
+
+ extern "system" fn fallback($($argname: $argtype),*) -> $rettype $fallback
+
+ ::std::intrinsics::atomic_load_relaxed(&ptr)($($argname),*)
+ }
+ );
+
+ ($module:ident::$symbol:ident($($argname:ident: $argtype:ty),*) $fallback:block) => (
+ compat_fn!($module::$symbol($($argname: $argtype),*) -> () $fallback)
+ )
+ )
+
+ /// Compatibility layer for functions in `kernel32.dll`
+ ///
+ /// Latest versions of Windows this is needed for:
+ ///
+ /// * `CreateSymbolicLinkW`: Windows XP, Windows Server 2003
+ /// * `GetFinalPathNameByHandleW`: Windows XP, Windows Server 2003
+ pub mod kernel32 {
+ use libc::types::os::arch::extra::{DWORD, LPCWSTR, BOOLEAN, HANDLE};
+ use libc::consts::os::extra::ERROR_CALL_NOT_IMPLEMENTED;
+
+ extern "system" {
+ fn SetLastError(dwErrCode: DWORD);
+ }
+
+ compat_fn!(kernel32::CreateSymbolicLinkW(_lpSymlinkFileName: LPCWSTR,
+ _lpTargetFileName: LPCWSTR,
+ _dwFlags: DWORD) -> BOOLEAN {
+ unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); }
+ 0
+ })
+
+ compat_fn!(kernel32::GetFinalPathNameByHandleW(_hFile: HANDLE,
+ _lpszFilePath: LPCWSTR,
+ _cchFilePath: DWORD,
+ _dwFlags: DWORD) -> DWORD {
+ unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); }
+ 0
+ })
+ }
+}
+
+extern "system" {
+ // FIXME - pInputControl should be PCONSOLE_READCONSOLE_CONTROL
+ pub fn ReadConsoleW(hConsoleInput: libc::HANDLE,
+ lpBuffer: libc::LPVOID,
+ nNumberOfCharsToRead: libc::DWORD,
+ lpNumberOfCharsRead: libc::LPDWORD,
+ pInputControl: libc::LPVOID) -> libc::BOOL;
+
+ pub fn WriteConsoleW(hConsoleOutput: libc::HANDLE,
+ lpBuffer: libc::types::os::arch::extra::LPCVOID,
+ nNumberOfCharsToWrite: libc::DWORD,
+ lpNumberOfCharsWritten: libc::LPDWORD,
+ lpReserved: libc::LPVOID) -> libc::BOOL;
+
+ pub fn GetConsoleMode(hConsoleHandle: libc::HANDLE,
+ lpMode: libc::LPDWORD) -> libc::BOOL;
+
+ pub fn SetConsoleMode(hConsoleHandle: libc::HANDLE,
+ lpMode: libc::DWORD) -> libc::BOOL;
+}
+++ /dev/null
-// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! Blocking win32-based file I/O
-
-use alloc::arc::Arc;
-use libc::{c_int, c_void};
-use libc;
-use std::c_str::CString;
-use std::mem;
-use std::os::win32::fill_utf16_buf_and_decode;
-use std::ptr;
-use std::rt::rtio;
-use std::rt::rtio::{IoResult, IoError};
-use std::str;
-use std::vec;
-
-pub type fd_t = libc::c_int;
-
-struct Inner {
- fd: fd_t,
- close_on_drop: bool,
-}
-
-pub struct FileDesc {
- inner: Arc<Inner>
-}
-
-impl FileDesc {
- /// Create a `FileDesc` from an open C file descriptor.
- ///
- /// The `FileDesc` will take ownership of the specified file descriptor and
- /// close it upon destruction if the `close_on_drop` flag is true, otherwise
- /// it will not close the file descriptor when this `FileDesc` is dropped.
- ///
- /// Note that all I/O operations done on this object will be *blocking*, but
- /// they do not require the runtime to be active.
- pub fn new(fd: fd_t, close_on_drop: bool) -> FileDesc {
- FileDesc { inner: Arc::new(Inner {
- fd: fd,
- close_on_drop: close_on_drop
- }) }
- }
-
- pub fn inner_read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
- let mut read = 0;
- let ret = unsafe {
- libc::ReadFile(self.handle(), buf.as_ptr() as libc::LPVOID,
- buf.len() as libc::DWORD, &mut read,
- ptr::mut_null())
- };
- if ret != 0 {
- Ok(read as uint)
- } else {
- Err(super::last_error())
- }
- }
- pub fn inner_write(&mut self, buf: &[u8]) -> IoResult<()> {
- let mut cur = buf.as_ptr();
- let mut remaining = buf.len();
- while remaining > 0 {
- let mut amt = 0;
- let ret = unsafe {
- libc::WriteFile(self.handle(), cur as libc::LPVOID,
- remaining as libc::DWORD, &mut amt,
- ptr::mut_null())
- };
- if ret != 0 {
- remaining -= amt as uint;
- cur = unsafe { cur.offset(amt as int) };
- } else {
- return Err(super::last_error())
- }
- }
- Ok(())
- }
-
- pub fn fd(&self) -> fd_t { self.inner.fd }
-
- pub fn handle(&self) -> libc::HANDLE {
- unsafe { libc::get_osfhandle(self.fd()) as libc::HANDLE }
- }
-
- // A version of seek that takes &self so that tell can call it
- // - the private seek should of course take &mut self.
- fn seek_common(&self, pos: i64, style: rtio::SeekStyle) -> IoResult<u64> {
- let whence = match style {
- rtio::SeekSet => libc::FILE_BEGIN,
- rtio::SeekEnd => libc::FILE_END,
- rtio::SeekCur => libc::FILE_CURRENT,
- };
- unsafe {
- let mut newpos = 0;
- match libc::SetFilePointerEx(self.handle(), pos, &mut newpos,
- whence) {
- 0 => Err(super::last_error()),
- _ => Ok(newpos as u64),
- }
- }
- }
-
-}
-
-impl rtio::RtioFileStream for FileDesc {
- fn read(&mut self, buf: &mut [u8]) -> IoResult<int> {
- self.inner_read(buf).map(|i| i as int)
- }
- fn write(&mut self, buf: &[u8]) -> IoResult<()> {
- self.inner_write(buf)
- }
-
- fn pread(&mut self, buf: &mut [u8], offset: u64) -> IoResult<int> {
- let mut read = 0;
- let mut overlap: libc::OVERLAPPED = unsafe { mem::zeroed() };
- overlap.Offset = offset as libc::DWORD;
- overlap.OffsetHigh = (offset >> 32) as libc::DWORD;
- let ret = unsafe {
- libc::ReadFile(self.handle(), buf.as_ptr() as libc::LPVOID,
- buf.len() as libc::DWORD, &mut read,
- &mut overlap)
- };
- if ret != 0 {
- Ok(read as int)
- } else {
- Err(super::last_error())
- }
- }
- fn pwrite(&mut self, buf: &[u8], mut offset: u64) -> IoResult<()> {
- let mut cur = buf.as_ptr();
- let mut remaining = buf.len();
- let mut overlap: libc::OVERLAPPED = unsafe { mem::zeroed() };
- while remaining > 0 {
- overlap.Offset = offset as libc::DWORD;
- overlap.OffsetHigh = (offset >> 32) as libc::DWORD;
- let mut amt = 0;
- let ret = unsafe {
- libc::WriteFile(self.handle(), cur as libc::LPVOID,
- remaining as libc::DWORD, &mut amt,
- &mut overlap)
- };
- if ret != 0 {
- remaining -= amt as uint;
- cur = unsafe { cur.offset(amt as int) };
- offset += amt as u64;
- } else {
- return Err(super::last_error())
- }
- }
- Ok(())
- }
-
- fn seek(&mut self, pos: i64, style: rtio::SeekStyle) -> IoResult<u64> {
- self.seek_common(pos, style)
- }
-
- fn tell(&self) -> IoResult<u64> {
- self.seek_common(0, rtio::SeekCur)
- }
-
- fn fsync(&mut self) -> IoResult<()> {
- super::mkerr_winbool(unsafe {
- libc::FlushFileBuffers(self.handle())
- })
- }
-
- fn datasync(&mut self) -> IoResult<()> { return self.fsync(); }
-
- fn truncate(&mut self, offset: i64) -> IoResult<()> {
- let orig_pos = try!(self.tell());
- let _ = try!(self.seek(offset, rtio::SeekSet));
- let ret = unsafe {
- match libc::SetEndOfFile(self.handle()) {
- 0 => Err(super::last_error()),
- _ => Ok(())
- }
- };
- let _ = self.seek(orig_pos as i64, rtio::SeekSet);
- return ret;
- }
-
- fn fstat(&mut self) -> IoResult<rtio::FileStat> {
- let mut stat: libc::stat = unsafe { mem::zeroed() };
- match unsafe { libc::fstat(self.fd(), &mut stat) } {
- 0 => Ok(mkstat(&stat)),
- _ => Err(super::last_error()),
- }
- }
-}
-
-impl rtio::RtioPipe for FileDesc {
- fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
- self.inner_read(buf)
- }
- fn write(&mut self, buf: &[u8]) -> IoResult<()> {
- self.inner_write(buf)
- }
- fn clone(&self) -> Box<rtio::RtioPipe + Send> {
- box FileDesc { inner: self.inner.clone() } as Box<rtio::RtioPipe + Send>
- }
-
- // Only supported on named pipes currently. Note that this doesn't have an
- // impact on the std::io primitives, this is never called via
- // std::io::PipeStream. If the functionality is exposed in the future, then
- // these methods will need to be implemented.
- fn close_read(&mut self) -> IoResult<()> {
- Err(super::unimpl())
- }
- fn close_write(&mut self) -> IoResult<()> {
- Err(super::unimpl())
- }
- fn set_timeout(&mut self, _t: Option<u64>) {}
- fn set_read_timeout(&mut self, _t: Option<u64>) {}
- fn set_write_timeout(&mut self, _t: Option<u64>) {}
-}
-
-impl rtio::RtioTTY for FileDesc {
- fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
- self.inner_read(buf)
- }
- fn write(&mut self, buf: &[u8]) -> IoResult<()> {
- self.inner_write(buf)
- }
- fn set_raw(&mut self, _raw: bool) -> IoResult<()> {
- Err(super::unimpl())
- }
- fn get_winsize(&mut self) -> IoResult<(int, int)> {
- Err(super::unimpl())
- }
- fn isatty(&self) -> bool { false }
-}
-
-impl Drop for Inner {
- fn drop(&mut self) {
- // closing stdio file handles makes no sense, so never do it. Also, note
- // that errors are ignored when closing a file descriptor. The reason
- // for this is that if an error occurs we don't actually know if the
- // file descriptor was closed or not, and if we retried (for something
- // like EINTR), we might close another valid file descriptor (opened
- // after we closed ours.
- if self.close_on_drop && self.fd > libc::STDERR_FILENO {
- let n = unsafe { libc::close(self.fd) };
- if n != 0 {
- println!("error {} when closing file descriptor {}", n, self.fd);
- }
- }
- }
-}
-
-pub fn to_utf16(s: &CString) -> IoResult<Vec<u16>> {
- match s.as_str() {
- Some(s) => Ok(s.utf16_units().collect::<Vec<u16>>().append_one(0)),
- None => Err(IoError {
- code: libc::ERROR_INVALID_NAME as uint,
- extra: 0,
- detail: Some("valid unicode input required".to_string()),
- })
- }
-}
-
-pub fn open(path: &CString, fm: rtio::FileMode, fa: rtio::FileAccess)
- -> IoResult<FileDesc> {
- // Flags passed to open_osfhandle
- let flags = match fm {
- rtio::Open => 0,
- rtio::Append => libc::O_APPEND,
- rtio::Truncate => libc::O_TRUNC,
- };
- let flags = match fa {
- rtio::Read => flags | libc::O_RDONLY,
- rtio::Write => flags | libc::O_WRONLY | libc::O_CREAT,
- rtio::ReadWrite => flags | libc::O_RDWR | libc::O_CREAT,
- };
-
- let mut dwDesiredAccess = match fa {
- rtio::Read => libc::FILE_GENERIC_READ,
- rtio::Write => libc::FILE_GENERIC_WRITE,
- rtio::ReadWrite => libc::FILE_GENERIC_READ | libc::FILE_GENERIC_WRITE
- };
-
- // libuv has a good comment about this, but the basic idea is what we try to
- // emulate unix semantics by enabling all sharing by allowing things such as
- // deleting a file while it's still open.
- let dwShareMode = libc::FILE_SHARE_READ | libc::FILE_SHARE_WRITE |
- libc::FILE_SHARE_DELETE;
-
- let dwCreationDisposition = match (fm, fa) {
- (rtio::Truncate, rtio::Read) => libc::TRUNCATE_EXISTING,
- (rtio::Truncate, _) => libc::CREATE_ALWAYS,
- (rtio::Open, rtio::Read) => libc::OPEN_EXISTING,
- (rtio::Open, _) => libc::OPEN_ALWAYS,
- (rtio::Append, rtio::Read) => {
- dwDesiredAccess |= libc::FILE_APPEND_DATA;
- libc::OPEN_EXISTING
- }
- (rtio::Append, _) => {
- dwDesiredAccess &= !libc::FILE_WRITE_DATA;
- dwDesiredAccess |= libc::FILE_APPEND_DATA;
- libc::OPEN_ALWAYS
- }
- };
-
- let mut dwFlagsAndAttributes = libc::FILE_ATTRIBUTE_NORMAL;
- // Compat with unix, this allows opening directories (see libuv)
- dwFlagsAndAttributes |= libc::FILE_FLAG_BACKUP_SEMANTICS;
-
- let path = try!(to_utf16(path));
- let handle = unsafe {
- libc::CreateFileW(path.as_ptr(),
- dwDesiredAccess,
- dwShareMode,
- ptr::mut_null(),
- dwCreationDisposition,
- dwFlagsAndAttributes,
- ptr::mut_null())
- };
- if handle == libc::INVALID_HANDLE_VALUE {
- Err(super::last_error())
- } else {
- let fd = unsafe {
- libc::open_osfhandle(handle as libc::intptr_t, flags)
- };
- if fd < 0 {
- let _ = unsafe { libc::CloseHandle(handle) };
- Err(super::last_error())
- } else {
- Ok(FileDesc::new(fd, true))
- }
- }
-}
-
-pub fn mkdir(p: &CString, _mode: uint) -> IoResult<()> {
- let p = try!(to_utf16(p));
- super::mkerr_winbool(unsafe {
- // FIXME: turn mode into something useful? #2623
- libc::CreateDirectoryW(p.as_ptr(), ptr::mut_null())
- })
-}
-
-pub fn readdir(p: &CString) -> IoResult<Vec<CString>> {
- use std::rt::libc_heap::malloc_raw;
-
- fn prune(root: &CString, dirs: Vec<Path>) -> Vec<CString> {
- let root = unsafe { CString::new(root.as_ptr(), false) };
- let root = Path::new(root);
-
- dirs.move_iter().filter(|path| {
- path.as_vec() != b"." && path.as_vec() != b".."
- }).map(|path| root.join(path).to_c_str()).collect()
- }
-
- extern {
- fn rust_list_dir_wfd_size() -> libc::size_t;
- fn rust_list_dir_wfd_fp_buf(wfd: *mut libc::c_void) -> *const u16;
- }
- let star = Path::new(unsafe {
- CString::new(p.as_ptr(), false)
- }).join("*");
- let path = try!(to_utf16(&star.to_c_str()));
-
- unsafe {
- let wfd_ptr = malloc_raw(rust_list_dir_wfd_size() as uint);
- let find_handle = libc::FindFirstFileW(path.as_ptr(),
- wfd_ptr as libc::HANDLE);
- if find_handle != libc::INVALID_HANDLE_VALUE {
- let mut paths = vec!();
- let mut more_files = 1 as libc::c_int;
- while more_files != 0 {
- let fp_buf = rust_list_dir_wfd_fp_buf(wfd_ptr as *mut c_void);
- if fp_buf as uint == 0 {
- fail!("os::list_dir() failure: got null ptr from wfd");
- } else {
- let fp_vec = vec::raw::from_buf(fp_buf, libc::wcslen(fp_buf) as uint);
- let fp_trimmed = str::truncate_utf16_at_nul(fp_vec.as_slice());
- let fp_str = String::from_utf16(fp_trimmed)
- .expect("rust_list_dir_wfd_fp_buf returned invalid UTF-16");
- paths.push(Path::new(fp_str));
- }
- more_files = libc::FindNextFileW(find_handle,
- wfd_ptr as libc::HANDLE);
- }
- assert!(libc::FindClose(find_handle) != 0);
- libc::free(wfd_ptr as *mut c_void);
- Ok(prune(p, paths))
- } else {
- Err(super::last_error())
- }
- }
-}
-
-pub fn unlink(p: &CString) -> IoResult<()> {
- let p = try!(to_utf16(p));
- super::mkerr_winbool(unsafe {
- libc::DeleteFileW(p.as_ptr())
- })
-}
-
-pub fn rename(old: &CString, new: &CString) -> IoResult<()> {
- let old = try!(to_utf16(old));
- let new = try!(to_utf16(new));
- super::mkerr_winbool(unsafe {
- libc::MoveFileExW(old.as_ptr(), new.as_ptr(),
- libc::MOVEFILE_REPLACE_EXISTING)
- })
-}
-
-pub fn chmod(p: &CString, mode: uint) -> IoResult<()> {
- let p = try!(to_utf16(p));
- super::mkerr_libc(unsafe {
- libc::wchmod(p.as_ptr(), mode as libc::c_int)
- })
-}
-
-pub fn rmdir(p: &CString) -> IoResult<()> {
- let p = try!(to_utf16(p));
- super::mkerr_libc(unsafe { libc::wrmdir(p.as_ptr()) })
-}
-
-pub fn chown(_p: &CString, _uid: int, _gid: int) -> IoResult<()> {
- // libuv has this as a no-op, so seems like this should as well?
- Ok(())
-}
-
-pub fn readlink(p: &CString) -> IoResult<CString> {
- // FIXME: I have a feeling that this reads intermediate symlinks as well.
- use io::c::compat::kernel32::GetFinalPathNameByHandleW;
- let p = try!(to_utf16(p));
- let handle = unsafe {
- libc::CreateFileW(p.as_ptr(),
- libc::GENERIC_READ,
- libc::FILE_SHARE_READ,
- ptr::mut_null(),
- libc::OPEN_EXISTING,
- libc::FILE_ATTRIBUTE_NORMAL,
- ptr::mut_null())
- };
- if handle == libc::INVALID_HANDLE_VALUE {
- return Err(super::last_error())
- }
- // Specify (sz - 1) because the documentation states that it's the size
- // without the null pointer
- let ret = fill_utf16_buf_and_decode(|buf, sz| unsafe {
- GetFinalPathNameByHandleW(handle,
- buf as *const u16,
- sz - 1,
- libc::VOLUME_NAME_DOS)
- });
- let ret = match ret {
- Some(ref s) if s.as_slice().starts_with(r"\\?\") => {
- Ok(Path::new(s.as_slice().slice_from(4)).to_c_str())
- }
- Some(s) => Ok(Path::new(s).to_c_str()),
- None => Err(super::last_error()),
- };
- assert!(unsafe { libc::CloseHandle(handle) } != 0);
- return ret;
-}
-
-pub fn symlink(src: &CString, dst: &CString) -> IoResult<()> {
- use io::c::compat::kernel32::CreateSymbolicLinkW;
- let src = try!(to_utf16(src));
- let dst = try!(to_utf16(dst));
- super::mkerr_winbool(unsafe {
- CreateSymbolicLinkW(dst.as_ptr(), src.as_ptr(), 0) as libc::BOOL
- })
-}
-
-pub fn link(src: &CString, dst: &CString) -> IoResult<()> {
- let src = try!(to_utf16(src));
- let dst = try!(to_utf16(dst));
- super::mkerr_winbool(unsafe {
- libc::CreateHardLinkW(dst.as_ptr(), src.as_ptr(), ptr::mut_null())
- })
-}
-
-fn mkstat(stat: &libc::stat) -> rtio::FileStat {
- rtio::FileStat {
- size: stat.st_size as u64,
- kind: stat.st_mode as u64,
- perm: stat.st_mode as u64,
- created: stat.st_ctime as u64,
- modified: stat.st_mtime as u64,
- accessed: stat.st_atime as u64,
- device: stat.st_dev as u64,
- inode: stat.st_ino as u64,
- rdev: stat.st_rdev as u64,
- nlink: stat.st_nlink as u64,
- uid: stat.st_uid as u64,
- gid: stat.st_gid as u64,
- blksize: 0,
- blocks: 0,
- flags: 0,
- gen: 0,
- }
-}
-
-pub fn stat(p: &CString) -> IoResult<rtio::FileStat> {
- let mut stat: libc::stat = unsafe { mem::zeroed() };
- let p = try!(to_utf16(p));
- match unsafe { libc::wstat(p.as_ptr(), &mut stat) } {
- 0 => Ok(mkstat(&stat)),
- _ => Err(super::last_error()),
- }
-}
-
-pub fn lstat(_p: &CString) -> IoResult<rtio::FileStat> {
- // FIXME: implementation is missing
- Err(super::unimpl())
-}
-
-pub fn utime(p: &CString, atime: u64, mtime: u64) -> IoResult<()> {
- let mut buf = libc::utimbuf {
- actime: atime as libc::time64_t,
- modtime: mtime as libc::time64_t,
- };
- let p = try!(to_utf16(p));
- super::mkerr_libc(unsafe {
- libc::wutime(p.as_ptr(), &mut buf)
- })
-}
--- /dev/null
+// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Blocking Windows-based file I/O
+
+use alloc::arc::Arc;
+use libc::{c_int, c_void};
+use libc;
+use std::c_str::CString;
+use std::mem;
+use std::os::windows::fill_utf16_buf_and_decode;
+use std::ptr;
+use std::rt::rtio;
+use std::rt::rtio::{IoResult, IoError};
+use std::str;
+use std::vec;
+
+pub type fd_t = libc::c_int;
+
+struct Inner {
+ fd: fd_t,
+ close_on_drop: bool,
+}
+
+pub struct FileDesc {
+ inner: Arc<Inner>
+}
+
+impl FileDesc {
+ /// Create a `FileDesc` from an open C file descriptor.
+ ///
+ /// The `FileDesc` will take ownership of the specified file descriptor and
+ /// close it upon destruction if the `close_on_drop` flag is true, otherwise
+ /// it will not close the file descriptor when this `FileDesc` is dropped.
+ ///
+ /// Note that all I/O operations done on this object will be *blocking*, but
+ /// they do not require the runtime to be active.
+ pub fn new(fd: fd_t, close_on_drop: bool) -> FileDesc {
+ FileDesc { inner: Arc::new(Inner {
+ fd: fd,
+ close_on_drop: close_on_drop
+ }) }
+ }
+
+ pub fn inner_read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
+ let mut read = 0;
+ let ret = unsafe {
+ libc::ReadFile(self.handle(), buf.as_ptr() as libc::LPVOID,
+ buf.len() as libc::DWORD, &mut read,
+ ptr::mut_null())
+ };
+ if ret != 0 {
+ Ok(read as uint)
+ } else {
+ Err(super::last_error())
+ }
+ }
+ pub fn inner_write(&mut self, buf: &[u8]) -> IoResult<()> {
+ let mut cur = buf.as_ptr();
+ let mut remaining = buf.len();
+ while remaining > 0 {
+ let mut amt = 0;
+ let ret = unsafe {
+ libc::WriteFile(self.handle(), cur as libc::LPVOID,
+ remaining as libc::DWORD, &mut amt,
+ ptr::mut_null())
+ };
+ if ret != 0 {
+ remaining -= amt as uint;
+ cur = unsafe { cur.offset(amt as int) };
+ } else {
+ return Err(super::last_error())
+ }
+ }
+ Ok(())
+ }
+
+ pub fn fd(&self) -> fd_t { self.inner.fd }
+
+ pub fn handle(&self) -> libc::HANDLE {
+ unsafe { libc::get_osfhandle(self.fd()) as libc::HANDLE }
+ }
+
+ // A version of seek that takes &self so that tell can call it
+ // - the private seek should of course take &mut self.
+ fn seek_common(&self, pos: i64, style: rtio::SeekStyle) -> IoResult<u64> {
+ let whence = match style {
+ rtio::SeekSet => libc::FILE_BEGIN,
+ rtio::SeekEnd => libc::FILE_END,
+ rtio::SeekCur => libc::FILE_CURRENT,
+ };
+ unsafe {
+ let mut newpos = 0;
+ match libc::SetFilePointerEx(self.handle(), pos, &mut newpos,
+ whence) {
+ 0 => Err(super::last_error()),
+ _ => Ok(newpos as u64),
+ }
+ }
+ }
+
+}
+
+impl rtio::RtioFileStream for FileDesc {
+ fn read(&mut self, buf: &mut [u8]) -> IoResult<int> {
+ self.inner_read(buf).map(|i| i as int)
+ }
+ fn write(&mut self, buf: &[u8]) -> IoResult<()> {
+ self.inner_write(buf)
+ }
+
+ fn pread(&mut self, buf: &mut [u8], offset: u64) -> IoResult<int> {
+ let mut read = 0;
+ let mut overlap: libc::OVERLAPPED = unsafe { mem::zeroed() };
+ overlap.Offset = offset as libc::DWORD;
+ overlap.OffsetHigh = (offset >> 32) as libc::DWORD;
+ let ret = unsafe {
+ libc::ReadFile(self.handle(), buf.as_ptr() as libc::LPVOID,
+ buf.len() as libc::DWORD, &mut read,
+ &mut overlap)
+ };
+ if ret != 0 {
+ Ok(read as int)
+ } else {
+ Err(super::last_error())
+ }
+ }
+ fn pwrite(&mut self, buf: &[u8], mut offset: u64) -> IoResult<()> {
+ let mut cur = buf.as_ptr();
+ let mut remaining = buf.len();
+ let mut overlap: libc::OVERLAPPED = unsafe { mem::zeroed() };
+ while remaining > 0 {
+ overlap.Offset = offset as libc::DWORD;
+ overlap.OffsetHigh = (offset >> 32) as libc::DWORD;
+ let mut amt = 0;
+ let ret = unsafe {
+ libc::WriteFile(self.handle(), cur as libc::LPVOID,
+ remaining as libc::DWORD, &mut amt,
+ &mut overlap)
+ };
+ if ret != 0 {
+ remaining -= amt as uint;
+ cur = unsafe { cur.offset(amt as int) };
+ offset += amt as u64;
+ } else {
+ return Err(super::last_error())
+ }
+ }
+ Ok(())
+ }
+
+ fn seek(&mut self, pos: i64, style: rtio::SeekStyle) -> IoResult<u64> {
+ self.seek_common(pos, style)
+ }
+
+ fn tell(&self) -> IoResult<u64> {
+ self.seek_common(0, rtio::SeekCur)
+ }
+
+ fn fsync(&mut self) -> IoResult<()> {
+ super::mkerr_winbool(unsafe {
+ libc::FlushFileBuffers(self.handle())
+ })
+ }
+
+ fn datasync(&mut self) -> IoResult<()> { return self.fsync(); }
+
+ fn truncate(&mut self, offset: i64) -> IoResult<()> {
+ let orig_pos = try!(self.tell());
+ let _ = try!(self.seek(offset, rtio::SeekSet));
+ let ret = unsafe {
+ match libc::SetEndOfFile(self.handle()) {
+ 0 => Err(super::last_error()),
+ _ => Ok(())
+ }
+ };
+ let _ = self.seek(orig_pos as i64, rtio::SeekSet);
+ return ret;
+ }
+
+ fn fstat(&mut self) -> IoResult<rtio::FileStat> {
+ let mut stat: libc::stat = unsafe { mem::zeroed() };
+ match unsafe { libc::fstat(self.fd(), &mut stat) } {
+ 0 => Ok(mkstat(&stat)),
+ _ => Err(super::last_error()),
+ }
+ }
+}
+
+impl rtio::RtioPipe for FileDesc {
+ fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
+ self.inner_read(buf)
+ }
+ fn write(&mut self, buf: &[u8]) -> IoResult<()> {
+ self.inner_write(buf)
+ }
+ fn clone(&self) -> Box<rtio::RtioPipe + Send> {
+ box FileDesc { inner: self.inner.clone() } as Box<rtio::RtioPipe + Send>
+ }
+
+ // Only supported on named pipes currently. Note that this doesn't have an
+ // impact on the std::io primitives, this is never called via
+ // std::io::PipeStream. If the functionality is exposed in the future, then
+ // these methods will need to be implemented.
+ fn close_read(&mut self) -> IoResult<()> {
+ Err(super::unimpl())
+ }
+ fn close_write(&mut self) -> IoResult<()> {
+ Err(super::unimpl())
+ }
+ fn set_timeout(&mut self, _t: Option<u64>) {}
+ fn set_read_timeout(&mut self, _t: Option<u64>) {}
+ fn set_write_timeout(&mut self, _t: Option<u64>) {}
+}
+
+impl rtio::RtioTTY for FileDesc {
+ fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
+ self.inner_read(buf)
+ }
+ fn write(&mut self, buf: &[u8]) -> IoResult<()> {
+ self.inner_write(buf)
+ }
+ fn set_raw(&mut self, _raw: bool) -> IoResult<()> {
+ Err(super::unimpl())
+ }
+ fn get_winsize(&mut self) -> IoResult<(int, int)> {
+ Err(super::unimpl())
+ }
+ fn isatty(&self) -> bool { false }
+}
+
+impl Drop for Inner {
+ fn drop(&mut self) {
+ // closing stdio file handles makes no sense, so never do it. Also, note
+ // that errors are ignored when closing a file descriptor. The reason
+ // for this is that if an error occurs we don't actually know if the
+ // file descriptor was closed or not, and if we retried (for something
+ // like EINTR), we might close another valid file descriptor (opened
+ // after we closed ours.
+ if self.close_on_drop && self.fd > libc::STDERR_FILENO {
+ let n = unsafe { libc::close(self.fd) };
+ if n != 0 {
+ println!("error {} when closing file descriptor {}", n, self.fd);
+ }
+ }
+ }
+}
+
+pub fn to_utf16(s: &CString) -> IoResult<Vec<u16>> {
+ match s.as_str() {
+ Some(s) => Ok(s.utf16_units().collect::<Vec<u16>>().append_one(0)),
+ None => Err(IoError {
+ code: libc::ERROR_INVALID_NAME as uint,
+ extra: 0,
+ detail: Some("valid unicode input required".to_string()),
+ })
+ }
+}
+
+pub fn open(path: &CString, fm: rtio::FileMode, fa: rtio::FileAccess)
+ -> IoResult<FileDesc> {
+ // Flags passed to open_osfhandle
+ let flags = match fm {
+ rtio::Open => 0,
+ rtio::Append => libc::O_APPEND,
+ rtio::Truncate => libc::O_TRUNC,
+ };
+ let flags = match fa {
+ rtio::Read => flags | libc::O_RDONLY,
+ rtio::Write => flags | libc::O_WRONLY | libc::O_CREAT,
+ rtio::ReadWrite => flags | libc::O_RDWR | libc::O_CREAT,
+ };
+
+ let mut dwDesiredAccess = match fa {
+ rtio::Read => libc::FILE_GENERIC_READ,
+ rtio::Write => libc::FILE_GENERIC_WRITE,
+ rtio::ReadWrite => libc::FILE_GENERIC_READ | libc::FILE_GENERIC_WRITE
+ };
+
+ // libuv has a good comment about this, but the basic idea is what we try to
+ // emulate unix semantics by enabling all sharing by allowing things such as
+ // deleting a file while it's still open.
+ let dwShareMode = libc::FILE_SHARE_READ | libc::FILE_SHARE_WRITE |
+ libc::FILE_SHARE_DELETE;
+
+ let dwCreationDisposition = match (fm, fa) {
+ (rtio::Truncate, rtio::Read) => libc::TRUNCATE_EXISTING,
+ (rtio::Truncate, _) => libc::CREATE_ALWAYS,
+ (rtio::Open, rtio::Read) => libc::OPEN_EXISTING,
+ (rtio::Open, _) => libc::OPEN_ALWAYS,
+ (rtio::Append, rtio::Read) => {
+ dwDesiredAccess |= libc::FILE_APPEND_DATA;
+ libc::OPEN_EXISTING
+ }
+ (rtio::Append, _) => {
+ dwDesiredAccess &= !libc::FILE_WRITE_DATA;
+ dwDesiredAccess |= libc::FILE_APPEND_DATA;
+ libc::OPEN_ALWAYS
+ }
+ };
+
+ let mut dwFlagsAndAttributes = libc::FILE_ATTRIBUTE_NORMAL;
+ // Compat with unix, this allows opening directories (see libuv)
+ dwFlagsAndAttributes |= libc::FILE_FLAG_BACKUP_SEMANTICS;
+
+ let path = try!(to_utf16(path));
+ let handle = unsafe {
+ libc::CreateFileW(path.as_ptr(),
+ dwDesiredAccess,
+ dwShareMode,
+ ptr::mut_null(),
+ dwCreationDisposition,
+ dwFlagsAndAttributes,
+ ptr::mut_null())
+ };
+ if handle == libc::INVALID_HANDLE_VALUE {
+ Err(super::last_error())
+ } else {
+ let fd = unsafe {
+ libc::open_osfhandle(handle as libc::intptr_t, flags)
+ };
+ if fd < 0 {
+ let _ = unsafe { libc::CloseHandle(handle) };
+ Err(super::last_error())
+ } else {
+ Ok(FileDesc::new(fd, true))
+ }
+ }
+}
+
+pub fn mkdir(p: &CString, _mode: uint) -> IoResult<()> {
+ let p = try!(to_utf16(p));
+ super::mkerr_winbool(unsafe {
+ // FIXME: turn mode into something useful? #2623
+ libc::CreateDirectoryW(p.as_ptr(), ptr::mut_null())
+ })
+}
+
+pub fn readdir(p: &CString) -> IoResult<Vec<CString>> {
+ use std::rt::libc_heap::malloc_raw;
+
+ fn prune(root: &CString, dirs: Vec<Path>) -> Vec<CString> {
+ let root = unsafe { CString::new(root.as_ptr(), false) };
+ let root = Path::new(root);
+
+ dirs.move_iter().filter(|path| {
+ path.as_vec() != b"." && path.as_vec() != b".."
+ }).map(|path| root.join(path).to_c_str()).collect()
+ }
+
+ extern {
+ fn rust_list_dir_wfd_size() -> libc::size_t;
+ fn rust_list_dir_wfd_fp_buf(wfd: *mut libc::c_void) -> *const u16;
+ }
+ let star = Path::new(unsafe {
+ CString::new(p.as_ptr(), false)
+ }).join("*");
+ let path = try!(to_utf16(&star.to_c_str()));
+
+ unsafe {
+ let wfd_ptr = malloc_raw(rust_list_dir_wfd_size() as uint);
+ let find_handle = libc::FindFirstFileW(path.as_ptr(),
+ wfd_ptr as libc::HANDLE);
+ if find_handle != libc::INVALID_HANDLE_VALUE {
+ let mut paths = vec!();
+ let mut more_files = 1 as libc::c_int;
+ while more_files != 0 {
+ let fp_buf = rust_list_dir_wfd_fp_buf(wfd_ptr as *mut c_void);
+ if fp_buf as uint == 0 {
+ fail!("os::list_dir() failure: got null ptr from wfd");
+ } else {
+ let fp_vec = vec::raw::from_buf(fp_buf, libc::wcslen(fp_buf) as uint);
+ let fp_trimmed = str::truncate_utf16_at_nul(fp_vec.as_slice());
+ let fp_str = String::from_utf16(fp_trimmed)
+ .expect("rust_list_dir_wfd_fp_buf returned invalid UTF-16");
+ paths.push(Path::new(fp_str));
+ }
+ more_files = libc::FindNextFileW(find_handle,
+ wfd_ptr as libc::HANDLE);
+ }
+ assert!(libc::FindClose(find_handle) != 0);
+ libc::free(wfd_ptr as *mut c_void);
+ Ok(prune(p, paths))
+ } else {
+ Err(super::last_error())
+ }
+ }
+}
+
+pub fn unlink(p: &CString) -> IoResult<()> {
+ let p = try!(to_utf16(p));
+ super::mkerr_winbool(unsafe {
+ libc::DeleteFileW(p.as_ptr())
+ })
+}
+
+pub fn rename(old: &CString, new: &CString) -> IoResult<()> {
+ let old = try!(to_utf16(old));
+ let new = try!(to_utf16(new));
+ super::mkerr_winbool(unsafe {
+ libc::MoveFileExW(old.as_ptr(), new.as_ptr(),
+ libc::MOVEFILE_REPLACE_EXISTING)
+ })
+}
+
+pub fn chmod(p: &CString, mode: uint) -> IoResult<()> {
+ let p = try!(to_utf16(p));
+ super::mkerr_libc(unsafe {
+ libc::wchmod(p.as_ptr(), mode as libc::c_int)
+ })
+}
+
+pub fn rmdir(p: &CString) -> IoResult<()> {
+ let p = try!(to_utf16(p));
+ super::mkerr_libc(unsafe { libc::wrmdir(p.as_ptr()) })
+}
+
+pub fn chown(_p: &CString, _uid: int, _gid: int) -> IoResult<()> {
+ // libuv has this as a no-op, so seems like this should as well?
+ Ok(())
+}
+
+pub fn readlink(p: &CString) -> IoResult<CString> {
+ // FIXME: I have a feeling that this reads intermediate symlinks as well.
+ use io::c::compat::kernel32::GetFinalPathNameByHandleW;
+ let p = try!(to_utf16(p));
+ let handle = unsafe {
+ libc::CreateFileW(p.as_ptr(),
+ libc::GENERIC_READ,
+ libc::FILE_SHARE_READ,
+ ptr::mut_null(),
+ libc::OPEN_EXISTING,
+ libc::FILE_ATTRIBUTE_NORMAL,
+ ptr::mut_null())
+ };
+ if handle == libc::INVALID_HANDLE_VALUE {
+ return Err(super::last_error())
+ }
+ // Specify (sz - 1) because the documentation states that it's the size
+ // without the null pointer
+ let ret = fill_utf16_buf_and_decode(|buf, sz| unsafe {
+ GetFinalPathNameByHandleW(handle,
+ buf as *const u16,
+ sz - 1,
+ libc::VOLUME_NAME_DOS)
+ });
+ let ret = match ret {
+ Some(ref s) if s.as_slice().starts_with(r"\\?\") => {
+ Ok(Path::new(s.as_slice().slice_from(4)).to_c_str())
+ }
+ Some(s) => Ok(Path::new(s).to_c_str()),
+ None => Err(super::last_error()),
+ };
+ assert!(unsafe { libc::CloseHandle(handle) } != 0);
+ return ret;
+}
+
+pub fn symlink(src: &CString, dst: &CString) -> IoResult<()> {
+ use io::c::compat::kernel32::CreateSymbolicLinkW;
+ let src = try!(to_utf16(src));
+ let dst = try!(to_utf16(dst));
+ super::mkerr_winbool(unsafe {
+ CreateSymbolicLinkW(dst.as_ptr(), src.as_ptr(), 0) as libc::BOOL
+ })
+}
+
+pub fn link(src: &CString, dst: &CString) -> IoResult<()> {
+ let src = try!(to_utf16(src));
+ let dst = try!(to_utf16(dst));
+ super::mkerr_winbool(unsafe {
+ libc::CreateHardLinkW(dst.as_ptr(), src.as_ptr(), ptr::mut_null())
+ })
+}
+
+fn mkstat(stat: &libc::stat) -> rtio::FileStat {
+ rtio::FileStat {
+ size: stat.st_size as u64,
+ kind: stat.st_mode as u64,
+ perm: stat.st_mode as u64,
+ created: stat.st_ctime as u64,
+ modified: stat.st_mtime as u64,
+ accessed: stat.st_atime as u64,
+ device: stat.st_dev as u64,
+ inode: stat.st_ino as u64,
+ rdev: stat.st_rdev as u64,
+ nlink: stat.st_nlink as u64,
+ uid: stat.st_uid as u64,
+ gid: stat.st_gid as u64,
+ blksize: 0,
+ blocks: 0,
+ flags: 0,
+ gen: 0,
+ }
+}
+
+pub fn stat(p: &CString) -> IoResult<rtio::FileStat> {
+ let mut stat: libc::stat = unsafe { mem::zeroed() };
+ let p = try!(to_utf16(p));
+ match unsafe { libc::wstat(p.as_ptr(), &mut stat) } {
+ 0 => Ok(mkstat(&stat)),
+ _ => Err(super::last_error()),
+ }
+}
+
+pub fn lstat(_p: &CString) -> IoResult<rtio::FileStat> {
+ // FIXME: implementation is missing
+ Err(super::unimpl())
+}
+
+pub fn utime(p: &CString, atime: u64, mtime: u64) -> IoResult<()> {
+ let mut buf = libc::utimbuf {
+ actime: atime as libc::time64_t,
+ modtime: mtime as libc::time64_t,
+ };
+ let p = try!(to_utf16(p));
+ super::mkerr_libc(unsafe {
+ libc::wutime(p.as_ptr(), &mut buf)
+ })
+}
#[path = "file_unix.rs"]
pub mod file;
#[cfg(windows)]
-#[path = "file_win32.rs"]
+#[path = "file_windows.rs"]
pub mod file;
#[cfg(target_os = "macos")]
pub mod timer;
#[cfg(target_os = "windows")]
-#[cfg(stage0, target_os = "win32")] // NOTE: Remove after snapshot
-#[path = "timer_win32.rs"]
+#[path = "timer_windows.rs"]
pub mod timer;
#[cfg(unix)]
pub mod pipe;
#[cfg(windows)]
-#[path = "pipe_win32.rs"]
+#[path = "pipe_windows.rs"]
pub mod pipe;
#[cfg(windows)]
-#[path = "tty_win32.rs"]
+#[path = "tty_windows.rs"]
mod tty;
#[cfg(unix)] #[path = "c_unix.rs"] mod c;
-#[cfg(windows)] #[path = "c_win32.rs"] mod c;
+#[cfg(windows)] #[path = "c_windows.rs"] mod c;
fn unimpl() -> IoError {
#[cfg(unix)] use libc::ENOSYS as ERROR;
+++ /dev/null
-// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! Named pipes implementation for windows
-//!
-//! If are unfortunate enough to be reading this code, I would like to first
-//! apologize. This was my first encounter with windows named pipes, and it
-//! didn't exactly turn out very cleanly. If you, too, are new to named pipes,
-//! read on as I'll try to explain some fun things that I ran into.
-//!
-//! # Unix pipes vs Named pipes
-//!
-//! As with everything else, named pipes on windows are pretty different from
-//! unix pipes on unix. On unix, you use one "server pipe" to accept new client
-//! pipes. So long as this server pipe is active, new children pipes can
-//! connect. On windows, you instead have a number of "server pipes", and each
-//! of these server pipes can throughout their lifetime be attached to a client
-//! or not. Once attached to a client, a server pipe may then disconnect at a
-//! later date.
-//!
-//! # Accepting clients
-//!
-//! As with most other I/O interfaces, our Listener/Acceptor/Stream interfaces
-//! are built around the unix flavors. This means that we have one "server
-//! pipe" to which many clients can connect. In order to make this compatible
-//! with the windows model, each connected client consumes ownership of a server
-//! pipe, and then a new server pipe is created for the next client.
-//!
-//! Note that the server pipes attached to clients are never given back to the
-//! listener for recycling. This could possibly be implemented with a channel so
-//! the listener half can re-use server pipes, but for now I err'd on the simple
-//! side of things. Each stream accepted by a listener will destroy the server
-//! pipe after the stream is dropped.
-//!
-//! This model ends up having a small race or two, and you can find more details
-//! on the `native_accept` method.
-//!
-//! # Simultaneous reads and writes
-//!
-//! In testing, I found that two simultaneous writes and two simultaneous reads
-//! on a pipe ended up working out just fine, but problems were encountered when
-//! a read was executed simultaneously with a write. After some googling around,
-//! it sounded like named pipes just weren't built for this kind of interaction,
-//! and the suggested solution was to use overlapped I/O.
-//!
-//! I don't really know what overlapped I/O is, but my basic understanding after
-//! reading about it is that you have an external Event which is used to signal
-//! I/O completion, passed around in some OVERLAPPED structures. As to what this
-//! is, I'm not exactly sure.
-//!
-//! This problem implies that all named pipes are created with the
-//! FILE_FLAG_OVERLAPPED option. This means that all of their I/O is
-//! asynchronous. Each I/O operation has an associated OVERLAPPED structure, and
-//! inside of this structure is a HANDLE from CreateEvent. After the I/O is
-//! determined to be pending (may complete in the future), the
-//! GetOverlappedResult function is used to block on the event, waiting for the
-//! I/O to finish.
-//!
-//! This scheme ended up working well enough. There were two snags that I ran
-//! into, however:
-//!
-//! * Each UnixStream instance needs its own read/write events to wait on. These
-//! can't be shared among clones of the same stream because the documentation
-//! states that it unsets the event when the I/O is started (would possibly
-//! corrupt other events simultaneously waiting). For convenience's sake,
-//! these events are lazily initialized.
-//!
-//! * Each server pipe needs to be created with FILE_FLAG_OVERLAPPED in addition
-//! to all pipes created through `connect`. Notably this means that the
-//! ConnectNamedPipe function is nonblocking, implying that the Listener needs
-//! to have yet another event to do the actual blocking.
-//!
-//! # Conclusion
-//!
-//! The conclusion here is that I probably don't know the best way to work with
-//! windows named pipes, but the solution here seems to work well enough to get
-//! the test suite passing (the suite is in libstd), and that's good enough for
-//! me!
-
-use alloc::arc::Arc;
-use libc;
-use std::c_str::CString;
-use std::mem;
-use std::os;
-use std::ptr;
-use std::rt::rtio;
-use std::rt::rtio::{IoResult, IoError};
-use std::sync::atomic;
-use std::rt::mutex;
-
-use super::c;
-use super::util;
-use super::file::to_utf16;
-
-struct Event(libc::HANDLE);
-
-impl Event {
- fn new(manual_reset: bool, initial_state: bool) -> IoResult<Event> {
- let event = unsafe {
- libc::CreateEventW(ptr::mut_null(),
- manual_reset as libc::BOOL,
- initial_state as libc::BOOL,
- ptr::null())
- };
- if event as uint == 0 {
- Err(super::last_error())
- } else {
- Ok(Event(event))
- }
- }
-
- fn handle(&self) -> libc::HANDLE { let Event(handle) = *self; handle }
-}
-
-impl Drop for Event {
- fn drop(&mut self) {
- unsafe { let _ = libc::CloseHandle(self.handle()); }
- }
-}
-
-struct Inner {
- handle: libc::HANDLE,
- lock: mutex::NativeMutex,
- read_closed: atomic::AtomicBool,
- write_closed: atomic::AtomicBool,
-}
-
-impl Inner {
- fn new(handle: libc::HANDLE) -> Inner {
- Inner {
- handle: handle,
- lock: unsafe { mutex::NativeMutex::new() },
- read_closed: atomic::AtomicBool::new(false),
- write_closed: atomic::AtomicBool::new(false),
- }
- }
-}
-
-impl Drop for Inner {
- fn drop(&mut self) {
- unsafe {
- let _ = libc::FlushFileBuffers(self.handle);
- let _ = libc::CloseHandle(self.handle);
- }
- }
-}
-
-unsafe fn pipe(name: *const u16, init: bool) -> libc::HANDLE {
- libc::CreateNamedPipeW(
- name,
- libc::PIPE_ACCESS_DUPLEX |
- if init {libc::FILE_FLAG_FIRST_PIPE_INSTANCE} else {0} |
- libc::FILE_FLAG_OVERLAPPED,
- libc::PIPE_TYPE_BYTE | libc::PIPE_READMODE_BYTE |
- libc::PIPE_WAIT,
- libc::PIPE_UNLIMITED_INSTANCES,
- 65536,
- 65536,
- 0,
- ptr::mut_null()
- )
-}
-
-pub fn await(handle: libc::HANDLE, deadline: u64,
- overlapped: &mut libc::OVERLAPPED) -> bool {
- if deadline == 0 { return true }
-
- // If we've got a timeout, use WaitForSingleObject in tandem with CancelIo
- // to figure out if we should indeed get the result.
- let now = ::io::timer::now();
- let timeout = deadline < now || unsafe {
- let ms = (deadline - now) as libc::DWORD;
- let r = libc::WaitForSingleObject(overlapped.hEvent,
- ms);
- r != libc::WAIT_OBJECT_0
- };
- if timeout {
- unsafe { let _ = c::CancelIo(handle); }
- false
- } else {
- true
- }
-}
-
-fn epipe() -> IoError {
- IoError {
- code: libc::ERROR_BROKEN_PIPE as uint,
- extra: 0,
- detail: None,
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Unix Streams
-////////////////////////////////////////////////////////////////////////////////
-
-pub struct UnixStream {
- inner: Arc<Inner>,
- write: Option<Event>,
- read: Option<Event>,
- read_deadline: u64,
- write_deadline: u64,
-}
-
-impl UnixStream {
- fn try_connect(p: *const u16) -> Option<libc::HANDLE> {
- // Note that most of this is lifted from the libuv implementation.
- // The idea is that if we fail to open a pipe in read/write mode
- // that we try afterwards in just read or just write
- let mut result = unsafe {
- libc::CreateFileW(p,
- libc::GENERIC_READ | libc::GENERIC_WRITE,
- 0,
- ptr::mut_null(),
- libc::OPEN_EXISTING,
- libc::FILE_FLAG_OVERLAPPED,
- ptr::mut_null())
- };
- if result != libc::INVALID_HANDLE_VALUE {
- return Some(result)
- }
-
- let err = unsafe { libc::GetLastError() };
- if err == libc::ERROR_ACCESS_DENIED as libc::DWORD {
- result = unsafe {
- libc::CreateFileW(p,
- libc::GENERIC_READ | libc::FILE_WRITE_ATTRIBUTES,
- 0,
- ptr::mut_null(),
- libc::OPEN_EXISTING,
- libc::FILE_FLAG_OVERLAPPED,
- ptr::mut_null())
- };
- if result != libc::INVALID_HANDLE_VALUE {
- return Some(result)
- }
- }
- let err = unsafe { libc::GetLastError() };
- if err == libc::ERROR_ACCESS_DENIED as libc::DWORD {
- result = unsafe {
- libc::CreateFileW(p,
- libc::GENERIC_WRITE | libc::FILE_READ_ATTRIBUTES,
- 0,
- ptr::mut_null(),
- libc::OPEN_EXISTING,
- libc::FILE_FLAG_OVERLAPPED,
- ptr::mut_null())
- };
- if result != libc::INVALID_HANDLE_VALUE {
- return Some(result)
- }
- }
- None
- }
-
- pub fn connect(addr: &CString, timeout: Option<u64>) -> IoResult<UnixStream> {
- let addr = try!(to_utf16(addr));
- let start = ::io::timer::now();
- loop {
- match UnixStream::try_connect(addr.as_ptr()) {
- Some(handle) => {
- let inner = Inner::new(handle);
- let mut mode = libc::PIPE_TYPE_BYTE |
- libc::PIPE_READMODE_BYTE |
- libc::PIPE_WAIT;
- let ret = unsafe {
- libc::SetNamedPipeHandleState(inner.handle,
- &mut mode,
- ptr::mut_null(),
- ptr::mut_null())
- };
- return if ret == 0 {
- Err(super::last_error())
- } else {
- Ok(UnixStream {
- inner: Arc::new(inner),
- read: None,
- write: None,
- read_deadline: 0,
- write_deadline: 0,
- })
- }
- }
- None => {}
- }
-
- // On windows, if you fail to connect, you may need to call the
- // `WaitNamedPipe` function, and this is indicated with an error
- // code of ERROR_PIPE_BUSY.
- let code = unsafe { libc::GetLastError() };
- if code as int != libc::ERROR_PIPE_BUSY as int {
- return Err(super::last_error())
- }
-
- match timeout {
- Some(timeout) => {
- let now = ::io::timer::now();
- let timed_out = (now - start) >= timeout || unsafe {
- let ms = (timeout - (now - start)) as libc::DWORD;
- libc::WaitNamedPipeW(addr.as_ptr(), ms) == 0
- };
- if timed_out {
- return Err(util::timeout("connect timed out"))
- }
- }
-
- // An example I found on Microsoft's website used 20
- // seconds, libuv uses 30 seconds, hence we make the
- // obvious choice of waiting for 25 seconds.
- None => {
- if unsafe { libc::WaitNamedPipeW(addr.as_ptr(), 25000) } == 0 {
- return Err(super::last_error())
- }
- }
- }
- }
- }
-
- fn handle(&self) -> libc::HANDLE { self.inner.handle }
-
- fn read_closed(&self) -> bool {
- self.inner.read_closed.load(atomic::SeqCst)
- }
-
- fn write_closed(&self) -> bool {
- self.inner.write_closed.load(atomic::SeqCst)
- }
-
- fn cancel_io(&self) -> IoResult<()> {
- match unsafe { c::CancelIoEx(self.handle(), ptr::mut_null()) } {
- 0 if os::errno() == libc::ERROR_NOT_FOUND as uint => {
- Ok(())
- }
- 0 => Err(super::last_error()),
- _ => Ok(())
- }
- }
-}
-
-impl rtio::RtioPipe for UnixStream {
- fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
- if self.read.is_none() {
- self.read = Some(try!(Event::new(true, false)));
- }
-
- let mut bytes_read = 0;
- let mut overlapped: libc::OVERLAPPED = unsafe { mem::zeroed() };
- overlapped.hEvent = self.read.get_ref().handle();
-
- // Pre-flight check to see if the reading half has been closed. This
- // must be done before issuing the ReadFile request, but after we
- // acquire the lock.
- //
- // See comments in close_read() about why this lock is necessary.
- let guard = unsafe { self.inner.lock.lock() };
- if self.read_closed() {
- return Err(util::eof())
- }
-
- // Issue a nonblocking requests, succeeding quickly if it happened to
- // succeed.
- let ret = unsafe {
- libc::ReadFile(self.handle(),
- buf.as_ptr() as libc::LPVOID,
- buf.len() as libc::DWORD,
- &mut bytes_read,
- &mut overlapped)
- };
- if ret != 0 { return Ok(bytes_read as uint) }
-
- // If our errno doesn't say that the I/O is pending, then we hit some
- // legitimate error and return immediately.
- if os::errno() != libc::ERROR_IO_PENDING as uint {
- return Err(super::last_error())
- }
-
- // Now that we've issued a successful nonblocking request, we need to
- // wait for it to finish. This can all be done outside the lock because
- // we'll see any invocation of CancelIoEx. We also call this in a loop
- // because we're woken up if the writing half is closed, we just need to
- // realize that the reading half wasn't closed and we go right back to
- // sleep.
- drop(guard);
- loop {
- // Process a timeout if one is pending
- let succeeded = await(self.handle(), self.read_deadline,
- &mut overlapped);
-
- let ret = unsafe {
- libc::GetOverlappedResult(self.handle(),
- &mut overlapped,
- &mut bytes_read,
- libc::TRUE)
- };
- // If we succeeded, or we failed for some reason other than
- // CancelIoEx, return immediately
- if ret != 0 { return Ok(bytes_read as uint) }
- if os::errno() != libc::ERROR_OPERATION_ABORTED as uint {
- return Err(super::last_error())
- }
-
- // If the reading half is now closed, then we're done. If we woke up
- // because the writing half was closed, keep trying.
- if !succeeded {
- return Err(util::timeout("read timed out"))
- }
- if self.read_closed() {
- return Err(util::eof())
- }
- }
- }
-
- fn write(&mut self, buf: &[u8]) -> IoResult<()> {
- if self.write.is_none() {
- self.write = Some(try!(Event::new(true, false)));
- }
-
- let mut offset = 0;
- let mut overlapped: libc::OVERLAPPED = unsafe { mem::zeroed() };
- overlapped.hEvent = self.write.get_ref().handle();
-
- while offset < buf.len() {
- let mut bytes_written = 0;
-
- // This sequence below is quite similar to the one found in read().
- // Some careful looping is done to ensure that if close_write() is
- // invoked we bail out early, and if close_read() is invoked we keep
- // going after we woke up.
- //
- // See comments in close_read() about why this lock is necessary.
- let guard = unsafe { self.inner.lock.lock() };
- if self.write_closed() {
- return Err(epipe())
- }
- let ret = unsafe {
- libc::WriteFile(self.handle(),
- buf.slice_from(offset).as_ptr() as libc::LPVOID,
- (buf.len() - offset) as libc::DWORD,
- &mut bytes_written,
- &mut overlapped)
- };
- let err = os::errno();
- drop(guard);
-
- if ret == 0 {
- if err != libc::ERROR_IO_PENDING as uint {
- return Err(IoError {
- code: err as uint,
- extra: 0,
- detail: Some(os::error_string(err as uint)),
- })
- }
- // Process a timeout if one is pending
- let succeeded = await(self.handle(), self.write_deadline,
- &mut overlapped);
- let ret = unsafe {
- libc::GetOverlappedResult(self.handle(),
- &mut overlapped,
- &mut bytes_written,
- libc::TRUE)
- };
- // If we weren't aborted, this was a legit error, if we were
- // aborted, then check to see if the write half was actually
- // closed or whether we woke up from the read half closing.
- if ret == 0 {
- if os::errno() != libc::ERROR_OPERATION_ABORTED as uint {
- return Err(super::last_error())
- }
- if !succeeded {
- let amt = offset + bytes_written as uint;
- return if amt > 0 {
- Err(IoError {
- code: libc::ERROR_OPERATION_ABORTED as uint,
- extra: amt,
- detail: Some("short write during write".to_string()),
- })
- } else {
- Err(util::timeout("write timed out"))
- }
- }
- if self.write_closed() {
- return Err(epipe())
- }
- continue // retry
- }
- }
- offset += bytes_written as uint;
- }
- Ok(())
- }
-
- fn clone(&self) -> Box<rtio::RtioPipe + Send> {
- box UnixStream {
- inner: self.inner.clone(),
- read: None,
- write: None,
- read_deadline: 0,
- write_deadline: 0,
- } as Box<rtio::RtioPipe + Send>
- }
-
- fn close_read(&mut self) -> IoResult<()> {
- // On windows, there's no actual shutdown() method for pipes, so we're
- // forced to emulate the behavior manually at the application level. To
- // do this, we need to both cancel any pending requests, as well as
- // prevent all future requests from succeeding. These two operations are
- // not atomic with respect to one another, so we must use a lock to do
- // so.
- //
- // The read() code looks like:
- //
- // 1. Make sure the pipe is still open
- // 2. Submit a read request
- // 3. Wait for the read request to finish
- //
- // The race this lock is preventing is if another thread invokes
- // close_read() between steps 1 and 2. By atomically executing steps 1
- // and 2 with a lock with respect to close_read(), we're guaranteed that
- // no thread will erroneously sit in a read forever.
- let _guard = unsafe { self.inner.lock.lock() };
- self.inner.read_closed.store(true, atomic::SeqCst);
- self.cancel_io()
- }
-
- fn close_write(&mut self) -> IoResult<()> {
- // see comments in close_read() for why this lock is necessary
- let _guard = unsafe { self.inner.lock.lock() };
- self.inner.write_closed.store(true, atomic::SeqCst);
- self.cancel_io()
- }
-
- fn set_timeout(&mut self, timeout: Option<u64>) {
- let deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0);
- self.read_deadline = deadline;
- self.write_deadline = deadline;
- }
- fn set_read_timeout(&mut self, timeout: Option<u64>) {
- self.read_deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0);
- }
- fn set_write_timeout(&mut self, timeout: Option<u64>) {
- self.write_deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0);
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Unix Listener
-////////////////////////////////////////////////////////////////////////////////
-
-pub struct UnixListener {
- handle: libc::HANDLE,
- name: CString,
-}
-
-impl UnixListener {
- pub fn bind(addr: &CString) -> IoResult<UnixListener> {
- // Although we technically don't need the pipe until much later, we
- // create the initial handle up front to test the validity of the name
- // and such.
- let addr_v = try!(to_utf16(addr));
- let ret = unsafe { pipe(addr_v.as_ptr(), true) };
- if ret == libc::INVALID_HANDLE_VALUE {
- Err(super::last_error())
- } else {
- Ok(UnixListener { handle: ret, name: addr.clone() })
- }
- }
-
- pub fn native_listen(self) -> IoResult<UnixAcceptor> {
- Ok(UnixAcceptor {
- listener: self,
- event: try!(Event::new(true, false)),
- deadline: 0,
- })
- }
-}
-
-impl Drop for UnixListener {
- fn drop(&mut self) {
- unsafe { let _ = libc::CloseHandle(self.handle); }
- }
-}
-
-impl rtio::RtioUnixListener for UnixListener {
- fn listen(self: Box<UnixListener>)
- -> IoResult<Box<rtio::RtioUnixAcceptor + Send>> {
- self.native_listen().map(|a| {
- box a as Box<rtio::RtioUnixAcceptor + Send>
- })
- }
-}
-
-pub struct UnixAcceptor {
- listener: UnixListener,
- event: Event,
- deadline: u64,
-}
-
-impl UnixAcceptor {
- pub fn native_accept(&mut self) -> IoResult<UnixStream> {
- // This function has some funky implementation details when working with
- // unix pipes. On windows, each server named pipe handle can be
- // connected to a one or zero clients. To the best of my knowledge, a
- // named server is considered active and present if there exists at
- // least one server named pipe for it.
- //
- // The model of this function is to take the current known server
- // handle, connect a client to it, and then transfer ownership to the
- // UnixStream instance. The next time accept() is invoked, it'll need a
- // different server handle to connect a client to.
- //
- // Note that there is a possible race here. Once our server pipe is
- // handed off to a `UnixStream` object, the stream could be closed,
- // meaning that there would be no active server pipes, hence even though
- // we have a valid `UnixAcceptor`, no one can connect to it. For this
- // reason, we generate the next accept call's server pipe at the end of
- // this function call.
- //
- // This provides us an invariant that we always have at least one server
- // connection open at a time, meaning that all connects to this acceptor
- // should succeed while this is active.
- //
- // The actual implementation of doing this is a little tricky. Once a
- // server pipe is created, a client can connect to it at any time. I
- // assume that which server a client connects to is nondeterministic, so
- // we also need to guarantee that the only server able to be connected
- // to is the one that we're calling ConnectNamedPipe on. This means that
- // we have to create the second server pipe *after* we've already
- // accepted a connection. In order to at least somewhat gracefully
- // handle errors, this means that if the second server pipe creation
- // fails that we disconnect the connected client and then just keep
- // using the original server pipe.
- let handle = self.listener.handle;
-
- let name = try!(to_utf16(&self.listener.name));
-
- // Once we've got a "server handle", we need to wait for a client to
- // connect. The ConnectNamedPipe function will block this thread until
- // someone on the other end connects. This function can "fail" if a
- // client connects after we created the pipe but before we got down
- // here. Thanks windows.
- let mut overlapped: libc::OVERLAPPED = unsafe { mem::zeroed() };
- overlapped.hEvent = self.event.handle();
- if unsafe { libc::ConnectNamedPipe(handle, &mut overlapped) == 0 } {
- let mut err = unsafe { libc::GetLastError() };
-
- if err == libc::ERROR_IO_PENDING as libc::DWORD {
- // Process a timeout if one is pending
- let _ = await(handle, self.deadline, &mut overlapped);
-
- // This will block until the overlapped I/O is completed. The
- // timeout was previously handled, so this will either block in
- // the normal case or succeed very quickly in the timeout case.
- let ret = unsafe {
- let mut transfer = 0;
- libc::GetOverlappedResult(handle,
- &mut overlapped,
- &mut transfer,
- libc::TRUE)
- };
- if ret == 0 {
- err = unsafe { libc::GetLastError() };
- } else {
- // we succeeded, bypass the check below
- err = libc::ERROR_PIPE_CONNECTED as libc::DWORD;
- }
- }
- if err != libc::ERROR_PIPE_CONNECTED as libc::DWORD {
- return Err(super::last_error())
- }
- }
-
- // Now that we've got a connected client to our handle, we need to
- // create a second server pipe. If this fails, we disconnect the
- // connected client and return an error (see comments above).
- let new_handle = unsafe { pipe(name.as_ptr(), false) };
- if new_handle == libc::INVALID_HANDLE_VALUE {
- let ret = Err(super::last_error());
- // If our disconnection fails, then there's not really a whole lot
- // that we can do, so fail the task.
- let err = unsafe { libc::DisconnectNamedPipe(handle) };
- assert!(err != 0);
- return ret;
- } else {
- self.listener.handle = new_handle;
- }
-
- // Transfer ownership of our handle into this stream
- Ok(UnixStream {
- inner: Arc::new(Inner::new(handle)),
- read: None,
- write: None,
- read_deadline: 0,
- write_deadline: 0,
- })
- }
-}
-
-impl rtio::RtioUnixAcceptor for UnixAcceptor {
- fn accept(&mut self) -> IoResult<Box<rtio::RtioPipe + Send>> {
- self.native_accept().map(|s| box s as Box<rtio::RtioPipe + Send>)
- }
- fn set_timeout(&mut self, timeout: Option<u64>) {
- self.deadline = timeout.map(|i| i + ::io::timer::now()).unwrap_or(0);
- }
-}
-
--- /dev/null
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Named pipes implementation for windows
+//!
+//! If are unfortunate enough to be reading this code, I would like to first
+//! apologize. This was my first encounter with windows named pipes, and it
+//! didn't exactly turn out very cleanly. If you, too, are new to named pipes,
+//! read on as I'll try to explain some fun things that I ran into.
+//!
+//! # Unix pipes vs Named pipes
+//!
+//! As with everything else, named pipes on windows are pretty different from
+//! unix pipes on unix. On unix, you use one "server pipe" to accept new client
+//! pipes. So long as this server pipe is active, new children pipes can
+//! connect. On windows, you instead have a number of "server pipes", and each
+//! of these server pipes can throughout their lifetime be attached to a client
+//! or not. Once attached to a client, a server pipe may then disconnect at a
+//! later date.
+//!
+//! # Accepting clients
+//!
+//! As with most other I/O interfaces, our Listener/Acceptor/Stream interfaces
+//! are built around the unix flavors. This means that we have one "server
+//! pipe" to which many clients can connect. In order to make this compatible
+//! with the windows model, each connected client consumes ownership of a server
+//! pipe, and then a new server pipe is created for the next client.
+//!
+//! Note that the server pipes attached to clients are never given back to the
+//! listener for recycling. This could possibly be implemented with a channel so
+//! the listener half can re-use server pipes, but for now I err'd on the simple
+//! side of things. Each stream accepted by a listener will destroy the server
+//! pipe after the stream is dropped.
+//!
+//! This model ends up having a small race or two, and you can find more details
+//! on the `native_accept` method.
+//!
+//! # Simultaneous reads and writes
+//!
+//! In testing, I found that two simultaneous writes and two simultaneous reads
+//! on a pipe ended up working out just fine, but problems were encountered when
+//! a read was executed simultaneously with a write. After some googling around,
+//! it sounded like named pipes just weren't built for this kind of interaction,
+//! and the suggested solution was to use overlapped I/O.
+//!
+//! I don't really know what overlapped I/O is, but my basic understanding after
+//! reading about it is that you have an external Event which is used to signal
+//! I/O completion, passed around in some OVERLAPPED structures. As to what this
+//! is, I'm not exactly sure.
+//!
+//! This problem implies that all named pipes are created with the
+//! FILE_FLAG_OVERLAPPED option. This means that all of their I/O is
+//! asynchronous. Each I/O operation has an associated OVERLAPPED structure, and
+//! inside of this structure is a HANDLE from CreateEvent. After the I/O is
+//! determined to be pending (may complete in the future), the
+//! GetOverlappedResult function is used to block on the event, waiting for the
+//! I/O to finish.
+//!
+//! This scheme ended up working well enough. There were two snags that I ran
+//! into, however:
+//!
+//! * Each UnixStream instance needs its own read/write events to wait on. These
+//! can't be shared among clones of the same stream because the documentation
+//! states that it unsets the event when the I/O is started (would possibly
+//! corrupt other events simultaneously waiting). For convenience's sake,
+//! these events are lazily initialized.
+//!
+//! * Each server pipe needs to be created with FILE_FLAG_OVERLAPPED in addition
+//! to all pipes created through `connect`. Notably this means that the
+//! ConnectNamedPipe function is nonblocking, implying that the Listener needs
+//! to have yet another event to do the actual blocking.
+//!
+//! # Conclusion
+//!
+//! The conclusion here is that I probably don't know the best way to work with
+//! windows named pipes, but the solution here seems to work well enough to get
+//! the test suite passing (the suite is in libstd), and that's good enough for
+//! me!
+
+use alloc::arc::Arc;
+use libc;
+use std::c_str::CString;
+use std::mem;
+use std::os;
+use std::ptr;
+use std::rt::rtio;
+use std::rt::rtio::{IoResult, IoError};
+use std::sync::atomic;
+use std::rt::mutex;
+
+use super::c;
+use super::util;
+use super::file::to_utf16;
+
+struct Event(libc::HANDLE);
+
+impl Event {
+ fn new(manual_reset: bool, initial_state: bool) -> IoResult<Event> {
+ let event = unsafe {
+ libc::CreateEventW(ptr::mut_null(),
+ manual_reset as libc::BOOL,
+ initial_state as libc::BOOL,
+ ptr::null())
+ };
+ if event as uint == 0 {
+ Err(super::last_error())
+ } else {
+ Ok(Event(event))
+ }
+ }
+
+ fn handle(&self) -> libc::HANDLE { let Event(handle) = *self; handle }
+}
+
+impl Drop for Event {
+ fn drop(&mut self) {
+ unsafe { let _ = libc::CloseHandle(self.handle()); }
+ }
+}
+
+struct Inner {
+ handle: libc::HANDLE,
+ lock: mutex::NativeMutex,
+ read_closed: atomic::AtomicBool,
+ write_closed: atomic::AtomicBool,
+}
+
+impl Inner {
+ fn new(handle: libc::HANDLE) -> Inner {
+ Inner {
+ handle: handle,
+ lock: unsafe { mutex::NativeMutex::new() },
+ read_closed: atomic::AtomicBool::new(false),
+ write_closed: atomic::AtomicBool::new(false),
+ }
+ }
+}
+
+impl Drop for Inner {
+ fn drop(&mut self) {
+ unsafe {
+ let _ = libc::FlushFileBuffers(self.handle);
+ let _ = libc::CloseHandle(self.handle);
+ }
+ }
+}
+
+unsafe fn pipe(name: *const u16, init: bool) -> libc::HANDLE {
+ libc::CreateNamedPipeW(
+ name,
+ libc::PIPE_ACCESS_DUPLEX |
+ if init {libc::FILE_FLAG_FIRST_PIPE_INSTANCE} else {0} |
+ libc::FILE_FLAG_OVERLAPPED,
+ libc::PIPE_TYPE_BYTE | libc::PIPE_READMODE_BYTE |
+ libc::PIPE_WAIT,
+ libc::PIPE_UNLIMITED_INSTANCES,
+ 65536,
+ 65536,
+ 0,
+ ptr::mut_null()
+ )
+}
+
+pub fn await(handle: libc::HANDLE, deadline: u64,
+ overlapped: &mut libc::OVERLAPPED) -> bool {
+ if deadline == 0 { return true }
+
+ // If we've got a timeout, use WaitForSingleObject in tandem with CancelIo
+ // to figure out if we should indeed get the result.
+ let now = ::io::timer::now();
+ let timeout = deadline < now || unsafe {
+ let ms = (deadline - now) as libc::DWORD;
+ let r = libc::WaitForSingleObject(overlapped.hEvent,
+ ms);
+ r != libc::WAIT_OBJECT_0
+ };
+ if timeout {
+ unsafe { let _ = c::CancelIo(handle); }
+ false
+ } else {
+ true
+ }
+}
+
+fn epipe() -> IoError {
+ IoError {
+ code: libc::ERROR_BROKEN_PIPE as uint,
+ extra: 0,
+ detail: None,
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Unix Streams
+////////////////////////////////////////////////////////////////////////////////
+
+pub struct UnixStream {
+ inner: Arc<Inner>,
+ write: Option<Event>,
+ read: Option<Event>,
+ read_deadline: u64,
+ write_deadline: u64,
+}
+
+impl UnixStream {
+ fn try_connect(p: *const u16) -> Option<libc::HANDLE> {
+ // Note that most of this is lifted from the libuv implementation.
+ // The idea is that if we fail to open a pipe in read/write mode
+ // that we try afterwards in just read or just write
+ let mut result = unsafe {
+ libc::CreateFileW(p,
+ libc::GENERIC_READ | libc::GENERIC_WRITE,
+ 0,
+ ptr::mut_null(),
+ libc::OPEN_EXISTING,
+ libc::FILE_FLAG_OVERLAPPED,
+ ptr::mut_null())
+ };
+ if result != libc::INVALID_HANDLE_VALUE {
+ return Some(result)
+ }
+
+ let err = unsafe { libc::GetLastError() };
+ if err == libc::ERROR_ACCESS_DENIED as libc::DWORD {
+ result = unsafe {
+ libc::CreateFileW(p,
+ libc::GENERIC_READ | libc::FILE_WRITE_ATTRIBUTES,
+ 0,
+ ptr::mut_null(),
+ libc::OPEN_EXISTING,
+ libc::FILE_FLAG_OVERLAPPED,
+ ptr::mut_null())
+ };
+ if result != libc::INVALID_HANDLE_VALUE {
+ return Some(result)
+ }
+ }
+ let err = unsafe { libc::GetLastError() };
+ if err == libc::ERROR_ACCESS_DENIED as libc::DWORD {
+ result = unsafe {
+ libc::CreateFileW(p,
+ libc::GENERIC_WRITE | libc::FILE_READ_ATTRIBUTES,
+ 0,
+ ptr::mut_null(),
+ libc::OPEN_EXISTING,
+ libc::FILE_FLAG_OVERLAPPED,
+ ptr::mut_null())
+ };
+ if result != libc::INVALID_HANDLE_VALUE {
+ return Some(result)
+ }
+ }
+ None
+ }
+
+ pub fn connect(addr: &CString, timeout: Option<u64>) -> IoResult<UnixStream> {
+ let addr = try!(to_utf16(addr));
+ let start = ::io::timer::now();
+ loop {
+ match UnixStream::try_connect(addr.as_ptr()) {
+ Some(handle) => {
+ let inner = Inner::new(handle);
+ let mut mode = libc::PIPE_TYPE_BYTE |
+ libc::PIPE_READMODE_BYTE |
+ libc::PIPE_WAIT;
+ let ret = unsafe {
+ libc::SetNamedPipeHandleState(inner.handle,
+ &mut mode,
+ ptr::mut_null(),
+ ptr::mut_null())
+ };
+ return if ret == 0 {
+ Err(super::last_error())
+ } else {
+ Ok(UnixStream {
+ inner: Arc::new(inner),
+ read: None,
+ write: None,
+ read_deadline: 0,
+ write_deadline: 0,
+ })
+ }
+ }
+ None => {}
+ }
+
+ // On windows, if you fail to connect, you may need to call the
+ // `WaitNamedPipe` function, and this is indicated with an error
+ // code of ERROR_PIPE_BUSY.
+ let code = unsafe { libc::GetLastError() };
+ if code as int != libc::ERROR_PIPE_BUSY as int {
+ return Err(super::last_error())
+ }
+
+ match timeout {
+ Some(timeout) => {
+ let now = ::io::timer::now();
+ let timed_out = (now - start) >= timeout || unsafe {
+ let ms = (timeout - (now - start)) as libc::DWORD;
+ libc::WaitNamedPipeW(addr.as_ptr(), ms) == 0
+ };
+ if timed_out {
+ return Err(util::timeout("connect timed out"))
+ }
+ }
+
+ // An example I found on Microsoft's website used 20
+ // seconds, libuv uses 30 seconds, hence we make the
+ // obvious choice of waiting for 25 seconds.
+ None => {
+ if unsafe { libc::WaitNamedPipeW(addr.as_ptr(), 25000) } == 0 {
+ return Err(super::last_error())
+ }
+ }
+ }
+ }
+ }
+
+ fn handle(&self) -> libc::HANDLE { self.inner.handle }
+
+ fn read_closed(&self) -> bool {
+ self.inner.read_closed.load(atomic::SeqCst)
+ }
+
+ fn write_closed(&self) -> bool {
+ self.inner.write_closed.load(atomic::SeqCst)
+ }
+
+ fn cancel_io(&self) -> IoResult<()> {
+ match unsafe { c::CancelIoEx(self.handle(), ptr::mut_null()) } {
+ 0 if os::errno() == libc::ERROR_NOT_FOUND as uint => {
+ Ok(())
+ }
+ 0 => Err(super::last_error()),
+ _ => Ok(())
+ }
+ }
+}
+
+impl rtio::RtioPipe for UnixStream {
+ fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
+ if self.read.is_none() {
+ self.read = Some(try!(Event::new(true, false)));
+ }
+
+ let mut bytes_read = 0;
+ let mut overlapped: libc::OVERLAPPED = unsafe { mem::zeroed() };
+ overlapped.hEvent = self.read.get_ref().handle();
+
+ // Pre-flight check to see if the reading half has been closed. This
+ // must be done before issuing the ReadFile request, but after we
+ // acquire the lock.
+ //
+ // See comments in close_read() about why this lock is necessary.
+ let guard = unsafe { self.inner.lock.lock() };
+ if self.read_closed() {
+ return Err(util::eof())
+ }
+
+ // Issue a nonblocking requests, succeeding quickly if it happened to
+ // succeed.
+ let ret = unsafe {
+ libc::ReadFile(self.handle(),
+ buf.as_ptr() as libc::LPVOID,
+ buf.len() as libc::DWORD,
+ &mut bytes_read,
+ &mut overlapped)
+ };
+ if ret != 0 { return Ok(bytes_read as uint) }
+
+ // If our errno doesn't say that the I/O is pending, then we hit some
+ // legitimate error and return immediately.
+ if os::errno() != libc::ERROR_IO_PENDING as uint {
+ return Err(super::last_error())
+ }
+
+ // Now that we've issued a successful nonblocking request, we need to
+ // wait for it to finish. This can all be done outside the lock because
+ // we'll see any invocation of CancelIoEx. We also call this in a loop
+ // because we're woken up if the writing half is closed, we just need to
+ // realize that the reading half wasn't closed and we go right back to
+ // sleep.
+ drop(guard);
+ loop {
+ // Process a timeout if one is pending
+ let succeeded = await(self.handle(), self.read_deadline,
+ &mut overlapped);
+
+ let ret = unsafe {
+ libc::GetOverlappedResult(self.handle(),
+ &mut overlapped,
+ &mut bytes_read,
+ libc::TRUE)
+ };
+ // If we succeeded, or we failed for some reason other than
+ // CancelIoEx, return immediately
+ if ret != 0 { return Ok(bytes_read as uint) }
+ if os::errno() != libc::ERROR_OPERATION_ABORTED as uint {
+ return Err(super::last_error())
+ }
+
+ // If the reading half is now closed, then we're done. If we woke up
+ // because the writing half was closed, keep trying.
+ if !succeeded {
+ return Err(util::timeout("read timed out"))
+ }
+ if self.read_closed() {
+ return Err(util::eof())
+ }
+ }
+ }
+
+ fn write(&mut self, buf: &[u8]) -> IoResult<()> {
+ if self.write.is_none() {
+ self.write = Some(try!(Event::new(true, false)));
+ }
+
+ let mut offset = 0;
+ let mut overlapped: libc::OVERLAPPED = unsafe { mem::zeroed() };
+ overlapped.hEvent = self.write.get_ref().handle();
+
+ while offset < buf.len() {
+ let mut bytes_written = 0;
+
+ // This sequence below is quite similar to the one found in read().
+ // Some careful looping is done to ensure that if close_write() is
+ // invoked we bail out early, and if close_read() is invoked we keep
+ // going after we woke up.
+ //
+ // See comments in close_read() about why this lock is necessary.
+ let guard = unsafe { self.inner.lock.lock() };
+ if self.write_closed() {
+ return Err(epipe())
+ }
+ let ret = unsafe {
+ libc::WriteFile(self.handle(),
+ buf.slice_from(offset).as_ptr() as libc::LPVOID,
+ (buf.len() - offset) as libc::DWORD,
+ &mut bytes_written,
+ &mut overlapped)
+ };
+ let err = os::errno();
+ drop(guard);
+
+ if ret == 0 {
+ if err != libc::ERROR_IO_PENDING as uint {
+ return Err(IoError {
+ code: err as uint,
+ extra: 0,
+ detail: Some(os::error_string(err as uint)),
+ })
+ }
+ // Process a timeout if one is pending
+ let succeeded = await(self.handle(), self.write_deadline,
+ &mut overlapped);
+ let ret = unsafe {
+ libc::GetOverlappedResult(self.handle(),
+ &mut overlapped,
+ &mut bytes_written,
+ libc::TRUE)
+ };
+ // If we weren't aborted, this was a legit error, if we were
+ // aborted, then check to see if the write half was actually
+ // closed or whether we woke up from the read half closing.
+ if ret == 0 {
+ if os::errno() != libc::ERROR_OPERATION_ABORTED as uint {
+ return Err(super::last_error())
+ }
+ if !succeeded {
+ let amt = offset + bytes_written as uint;
+ return if amt > 0 {
+ Err(IoError {
+ code: libc::ERROR_OPERATION_ABORTED as uint,
+ extra: amt,
+ detail: Some("short write during write".to_string()),
+ })
+ } else {
+ Err(util::timeout("write timed out"))
+ }
+ }
+ if self.write_closed() {
+ return Err(epipe())
+ }
+ continue // retry
+ }
+ }
+ offset += bytes_written as uint;
+ }
+ Ok(())
+ }
+
+ fn clone(&self) -> Box<rtio::RtioPipe + Send> {
+ box UnixStream {
+ inner: self.inner.clone(),
+ read: None,
+ write: None,
+ read_deadline: 0,
+ write_deadline: 0,
+ } as Box<rtio::RtioPipe + Send>
+ }
+
+ fn close_read(&mut self) -> IoResult<()> {
+ // On windows, there's no actual shutdown() method for pipes, so we're
+ // forced to emulate the behavior manually at the application level. To
+ // do this, we need to both cancel any pending requests, as well as
+ // prevent all future requests from succeeding. These two operations are
+ // not atomic with respect to one another, so we must use a lock to do
+ // so.
+ //
+ // The read() code looks like:
+ //
+ // 1. Make sure the pipe is still open
+ // 2. Submit a read request
+ // 3. Wait for the read request to finish
+ //
+ // The race this lock is preventing is if another thread invokes
+ // close_read() between steps 1 and 2. By atomically executing steps 1
+ // and 2 with a lock with respect to close_read(), we're guaranteed that
+ // no thread will erroneously sit in a read forever.
+ let _guard = unsafe { self.inner.lock.lock() };
+ self.inner.read_closed.store(true, atomic::SeqCst);
+ self.cancel_io()
+ }
+
+ fn close_write(&mut self) -> IoResult<()> {
+ // see comments in close_read() for why this lock is necessary
+ let _guard = unsafe { self.inner.lock.lock() };
+ self.inner.write_closed.store(true, atomic::SeqCst);
+ self.cancel_io()
+ }
+
+ fn set_timeout(&mut self, timeout: Option<u64>) {
+ let deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0);
+ self.read_deadline = deadline;
+ self.write_deadline = deadline;
+ }
+ fn set_read_timeout(&mut self, timeout: Option<u64>) {
+ self.read_deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0);
+ }
+ fn set_write_timeout(&mut self, timeout: Option<u64>) {
+ self.write_deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Unix Listener
+////////////////////////////////////////////////////////////////////////////////
+
+pub struct UnixListener {
+ handle: libc::HANDLE,
+ name: CString,
+}
+
+impl UnixListener {
+ pub fn bind(addr: &CString) -> IoResult<UnixListener> {
+ // Although we technically don't need the pipe until much later, we
+ // create the initial handle up front to test the validity of the name
+ // and such.
+ let addr_v = try!(to_utf16(addr));
+ let ret = unsafe { pipe(addr_v.as_ptr(), true) };
+ if ret == libc::INVALID_HANDLE_VALUE {
+ Err(super::last_error())
+ } else {
+ Ok(UnixListener { handle: ret, name: addr.clone() })
+ }
+ }
+
+ pub fn native_listen(self) -> IoResult<UnixAcceptor> {
+ Ok(UnixAcceptor {
+ listener: self,
+ event: try!(Event::new(true, false)),
+ deadline: 0,
+ })
+ }
+}
+
+impl Drop for UnixListener {
+ fn drop(&mut self) {
+ unsafe { let _ = libc::CloseHandle(self.handle); }
+ }
+}
+
+impl rtio::RtioUnixListener for UnixListener {
+ fn listen(self: Box<UnixListener>)
+ -> IoResult<Box<rtio::RtioUnixAcceptor + Send>> {
+ self.native_listen().map(|a| {
+ box a as Box<rtio::RtioUnixAcceptor + Send>
+ })
+ }
+}
+
+pub struct UnixAcceptor {
+ listener: UnixListener,
+ event: Event,
+ deadline: u64,
+}
+
+impl UnixAcceptor {
+ pub fn native_accept(&mut self) -> IoResult<UnixStream> {
+ // This function has some funky implementation details when working with
+ // unix pipes. On windows, each server named pipe handle can be
+ // connected to a one or zero clients. To the best of my knowledge, a
+ // named server is considered active and present if there exists at
+ // least one server named pipe for it.
+ //
+ // The model of this function is to take the current known server
+ // handle, connect a client to it, and then transfer ownership to the
+ // UnixStream instance. The next time accept() is invoked, it'll need a
+ // different server handle to connect a client to.
+ //
+ // Note that there is a possible race here. Once our server pipe is
+ // handed off to a `UnixStream` object, the stream could be closed,
+ // meaning that there would be no active server pipes, hence even though
+ // we have a valid `UnixAcceptor`, no one can connect to it. For this
+ // reason, we generate the next accept call's server pipe at the end of
+ // this function call.
+ //
+ // This provides us an invariant that we always have at least one server
+ // connection open at a time, meaning that all connects to this acceptor
+ // should succeed while this is active.
+ //
+ // The actual implementation of doing this is a little tricky. Once a
+ // server pipe is created, a client can connect to it at any time. I
+ // assume that which server a client connects to is nondeterministic, so
+ // we also need to guarantee that the only server able to be connected
+ // to is the one that we're calling ConnectNamedPipe on. This means that
+ // we have to create the second server pipe *after* we've already
+ // accepted a connection. In order to at least somewhat gracefully
+ // handle errors, this means that if the second server pipe creation
+ // fails that we disconnect the connected client and then just keep
+ // using the original server pipe.
+ let handle = self.listener.handle;
+
+ let name = try!(to_utf16(&self.listener.name));
+
+ // Once we've got a "server handle", we need to wait for a client to
+ // connect. The ConnectNamedPipe function will block this thread until
+ // someone on the other end connects. This function can "fail" if a
+ // client connects after we created the pipe but before we got down
+ // here. Thanks windows.
+ let mut overlapped: libc::OVERLAPPED = unsafe { mem::zeroed() };
+ overlapped.hEvent = self.event.handle();
+ if unsafe { libc::ConnectNamedPipe(handle, &mut overlapped) == 0 } {
+ let mut err = unsafe { libc::GetLastError() };
+
+ if err == libc::ERROR_IO_PENDING as libc::DWORD {
+ // Process a timeout if one is pending
+ let _ = await(handle, self.deadline, &mut overlapped);
+
+ // This will block until the overlapped I/O is completed. The
+ // timeout was previously handled, so this will either block in
+ // the normal case or succeed very quickly in the timeout case.
+ let ret = unsafe {
+ let mut transfer = 0;
+ libc::GetOverlappedResult(handle,
+ &mut overlapped,
+ &mut transfer,
+ libc::TRUE)
+ };
+ if ret == 0 {
+ err = unsafe { libc::GetLastError() };
+ } else {
+ // we succeeded, bypass the check below
+ err = libc::ERROR_PIPE_CONNECTED as libc::DWORD;
+ }
+ }
+ if err != libc::ERROR_PIPE_CONNECTED as libc::DWORD {
+ return Err(super::last_error())
+ }
+ }
+
+ // Now that we've got a connected client to our handle, we need to
+ // create a second server pipe. If this fails, we disconnect the
+ // connected client and return an error (see comments above).
+ let new_handle = unsafe { pipe(name.as_ptr(), false) };
+ if new_handle == libc::INVALID_HANDLE_VALUE {
+ let ret = Err(super::last_error());
+ // If our disconnection fails, then there's not really a whole lot
+ // that we can do, so fail the task.
+ let err = unsafe { libc::DisconnectNamedPipe(handle) };
+ assert!(err != 0);
+ return ret;
+ } else {
+ self.listener.handle = new_handle;
+ }
+
+ // Transfer ownership of our handle into this stream
+ Ok(UnixStream {
+ inner: Arc::new(Inner::new(handle)),
+ read: None,
+ write: None,
+ read_deadline: 0,
+ write_deadline: 0,
+ })
+ }
+}
+
+impl rtio::RtioUnixAcceptor for UnixAcceptor {
+ fn accept(&mut self) -> IoResult<Box<rtio::RtioPipe + Send>> {
+ self.native_accept().map(|s| box s as Box<rtio::RtioPipe + Send>)
+ }
+ fn set_timeout(&mut self, timeout: Option<u64>) {
+ self.deadline = timeout.map(|i| i + ::io::timer::now()).unwrap_or(0);
+ }
+}
+
#[cfg(windows)]
fn with_envp<T>(env: Option<&[(&CString, &CString)]>, cb: |*mut c_void| -> T) -> T {
- // On win32 we pass an "environment block" which is not a char**, but
+ // On Windows we pass an "environment block" which is not a char**, but
// rather a concatenation of null-terminated k=v\0 sequences, with a final
// \0 to terminate.
match env {
+++ /dev/null
-// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! Timers based on win32 WaitableTimers
-//!
-//! This implementation is meant to be used solely on windows. As with other
-//! implementations, there is a worker thread which is doing all the waiting on
-//! a large number of timers for all active timers in the system. This worker
-//! thread uses the select() equivalent, WaitForMultipleObjects. One of the
-//! objects being waited on is a signal into the worker thread to notify that
-//! the incoming channel should be looked at.
-//!
-//! Other than that, the implementation is pretty straightforward in terms of
-//! the other two implementations of timers with nothing *that* new showing up.
-
-use libc;
-use std::ptr;
-use std::rt::rtio;
-use std::rt::rtio::{IoResult, Callback};
-use std::comm;
-
-use io::helper_thread::Helper;
-
-helper_init!(static mut HELPER: Helper<Req>)
-
-pub struct Timer {
- obj: libc::HANDLE,
- on_worker: bool,
-}
-
-pub enum Req {
- NewTimer(libc::HANDLE, Box<Callback + Send>, bool),
- RemoveTimer(libc::HANDLE, Sender<()>),
-}
-
-fn helper(input: libc::HANDLE, messages: Receiver<Req>, _: ()) {
- let mut objs = vec![input];
- let mut chans = vec![];
-
- 'outer: loop {
- let idx = unsafe {
- imp::WaitForMultipleObjects(objs.len() as libc::DWORD,
- objs.as_ptr(),
- 0 as libc::BOOL,
- libc::INFINITE)
- };
-
- if idx == 0 {
- loop {
- match messages.try_recv() {
- Ok(NewTimer(obj, c, one)) => {
- objs.push(obj);
- chans.push((c, one));
- }
- Ok(RemoveTimer(obj, c)) => {
- c.send(());
- match objs.iter().position(|&o| o == obj) {
- Some(i) => {
- drop(objs.remove(i));
- drop(chans.remove(i - 1));
- }
- None => {}
- }
- }
- Err(comm::Disconnected) => {
- assert_eq!(objs.len(), 1);
- assert_eq!(chans.len(), 0);
- break 'outer;
- }
- Err(..) => break
- }
- }
- } else {
- let remove = {
- match chans.get_mut(idx as uint - 1) {
- &(ref mut c, oneshot) => { c.call(); oneshot }
- }
- };
- if remove {
- drop(objs.remove(idx as uint));
- drop(chans.remove(idx as uint - 1));
- }
- }
- }
-}
-
-// returns the current time (in milliseconds)
-pub fn now() -> u64 {
- let mut ticks_per_s = 0;
- assert_eq!(unsafe { libc::QueryPerformanceFrequency(&mut ticks_per_s) }, 1);
- let ticks_per_s = if ticks_per_s == 0 {1} else {ticks_per_s};
- let mut ticks = 0;
- assert_eq!(unsafe { libc::QueryPerformanceCounter(&mut ticks) }, 1);
-
- return (ticks as u64 * 1000) / (ticks_per_s as u64);
-}
-
-impl Timer {
- pub fn new() -> IoResult<Timer> {
- unsafe { HELPER.boot(|| {}, helper) }
-
- let obj = unsafe {
- imp::CreateWaitableTimerA(ptr::mut_null(), 0, ptr::null())
- };
- if obj.is_null() {
- Err(super::last_error())
- } else {
- Ok(Timer { obj: obj, on_worker: false, })
- }
- }
-
- pub fn sleep(ms: u64) {
- use std::rt::rtio::RtioTimer;
- let mut t = Timer::new().ok().expect("must allocate a timer!");
- t.sleep(ms);
- }
-
- fn remove(&mut self) {
- if !self.on_worker { return }
-
- let (tx, rx) = channel();
- unsafe { HELPER.send(RemoveTimer(self.obj, tx)) }
- rx.recv();
-
- self.on_worker = false;
- }
-}
-
-impl rtio::RtioTimer for Timer {
- fn sleep(&mut self, msecs: u64) {
- self.remove();
-
- // there are 10^6 nanoseconds in a millisecond, and the parameter is in
- // 100ns intervals, so we multiply by 10^4.
- let due = -(msecs as i64 * 10000) as libc::LARGE_INTEGER;
- assert_eq!(unsafe {
- imp::SetWaitableTimer(self.obj, &due, 0, ptr::mut_null(),
- ptr::mut_null(), 0)
- }, 1);
-
- let _ = unsafe { imp::WaitForSingleObject(self.obj, libc::INFINITE) };
- }
-
- fn oneshot(&mut self, msecs: u64, cb: Box<Callback + Send>) {
- self.remove();
-
- // see above for the calculation
- let due = -(msecs as i64 * 10000) as libc::LARGE_INTEGER;
- assert_eq!(unsafe {
- imp::SetWaitableTimer(self.obj, &due, 0, ptr::mut_null(),
- ptr::mut_null(), 0)
- }, 1);
-
- unsafe { HELPER.send(NewTimer(self.obj, cb, true)) }
- self.on_worker = true;
- }
-
- fn period(&mut self, msecs: u64, cb: Box<Callback + Send>) {
- self.remove();
-
- // see above for the calculation
- let due = -(msecs as i64 * 10000) as libc::LARGE_INTEGER;
- assert_eq!(unsafe {
- imp::SetWaitableTimer(self.obj, &due, msecs as libc::LONG,
- ptr::mut_null(), ptr::mut_null(), 0)
- }, 1);
-
- unsafe { HELPER.send(NewTimer(self.obj, cb, false)) }
- self.on_worker = true;
- }
-}
-
-impl Drop for Timer {
- fn drop(&mut self) {
- self.remove();
- assert!(unsafe { libc::CloseHandle(self.obj) != 0 });
- }
-}
-
-mod imp {
- use libc::{LPSECURITY_ATTRIBUTES, BOOL, LPCSTR, HANDLE, LARGE_INTEGER,
- LONG, LPVOID, DWORD, c_void};
-
- pub type PTIMERAPCROUTINE = *mut c_void;
-
- extern "system" {
- pub fn CreateWaitableTimerA(lpTimerAttributes: LPSECURITY_ATTRIBUTES,
- bManualReset: BOOL,
- lpTimerName: LPCSTR) -> HANDLE;
- pub fn SetWaitableTimer(hTimer: HANDLE,
- pDueTime: *const LARGE_INTEGER,
- lPeriod: LONG,
- pfnCompletionRoutine: PTIMERAPCROUTINE,
- lpArgToCompletionRoutine: LPVOID,
- fResume: BOOL) -> BOOL;
- pub fn WaitForMultipleObjects(nCount: DWORD,
- lpHandles: *const HANDLE,
- bWaitAll: BOOL,
- dwMilliseconds: DWORD) -> DWORD;
- pub fn WaitForSingleObject(hHandle: HANDLE,
- dwMilliseconds: DWORD) -> DWORD;
- }
-}
--- /dev/null
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Timers based on Windows WaitableTimers
+//!
+//! This implementation is meant to be used solely on windows. As with other
+//! implementations, there is a worker thread which is doing all the waiting on
+//! a large number of timers for all active timers in the system. This worker
+//! thread uses the select() equivalent, WaitForMultipleObjects. One of the
+//! objects being waited on is a signal into the worker thread to notify that
+//! the incoming channel should be looked at.
+//!
+//! Other than that, the implementation is pretty straightforward in terms of
+//! the other two implementations of timers with nothing *that* new showing up.
+
+use libc;
+use std::ptr;
+use std::rt::rtio;
+use std::rt::rtio::{IoResult, Callback};
+use std::comm;
+
+use io::helper_thread::Helper;
+
+helper_init!(static mut HELPER: Helper<Req>)
+
+pub struct Timer {
+ obj: libc::HANDLE,
+ on_worker: bool,
+}
+
+pub enum Req {
+ NewTimer(libc::HANDLE, Box<Callback + Send>, bool),
+ RemoveTimer(libc::HANDLE, Sender<()>),
+}
+
+fn helper(input: libc::HANDLE, messages: Receiver<Req>, _: ()) {
+ let mut objs = vec![input];
+ let mut chans = vec![];
+
+ 'outer: loop {
+ let idx = unsafe {
+ imp::WaitForMultipleObjects(objs.len() as libc::DWORD,
+ objs.as_ptr(),
+ 0 as libc::BOOL,
+ libc::INFINITE)
+ };
+
+ if idx == 0 {
+ loop {
+ match messages.try_recv() {
+ Ok(NewTimer(obj, c, one)) => {
+ objs.push(obj);
+ chans.push((c, one));
+ }
+ Ok(RemoveTimer(obj, c)) => {
+ c.send(());
+ match objs.iter().position(|&o| o == obj) {
+ Some(i) => {
+ drop(objs.remove(i));
+ drop(chans.remove(i - 1));
+ }
+ None => {}
+ }
+ }
+ Err(comm::Disconnected) => {
+ assert_eq!(objs.len(), 1);
+ assert_eq!(chans.len(), 0);
+ break 'outer;
+ }
+ Err(..) => break
+ }
+ }
+ } else {
+ let remove = {
+ match chans.get_mut(idx as uint - 1) {
+ &(ref mut c, oneshot) => { c.call(); oneshot }
+ }
+ };
+ if remove {
+ drop(objs.remove(idx as uint));
+ drop(chans.remove(idx as uint - 1));
+ }
+ }
+ }
+}
+
+// returns the current time (in milliseconds)
+pub fn now() -> u64 {
+ let mut ticks_per_s = 0;
+ assert_eq!(unsafe { libc::QueryPerformanceFrequency(&mut ticks_per_s) }, 1);
+ let ticks_per_s = if ticks_per_s == 0 {1} else {ticks_per_s};
+ let mut ticks = 0;
+ assert_eq!(unsafe { libc::QueryPerformanceCounter(&mut ticks) }, 1);
+
+ return (ticks as u64 * 1000) / (ticks_per_s as u64);
+}
+
+impl Timer {
+ pub fn new() -> IoResult<Timer> {
+ unsafe { HELPER.boot(|| {}, helper) }
+
+ let obj = unsafe {
+ imp::CreateWaitableTimerA(ptr::mut_null(), 0, ptr::null())
+ };
+ if obj.is_null() {
+ Err(super::last_error())
+ } else {
+ Ok(Timer { obj: obj, on_worker: false, })
+ }
+ }
+
+ pub fn sleep(ms: u64) {
+ use std::rt::rtio::RtioTimer;
+ let mut t = Timer::new().ok().expect("must allocate a timer!");
+ t.sleep(ms);
+ }
+
+ fn remove(&mut self) {
+ if !self.on_worker { return }
+
+ let (tx, rx) = channel();
+ unsafe { HELPER.send(RemoveTimer(self.obj, tx)) }
+ rx.recv();
+
+ self.on_worker = false;
+ }
+}
+
+impl rtio::RtioTimer for Timer {
+ fn sleep(&mut self, msecs: u64) {
+ self.remove();
+
+ // there are 10^6 nanoseconds in a millisecond, and the parameter is in
+ // 100ns intervals, so we multiply by 10^4.
+ let due = -(msecs as i64 * 10000) as libc::LARGE_INTEGER;
+ assert_eq!(unsafe {
+ imp::SetWaitableTimer(self.obj, &due, 0, ptr::mut_null(),
+ ptr::mut_null(), 0)
+ }, 1);
+
+ let _ = unsafe { imp::WaitForSingleObject(self.obj, libc::INFINITE) };
+ }
+
+ fn oneshot(&mut self, msecs: u64, cb: Box<Callback + Send>) {
+ self.remove();
+
+ // see above for the calculation
+ let due = -(msecs as i64 * 10000) as libc::LARGE_INTEGER;
+ assert_eq!(unsafe {
+ imp::SetWaitableTimer(self.obj, &due, 0, ptr::mut_null(),
+ ptr::mut_null(), 0)
+ }, 1);
+
+ unsafe { HELPER.send(NewTimer(self.obj, cb, true)) }
+ self.on_worker = true;
+ }
+
+ fn period(&mut self, msecs: u64, cb: Box<Callback + Send>) {
+ self.remove();
+
+ // see above for the calculation
+ let due = -(msecs as i64 * 10000) as libc::LARGE_INTEGER;
+ assert_eq!(unsafe {
+ imp::SetWaitableTimer(self.obj, &due, msecs as libc::LONG,
+ ptr::mut_null(), ptr::mut_null(), 0)
+ }, 1);
+
+ unsafe { HELPER.send(NewTimer(self.obj, cb, false)) }
+ self.on_worker = true;
+ }
+}
+
+impl Drop for Timer {
+ fn drop(&mut self) {
+ self.remove();
+ assert!(unsafe { libc::CloseHandle(self.obj) != 0 });
+ }
+}
+
+mod imp {
+ use libc::{LPSECURITY_ATTRIBUTES, BOOL, LPCSTR, HANDLE, LARGE_INTEGER,
+ LONG, LPVOID, DWORD, c_void};
+
+ pub type PTIMERAPCROUTINE = *mut c_void;
+
+ extern "system" {
+ pub fn CreateWaitableTimerA(lpTimerAttributes: LPSECURITY_ATTRIBUTES,
+ bManualReset: BOOL,
+ lpTimerName: LPCSTR) -> HANDLE;
+ pub fn SetWaitableTimer(hTimer: HANDLE,
+ pDueTime: *const LARGE_INTEGER,
+ lPeriod: LONG,
+ pfnCompletionRoutine: PTIMERAPCROUTINE,
+ lpArgToCompletionRoutine: LPVOID,
+ fResume: BOOL) -> BOOL;
+ pub fn WaitForMultipleObjects(nCount: DWORD,
+ lpHandles: *const HANDLE,
+ bWaitAll: BOOL,
+ dwMilliseconds: DWORD) -> DWORD;
+ pub fn WaitForSingleObject(hHandle: HANDLE,
+ dwMilliseconds: DWORD) -> DWORD;
+ }
+}
+++ /dev/null
-// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// ignore-lexer-test FIXME #15877
-
-//! Windows specific console TTY implementation
-//!
-//! This module contains the implementation of a Windows specific console TTY.
-//! Also converts between UTF-16 and UTF-8. Windows has very poor support for
-//! UTF-8 and some functions will fail. In particular ReadFile and ReadConsole
-//! will fail when the codepage is set to UTF-8 and a unicode character is
-//! entered.
-//!
-//! FIXME
-//! This implementation does not account for codepoints that are split across
-//! multiple reads and writes. Also, this implementation does not expose a way
-//! to read/write UTF-16 directly. When/if Rust receives a Reader/Writer
-//! wrapper that performs encoding/decoding, this implementation should switch
-//! to working in raw UTF-16, with such a wrapper around it.
-
-use super::c::{ReadConsoleW, WriteConsoleW, GetConsoleMode, SetConsoleMode};
-use super::c::{ERROR_ILLEGAL_CHARACTER};
-use super::c::{ENABLE_ECHO_INPUT, ENABLE_EXTENDED_FLAGS};
-use super::c::{ENABLE_INSERT_MODE, ENABLE_LINE_INPUT};
-use super::c::{ENABLE_PROCESSED_INPUT, ENABLE_QUICK_EDIT_MODE};
-use libc::{c_int, HANDLE, LPDWORD, DWORD, LPVOID};
-use libc::{get_osfhandle, CloseHandle};
-use libc::types::os::arch::extra::LPCVOID;
-use std::io::MemReader;
-use std::ptr;
-use std::rt::rtio::{IoResult, IoError, RtioTTY};
-use std::str::{from_utf16, from_utf8};
-
-fn invalid_encoding() -> IoError {
- IoError {
- code: ERROR_ILLEGAL_CHARACTER as uint,
- extra: 0,
- detail: Some("text was not valid unicode".to_string()),
- }
-}
-
-pub fn is_tty(fd: c_int) -> bool {
- let mut out: DWORD = 0;
- // If this function doesn't fail then fd is a TTY
- match unsafe { GetConsoleMode(get_osfhandle(fd) as HANDLE,
- &mut out as LPDWORD) } {
- 0 => false,
- _ => true,
- }
-}
-
-pub struct WindowsTTY {
- closeme: bool,
- handle: HANDLE,
- utf8: MemReader,
-}
-
-impl WindowsTTY {
- pub fn new(fd: c_int) -> WindowsTTY {
- // If the file descriptor is one of stdin, stderr, or stdout
- // then it should not be closed by us
- let closeme = match fd {
- 0..2 => false,
- _ => true,
- };
- let handle = unsafe { get_osfhandle(fd) as HANDLE };
- WindowsTTY {
- handle: handle,
- utf8: MemReader::new(Vec::new()),
- closeme: closeme,
- }
- }
-}
-
-impl Drop for WindowsTTY {
- fn drop(&mut self) {
- if self.closeme {
- // Nobody cares about the return value
- let _ = unsafe { CloseHandle(self.handle) };
- }
- }
-}
-
-impl RtioTTY for WindowsTTY {
- fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
- // Read more if the buffer is empty
- if self.utf8.eof() {
- let mut utf16 = Vec::from_elem(0x1000, 0u16);
- let mut num: DWORD = 0;
- match unsafe { ReadConsoleW(self.handle,
- utf16.as_mut_ptr() as LPVOID,
- utf16.len() as u32,
- &mut num as LPDWORD,
- ptr::mut_null()) } {
- 0 => return Err(super::last_error()),
- _ => (),
- };
- utf16.truncate(num as uint);
- let utf8 = match from_utf16(utf16.as_slice()) {
- Some(utf8) => utf8.into_bytes(),
- None => return Err(invalid_encoding()),
- };
- self.utf8 = MemReader::new(utf8);
- }
- // MemReader shouldn't error here since we just filled it
- Ok(self.utf8.read(buf).unwrap())
- }
-
- fn write(&mut self, buf: &[u8]) -> IoResult<()> {
- let utf16 = match from_utf8(buf) {
- Some(utf8) => utf8.to_utf16(),
- None => return Err(invalid_encoding()),
- };
- let mut num: DWORD = 0;
- match unsafe { WriteConsoleW(self.handle,
- utf16.as_ptr() as LPCVOID,
- utf16.len() as u32,
- &mut num as LPDWORD,
- ptr::mut_null()) } {
- 0 => Err(super::last_error()),
- _ => Ok(()),
- }
- }
-
- fn set_raw(&mut self, raw: bool) -> IoResult<()> {
- // FIXME
- // Somebody needs to decide on which of these flags we want
- match unsafe { SetConsoleMode(self.handle,
- match raw {
- true => 0,
- false => ENABLE_ECHO_INPUT | ENABLE_EXTENDED_FLAGS |
- ENABLE_INSERT_MODE | ENABLE_LINE_INPUT |
- ENABLE_PROCESSED_INPUT | ENABLE_QUICK_EDIT_MODE,
- }) } {
- 0 => Err(super::last_error()),
- _ => Ok(()),
- }
- }
-
- fn get_winsize(&mut self) -> IoResult<(int, int)> {
- // FIXME
- // Get console buffer via CreateFile with CONOUT$
- // Make a CONSOLE_SCREEN_BUFFER_INFO
- // Call GetConsoleScreenBufferInfo
- // Maybe call GetLargestConsoleWindowSize instead?
- Err(super::unimpl())
- }
-
- // Let us magically declare this as a TTY
- fn isatty(&self) -> bool { true }
-}
--- /dev/null
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-lexer-test FIXME #15877
+
+//! Windows specific console TTY implementation
+//!
+//! This module contains the implementation of a Windows specific console TTY.
+//! Also converts between UTF-16 and UTF-8. Windows has very poor support for
+//! UTF-8 and some functions will fail. In particular ReadFile and ReadConsole
+//! will fail when the codepage is set to UTF-8 and a unicode character is
+//! entered.
+//!
+//! FIXME
+//! This implementation does not account for codepoints that are split across
+//! multiple reads and writes. Also, this implementation does not expose a way
+//! to read/write UTF-16 directly. When/if Rust receives a Reader/Writer
+//! wrapper that performs encoding/decoding, this implementation should switch
+//! to working in raw UTF-16, with such a wrapper around it.
+
+use super::c::{ReadConsoleW, WriteConsoleW, GetConsoleMode, SetConsoleMode};
+use super::c::{ERROR_ILLEGAL_CHARACTER};
+use super::c::{ENABLE_ECHO_INPUT, ENABLE_EXTENDED_FLAGS};
+use super::c::{ENABLE_INSERT_MODE, ENABLE_LINE_INPUT};
+use super::c::{ENABLE_PROCESSED_INPUT, ENABLE_QUICK_EDIT_MODE};
+use libc::{c_int, HANDLE, LPDWORD, DWORD, LPVOID};
+use libc::{get_osfhandle, CloseHandle};
+use libc::types::os::arch::extra::LPCVOID;
+use std::io::MemReader;
+use std::ptr;
+use std::rt::rtio::{IoResult, IoError, RtioTTY};
+use std::str::{from_utf16, from_utf8};
+
+fn invalid_encoding() -> IoError {
+ IoError {
+ code: ERROR_ILLEGAL_CHARACTER as uint,
+ extra: 0,
+ detail: Some("text was not valid unicode".to_string()),
+ }
+}
+
+pub fn is_tty(fd: c_int) -> bool {
+ let mut out: DWORD = 0;
+ // If this function doesn't fail then fd is a TTY
+ match unsafe { GetConsoleMode(get_osfhandle(fd) as HANDLE,
+ &mut out as LPDWORD) } {
+ 0 => false,
+ _ => true,
+ }
+}
+
+pub struct WindowsTTY {
+ closeme: bool,
+ handle: HANDLE,
+ utf8: MemReader,
+}
+
+impl WindowsTTY {
+ pub fn new(fd: c_int) -> WindowsTTY {
+ // If the file descriptor is one of stdin, stderr, or stdout
+ // then it should not be closed by us
+ let closeme = match fd {
+ 0..2 => false,
+ _ => true,
+ };
+ let handle = unsafe { get_osfhandle(fd) as HANDLE };
+ WindowsTTY {
+ handle: handle,
+ utf8: MemReader::new(Vec::new()),
+ closeme: closeme,
+ }
+ }
+}
+
+impl Drop for WindowsTTY {
+ fn drop(&mut self) {
+ if self.closeme {
+ // Nobody cares about the return value
+ let _ = unsafe { CloseHandle(self.handle) };
+ }
+ }
+}
+
+impl RtioTTY for WindowsTTY {
+ fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
+ // Read more if the buffer is empty
+ if self.utf8.eof() {
+ let mut utf16 = Vec::from_elem(0x1000, 0u16);
+ let mut num: DWORD = 0;
+ match unsafe { ReadConsoleW(self.handle,
+ utf16.as_mut_ptr() as LPVOID,
+ utf16.len() as u32,
+ &mut num as LPDWORD,
+ ptr::mut_null()) } {
+ 0 => return Err(super::last_error()),
+ _ => (),
+ };
+ utf16.truncate(num as uint);
+ let utf8 = match from_utf16(utf16.as_slice()) {
+ Some(utf8) => utf8.into_bytes(),
+ None => return Err(invalid_encoding()),
+ };
+ self.utf8 = MemReader::new(utf8);
+ }
+ // MemReader shouldn't error here since we just filled it
+ Ok(self.utf8.read(buf).unwrap())
+ }
+
+ fn write(&mut self, buf: &[u8]) -> IoResult<()> {
+ let utf16 = match from_utf8(buf) {
+ Some(utf8) => utf8.to_utf16(),
+ None => return Err(invalid_encoding()),
+ };
+ let mut num: DWORD = 0;
+ match unsafe { WriteConsoleW(self.handle,
+ utf16.as_ptr() as LPCVOID,
+ utf16.len() as u32,
+ &mut num as LPDWORD,
+ ptr::mut_null()) } {
+ 0 => Err(super::last_error()),
+ _ => Ok(()),
+ }
+ }
+
+ fn set_raw(&mut self, raw: bool) -> IoResult<()> {
+ // FIXME
+ // Somebody needs to decide on which of these flags we want
+ match unsafe { SetConsoleMode(self.handle,
+ match raw {
+ true => 0,
+ false => ENABLE_ECHO_INPUT | ENABLE_EXTENDED_FLAGS |
+ ENABLE_INSERT_MODE | ENABLE_LINE_INPUT |
+ ENABLE_PROCESSED_INPUT | ENABLE_QUICK_EDIT_MODE,
+ }) } {
+ 0 => Err(super::last_error()),
+ _ => Ok(()),
+ }
+ }
+
+ fn get_winsize(&mut self) -> IoResult<(int, int)> {
+ // FIXME
+ // Get console buffer via CreateFile with CONOUT$
+ // Make a CONSOLE_SCREEN_BUFFER_INFO
+ // Call GetConsoleScreenBufferInfo
+ // Maybe call GetLargestConsoleWindowSize instead?
+ Err(super::unimpl())
+ }
+
+ // Let us magically declare this as a TTY
+ fn isatty(&self) -> bool { true }
+}
// In the future, FreeBSD will use clang as default compiler.
// It would be flexible to use cc (system's default C compiler)
// instead of hard-coded gcc.
- // For win32, there is no cc command, so we add a condition to make it use gcc.
+ // For Windows, there is no cc command, so we add a condition to make it use gcc.
match sess.targ_cfg.os {
abi::OsWindows => "gcc",
_ => "cc",
* depending on which case of an enum it is.
*
* To understand the alignment situation, consider `enum E { V64(u64),
- * V32(u32, u32) }` on win32. The type has 8-byte alignment to
+ * V32(u32, u32) }` on Windows. The type has 8-byte alignment to
* accommodate the u64, but `V32(x, y)` would have LLVM type `{i32,
* i32, i32}`, which is 4-byte aligned.
*
// libuv maps this error code to EISDIR. we do too. if it is found
// to be incorrect, we can add in some more machinery to only
// return this message when ERROR_INVALID_FUNCTION after certain
- // win32 calls.
+ // Windows calls.
libc::ERROR_INVALID_FUNCTION => (InvalidInput,
"illegal operation on a directory"),
}
#[cfg(windows)]
-pub mod win32 {
+pub mod windows {
use libc::types::os::arch::extra::DWORD;
use libc;
use option::{None, Option};
pub fn getenv(n: &str) -> Option<String> {
unsafe {
with_env_lock(|| {
- use os::win32::{fill_utf16_buf_and_decode};
+ use os::windows::{fill_utf16_buf_and_decode};
let n: Vec<u16> = n.utf16_units().collect();
let n = n.append_one(0);
fill_utf16_buf_and_decode(|buf, sz| {
#[cfg(windows)]
fn load_self() -> Option<Vec<u8>> {
unsafe {
- use os::win32::fill_utf16_buf_and_decode;
+ use os::windows::fill_utf16_buf_and_decode;
fill_utf16_buf_and_decode(|buf, sz| {
libc::GetModuleFileNameW(0u as libc::DWORD, buf, sz)
}).map(|s| s.into_string().into_bytes())
/// A memory mapped file or chunk of memory. This is a very system-specific
/// interface to the OS's memory mapping facilities (`mmap` on POSIX,
-/// `VirtualAlloc`/`CreateFileMapping` on win32). It makes no attempt at
+/// `VirtualAlloc`/`CreateFileMapping` on Windows). It makes no attempt at
/// abstracting platform differences, besides in error values returned. Consider
/// yourself warned.
///
ErrZeroLength,
/// Unrecognized error. The inner value is the unrecognized errno.
ErrUnknown(int),
- /// ## The following are win32-specific
+ /// ## The following are Windows-specific
///
/// Unsupported combination of protection flags
/// (`MapReadable`/`MapWritable`/`MapExecutable`).
pub static FAMILY: &'static str = "windows";
/// A string describing the specific operating system in use: in this
- /// case, `win32`.
+ /// case, `windows`.
pub static SYSNAME: &'static str = "windows";
/// Specifies the filename prefix used for shared libraries on this
//! Windows console handling
-// FIXME (#13400): this is only a tiny fraction of the win32 console api
+// FIXME (#13400): this is only a tiny fraction of the Windows console api
extern crate libc;
// aux-build:linkage-visibility.rs
// ignore-android: FIXME(#10379)
-// ignore-windows: std::dynamic_lib does not work on win32 well
+// ignore-windows: std::dynamic_lib does not work on Windows well
extern crate foo = "linkage-visibility";