diff options
| author | Johannes Hoff <johshoff@gmail.com> | 2014-12-24 13:22:11 +0100 |
|---|---|---|
| committer | Johannes Hoff <johshoff@gmail.com> | 2014-12-24 13:22:11 +0100 |
| commit | 0128159c95d0544e0c30b8b52ce3e7ce348fc114 (patch) | |
| tree | 8af4db0f2758f86434b895169122a9962fb79b21 /src/libstd/sys | |
| parent | 8f827d33cab1be648120fc8ac34651d9cc079b5e (diff) | |
| parent | e64a8193b02ce72ef183274994a25eae281cb89c (diff) | |
| download | rust-0128159c95d0544e0c30b8b52ce3e7ce348fc114.tar.gz rust-0128159c95d0544e0c30b8b52ce3e7ce348fc114.zip | |
Merge branch 'master' into cfg_tmp_dir
Conflicts: src/etc/rustup.sh
Diffstat (limited to 'src/libstd/sys')
45 files changed, 4507 insertions, 213 deletions
diff --git a/src/libstd/sys/common/backtrace.rs b/src/libstd/sys/common/backtrace.rs new file mode 100644 index 00000000000..1d646eb06b1 --- /dev/null +++ b/src/libstd/sys/common/backtrace.rs @@ -0,0 +1,136 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use prelude::*; + +use io::IoResult; + +#[cfg(target_word_size = "64")] pub const HEX_WIDTH: uint = 18; +#[cfg(target_word_size = "32")] pub const HEX_WIDTH: uint = 10; + +// All rust symbols are in theory lists of "::"-separated identifiers. Some +// assemblers, however, can't handle these characters in symbol names. To get +// around this, we use C++-style mangling. The mangling method is: +// +// 1. Prefix the symbol with "_ZN" +// 2. For each element of the path, emit the length plus the element +// 3. End the path with "E" +// +// For example, "_ZN4testE" => "test" and "_ZN3foo3bar" => "foo::bar". +// +// We're the ones printing our backtraces, so we can't rely on anything else to +// demangle our symbols. It's *much* nicer to look at demangled symbols, so +// this function is implemented to give us nice pretty output. +// +// Note that this demangler isn't quite as fancy as it could be. We have lots +// of other information in our symbols like hashes, version, type information, +// etc. Additionally, this doesn't handle glue symbols at all. +pub fn demangle(writer: &mut Writer, s: &str) -> IoResult<()> { + // First validate the symbol. If it doesn't look like anything we're + // expecting, we just print it literally. Note that we must handle non-rust + // symbols because we could have any function in the backtrace. + let mut valid = true; + let mut inner = s; + if s.len() > 4 && s.starts_with("_ZN") && s.ends_with("E") { + inner = s.slice(3, s.len() - 1); + // On Windows, dbghelp strips leading underscores, so we accept "ZN...E" form too. + } else if s.len() > 3 && s.starts_with("ZN") && s.ends_with("E") { + inner = s.slice(2, s.len() - 1); + } else { + valid = false; + } + + if valid { + let mut chars = inner.chars(); + while valid { + let mut i = 0; + for c in chars { + if c.is_numeric() { + i = i * 10 + c as uint - '0' as uint; + } else { + break + } + } + if i == 0 { + valid = chars.next().is_none(); + break + } else if chars.by_ref().take(i - 1).count() != i - 1 { + valid = false; + } + } + } + + // Alright, let's do this. + if !valid { + try!(writer.write_str(s)); + } else { + let mut first = true; + while inner.len() > 0 { + if !first { + try!(writer.write_str("::")); + } else { + first = false; + } + let mut rest = inner; + while rest.char_at(0).is_numeric() { + rest = rest.slice_from(1); + } + let i: uint = inner.slice_to(inner.len() - rest.len()).parse().unwrap(); + inner = rest.slice_from(i); + rest = rest.slice_to(i); + while rest.len() > 0 { + if rest.starts_with("$") { + macro_rules! demangle { + ($($pat:expr => $demangled:expr),*) => ({ + $(if rest.starts_with($pat) { + try!(writer.write_str($demangled)); + rest = rest.slice_from($pat.len()); + } else)* + { + try!(writer.write_str(rest)); + break; + } + + }) + } + + // see src/librustc/back/link.rs for these mappings + demangle! ( + "$SP$" => "@", + "$UP$" => "Box", + "$RP$" => "*", + "$BP$" => "&", + "$LT$" => "<", + "$GT$" => ">", + "$LP$" => "(", + "$RP$" => ")", + "$C$" => ",", + + // in theory we can demangle any Unicode code point, but + // for simplicity we just catch the common ones. + "$x20" => " ", + "$x27" => "'", + "$x5b" => "[", + "$x5d" => "]" + ) + } else { + let idx = match rest.find('$') { + None => rest.len(), + Some(i) => i, + }; + try!(writer.write_str(rest.slice_to(idx))); + rest = rest.slice_from(idx); + } + } + } + } + + Ok(()) +} diff --git a/src/libstd/sys/common/condvar.rs b/src/libstd/sys/common/condvar.rs new file mode 100644 index 00000000000..e09d9704029 --- /dev/null +++ b/src/libstd/sys/common/condvar.rs @@ -0,0 +1,67 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use time::Duration; +use sys_common::mutex::{mod, Mutex}; +use sys::condvar as imp; + +/// An OS-based condition variable. +/// +/// This structure is the lowest layer possible on top of the OS-provided +/// condition variables. It is consequently entirely unsafe to use. It is +/// recommended to use the safer types at the top level of this crate instead of +/// this type. +pub struct Condvar(imp::Condvar); + +/// Static initializer for condition variables. +pub const CONDVAR_INIT: Condvar = Condvar(imp::CONDVAR_INIT); + +impl Condvar { + /// Creates a new condition variable for use. + /// + /// Behavior is undefined if the condition variable is moved after it is + /// first used with any of the functions below. + #[inline] + pub unsafe fn new() -> Condvar { Condvar(imp::Condvar::new()) } + + /// Signal one waiter on this condition variable to wake up. + #[inline] + pub unsafe fn notify_one(&self) { self.0.notify_one() } + + /// Awaken all current waiters on this condition variable. + #[inline] + pub unsafe fn notify_all(&self) { self.0.notify_all() } + + /// Wait for a signal on the specified mutex. + /// + /// Behavior is undefined if the mutex is not locked by the current thread. + /// Behavior is also undefined if more than one mutex is used concurrently + /// on this condition variable. + #[inline] + pub unsafe fn wait(&self, mutex: &Mutex) { self.0.wait(mutex::raw(mutex)) } + + /// Wait for a signal on the specified mutex with a timeout duration + /// specified by `dur` (a relative time into the future). + /// + /// Behavior is undefined if the mutex is not locked by the current thread. + /// Behavior is also undefined if more than one mutex is used concurrently + /// on this condition variable. + #[inline] + pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { + self.0.wait_timeout(mutex::raw(mutex), dur) + } + + /// Deallocate all resources associated with this condition variable. + /// + /// Behavior is undefined if there are current or will be future users of + /// this condition variable. + #[inline] + pub unsafe fn destroy(&self) { self.0.destroy() } +} diff --git a/src/libstd/sys/common/helper_thread.rs b/src/libstd/sys/common/helper_thread.rs index 9508d8d9232..421778e2012 100644 --- a/src/libstd/sys/common/helper_thread.rs +++ b/src/libstd/sys/common/helper_thread.rs @@ -20,15 +20,15 @@ //! can be created in the future and there must be no active timers at that //! time. -use mem; -use rustrt::bookkeeping; -use rustrt::mutex::StaticNativeMutex; -use rustrt; +use prelude::*; + use cell::UnsafeCell; +use mem; +use sync::{StaticMutex, StaticCondvar}; +use rt; use sys::helper_signal; -use prelude::*; -use task; +use thread::Thread; /// A structure for management of a helper thread. /// @@ -39,7 +39,8 @@ use task; /// is for static initialization. pub struct Helper<M> { /// Internal lock which protects the remaining fields - pub lock: StaticNativeMutex, + pub lock: StaticMutex, + pub cond: StaticCondvar, // You'll notice that the remaining fields are UnsafeCell<T>, and this is // because all helper thread operations are done through &self, but we need @@ -53,6 +54,9 @@ pub struct Helper<M> { /// Flag if this helper thread has booted and been initialized yet. pub initialized: UnsafeCell<bool>, + + /// Flag if this helper thread has shut down + pub shutdown: UnsafeCell<bool>, } impl<M: Send> Helper<M> { @@ -65,9 +69,10 @@ impl<M: Send> Helper<M> { /// passed to the helper thread in a separate task. /// /// This function is safe to be called many times. - pub fn boot<T: Send>(&'static self, - f: || -> T, - helper: fn(helper_signal::signal, Receiver<M>, T)) { + pub fn boot<T, F>(&'static self, f: F, helper: fn(helper_signal::signal, Receiver<M>, T)) where + T: Send, + F: FnOnce() -> T, + { unsafe { let _guard = self.lock.lock(); if !*self.initialized.get() { @@ -77,13 +82,14 @@ impl<M: Send> Helper<M> { *self.signal.get() = send as uint; let t = f(); - task::spawn(proc() { - bookkeeping::decrement(); + Thread::spawn(move |:| { helper(receive, rx, t); - self.lock.lock().signal() - }); + let _g = self.lock.lock(); + *self.shutdown.get() = true; + self.cond.notify_one() + }).detach(); - rustrt::at_exit(proc() { self.shutdown() }); + rt::at_exit(move|:| { self.shutdown() }); *self.initialized.get() = true; } } @@ -119,7 +125,9 @@ impl<M: Send> Helper<M> { helper_signal::signal(*self.signal.get() as helper_signal::signal); // Wait for the child to exit - guard.wait(); + while !*self.shutdown.get() { + self.cond.wait(&guard); + } drop(guard); // Clean up after ourselves diff --git a/src/libstd/sys/common/mod.rs b/src/libstd/sys/common/mod.rs index cacb128faa5..dc0ad08cdbe 100644 --- a/src/libstd/sys/common/mod.rs +++ b/src/libstd/sys/common/mod.rs @@ -13,14 +13,22 @@ use io::{mod, IoError, IoResult}; use prelude::*; -use sys::{last_error, retry, fs}; +use sys::{last_error, retry}; use c_str::CString; use num::Int; use path::BytesContainer; use collections; -pub mod net; +pub mod backtrace; +pub mod condvar; pub mod helper_thread; +pub mod mutex; +pub mod net; +pub mod rwlock; +pub mod stack; +pub mod thread; +pub mod thread_info; +pub mod thread_local; // common error constructors @@ -65,7 +73,9 @@ pub fn mkerr_libc<T: Int>(ret: T) -> IoResult<()> { } } -pub fn keep_going(data: &[u8], f: |*const u8, uint| -> i64) -> i64 { +pub fn keep_going<F>(data: &[u8], mut f: F) -> i64 where + F: FnMut(*const u8, uint) -> i64, +{ let origamt = data.len(); let mut data = data.as_ptr(); let mut amt = origamt; @@ -83,10 +93,9 @@ pub fn keep_going(data: &[u8], f: |*const u8, uint| -> i64) -> i64 { return (origamt - amt) as i64; } -// traits for extracting representations from - -pub trait AsFileDesc { - fn as_fd(&self) -> &fs::FileDesc; +// A trait for extracting representations from std::io types +pub trait AsInner<Inner> { + fn as_inner(&self) -> &Inner; } pub trait ProcessConfig<K: BytesContainer, V: BytesContainer> { diff --git a/src/libstd/sys/common/mutex.rs b/src/libstd/sys/common/mutex.rs new file mode 100644 index 00000000000..1a8a92a105a --- /dev/null +++ b/src/libstd/sys/common/mutex.rs @@ -0,0 +1,62 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use sys::mutex as imp; + +/// An OS-based mutual exclusion lock. +/// +/// This is the thinnest cross-platform wrapper around OS mutexes. All usage of +/// this mutex is unsafe and it is recommended to instead use the safe wrapper +/// at the top level of the crate instead of this type. +pub struct Mutex(imp::Mutex); + +/// Constant initializer for statically allocated mutexes. +pub const MUTEX_INIT: Mutex = Mutex(imp::MUTEX_INIT); + +impl Mutex { + /// Creates a newly initialized mutex. + /// + /// Behavior is undefined if the mutex is moved after the first method is + /// called on the mutex. + #[inline] + pub unsafe fn new() -> Mutex { Mutex(imp::Mutex::new()) } + + /// Lock the mutex blocking the current thread until it is available. + /// + /// Behavior is undefined if the mutex has been moved between this and any + /// previous function call. + #[inline] + pub unsafe fn lock(&self) { self.0.lock() } + + /// Attempt to lock the mutex without blocking, returning whether it was + /// successfully acquired or not. + /// + /// Behavior is undefined if the mutex has been moved between this and any + /// previous function call. + #[inline] + pub unsafe fn try_lock(&self) -> bool { self.0.try_lock() } + + /// Unlock the mutex. + /// + /// Behavior is undefined if the current thread does not actually hold the + /// mutex. + #[inline] + pub unsafe fn unlock(&self) { self.0.unlock() } + + /// Deallocate all resources associated with this mutex. + /// + /// Behavior is undefined if there are current or will be future users of + /// this mutex. + #[inline] + pub unsafe fn destroy(&self) { self.0.destroy() } +} + +// not meant to be exported to the outside world, just the containing module +pub fn raw(mutex: &Mutex) -> &imp::Mutex { &mutex.0 } diff --git a/src/libstd/sys/common/net.rs b/src/libstd/sys/common/net.rs index 029fc852742..382f6875b28 100644 --- a/src/libstd/sys/common/net.rs +++ b/src/libstd/sys/common/net.rs @@ -8,21 +8,21 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -pub use self::SocketStatus::*; -pub use self::InAddr::*; +use self::SocketStatus::*; +use self::InAddr::*; use alloc::arc::Arc; use libc::{mod, c_char, c_int}; use mem; use num::Int; use ptr::{mod, null, null_mut}; -use rustrt::mutex; use io::net::ip::{SocketAddr, IpAddr, Ipv4Addr, Ipv6Addr}; use io::net::addrinfo; use io::{IoResult, IoError}; use sys::{mod, retry, c, sock_t, last_error, last_net_error, last_gai_error, close_sock, wrlen, msglen_t, os, wouldblock, set_nonblocking, timer, ms_to_timeval, decode_error_detailed}; +use sync::{Mutex, MutexGuard}; use sys_common::{mod, keep_going, short_write, timeout}; use prelude::*; use cmp; @@ -344,10 +344,10 @@ pub fn get_host_addresses(host: Option<&str>, servname: Option<&str>, // [1] http://twistedmatrix.com/pipermail/twisted-commits/2012-April/034692.html // [2] http://stackoverflow.com/questions/19819198/does-send-msg-dontwait -pub fn read<T>(fd: sock_t, - deadline: u64, - lock: || -> T, - read: |bool| -> libc::c_int) -> IoResult<uint> { +pub fn read<T, L, R>(fd: sock_t, deadline: u64, mut lock: L, mut read: R) -> IoResult<uint> where + L: FnMut() -> T, + R: FnMut(bool) -> libc::c_int, +{ let mut ret = -1; if deadline == 0 { ret = retry(|| read(false)); @@ -386,12 +386,15 @@ pub fn read<T>(fd: sock_t, } } -pub fn write<T>(fd: sock_t, - deadline: u64, - buf: &[u8], - write_everything: bool, - lock: || -> T, - write: |bool, *const u8, uint| -> i64) -> IoResult<uint> { +pub fn write<T, L, W>(fd: sock_t, + deadline: u64, + buf: &[u8], + write_everything: bool, + mut lock: L, + mut write: W) -> IoResult<uint> where + L: FnMut() -> T, + W: FnMut(bool, *const u8, uint) -> i64, +{ let mut ret = -1; let mut written = 0; if deadline == 0 { @@ -557,12 +560,12 @@ struct Inner { // Unused on Linux, where this lock is not necessary. #[allow(dead_code)] - lock: mutex::NativeMutex + lock: Mutex<()>, } impl Inner { fn new(fd: sock_t) -> Inner { - Inner { fd: fd, lock: unsafe { mutex::NativeMutex::new() } } + Inner { fd: fd, lock: Mutex::new(()) } } } @@ -572,7 +575,7 @@ impl Drop for Inner { pub struct Guard<'a> { pub fd: sock_t, - pub guard: mutex::LockGuard<'a>, + pub guard: MutexGuard<'a, ()>, } #[unsafe_destructor] @@ -666,7 +669,7 @@ impl TcpStream { fn lock_nonblocking<'a>(&'a self) -> Guard<'a> { let ret = Guard { fd: self.fd(), - guard: unsafe { self.inner.lock.lock() }, + guard: self.inner.lock.lock(), }; assert!(set_nonblocking(self.fd(), true).is_ok()); ret @@ -674,8 +677,8 @@ impl TcpStream { pub fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> { let fd = self.fd(); - let dolock = || self.lock_nonblocking(); - let doread = |nb| unsafe { + let dolock = |&:| self.lock_nonblocking(); + let doread = |&mut: nb| unsafe { let flags = if nb {c::MSG_DONTWAIT} else {0}; libc::recv(fd, buf.as_mut_ptr() as *mut libc::c_void, @@ -687,8 +690,8 @@ impl TcpStream { pub fn write(&mut self, buf: &[u8]) -> IoResult<()> { let fd = self.fd(); - let dolock = || self.lock_nonblocking(); - let dowrite = |nb: bool, buf: *const u8, len: uint| unsafe { + let dolock = |&:| self.lock_nonblocking(); + let dowrite = |&: nb: bool, buf: *const u8, len: uint| unsafe { let flags = if nb {c::MSG_DONTWAIT} else {0}; libc::send(fd, buf as *const _, @@ -805,7 +808,7 @@ impl UdpSocket { fn lock_nonblocking<'a>(&'a self) -> Guard<'a> { let ret = Guard { fd: self.fd(), - guard: unsafe { self.inner.lock.lock() }, + guard: self.inner.lock.lock(), }; assert!(set_nonblocking(self.fd(), true).is_ok()); ret @@ -822,7 +825,7 @@ impl UdpSocket { let mut addrlen: libc::socklen_t = mem::size_of::<libc::sockaddr_storage>() as libc::socklen_t; - let dolock = || self.lock_nonblocking(); + let dolock = |&:| self.lock_nonblocking(); let n = try!(read(fd, self.read_deadline, dolock, |nb| unsafe { let flags = if nb {c::MSG_DONTWAIT} else {0}; libc::recvfrom(fd, @@ -843,8 +846,8 @@ impl UdpSocket { let dstp = &storage as *const _ as *const libc::sockaddr; let fd = self.fd(); - let dolock = || self.lock_nonblocking(); - let dowrite = |nb, buf: *const u8, len: uint| unsafe { + let dolock = |&: | self.lock_nonblocking(); + let dowrite = |&mut: nb, buf: *const u8, len: uint| unsafe { let flags = if nb {c::MSG_DONTWAIT} else {0}; libc::sendto(fd, buf as *const libc::c_void, diff --git a/src/libstd/sys/common/rwlock.rs b/src/libstd/sys/common/rwlock.rs new file mode 100644 index 00000000000..df016b9e293 --- /dev/null +++ b/src/libstd/sys/common/rwlock.rs @@ -0,0 +1,86 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use sys::rwlock as imp; + +/// An OS-based reader-writer lock. +/// +/// This structure is entirely unsafe and serves as the lowest layer of a +/// cross-platform binding of system rwlocks. It is recommended to use the +/// safer types at the top level of this crate instead of this type. +pub struct RWLock(imp::RWLock); + +/// Constant initializer for static RWLocks. +pub const RWLOCK_INIT: RWLock = RWLock(imp::RWLOCK_INIT); + +impl RWLock { + /// Creates a new instance of an RWLock. + /// + /// Usage of an RWLock is undefined if it is moved after its first use (any + /// function calls below). + #[inline] + pub unsafe fn new() -> RWLock { RWLock(imp::RWLock::new()) } + + /// Acquire shared access to the underlying lock, blocking the current + /// thread to do so. + /// + /// Behavior is undefined if the rwlock has been moved between this and any + /// previous methodo call. + #[inline] + pub unsafe fn read(&self) { self.0.read() } + + /// Attempt to acquire shared access to this lock, returning whether it + /// succeeded or not. + /// + /// This function does not block the current thread. + /// + /// Behavior is undefined if the rwlock has been moved between this and any + /// previous methodo call. + #[inline] + pub unsafe fn try_read(&self) -> bool { self.0.try_read() } + + /// Acquire write access to the underlying lock, blocking the current thread + /// to do so. + /// + /// Behavior is undefined if the rwlock has been moved between this and any + /// previous methodo call. + #[inline] + pub unsafe fn write(&self) { self.0.write() } + + /// Attempt to acquire exclusive access to this lock, returning whether it + /// succeeded or not. + /// + /// This function does not block the current thread. + /// + /// Behavior is undefined if the rwlock has been moved between this and any + /// previous methodo call. + #[inline] + pub unsafe fn try_write(&self) -> bool { self.0.try_write() } + + /// Unlock previously acquired shared access to this lock. + /// + /// Behavior is undefined if the current thread does not have shared access. + #[inline] + pub unsafe fn read_unlock(&self) { self.0.read_unlock() } + + /// Unlock previously acquired exclusive access to this lock. + /// + /// Behavior is undefined if the current thread does not currently have + /// exclusive access. + #[inline] + pub unsafe fn write_unlock(&self) { self.0.write_unlock() } + + /// Destroy OS-related resources with this RWLock. + /// + /// Behavior is undefined if there are any currently active users of this + /// lock. + #[inline] + pub unsafe fn destroy(&self) { self.0.destroy() } +} diff --git a/src/libstd/sys/common/stack.rs b/src/libstd/sys/common/stack.rs new file mode 100644 index 00000000000..2a88e20c8fa --- /dev/null +++ b/src/libstd/sys/common/stack.rs @@ -0,0 +1,325 @@ +// 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. + +//! Rust stack-limit management +//! +//! Currently Rust uses a segmented-stack-like scheme in order to detect stack +//! overflow for rust tasks. In this scheme, the prologue of all functions are +//! preceded with a check to see whether the current stack limits are being +//! exceeded. +//! +//! This module provides the functionality necessary in order to manage these +//! stack limits (which are stored in platform-specific locations). The +//! functions here are used at the borders of the task lifetime in order to +//! manage these limits. +//! +//! This function is an unstable module because this scheme for stack overflow +//! detection is not guaranteed to continue in the future. Usage of this module +//! is discouraged unless absolutely necessary. + +// iOS related notes +// +// It is possible to implement it using idea from +// http://www.opensource.apple.com/source/Libc/Libc-825.40.1/pthreads/pthread_machdep.h +// +// In short: _pthread_{get,set}_specific_direct allows extremely fast +// access, exactly what is required for segmented stack +// There is a pool of reserved slots for Apple internal use (0..119) +// First dynamic allocated pthread key starts with 257 (on iOS7) +// So using slot 149 should be pretty safe ASSUMING space is reserved +// for every key < first dynamic key +// +// There is also an opportunity to steal keys reserved for Garbage Collection +// ranges 80..89 and 110..119, especially considering the fact Garbage Collection +// never supposed to work on iOS. But as everybody knows it - there is a chance +// that those slots will be re-used, like it happened with key 95 (moved from +// JavaScriptCore to CoreText) +// +// Unfortunately Apple rejected patch to LLVM which generated +// corresponding prolog, decision was taken to disable segmented +// stack support on iOS. + +pub const RED_ZONE: uint = 20 * 1024; + +/// This function is invoked from rust's current __morestack function. Segmented +/// stacks are currently not enabled as segmented stacks, but rather one giant +/// stack segment. This means that whenever we run out of stack, we want to +/// truly consider it to be stack overflow rather than allocating a new stack. +#[cfg(not(test))] // in testing, use the original libstd's version +#[lang = "stack_exhausted"] +extern fn stack_exhausted() { + use intrinsics; + + unsafe { + // We're calling this function because the stack just ran out. We need + // to call some other rust functions, but if we invoke the functions + // right now it'll just trigger this handler being called again. In + // order to alleviate this, we move the stack limit to be inside of the + // red zone that was allocated for exactly this reason. + let limit = get_sp_limit(); + record_sp_limit(limit - RED_ZONE / 2); + + // This probably isn't the best course of action. Ideally one would want + // to unwind the stack here instead of just aborting the entire process. + // This is a tricky problem, however. There's a few things which need to + // be considered: + // + // 1. We're here because of a stack overflow, yet unwinding will run + // destructors and hence arbitrary code. What if that code overflows + // the stack? One possibility is to use the above allocation of an + // extra 10k to hope that we don't hit the limit, and if we do then + // abort the whole program. Not the best, but kind of hard to deal + // with unless we want to switch stacks. + // + // 2. LLVM will optimize functions based on whether they can unwind or + // not. It will flag functions with 'nounwind' if it believes that + // the function cannot trigger unwinding, but if we do unwind on + // stack overflow then it means that we could unwind in any function + // anywhere. We would have to make sure that LLVM only places the + // nounwind flag on functions which don't call any other functions. + // + // 3. The function that overflowed may have owned arguments. These + // arguments need to have their destructors run, but we haven't even + // begun executing the function yet, so unwinding will not run the + // any landing pads for these functions. If this is ignored, then + // the arguments will just be leaked. + // + // Exactly what to do here is a very delicate topic, and is possibly + // still up in the air for what exactly to do. Some relevant issues: + // + // #3555 - out-of-stack failure leaks arguments + // #3695 - should there be a stack limit? + // #9855 - possible strategies which could be taken + // #9854 - unwinding on windows through __morestack has never worked + // #2361 - possible implementation of not using landing pads + + ::rt::util::report_overflow(); + + intrinsics::abort(); + } +} + +// Windows maintains a record of upper and lower stack bounds in the Thread Information +// Block (TIB), and some syscalls do check that addresses which are supposed to be in +// the stack, indeed lie between these two values. +// (See https://github.com/rust-lang/rust/issues/3445#issuecomment-26114839) +// +// When using Rust-managed stacks (libgreen), we must maintain these values accordingly. +// For OS-managed stacks (libnative), we let the OS manage them for us. +// +// On all other platforms both variants behave identically. + +#[inline(always)] +pub unsafe fn record_os_managed_stack_bounds(stack_lo: uint, _stack_hi: uint) { + record_sp_limit(stack_lo + RED_ZONE); +} + +#[inline(always)] +pub unsafe fn record_rust_managed_stack_bounds(stack_lo: uint, stack_hi: uint) { + // When the old runtime had segmented stacks, it used a calculation that was + // "limit + RED_ZONE + FUDGE". The red zone was for things like dynamic + // symbol resolution, llvm function calls, etc. In theory this red zone + // value is 0, but it matters far less when we have gigantic stacks because + // we don't need to be so exact about our stack budget. The "fudge factor" + // was because LLVM doesn't emit a stack check for functions < 256 bytes in + // size. Again though, we have giant stacks, so we round all these + // calculations up to the nice round number of 20k. + record_sp_limit(stack_lo + RED_ZONE); + + return target_record_stack_bounds(stack_lo, stack_hi); + + #[cfg(not(windows))] #[inline(always)] + unsafe fn target_record_stack_bounds(_stack_lo: uint, _stack_hi: uint) {} + + #[cfg(all(windows, target_arch = "x86"))] #[inline(always)] + unsafe fn target_record_stack_bounds(stack_lo: uint, stack_hi: uint) { + // stack range is at TIB: %fs:0x04 (top) and %fs:0x08 (bottom) + asm!("mov $0, %fs:0x04" :: "r"(stack_hi) :: "volatile"); + asm!("mov $0, %fs:0x08" :: "r"(stack_lo) :: "volatile"); + } + #[cfg(all(windows, target_arch = "x86_64"))] #[inline(always)] + unsafe fn target_record_stack_bounds(stack_lo: uint, stack_hi: uint) { + // stack range is at TIB: %gs:0x08 (top) and %gs:0x10 (bottom) + asm!("mov $0, %gs:0x08" :: "r"(stack_hi) :: "volatile"); + asm!("mov $0, %gs:0x10" :: "r"(stack_lo) :: "volatile"); + } +} + +/// Records the current limit of the stack as specified by `end`. +/// +/// This is stored in an OS-dependent location, likely inside of the thread +/// local storage. The location that the limit is stored is a pre-ordained +/// location because it's where LLVM has emitted code to check. +/// +/// Note that this cannot be called under normal circumstances. This function is +/// changing the stack limit, so upon returning any further function calls will +/// possibly be triggering the morestack logic if you're not careful. +/// +/// Also note that this and all of the inside functions are all flagged as +/// "inline(always)" because they're messing around with the stack limits. This +/// would be unfortunate for the functions themselves to trigger a morestack +/// invocation (if they were an actual function call). +#[inline(always)] +pub unsafe fn record_sp_limit(limit: uint) { + return target_record_sp_limit(limit); + + // x86-64 + #[cfg(all(target_arch = "x86_64", + any(target_os = "macos", target_os = "ios")))] + #[inline(always)] + unsafe fn target_record_sp_limit(limit: uint) { + asm!("movq $$0x60+90*8, %rsi + movq $0, %gs:(%rsi)" :: "r"(limit) : "rsi" : "volatile") + } + #[cfg(all(target_arch = "x86_64", target_os = "linux"))] #[inline(always)] + unsafe fn target_record_sp_limit(limit: uint) { + asm!("movq $0, %fs:112" :: "r"(limit) :: "volatile") + } + #[cfg(all(target_arch = "x86_64", target_os = "windows"))] #[inline(always)] + unsafe fn target_record_sp_limit(_: uint) { + } + #[cfg(all(target_arch = "x86_64", target_os = "freebsd"))] #[inline(always)] + unsafe fn target_record_sp_limit(limit: uint) { + asm!("movq $0, %fs:24" :: "r"(limit) :: "volatile") + } + #[cfg(all(target_arch = "x86_64", target_os = "dragonfly"))] #[inline(always)] + unsafe fn target_record_sp_limit(limit: uint) { + asm!("movq $0, %fs:32" :: "r"(limit) :: "volatile") + } + + // x86 + #[cfg(all(target_arch = "x86", + any(target_os = "macos", target_os = "ios")))] + #[inline(always)] + unsafe fn target_record_sp_limit(limit: uint) { + asm!("movl $$0x48+90*4, %eax + movl $0, %gs:(%eax)" :: "r"(limit) : "eax" : "volatile") + } + #[cfg(all(target_arch = "x86", + any(target_os = "linux", target_os = "freebsd")))] + #[inline(always)] + unsafe fn target_record_sp_limit(limit: uint) { + asm!("movl $0, %gs:48" :: "r"(limit) :: "volatile") + } + #[cfg(all(target_arch = "x86", target_os = "windows"))] #[inline(always)] + unsafe fn target_record_sp_limit(_: uint) { + } + + // mips, arm - Some brave soul can port these to inline asm, but it's over + // my head personally + #[cfg(any(target_arch = "mips", + target_arch = "mipsel", + all(target_arch = "arm", not(target_os = "ios"))))] + #[inline(always)] + unsafe fn target_record_sp_limit(limit: uint) { + use libc::c_void; + return record_sp_limit(limit as *const c_void); + extern { + fn record_sp_limit(limit: *const c_void); + } + } + + // iOS segmented stack is disabled for now, see related notes + #[cfg(all(target_arch = "arm", target_os = "ios"))] #[inline(always)] + unsafe fn target_record_sp_limit(_: uint) { + } +} + +/// The counterpart of the function above, this function will fetch the current +/// stack limit stored in TLS. +/// +/// Note that all of these functions are meant to be exact counterparts of their +/// brethren above, except that the operands are reversed. +/// +/// As with the setter, this function does not have a __morestack header and can +/// therefore be called in a "we're out of stack" situation. +#[inline(always)] +pub unsafe fn get_sp_limit() -> uint { + return target_get_sp_limit(); + + // x86-64 + #[cfg(all(target_arch = "x86_64", + any(target_os = "macos", target_os = "ios")))] + #[inline(always)] + unsafe fn target_get_sp_limit() -> uint { + let limit; + asm!("movq $$0x60+90*8, %rsi + movq %gs:(%rsi), $0" : "=r"(limit) :: "rsi" : "volatile"); + return limit; + } + #[cfg(all(target_arch = "x86_64", target_os = "linux"))] #[inline(always)] + unsafe fn target_get_sp_limit() -> uint { + let limit; + asm!("movq %fs:112, $0" : "=r"(limit) ::: "volatile"); + return limit; + } + #[cfg(all(target_arch = "x86_64", target_os = "windows"))] #[inline(always)] + unsafe fn target_get_sp_limit() -> uint { + return 1024; + } + #[cfg(all(target_arch = "x86_64", target_os = "freebsd"))] #[inline(always)] + unsafe fn target_get_sp_limit() -> uint { + let limit; + asm!("movq %fs:24, $0" : "=r"(limit) ::: "volatile"); + return limit; + } + #[cfg(all(target_arch = "x86_64", target_os = "dragonfly"))] #[inline(always)] + unsafe fn target_get_sp_limit() -> uint { + let limit; + asm!("movq %fs:32, $0" : "=r"(limit) ::: "volatile"); + return limit; + } + + + // x86 + #[cfg(all(target_arch = "x86", + any(target_os = "macos", target_os = "ios")))] + #[inline(always)] + unsafe fn target_get_sp_limit() -> uint { + let limit; + asm!("movl $$0x48+90*4, %eax + movl %gs:(%eax), $0" : "=r"(limit) :: "eax" : "volatile"); + return limit; + } + #[cfg(all(target_arch = "x86", + any(target_os = "linux", target_os = "freebsd")))] + #[inline(always)] + unsafe fn target_get_sp_limit() -> uint { + let limit; + asm!("movl %gs:48, $0" : "=r"(limit) ::: "volatile"); + return limit; + } + #[cfg(all(target_arch = "x86", target_os = "windows"))] #[inline(always)] + unsafe fn target_get_sp_limit() -> uint { + return 1024; + } + + // mips, arm - Some brave soul can port these to inline asm, but it's over + // my head personally + #[cfg(any(target_arch = "mips", + target_arch = "mipsel", + all(target_arch = "arm", not(target_os = "ios"))))] + #[inline(always)] + unsafe fn target_get_sp_limit() -> uint { + use libc::c_void; + return get_sp_limit() as uint; + extern { + fn get_sp_limit() -> *const c_void; + } + } + + // iOS doesn't support segmented stacks yet. This function might + // be called by runtime though so it is unsafe to mark it as + // unreachable, let's return a fixed constant. + #[cfg(all(target_arch = "arm", target_os = "ios"))] #[inline(always)] + unsafe fn target_get_sp_limit() -> uint { + 1024 + } +} diff --git a/src/libstd/sys/common/thread.rs b/src/libstd/sys/common/thread.rs new file mode 100644 index 00000000000..048e33399a3 --- /dev/null +++ b/src/libstd/sys/common/thread.rs @@ -0,0 +1,35 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::prelude::*; + +use boxed::Box; +use mem; +use uint; +use libc; +use thunk::Thunk; +use sys_common::stack; +use sys::{thread, stack_overflow}; + +// This is the starting point of rust os threads. The first thing we do +// is make sure that we don't trigger __morestack (also why this has a +// no_stack_check annotation), and then we extract the main function +// and invoke it. +#[no_stack_check] +pub fn start_thread(main: *mut libc::c_void) -> thread::rust_thread_return { + unsafe { + stack::record_os_managed_stack_bounds(0, uint::MAX); + let handler = stack_overflow::Handler::new(); + let f: Box<Thunk> = mem::transmute(main); + f.invoke(()); + drop(handler); + mem::transmute(0 as thread::rust_thread_return) + } +} diff --git a/src/libstd/sys/common/thread_info.rs b/src/libstd/sys/common/thread_info.rs new file mode 100644 index 00000000000..dc21feb17a8 --- /dev/null +++ b/src/libstd/sys/common/thread_info.rs @@ -0,0 +1,68 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::prelude::*; + +use thread::Thread; +use cell::RefCell; +use string::String; + +struct ThreadInfo { + // This field holds the known bounds of the stack in (lo, hi) + // form. Not all threads necessarily know their precise bounds, + // hence this is optional. + stack_bounds: (uint, uint), + stack_guard: uint, + thread: Thread, +} + +thread_local! { static THREAD_INFO: RefCell<Option<ThreadInfo>> = RefCell::new(None) } + +impl ThreadInfo { + fn with<R>(f: |&mut ThreadInfo| -> R) -> R { + if THREAD_INFO.destroyed() { + panic!("Use of std::thread::Thread::current() is not possible after \ + the thread's local data has been destroyed"); + } + + THREAD_INFO.with(|c| { + if c.borrow().is_none() { + *c.borrow_mut() = Some(ThreadInfo { + stack_bounds: (0, 0), + stack_guard: 0, + thread: NewThread::new(None), + }) + } + f(c.borrow_mut().as_mut().unwrap()) + }) + } +} + +pub fn current_thread() -> Thread { + ThreadInfo::with(|info| info.thread.clone()) +} + +pub fn stack_guard() -> uint { + ThreadInfo::with(|info| info.stack_guard) +} + +pub fn set(stack_bounds: (uint, uint), stack_guard: uint, thread: Thread) { + THREAD_INFO.with(|c| assert!(c.borrow().is_none())); + THREAD_INFO.with(move |c| *c.borrow_mut() = Some(ThreadInfo{ + stack_bounds: stack_bounds, + stack_guard: stack_guard, + thread: thread, + })); +} + +// a hack to get around privacy restrictions; implemented by `std::thread::Thread` +pub trait NewThread { + fn new(name: Option<String>) -> Self; +} diff --git a/src/libstd/sys/common/thread_local.rs b/src/libstd/sys/common/thread_local.rs new file mode 100644 index 00000000000..fe7a7d8d037 --- /dev/null +++ b/src/libstd/sys/common/thread_local.rs @@ -0,0 +1,284 @@ +// 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. + +//! OS-based thread local storage +//! +//! This module provides an implementation of OS-based thread local storage, +//! using the native OS-provided facilities (think `TlsAlloc` or +//! `pthread_setspecific`). The interface of this differs from the other types +//! of thread-local-storage provided in this crate in that OS-based TLS can only +//! get/set pointers, +//! +//! This module also provides two flavors of TLS. One is intended for static +//! initialization, and does not contain a `Drop` implementation to deallocate +//! the OS-TLS key. The other is a type which does implement `Drop` and hence +//! has a safe interface. +//! +//! # Usage +//! +//! This module should likely not be used directly unless other primitives are +//! being built on. types such as `thread_local::scoped::Key` are likely much +//! more useful in practice than this OS-based version which likely requires +//! unsafe code to interoperate with. +//! +//! # Example +//! +//! Using a dynamically allocated TLS key. Note that this key can be shared +//! among many threads via an `Arc`. +//! +//! ```rust,ignore +//! let key = Key::new(None); +//! assert!(key.get().is_null()); +//! key.set(1 as *mut u8); +//! assert!(!key.get().is_null()); +//! +//! drop(key); // deallocate this TLS slot. +//! ``` +//! +//! Sometimes a statically allocated key is either required or easier to work +//! with, however. +//! +//! ```rust,ignore +//! static KEY: StaticKey = INIT; +//! +//! unsafe { +//! assert!(KEY.get().is_null()); +//! KEY.set(1 as *mut u8); +//! } +//! ``` + +#![allow(non_camel_case_types)] + +use prelude::*; + +use sync::atomic::{mod, AtomicUint}; +use sync::{Mutex, Once, ONCE_INIT}; + +use sys::thread_local as imp; + +/// A type for TLS keys that are statically allocated. +/// +/// This type is entirely `unsafe` to use as it does not protect against +/// use-after-deallocation or use-during-deallocation. +/// +/// The actual OS-TLS key is lazily allocated when this is used for the first +/// time. The key is also deallocated when the Rust runtime exits or `destroy` +/// is called, whichever comes first. +/// +/// # Example +/// +/// ```ignore +/// use tls::os::{StaticKey, INIT}; +/// +/// static KEY: StaticKey = INIT; +/// +/// unsafe { +/// assert!(KEY.get().is_null()); +/// KEY.set(1 as *mut u8); +/// } +/// ``` +pub struct StaticKey { + /// Inner static TLS key (internals), created with by `INIT_INNER` in this + /// module. + pub inner: StaticKeyInner, + /// Destructor for the TLS value. + /// + /// See `Key::new` for information about when the destructor runs and how + /// it runs. + pub dtor: Option<unsafe extern fn(*mut u8)>, +} + +/// Inner contents of `StaticKey`, created by the `INIT_INNER` constant. +pub struct StaticKeyInner { + key: AtomicUint, +} + +/// A type for a safely managed OS-based TLS slot. +/// +/// This type allocates an OS TLS key when it is initialized and will deallocate +/// the key when it falls out of scope. When compared with `StaticKey`, this +/// type is entirely safe to use. +/// +/// Implementations will likely, however, contain unsafe code as this type only +/// operates on `*mut u8`, an unsafe pointer. +/// +/// # Example +/// +/// ```rust,ignore +/// use tls::os::Key; +/// +/// let key = Key::new(None); +/// assert!(key.get().is_null()); +/// key.set(1 as *mut u8); +/// assert!(!key.get().is_null()); +/// +/// drop(key); // deallocate this TLS slot. +/// ``` +pub struct Key { + key: imp::Key, +} + +/// Constant initialization value for static TLS keys. +/// +/// This value specifies no destructor by default. +pub const INIT: StaticKey = StaticKey { + inner: INIT_INNER, + dtor: None, +}; + +/// Constant initialization value for the inner part of static TLS keys. +/// +/// This value allows specific configuration of the destructor for a TLS key. +pub const INIT_INNER: StaticKeyInner = StaticKeyInner { + key: atomic::INIT_ATOMIC_UINT, +}; + +static INIT_KEYS: Once = ONCE_INIT; +static mut KEYS: *mut Mutex<Vec<imp::Key>> = 0 as *mut _; + +impl StaticKey { + /// Gets the value associated with this TLS key + /// + /// This will lazily allocate a TLS key from the OS if one has not already + /// been allocated. + #[inline] + pub unsafe fn get(&self) -> *mut u8 { imp::get(self.key()) } + + /// Sets this TLS key to a new value. + /// + /// This will lazily allocate a TLS key from the OS if one has not already + /// been allocated. + #[inline] + pub unsafe fn set(&self, val: *mut u8) { imp::set(self.key(), val) } + + /// Deallocates this OS TLS key. + /// + /// This function is unsafe as there is no guarantee that the key is not + /// currently in use by other threads or will not ever be used again. + /// + /// Note that this does *not* run the user-provided destructor if one was + /// specified at definition time. Doing so must be done manually. + pub unsafe fn destroy(&self) { + match self.inner.key.swap(0, atomic::SeqCst) { + 0 => {} + n => { imp::destroy(n as imp::Key) } + } + } + + #[inline] + unsafe fn key(&self) -> imp::Key { + match self.inner.key.load(atomic::Relaxed) { + 0 => self.lazy_init() as imp::Key, + n => n as imp::Key + } + } + + unsafe fn lazy_init(&self) -> uint { + // POSIX allows the key created here to be 0, but the compare_and_swap + // below relies on using 0 as a sentinel value to check who won the + // race to set the shared TLS key. As far as I know, there is no + // guaranteed value that cannot be returned as a posix_key_create key, + // so there is no value we can initialize the inner key with to + // prove that it has not yet been set. As such, we'll continue using a + // value of 0, but with some gyrations to make sure we have a non-0 + // value returned from the creation routine. + // FIXME: this is clearly a hack, and should be cleaned up. + let key1 = imp::create(self.dtor); + let key = if key1 != 0 { + key1 + } else { + let key2 = imp::create(self.dtor); + imp::destroy(key1); + key2 + }; + assert!(key != 0); + match self.inner.key.compare_and_swap(0, key as uint, atomic::SeqCst) { + // The CAS succeeded, so we've created the actual key + 0 => key as uint, + // If someone beat us to the punch, use their key instead + n => { imp::destroy(key); n } + } + } +} + +impl Key { + /// Create a new managed OS TLS key. + /// + /// This key will be deallocated when the key falls out of scope. + /// + /// The argument provided is an optionally-specified destructor for the + /// value of this TLS key. When a thread exits and the value for this key + /// is non-null the destructor will be invoked. The TLS value will be reset + /// to null before the destructor is invoked. + /// + /// Note that the destructor will not be run when the `Key` goes out of + /// scope. + #[inline] + pub fn new(dtor: Option<unsafe extern fn(*mut u8)>) -> Key { + Key { key: unsafe { imp::create(dtor) } } + } + + /// See StaticKey::get + #[inline] + pub fn get(&self) -> *mut u8 { + unsafe { imp::get(self.key) } + } + + /// See StaticKey::set + #[inline] + pub fn set(&self, val: *mut u8) { + unsafe { imp::set(self.key, val) } + } +} + +impl Drop for Key { + fn drop(&mut self) { + unsafe { imp::destroy(self.key) } + } +} + +#[cfg(test)] +mod tests { + use prelude::*; + use super::{Key, StaticKey, INIT_INNER}; + + fn assert_sync<T: Sync>() {} + fn assert_send<T: Send>() {} + + #[test] + fn smoke() { + assert_sync::<Key>(); + assert_send::<Key>(); + + let k1 = Key::new(None); + let k2 = Key::new(None); + assert!(k1.get().is_null()); + assert!(k2.get().is_null()); + k1.set(1 as *mut _); + k2.set(2 as *mut _); + assert_eq!(k1.get() as uint, 1); + assert_eq!(k2.get() as uint, 2); + } + + #[test] + fn statik() { + static K1: StaticKey = StaticKey { inner: INIT_INNER, dtor: None }; + static K2: StaticKey = StaticKey { inner: INIT_INNER, dtor: None }; + + unsafe { + assert!(K1.get().is_null()); + assert!(K2.get().is_null()); + K1.set(1 as *mut _); + K2.set(2 as *mut _); + assert_eq!(K1.get() as uint, 1); + assert_eq!(K2.get() as uint, 2); + } + } +} diff --git a/src/libstd/sys/unix/backtrace.rs b/src/libstd/sys/unix/backtrace.rs new file mode 100644 index 00000000000..983d0e5fa14 --- /dev/null +++ b/src/libstd/sys/unix/backtrace.rs @@ -0,0 +1,493 @@ +// 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. + +/// Backtrace support built on libgcc with some extra OS-specific support +/// +/// Some methods of getting a backtrace: +/// +/// * The backtrace() functions on unix. It turns out this doesn't work very +/// well for green threads on OSX, and the address to symbol portion of it +/// suffers problems that are described below. +/// +/// * Using libunwind. This is more difficult than it sounds because libunwind +/// isn't installed everywhere by default. It's also a bit of a hefty library, +/// so possibly not the best option. When testing, libunwind was excellent at +/// getting both accurate backtraces and accurate symbols across platforms. +/// This route was not chosen in favor of the next option, however. +/// +/// * We're already using libgcc_s for exceptions in rust (triggering task +/// unwinding and running destructors on the stack), and it turns out that it +/// conveniently comes with a function that also gives us a backtrace. All of +/// these functions look like _Unwind_*, but it's not quite the full +/// repertoire of the libunwind API. Due to it already being in use, this was +/// the chosen route of getting a backtrace. +/// +/// After choosing libgcc_s for backtraces, the sad part is that it will only +/// give us a stack trace of instruction pointers. Thankfully these instruction +/// pointers are accurate (they work for green and native threads), but it's +/// then up to us again to figure out how to translate these addresses to +/// symbols. As with before, we have a few options. Before, that, a little bit +/// of an interlude about symbols. This is my very limited knowledge about +/// symbol tables, and this information is likely slightly wrong, but the +/// general idea should be correct. +/// +/// When talking about symbols, it's helpful to know a few things about where +/// symbols are located. Some symbols are located in the dynamic symbol table +/// of the executable which in theory means that they're available for dynamic +/// linking and lookup. Other symbols end up only in the local symbol table of +/// the file. This loosely corresponds to pub and priv functions in Rust. +/// +/// Armed with this knowledge, we know that our solution for address to symbol +/// translation will need to consult both the local and dynamic symbol tables. +/// With that in mind, here's our options of translating an address to +/// a symbol. +/// +/// * Use dladdr(). The original backtrace()-based idea actually uses dladdr() +/// behind the scenes to translate, and this is why backtrace() was not used. +/// Conveniently, this method works fantastically on OSX. It appears dladdr() +/// uses magic to consult the local symbol table, or we're putting everything +/// in the dynamic symbol table anyway. Regardless, for OSX, this is the +/// method used for translation. It's provided by the system and easy to do.o +/// +/// Sadly, all other systems have a dladdr() implementation that does not +/// consult the local symbol table. This means that most functions are blank +/// because they don't have symbols. This means that we need another solution. +/// +/// * Use unw_get_proc_name(). This is part of the libunwind api (not the +/// libgcc_s version of the libunwind api), but involves taking a dependency +/// to libunwind. We may pursue this route in the future if we bundle +/// libunwind, but libunwind was unwieldy enough that it was not chosen at +/// this time to provide this functionality. +/// +/// * Shell out to a utility like `readelf`. Crazy though it may sound, it's a +/// semi-reasonable solution. The stdlib already knows how to spawn processes, +/// so in theory it could invoke readelf, parse the output, and consult the +/// local/dynamic symbol tables from there. This ended up not getting chosen +/// due to the craziness of the idea plus the advent of the next option. +/// +/// * Use `libbacktrace`. It turns out that this is a small library bundled in +/// the gcc repository which provides backtrace and symbol translation +/// functionality. All we really need from it is the backtrace functionality, +/// and we only really need this on everything that's not OSX, so this is the +/// chosen route for now. +/// +/// In summary, the current situation uses libgcc_s to get a trace of stack +/// pointers, and we use dladdr() or libbacktrace to translate these addresses +/// to symbols. This is a bit of a hokey implementation as-is, but it works for +/// all unix platforms we support right now, so it at least gets the job done. + +use c_str::CString; +use io::{IoResult, Writer}; +use libc; +use mem; +use option::Option::{mod, Some, None}; +use result::Result::{Ok, Err}; +use sync::{StaticMutex, MUTEX_INIT}; + +use sys_common::backtrace::*; + +/// As always - iOS on arm uses SjLj exceptions and +/// _Unwind_Backtrace is even not available there. Still, +/// backtraces could be extracted using a backtrace function, +/// which thanks god is public +/// +/// As mentioned in a huge comment block above, backtrace doesn't +/// play well with green threads, so while it is extremely nice +/// and simple to use it should be used only on iOS devices as the +/// only viable option. +#[cfg(all(target_os = "ios", target_arch = "arm"))] +#[inline(never)] +pub fn write(w: &mut Writer) -> IoResult<()> { + use iter::{IteratorExt, range}; + use result; + use slice::SliceExt; + + extern { + fn backtrace(buf: *mut *mut libc::c_void, + sz: libc::c_int) -> libc::c_int; + } + + // while it doesn't requires lock for work as everything is + // local, it still displays much nicer backtraces when a + // couple of tasks panic simultaneously + static LOCK: StaticMutex = MUTEX_INIT; + let _g = unsafe { LOCK.lock() }; + + try!(writeln!(w, "stack backtrace:")); + // 100 lines should be enough + const SIZE: uint = 100; + let mut buf: [*mut libc::c_void, ..SIZE] = unsafe {mem::zeroed()}; + let cnt = unsafe { backtrace(buf.as_mut_ptr(), SIZE as libc::c_int) as uint}; + + // skipping the first one as it is write itself + let iter = range(1, cnt).map(|i| { + print(w, i as int, buf[i]) + }); + result::fold(iter, (), |_, _| ()) +} + +#[cfg(not(all(target_os = "ios", target_arch = "arm")))] +#[inline(never)] // if we know this is a function call, we can skip it when + // tracing +pub fn write(w: &mut Writer) -> IoResult<()> { + use io::IoError; + + struct Context<'a> { + idx: int, + writer: &'a mut (Writer+'a), + last_error: Option<IoError>, + } + + // When using libbacktrace, we use some necessary global state, so we + // need to prevent more than one thread from entering this block. This + // is semi-reasonable in terms of printing anyway, and we know that all + // I/O done here is blocking I/O, not green I/O, so we don't have to + // worry about this being a native vs green mutex. + static LOCK: StaticMutex = MUTEX_INIT; + let _g = unsafe { LOCK.lock() }; + + try!(writeln!(w, "stack backtrace:")); + + let mut cx = Context { writer: w, last_error: None, idx: 0 }; + return match unsafe { + uw::_Unwind_Backtrace(trace_fn, + &mut cx as *mut Context as *mut libc::c_void) + } { + uw::_URC_NO_REASON => { + match cx.last_error { + Some(err) => Err(err), + None => Ok(()) + } + } + _ => Ok(()), + }; + + extern fn trace_fn(ctx: *mut uw::_Unwind_Context, + arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code { + let cx: &mut Context = unsafe { mem::transmute(arg) }; + let ip = unsafe { uw::_Unwind_GetIP(ctx) as *mut libc::c_void }; + // dladdr() on osx gets whiny when we use FindEnclosingFunction, and + // it appears to work fine without it, so we only use + // FindEnclosingFunction on non-osx platforms. In doing so, we get a + // slightly more accurate stack trace in the process. + // + // This is often because panic involves the last instruction of a + // function being "call std::rt::begin_unwind", with no ret + // instructions after it. This means that the return instruction + // pointer points *outside* of the calling function, and by + // unwinding it we go back to the original function. + let ip = if cfg!(target_os = "macos") || cfg!(target_os = "ios") { + ip + } else { + unsafe { uw::_Unwind_FindEnclosingFunction(ip) } + }; + + // Don't print out the first few frames (they're not user frames) + cx.idx += 1; + if cx.idx <= 0 { return uw::_URC_NO_REASON } + // Don't print ginormous backtraces + if cx.idx > 100 { + match write!(cx.writer, " ... <frames omitted>\n") { + Ok(()) => {} + Err(e) => { cx.last_error = Some(e); } + } + return uw::_URC_FAILURE + } + + // Once we hit an error, stop trying to print more frames + if cx.last_error.is_some() { return uw::_URC_FAILURE } + + match print(cx.writer, cx.idx, ip) { + Ok(()) => {} + Err(e) => { cx.last_error = Some(e); } + } + + // keep going + return uw::_URC_NO_REASON + } +} + +#[cfg(any(target_os = "macos", target_os = "ios"))] +fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> { + use intrinsics; + #[repr(C)] + struct Dl_info { + dli_fname: *const libc::c_char, + dli_fbase: *mut libc::c_void, + dli_sname: *const libc::c_char, + dli_saddr: *mut libc::c_void, + } + extern { + fn dladdr(addr: *const libc::c_void, + info: *mut Dl_info) -> libc::c_int; + } + + let mut info: Dl_info = unsafe { intrinsics::init() }; + if unsafe { dladdr(addr as *const libc::c_void, &mut info) == 0 } { + output(w, idx,addr, None) + } else { + output(w, idx, addr, Some(unsafe { + CString::new(info.dli_sname, false) + })) + } +} + +#[cfg(not(any(target_os = "macos", target_os = "ios")))] +fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> { + use iter::{Iterator, IteratorExt}; + use os; + use path::GenericPath; + use ptr::RawPtr; + use ptr; + use slice::SliceExt; + + //////////////////////////////////////////////////////////////////////// + // libbacktrace.h API + //////////////////////////////////////////////////////////////////////// + type backtrace_syminfo_callback = + extern "C" fn(data: *mut libc::c_void, + pc: libc::uintptr_t, + symname: *const libc::c_char, + symval: libc::uintptr_t, + symsize: libc::uintptr_t); + type backtrace_error_callback = + extern "C" fn(data: *mut libc::c_void, + msg: *const libc::c_char, + errnum: libc::c_int); + enum backtrace_state {} + #[link(name = "backtrace", kind = "static")] + #[cfg(not(test))] + extern {} + + extern { + fn backtrace_create_state(filename: *const libc::c_char, + threaded: libc::c_int, + error: backtrace_error_callback, + data: *mut libc::c_void) + -> *mut backtrace_state; + fn backtrace_syminfo(state: *mut backtrace_state, + addr: libc::uintptr_t, + cb: backtrace_syminfo_callback, + error: backtrace_error_callback, + data: *mut libc::c_void) -> libc::c_int; + } + + //////////////////////////////////////////////////////////////////////// + // helper callbacks + //////////////////////////////////////////////////////////////////////// + + extern fn error_cb(_data: *mut libc::c_void, _msg: *const libc::c_char, + _errnum: libc::c_int) { + // do nothing for now + } + extern fn syminfo_cb(data: *mut libc::c_void, + _pc: libc::uintptr_t, + symname: *const libc::c_char, + _symval: libc::uintptr_t, + _symsize: libc::uintptr_t) { + let slot = data as *mut *const libc::c_char; + unsafe { *slot = symname; } + } + + // The libbacktrace API supports creating a state, but it does not + // support destroying a state. I personally take this to mean that a + // state is meant to be created and then live forever. + // + // I would love to register an at_exit() handler which cleans up this + // state, but libbacktrace provides no way to do so. + // + // With these constraints, this function has a statically cached state + // that is calculated the first time this is requested. Remember that + // backtracing all happens serially (one global lock). + // + // An additionally oddity in this function is that we initialize the + // filename via self_exe_name() to pass to libbacktrace. It turns out + // that on Linux libbacktrace seamlessly gets the filename of the + // current executable, but this fails on freebsd. by always providing + // it, we make sure that libbacktrace never has a reason to not look up + // the symbols. The libbacktrace API also states that the filename must + // be in "permanent memory", so we copy it to a static and then use the + // static as the pointer. + // + // FIXME: We also call self_exe_name() on DragonFly BSD. I haven't + // tested if this is required or not. + unsafe fn init_state() -> *mut backtrace_state { + static mut STATE: *mut backtrace_state = 0 as *mut backtrace_state; + static mut LAST_FILENAME: [libc::c_char, ..256] = [0, ..256]; + if !STATE.is_null() { return STATE } + let selfname = if cfg!(target_os = "freebsd") || + cfg!(target_os = "dragonfly") { + os::self_exe_name() + } else { + None + }; + let filename = match selfname { + Some(path) => { + let bytes = path.as_vec(); + if bytes.len() < LAST_FILENAME.len() { + let i = bytes.iter(); + for (slot, val) in LAST_FILENAME.iter_mut().zip(i) { + *slot = *val as libc::c_char; + } + LAST_FILENAME.as_ptr() + } else { + ptr::null() + } + } + None => ptr::null(), + }; + STATE = backtrace_create_state(filename, 0, error_cb, + ptr::null_mut()); + return STATE + } + + //////////////////////////////////////////////////////////////////////// + // translation + //////////////////////////////////////////////////////////////////////// + + // backtrace errors are currently swept under the rug, only I/O + // errors are reported + let state = unsafe { init_state() }; + if state.is_null() { + return output(w, idx, addr, None) + } + let mut data = 0 as *const libc::c_char; + let data_addr = &mut data as *mut *const libc::c_char; + let ret = unsafe { + backtrace_syminfo(state, addr as libc::uintptr_t, + syminfo_cb, error_cb, + data_addr as *mut libc::c_void) + }; + if ret == 0 || data.is_null() { + output(w, idx, addr, None) + } else { + output(w, idx, addr, Some(unsafe { CString::new(data, false) })) + } +} + +// Finally, after all that work above, we can emit a symbol. +fn output(w: &mut Writer, idx: int, addr: *mut libc::c_void, + s: Option<CString>) -> IoResult<()> { + try!(write!(w, " {:2}: {:2$} - ", idx, addr, HEX_WIDTH)); + match s.as_ref().and_then(|c| c.as_str()) { + Some(string) => try!(demangle(w, string)), + None => try!(write!(w, "<unknown>")), + } + w.write(&['\n' as u8]) +} + +/// Unwind library interface used for backtraces +/// +/// Note that dead code is allowed as here are just bindings +/// iOS doesn't use all of them it but adding more +/// platform-specific configs pollutes the code too much +#[allow(non_camel_case_types)] +#[allow(non_snake_case)] +#[allow(dead_code)] +mod uw { + pub use self::_Unwind_Reason_Code::*; + + use libc; + + #[repr(C)] + pub enum _Unwind_Reason_Code { + _URC_NO_REASON = 0, + _URC_FOREIGN_EXCEPTION_CAUGHT = 1, + _URC_FATAL_PHASE2_ERROR = 2, + _URC_FATAL_PHASE1_ERROR = 3, + _URC_NORMAL_STOP = 4, + _URC_END_OF_STACK = 5, + _URC_HANDLER_FOUND = 6, + _URC_INSTALL_CONTEXT = 7, + _URC_CONTINUE_UNWIND = 8, + _URC_FAILURE = 9, // used only by ARM EABI + } + + pub enum _Unwind_Context {} + + pub type _Unwind_Trace_Fn = + extern fn(ctx: *mut _Unwind_Context, + arg: *mut libc::c_void) -> _Unwind_Reason_Code; + + extern { + // No native _Unwind_Backtrace on iOS + #[cfg(not(all(target_os = "ios", target_arch = "arm")))] + pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn, + trace_argument: *mut libc::c_void) + -> _Unwind_Reason_Code; + + #[cfg(all(not(target_os = "android"), + not(all(target_os = "linux", target_arch = "arm"))))] + pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t; + + #[cfg(all(not(target_os = "android"), + not(all(target_os = "linux", target_arch = "arm"))))] + pub fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) + -> *mut libc::c_void; + } + + // On android, the function _Unwind_GetIP is a macro, and this is the + // expansion of the macro. This is all copy/pasted directly from the + // header file with the definition of _Unwind_GetIP. + #[cfg(any(target_os = "android", + all(target_os = "linux", target_arch = "arm")))] + pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t { + #[repr(C)] + enum _Unwind_VRS_Result { + _UVRSR_OK = 0, + _UVRSR_NOT_IMPLEMENTED = 1, + _UVRSR_FAILED = 2, + } + #[repr(C)] + enum _Unwind_VRS_RegClass { + _UVRSC_CORE = 0, + _UVRSC_VFP = 1, + _UVRSC_FPA = 2, + _UVRSC_WMMXD = 3, + _UVRSC_WMMXC = 4, + } + #[repr(C)] + enum _Unwind_VRS_DataRepresentation { + _UVRSD_UINT32 = 0, + _UVRSD_VFPX = 1, + _UVRSD_FPAX = 2, + _UVRSD_UINT64 = 3, + _UVRSD_FLOAT = 4, + _UVRSD_DOUBLE = 5, + } + + type _Unwind_Word = libc::c_uint; + extern { + fn _Unwind_VRS_Get(ctx: *mut _Unwind_Context, + klass: _Unwind_VRS_RegClass, + word: _Unwind_Word, + repr: _Unwind_VRS_DataRepresentation, + data: *mut libc::c_void) + -> _Unwind_VRS_Result; + } + + let mut val: _Unwind_Word = 0; + let ptr = &mut val as *mut _Unwind_Word; + let _ = _Unwind_VRS_Get(ctx, _Unwind_VRS_RegClass::_UVRSC_CORE, 15, + _Unwind_VRS_DataRepresentation::_UVRSD_UINT32, + ptr as *mut libc::c_void); + (val & !1) as libc::uintptr_t + } + + // This function also doesn't exist on Android or ARM/Linux, so make it + // a no-op + #[cfg(any(target_os = "android", + all(target_os = "linux", target_arch = "arm")))] + pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) + -> *mut libc::c_void + { + pc + } +} diff --git a/src/libstd/sys/unix/condvar.rs b/src/libstd/sys/unix/condvar.rs new file mode 100644 index 00000000000..f64718539ef --- /dev/null +++ b/src/libstd/sys/unix/condvar.rs @@ -0,0 +1,83 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use cell::UnsafeCell; +use libc; +use sys::mutex::{mod, Mutex}; +use sys::sync as ffi; +use time::Duration; + +pub struct Condvar { inner: UnsafeCell<ffi::pthread_cond_t> } + +pub const CONDVAR_INIT: Condvar = Condvar { + inner: UnsafeCell { value: ffi::PTHREAD_COND_INITIALIZER }, +}; + +impl Condvar { + #[inline] + pub unsafe fn new() -> Condvar { + // Might be moved and address is changing it is better to avoid + // initialization of potentially opaque OS data before it landed + Condvar { inner: UnsafeCell::new(ffi::PTHREAD_COND_INITIALIZER) } + } + + #[inline] + pub unsafe fn notify_one(&self) { + let r = ffi::pthread_cond_signal(self.inner.get()); + debug_assert_eq!(r, 0); + } + + #[inline] + pub unsafe fn notify_all(&self) { + let r = ffi::pthread_cond_broadcast(self.inner.get()); + debug_assert_eq!(r, 0); + } + + #[inline] + pub unsafe fn wait(&self, mutex: &Mutex) { + let r = ffi::pthread_cond_wait(self.inner.get(), mutex::raw(mutex)); + debug_assert_eq!(r, 0); + } + + pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { + assert!(dur >= Duration::nanoseconds(0)); + + // First, figure out what time it currently is + let mut tv = libc::timeval { tv_sec: 0, tv_usec: 0 }; + let r = ffi::gettimeofday(&mut tv, 0 as *mut _); + debug_assert_eq!(r, 0); + + // Offset that time with the specified duration + let abs = Duration::seconds(tv.tv_sec as i64) + + Duration::microseconds(tv.tv_usec as i64) + + dur; + let ns = abs.num_nanoseconds().unwrap() as u64; + let timeout = libc::timespec { + tv_sec: (ns / 1000000000) as libc::time_t, + tv_nsec: (ns % 1000000000) as libc::c_long, + }; + + // And wait! + let r = ffi::pthread_cond_timedwait(self.inner.get(), mutex::raw(mutex), + &timeout); + if r != 0 { + debug_assert_eq!(r as int, libc::ETIMEDOUT as int); + false + } else { + true + } + } + + #[inline] + pub unsafe fn destroy(&self) { + let r = ffi::pthread_cond_destroy(self.inner.get()); + debug_assert_eq!(r, 0); + } +} diff --git a/src/libstd/sys/unix/ext.rs b/src/libstd/sys/unix/ext.rs new file mode 100644 index 00000000000..ae3c939bf78 --- /dev/null +++ b/src/libstd/sys/unix/ext.rs @@ -0,0 +1,107 @@ +// 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. + +//! Experimental extensions to `std` for Unix platforms. +//! +//! For now, this module is limited to extracting file descriptors, +//! but its functionality will grow over time. +//! +//! # Example +//! +//! ```rust,ignore +//! #![feature(globs)] +//! +//! use std::io::fs::File; +//! use std::os::unix::prelude::*; +//! +//! fn main() { +//! let f = File::create(&Path::new("foo.txt")).unwrap(); +//! let fd = f.as_raw_fd(); +//! +//! // use fd with native unix bindings +//! } +//! ``` + +#![experimental] + +use sys_common::AsInner; +use libc; + +use io; + +/// Raw file descriptors. +pub type Fd = libc::c_int; + +/// Extract raw file descriptor +pub trait AsRawFd { + /// Extract the raw file descriptor, without taking any ownership. + fn as_raw_fd(&self) -> Fd; +} + +impl AsRawFd for io::fs::File { + fn as_raw_fd(&self) -> Fd { + self.as_inner().fd() + } +} + +impl AsRawFd for io::pipe::PipeStream { + fn as_raw_fd(&self) -> Fd { + self.as_inner().fd() + } +} + +impl AsRawFd for io::net::pipe::UnixStream { + fn as_raw_fd(&self) -> Fd { + self.as_inner().fd() + } +} + +impl AsRawFd for io::net::pipe::UnixListener { + fn as_raw_fd(&self) -> Fd { + self.as_inner().fd() + } +} + +impl AsRawFd for io::net::pipe::UnixAcceptor { + fn as_raw_fd(&self) -> Fd { + self.as_inner().fd() + } +} + +impl AsRawFd for io::net::tcp::TcpStream { + fn as_raw_fd(&self) -> Fd { + self.as_inner().fd() + } +} + +impl AsRawFd for io::net::tcp::TcpListener { + fn as_raw_fd(&self) -> Fd { + self.as_inner().fd() + } +} + +impl AsRawFd for io::net::tcp::TcpAcceptor { + fn as_raw_fd(&self) -> Fd { + self.as_inner().fd() + } +} + +impl AsRawFd for io::net::udp::UdpSocket { + fn as_raw_fd(&self) -> Fd { + self.as_inner().fd() + } +} + +/// A prelude for conveniently writing platform-specific code. +/// +/// Includes all extension traits, and some important type definitions. +pub mod prelude { + pub use super::{Fd, AsRawFd}; +} diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs index 816876b5e4a..98d860f9646 100644 --- a/src/libstd/sys/unix/fs.rs +++ b/src/libstd/sys/unix/fs.rs @@ -18,14 +18,11 @@ use io; use prelude::*; use io::{FilePermission, Write, UnstableFileStat, Open, FileAccess, FileMode}; -use io::{IoResult, FileStat, SeekStyle, Reader}; +use io::{IoResult, FileStat, SeekStyle}; use io::{Read, Truncate, SeekCur, SeekSet, ReadWrite, SeekEnd, Append}; -use result::{Ok, Err}; use sys::retry; use sys_common::{keep_going, eof, mkerr_libc}; -pub use path::PosixPath as Path; - pub type fd_t = libc::c_int; pub struct FileDesc { @@ -201,7 +198,7 @@ pub fn readdir(p: &Path) -> IoResult<Vec<Path>> { let size = unsafe { rust_dirent_t_size() }; let mut buf = Vec::<u8>::with_capacity(size as uint); - let ptr = buf.as_mut_slice().as_mut_ptr() as *mut dirent_t; + let ptr = buf.as_mut_ptr() as *mut dirent_t; let p = p.to_c_str(); let dir_ptr = unsafe {opendir(p.as_ptr())}; @@ -305,12 +302,12 @@ fn mkstat(stat: &libc::stat) -> FileStat { FileStat { size: stat.st_size as u64, kind: match (stat.st_mode as libc::mode_t) & libc::S_IFMT { - libc::S_IFREG => io::TypeFile, - libc::S_IFDIR => io::TypeDirectory, - libc::S_IFIFO => io::TypeNamedPipe, - libc::S_IFBLK => io::TypeBlockSpecial, - libc::S_IFLNK => io::TypeSymlink, - _ => io::TypeUnknown, + libc::S_IFREG => io::FileType::RegularFile, + libc::S_IFDIR => io::FileType::Directory, + libc::S_IFIFO => io::FileType::NamedPipe, + libc::S_IFBLK => io::FileType::BlockSpecial, + libc::S_IFLNK => io::FileType::Symlink, + _ => io::FileType::Unknown, }, perm: FilePermission::from_bits_truncate(stat.st_mode as u32), created: mktime(stat.st_ctime as u64, stat.st_ctime_nsec as u64), diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index 664a6a1e70c..f3babca3287 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -23,25 +23,36 @@ use prelude::*; use io::{mod, IoResult, IoError}; use sys_common::mkerr_libc; -macro_rules! helper_init( (static $name:ident: Helper<$m:ty>) => ( +macro_rules! helper_init { (static $name:ident: Helper<$m:ty>) => ( static $name: Helper<$m> = Helper { - lock: ::rustrt::mutex::NATIVE_MUTEX_INIT, + lock: ::sync::MUTEX_INIT, + cond: ::sync::CONDVAR_INIT, chan: ::cell::UnsafeCell { value: 0 as *mut Sender<$m> }, signal: ::cell::UnsafeCell { value: 0 }, initialized: ::cell::UnsafeCell { value: false }, + shutdown: ::cell::UnsafeCell { value: false }, }; -) ) +) } +pub mod backtrace; pub mod c; +pub mod ext; +pub mod condvar; pub mod fs; +pub mod helper_signal; +pub mod mutex; pub mod os; -pub mod tcp; -pub mod udp; pub mod pipe; -pub mod helper_signal; pub mod process; +pub mod rwlock; +pub mod stack_overflow; +pub mod sync; +pub mod tcp; +pub mod thread; +pub mod thread_local; pub mod timer; pub mod tty; +pub mod udp; pub mod addrinfo { pub use sys_common::net::get_host_addresses; @@ -117,7 +128,10 @@ pub fn decode_error_detailed(errno: i32) -> IoError { } #[inline] -pub fn retry<T: SignedInt> (f: || -> T) -> T { +pub fn retry<T, F> (mut f: F) -> T where + T: SignedInt, + F: FnMut() -> T, +{ let one: T = Int::one(); loop { let n = f(); diff --git a/src/libstd/sys/unix/mutex.rs b/src/libstd/sys/unix/mutex.rs new file mode 100644 index 00000000000..2f01c53cb2c --- /dev/null +++ b/src/libstd/sys/unix/mutex.rs @@ -0,0 +1,52 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use cell::UnsafeCell; +use sys::sync as ffi; +use sys_common::mutex; + +pub struct Mutex { inner: UnsafeCell<ffi::pthread_mutex_t> } + +#[inline] +pub unsafe fn raw(m: &Mutex) -> *mut ffi::pthread_mutex_t { + m.inner.get() +} + +pub const MUTEX_INIT: Mutex = Mutex { + inner: UnsafeCell { value: ffi::PTHREAD_MUTEX_INITIALIZER }, +}; + +impl Mutex { + #[inline] + pub unsafe fn new() -> Mutex { + // Might be moved and address is changing it is better to avoid + // initialization of potentially opaque OS data before it landed + MUTEX_INIT + } + #[inline] + pub unsafe fn lock(&self) { + let r = ffi::pthread_mutex_lock(self.inner.get()); + debug_assert_eq!(r, 0); + } + #[inline] + pub unsafe fn unlock(&self) { + let r = ffi::pthread_mutex_unlock(self.inner.get()); + debug_assert_eq!(r, 0); + } + #[inline] + pub unsafe fn try_lock(&self) -> bool { + ffi::pthread_mutex_trylock(self.inner.get()) == 0 + } + #[inline] + pub unsafe fn destroy(&self) { + let r = ffi::pthread_mutex_destroy(self.inner.get()); + debug_assert_eq!(r, 0); + } +} diff --git a/src/libstd/sys/unix/os.rs b/src/libstd/sys/unix/os.rs index 4e495f043bc..316d97064ee 100644 --- a/src/libstd/sys/unix/os.rs +++ b/src/libstd/sys/unix/os.rs @@ -8,14 +8,24 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use libc; -use libc::{c_int, c_char}; +//! Implementation of `std::os` functionality for unix systems + use prelude::*; -use io::IoResult; + +use error::{FromError, Error}; +use fmt; +use io::{IoError, IoResult}; +use libc::{mod, c_int, c_char, c_void}; +use path::BytesContainer; +use ptr; +use sync::atomic::{AtomicInt, INIT_ATOMIC_INT, SeqCst}; use sys::fs::FileDesc; +use os; use os::TMPBUF_SZ; +const BUF_BYTES : uint = 2048u; + /// Returns the platform-specific value of errno pub fn errno() -> int { #[cfg(any(target_os = "macos", @@ -98,7 +108,7 @@ pub fn error_string(errno: i32) -> String { panic!("strerror_r failure"); } - ::string::raw::from_buf(p as *const u8) + String::from_raw_buf(p as *const u8) } } @@ -110,3 +120,122 @@ pub unsafe fn pipe() -> IoResult<(FileDesc, FileDesc)> { Err(super::last_error()) } } + +pub fn getcwd() -> IoResult<Path> { + use c_str::CString; + + let mut buf = [0 as c_char, ..BUF_BYTES]; + unsafe { + if libc::getcwd(buf.as_mut_ptr(), buf.len() as libc::size_t).is_null() { + Err(IoError::last_error()) + } else { + Ok(Path::new(CString::new(buf.as_ptr(), false))) + } + } +} + +pub unsafe fn get_env_pairs() -> Vec<Vec<u8>> { + use c_str::CString; + + extern { + fn rust_env_pairs() -> *const *const c_char; + } + let mut environ = rust_env_pairs(); + if environ as uint == 0 { + panic!("os::env() failure getting env string from OS: {}", + os::last_os_error()); + } + let mut result = Vec::new(); + while *environ != 0 as *const _ { + let env_pair = + CString::new(*environ, false).as_bytes_no_nul().to_vec(); + result.push(env_pair); + environ = environ.offset(1); + } + result +} + +pub fn split_paths(unparsed: &[u8]) -> Vec<Path> { + unparsed.split(|b| *b == b':').map(Path::new).collect() +} + +pub fn join_paths<T: BytesContainer>(paths: &[T]) -> Result<Vec<u8>, &'static str> { + let mut joined = Vec::new(); + let sep = b':'; + + for (i, path) in paths.iter().map(|p| p.container_as_bytes()).enumerate() { + if i > 0 { joined.push(sep) } + if path.contains(&sep) { return Err("path segment contains separator `:`") } + joined.push_all(path); + } + + Ok(joined) +} + +#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] +pub fn load_self() -> Option<Vec<u8>> { + unsafe { + use libc::funcs::bsd44::*; + use libc::consts::os::extra::*; + let mut mib = vec![CTL_KERN as c_int, + KERN_PROC as c_int, + KERN_PROC_PATHNAME as c_int, + -1 as c_int]; + let mut sz: libc::size_t = 0; + let err = sysctl(mib.as_mut_ptr(), mib.len() as ::libc::c_uint, + ptr::null_mut(), &mut sz, ptr::null_mut(), + 0u as libc::size_t); + if err != 0 { return None; } + if sz == 0 { return None; } + let mut v: Vec<u8> = Vec::with_capacity(sz as uint); + let err = sysctl(mib.as_mut_ptr(), mib.len() as ::libc::c_uint, + v.as_mut_ptr() as *mut libc::c_void, &mut sz, + ptr::null_mut(), 0u as libc::size_t); + if err != 0 { return None; } + if sz == 0 { return None; } + v.set_len(sz as uint - 1); // chop off trailing NUL + Some(v) + } +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +pub fn load_self() -> Option<Vec<u8>> { + use std::io; + + match io::fs::readlink(&Path::new("/proc/self/exe")) { + Ok(path) => Some(path.into_vec()), + Err(..) => None + } +} + +#[cfg(any(target_os = "macos", target_os = "ios"))] +pub fn load_self() -> Option<Vec<u8>> { + unsafe { + use libc::funcs::extra::_NSGetExecutablePath; + let mut sz: u32 = 0; + _NSGetExecutablePath(ptr::null_mut(), &mut sz); + if sz == 0 { return None; } + let mut v: Vec<u8> = Vec::with_capacity(sz as uint); + let err = _NSGetExecutablePath(v.as_mut_ptr() as *mut i8, &mut sz); + if err != 0 { return None; } + v.set_len(sz as uint - 1); // chop off trailing NUL + Some(v) + } +} + +pub fn chdir(p: &Path) -> IoResult<()> { + p.with_c_str(|buf| { + unsafe { + match libc::chdir(buf) == (0 as c_int) { + true => Ok(()), + false => Err(IoError::last_error()), + } + } + }) +} + +pub fn page_size() -> uint { + unsafe { + libc::sysconf(libc::_SC_PAGESIZE) as uint + } +} diff --git a/src/libstd/sys/unix/pipe.rs b/src/libstd/sys/unix/pipe.rs index 4d3469a9c24..348b7cfad33 100644 --- a/src/libstd/sys/unix/pipe.rs +++ b/src/libstd/sys/unix/pipe.rs @@ -12,14 +12,14 @@ use alloc::arc::Arc; use libc; use c_str::CString; use mem; -use rustrt::mutex; -use sync::atomic; +use sync::{atomic, Mutex}; use io::{mod, IoResult, IoError}; use prelude::*; use sys::{mod, timer, retry, c, set_nonblocking, wouldblock}; use sys::fs::{fd_t, FileDesc}; use sys_common::net::*; +use sys_common::net::SocketStatus::*; use sys_common::{eof, mkerr_libc}; fn unix_socket(ty: libc::c_int) -> IoResult<fd_t> { @@ -60,12 +60,12 @@ struct Inner { // Unused on Linux, where this lock is not necessary. #[allow(dead_code)] - lock: mutex::NativeMutex + lock: Mutex<()>, } impl Inner { fn new(fd: fd_t) -> Inner { - Inner { fd: fd, lock: unsafe { mutex::NativeMutex::new() } } + Inner { fd: fd, lock: Mutex::new(()) } } } @@ -133,7 +133,7 @@ impl UnixStream { } } - fn fd(&self) -> fd_t { self.inner.fd } + pub fn fd(&self) -> fd_t { self.inner.fd } #[cfg(target_os = "linux")] fn lock_nonblocking(&self) {} @@ -150,8 +150,8 @@ impl UnixStream { pub fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> { let fd = self.fd(); - let dolock = || self.lock_nonblocking(); - let doread = |nb| unsafe { + let dolock = |&:| self.lock_nonblocking(); + let doread = |&mut: nb| unsafe { let flags = if nb {c::MSG_DONTWAIT} else {0}; libc::recv(fd, buf.as_mut_ptr() as *mut libc::c_void, @@ -163,8 +163,8 @@ impl UnixStream { pub fn write(&mut self, buf: &[u8]) -> IoResult<()> { let fd = self.fd(); - let dolock = || self.lock_nonblocking(); - let dowrite = |nb: bool, buf: *const u8, len: uint| unsafe { + let dolock = |&: | self.lock_nonblocking(); + let dowrite = |&: nb: bool, buf: *const u8, len: uint| unsafe { let flags = if nb {c::MSG_DONTWAIT} else {0}; libc::send(fd, buf as *const _, @@ -222,7 +222,7 @@ impl UnixListener { }) } - fn fd(&self) -> fd_t { self.inner.fd } + pub fn fd(&self) -> fd_t { self.inner.fd } pub fn listen(self) -> IoResult<UnixAcceptor> { match unsafe { libc::listen(self.fd(), 128) } { @@ -260,7 +260,7 @@ struct AcceptorInner { } impl UnixAcceptor { - fn fd(&self) -> fd_t { self.inner.listener.fd() } + pub fn fd(&self) -> fd_t { self.inner.listener.fd() } pub fn accept(&mut self) -> IoResult<UnixStream> { let deadline = if self.deadline == 0 {None} else {Some(self.deadline)}; diff --git a/src/libstd/sys/unix/process.rs b/src/libstd/sys/unix/process.rs index 81bc138ca91..835f4279d9b 100644 --- a/src/libstd/sys/unix/process.rs +++ b/src/libstd/sys/unix/process.rs @@ -11,7 +11,7 @@ use self::Req::*; use libc::{mod, pid_t, c_void, c_int}; use c_str::CString; -use io::{mod, IoResult, IoError}; +use io::{mod, IoResult, IoError, EndOfFile}; use mem; use os; use ptr; @@ -24,11 +24,11 @@ use hash::Hash; use sys::{mod, retry, c, wouldblock, set_nonblocking, ms_to_timeval}; use sys::fs::FileDesc; use sys_common::helper_thread::Helper; -use sys_common::{AsFileDesc, mkerr_libc, timeout}; +use sys_common::{AsInner, mkerr_libc, timeout}; pub use sys_common::ProcessConfig; -helper_init!(static HELPER: Helper<Req>) +helper_init! { static HELPER: Helper<Req> } /// The unique id of the process (this should never be negative). pub struct Process { @@ -39,6 +39,8 @@ enum Req { NewChild(libc::pid_t, Sender<ProcessExit>, u64), } +const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX"; + impl Process { pub fn id(&self) -> pid_t { self.pid @@ -56,7 +58,7 @@ impl Process { pub fn spawn<K, V, C, P>(cfg: &C, in_fd: Option<P>, out_fd: Option<P>, err_fd: Option<P>) -> IoResult<Process> - where C: ProcessConfig<K, V>, P: AsFileDesc, + where C: ProcessConfig<K, V>, P: AsInner<FileDesc>, K: BytesContainer + Eq + Hash, V: BytesContainer { use libc::funcs::posix88::unistd::{fork, dup2, close, chdir, execvp}; @@ -92,8 +94,8 @@ impl Process { mem::transmute::<&ProcessConfig<K,V>,&'static ProcessConfig<K,V>>(cfg) }; - with_envp(cfg.env(), proc(envp) { - with_argv(cfg.program(), cfg.args(), proc(argv) unsafe { + with_envp(cfg.env(), move|: envp: *const c_void| { + with_argv(cfg.program(), cfg.args(), move|: argv: *const *const libc::c_char| unsafe { let (input, mut output) = try!(sys::os::pipe()); // We may use this in the child, so perform allocations before the @@ -106,18 +108,36 @@ impl Process { if pid < 0 { return Err(super::last_error()) } else if pid > 0 { + #[inline] + fn combine(arr: &[u8]) -> i32 { + let a = arr[0] as u32; + let b = arr[1] as u32; + let c = arr[2] as u32; + let d = arr[3] as u32; + + ((a << 24) | (b << 16) | (c << 8) | (d << 0)) as i32 + } + + let p = Process{ pid: pid }; drop(output); - let mut bytes = [0, ..4]; + let mut bytes = [0, ..8]; return match input.read(&mut bytes) { - Ok(4) => { - let errno = (bytes[0] as i32 << 24) | - (bytes[1] as i32 << 16) | - (bytes[2] as i32 << 8) | - (bytes[3] as i32 << 0); + Ok(8) => { + assert!(combine(CLOEXEC_MSG_FOOTER) == combine(bytes.slice(4, 8)), + "Validation on the CLOEXEC pipe failed: {}", bytes); + let errno = combine(bytes.slice(0, 4)); + assert!(p.wait(0).is_ok(), "wait(0) should either return Ok or panic"); Err(super::decode_error(errno)) } - Err(..) => Ok(Process { pid: pid }), - Ok(..) => panic!("short read on the cloexec pipe"), + Err(ref e) if e.kind == EndOfFile => Ok(p), + Err(e) => { + assert!(p.wait(0).is_ok(), "wait(0) should either return Ok or panic"); + panic!("the CLOEXEC pipe failed: {}", e) + }, + Ok(..) => { // pipe I/O up to PIPE_BUF bytes should be atomic + assert!(p.wait(0).is_ok(), "wait(0) should either return Ok or panic"); + panic!("short read on the CLOEXEC pipe") + } }; } @@ -154,13 +174,16 @@ impl Process { let _ = libc::close(input.fd()); fn fail(output: &mut FileDesc) -> ! { - let errno = sys::os::errno(); + let errno = sys::os::errno() as u32; let bytes = [ (errno >> 24) as u8, (errno >> 16) as u8, (errno >> 8) as u8, (errno >> 0) as u8, + CLOEXEC_MSG_FOOTER[0], CLOEXEC_MSG_FOOTER[1], + CLOEXEC_MSG_FOOTER[2], CLOEXEC_MSG_FOOTER[3] ]; + // pipe I/O up to PIPE_BUF bytes should be atomic assert!(output.write(&bytes).is_ok()); unsafe { libc::_exit(1) } } @@ -183,7 +206,7 @@ impl Process { libc::open(devnull.as_ptr(), flags, 0) } Some(obj) => { - let fd = obj.as_fd().fd(); + let fd = obj.as_inner().fd(); // Leak the memory and the file descriptor. We're in the // child now an all our resources are going to be // cleaned up very soon @@ -356,8 +379,8 @@ impl Process { // wait indefinitely for a message to arrive. // // FIXME: sure would be nice to not have to scan the entire array - let min = active.iter().map(|a| *a.ref2()).enumerate().min_by(|p| { - p.val1() + let min = active.iter().map(|a| a.2).enumerate().min_by(|p| { + p.1 }); let (p, idx) = match min { Some((idx, deadline)) => { @@ -508,8 +531,11 @@ impl Process { } } -fn with_argv<T>(prog: &CString, args: &[CString], - cb: proc(*const *const libc::c_char) -> T) -> T { +fn with_argv<T,F>(prog: &CString, args: &[CString], + cb: F) + -> T + where F : FnOnce(*const *const libc::c_char) -> T +{ let mut ptrs: Vec<*const libc::c_char> = Vec::with_capacity(args.len()+1); // Convert the CStrings into an array of pointers. Note: the @@ -526,9 +552,12 @@ fn with_argv<T>(prog: &CString, args: &[CString], cb(ptrs.as_ptr()) } -fn with_envp<K, V, T>(env: Option<&collections::HashMap<K, V>>, - cb: proc(*const c_void) -> T) -> T - where K: BytesContainer + Eq + Hash, V: BytesContainer +fn with_envp<K,V,T,F>(env: Option<&collections::HashMap<K, V>>, + cb: F) + -> T + where F : FnOnce(*const c_void) -> T, + K : BytesContainer + Eq + Hash, + V : BytesContainer { // On posixy systems we can pass a char** for envp, which is a // null-terminated array of "k=v\0" strings. Since we must create @@ -541,9 +570,9 @@ fn with_envp<K, V, T>(env: Option<&collections::HashMap<K, V>>, for pair in env.iter() { let mut kv = Vec::new(); - kv.push_all(pair.ref0().container_as_bytes()); + kv.push_all(pair.0.container_as_bytes()); kv.push('=' as u8); - kv.push_all(pair.ref1().container_as_bytes()); + kv.push_all(pair.1.container_as_bytes()); kv.push(0); // terminating null tmps.push(kv); } diff --git a/src/libstd/sys/unix/rwlock.rs b/src/libstd/sys/unix/rwlock.rs new file mode 100644 index 00000000000..0d63ff14ff2 --- /dev/null +++ b/src/libstd/sys/unix/rwlock.rs @@ -0,0 +1,57 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use cell::UnsafeCell; +use sys::sync as ffi; + +pub struct RWLock { inner: UnsafeCell<ffi::pthread_rwlock_t> } + +pub const RWLOCK_INIT: RWLock = RWLock { + inner: UnsafeCell { value: ffi::PTHREAD_RWLOCK_INITIALIZER }, +}; + +impl RWLock { + #[inline] + pub unsafe fn new() -> RWLock { + // Might be moved and address is changing it is better to avoid + // initialization of potentially opaque OS data before it landed + RWLOCK_INIT + } + #[inline] + pub unsafe fn read(&self) { + let r = ffi::pthread_rwlock_rdlock(self.inner.get()); + debug_assert_eq!(r, 0); + } + #[inline] + pub unsafe fn try_read(&self) -> bool { + ffi::pthread_rwlock_tryrdlock(self.inner.get()) == 0 + } + #[inline] + pub unsafe fn write(&self) { + let r = ffi::pthread_rwlock_wrlock(self.inner.get()); + debug_assert_eq!(r, 0); + } + #[inline] + pub unsafe fn try_write(&self) -> bool { + ffi::pthread_rwlock_trywrlock(self.inner.get()) == 0 + } + #[inline] + pub unsafe fn read_unlock(&self) { + let r = ffi::pthread_rwlock_unlock(self.inner.get()); + debug_assert_eq!(r, 0); + } + #[inline] + pub unsafe fn write_unlock(&self) { self.read_unlock() } + #[inline] + pub unsafe fn destroy(&self) { + let r = ffi::pthread_rwlock_destroy(self.inner.get()); + debug_assert_eq!(r, 0); + } +} diff --git a/src/libstd/sys/unix/stack_overflow.rs b/src/libstd/sys/unix/stack_overflow.rs new file mode 100644 index 00000000000..340f9514241 --- /dev/null +++ b/src/libstd/sys/unix/stack_overflow.rs @@ -0,0 +1,277 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use libc; +use core::prelude::*; +use self::imp::{make_handler, drop_handler}; + +pub use self::imp::{init, cleanup}; + +pub struct Handler { + _data: *mut libc::c_void +} + +impl Handler { + pub unsafe fn new() -> Handler { + make_handler() + } +} + +impl Drop for Handler { + fn drop(&mut self) { + unsafe { + drop_handler(self); + } + } +} + +#[cfg(any(target_os = "linux", target_os = "macos"))] +mod imp { + use core::prelude::*; + use sys_common::stack; + + use super::Handler; + use rt::util::report_overflow; + use mem; + use ptr; + use intrinsics; + use self::signal::{siginfo, sigaction, SIGBUS, SIG_DFL, + SA_SIGINFO, SA_ONSTACK, sigaltstack, + SIGSTKSZ}; + use libc; + use libc::funcs::posix88::mman::{mmap, munmap}; + use libc::consts::os::posix88::{SIGSEGV, + PROT_READ, + PROT_WRITE, + MAP_PRIVATE, + MAP_ANON, + MAP_FAILED}; + + use sys_common::thread_info; + + + // This is initialized in init() and only read from after + static mut PAGE_SIZE: uint = 0; + + #[no_stack_check] + unsafe extern fn signal_handler(signum: libc::c_int, + info: *mut siginfo, + _data: *mut libc::c_void) { + + // We can not return from a SIGSEGV or SIGBUS signal. + // See: https://www.gnu.org/software/libc/manual/html_node/Handler-Returns.html + + unsafe fn term(signum: libc::c_int) -> ! { + use core::mem::transmute; + + signal(signum, transmute(SIG_DFL)); + raise(signum); + intrinsics::abort(); + } + + // We're calling into functions with stack checks + stack::record_sp_limit(0); + + let guard = thread_info::stack_guard(); + let addr = (*info).si_addr as uint; + + if guard == 0 || addr < guard - PAGE_SIZE || addr >= guard { + term(signum); + } + + report_overflow(); + + intrinsics::abort() + } + + static mut MAIN_ALTSTACK: *mut libc::c_void = 0 as *mut libc::c_void; + + pub unsafe fn init() { + let psize = libc::sysconf(libc::consts::os::sysconf::_SC_PAGESIZE); + if psize == -1 { + panic!("failed to get page size"); + } + + PAGE_SIZE = psize as uint; + + let mut action: sigaction = mem::zeroed(); + action.sa_flags = SA_SIGINFO | SA_ONSTACK; + action.sa_sigaction = signal_handler as sighandler_t; + sigaction(SIGSEGV, &action, ptr::null_mut()); + sigaction(SIGBUS, &action, ptr::null_mut()); + + let handler = make_handler(); + MAIN_ALTSTACK = handler._data; + mem::forget(handler); + } + + pub unsafe fn cleanup() { + Handler { _data: MAIN_ALTSTACK }; + } + + pub unsafe fn make_handler() -> Handler { + let alt_stack = mmap(ptr::null_mut(), + signal::SIGSTKSZ, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, + -1, + 0); + if alt_stack == MAP_FAILED { + panic!("failed to allocate an alternative stack"); + } + + let mut stack: sigaltstack = mem::zeroed(); + + stack.ss_sp = alt_stack; + stack.ss_flags = 0; + stack.ss_size = SIGSTKSZ; + + sigaltstack(&stack, ptr::null_mut()); + + Handler { _data: alt_stack } + } + + pub unsafe fn drop_handler(handler: &mut Handler) { + munmap(handler._data, SIGSTKSZ); + } + + type sighandler_t = *mut libc::c_void; + + #[cfg(any(all(target_os = "linux", target_arch = "x86"), // may not match + all(target_os = "linux", target_arch = "x86_64"), + all(target_os = "linux", target_arch = "arm"), // may not match + all(target_os = "linux", target_arch = "mips"), // may not match + all(target_os = "linux", target_arch = "mipsel"), // may not match + target_os = "android"))] // may not match + mod signal { + use libc; + use super::sighandler_t; + + pub static SA_ONSTACK: libc::c_int = 0x08000000; + pub static SA_SIGINFO: libc::c_int = 0x00000004; + pub static SIGBUS: libc::c_int = 7; + + pub static SIGSTKSZ: libc::size_t = 8192; + + pub static SIG_DFL: sighandler_t = 0i as sighandler_t; + + // This definition is not as accurate as it could be, {si_addr} is + // actually a giant union. Currently we're only interested in that field, + // however. + #[repr(C)] + pub struct siginfo { + si_signo: libc::c_int, + si_errno: libc::c_int, + si_code: libc::c_int, + pub si_addr: *mut libc::c_void + } + + #[repr(C)] + pub struct sigaction { + pub sa_sigaction: sighandler_t, + pub sa_mask: sigset_t, + pub sa_flags: libc::c_int, + sa_restorer: *mut libc::c_void, + } + + #[cfg(target_word_size = "32")] + #[repr(C)] + pub struct sigset_t { + __val: [libc::c_ulong, ..32], + } + #[cfg(target_word_size = "64")] + #[repr(C)] + pub struct sigset_t { + __val: [libc::c_ulong, ..16], + } + + #[repr(C)] + pub struct sigaltstack { + pub ss_sp: *mut libc::c_void, + pub ss_flags: libc::c_int, + pub ss_size: libc::size_t + } + + } + + #[cfg(target_os = "macos")] + mod signal { + use libc; + use super::sighandler_t; + + pub const SA_ONSTACK: libc::c_int = 0x0001; + pub const SA_SIGINFO: libc::c_int = 0x0040; + pub const SIGBUS: libc::c_int = 10; + + pub const SIGSTKSZ: libc::size_t = 131072; + + pub const SIG_DFL: sighandler_t = 0i as sighandler_t; + + pub type sigset_t = u32; + + // This structure has more fields, but we're not all that interested in + // them. + #[repr(C)] + pub struct siginfo { + pub si_signo: libc::c_int, + pub si_errno: libc::c_int, + pub si_code: libc::c_int, + pub pid: libc::pid_t, + pub uid: libc::uid_t, + pub status: libc::c_int, + pub si_addr: *mut libc::c_void + } + + #[repr(C)] + pub struct sigaltstack { + pub ss_sp: *mut libc::c_void, + pub ss_size: libc::size_t, + pub ss_flags: libc::c_int + } + + #[repr(C)] + pub struct sigaction { + pub sa_sigaction: sighandler_t, + pub sa_mask: sigset_t, + pub sa_flags: libc::c_int, + } + } + + extern { + pub fn signal(signum: libc::c_int, handler: sighandler_t) -> sighandler_t; + pub fn raise(signum: libc::c_int) -> libc::c_int; + + pub fn sigaction(signum: libc::c_int, + act: *const sigaction, + oldact: *mut sigaction) -> libc::c_int; + + pub fn sigaltstack(ss: *const sigaltstack, + oss: *mut sigaltstack) -> libc::c_int; + } +} + +#[cfg(not(any(target_os = "linux", + target_os = "macos")))] +mod imp { + use libc; + + pub unsafe fn init() { + } + + pub unsafe fn cleanup() { + } + + pub unsafe fn make_handler() -> super::Handler { + super::Handler { _data: 0i as *mut libc::c_void } + } + + pub unsafe fn drop_handler(_handler: &mut super::Handler) { + } +} diff --git a/src/libstd/sys/unix/sync.rs b/src/libstd/sys/unix/sync.rs new file mode 100644 index 00000000000..007826b4b9d --- /dev/null +++ b/src/libstd/sys/unix/sync.rs @@ -0,0 +1,208 @@ +// 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. + +#![allow(bad_style)] + +use libc; + +pub use self::os::{PTHREAD_MUTEX_INITIALIZER, pthread_mutex_t}; +pub use self::os::{PTHREAD_COND_INITIALIZER, pthread_cond_t}; +pub use self::os::{PTHREAD_RWLOCK_INITIALIZER, pthread_rwlock_t}; + +extern { + // mutexes + pub fn pthread_mutex_destroy(lock: *mut pthread_mutex_t) -> libc::c_int; + pub fn pthread_mutex_lock(lock: *mut pthread_mutex_t) -> libc::c_int; + pub fn pthread_mutex_trylock(lock: *mut pthread_mutex_t) -> libc::c_int; + pub fn pthread_mutex_unlock(lock: *mut pthread_mutex_t) -> libc::c_int; + + // cvars + pub fn pthread_cond_wait(cond: *mut pthread_cond_t, + lock: *mut pthread_mutex_t) -> libc::c_int; + pub fn pthread_cond_timedwait(cond: *mut pthread_cond_t, + lock: *mut pthread_mutex_t, + abstime: *const libc::timespec) -> libc::c_int; + pub fn pthread_cond_signal(cond: *mut pthread_cond_t) -> libc::c_int; + pub fn pthread_cond_broadcast(cond: *mut pthread_cond_t) -> libc::c_int; + pub fn pthread_cond_destroy(cond: *mut pthread_cond_t) -> libc::c_int; + pub fn gettimeofday(tp: *mut libc::timeval, + tz: *mut libc::c_void) -> libc::c_int; + + // rwlocks + pub fn pthread_rwlock_destroy(lock: *mut pthread_rwlock_t) -> libc::c_int; + pub fn pthread_rwlock_rdlock(lock: *mut pthread_rwlock_t) -> libc::c_int; + pub fn pthread_rwlock_tryrdlock(lock: *mut pthread_rwlock_t) -> libc::c_int; + pub fn pthread_rwlock_wrlock(lock: *mut pthread_rwlock_t) -> libc::c_int; + pub fn pthread_rwlock_trywrlock(lock: *mut pthread_rwlock_t) -> libc::c_int; + pub fn pthread_rwlock_unlock(lock: *mut pthread_rwlock_t) -> libc::c_int; +} + +#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] +mod os { + use libc; + + pub type pthread_mutex_t = *mut libc::c_void; + pub type pthread_cond_t = *mut libc::c_void; + pub type pthread_rwlock_t = *mut libc::c_void; + + pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = 0 as *mut _; + pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = 0 as *mut _; + pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = 0 as *mut _; +} + +#[cfg(any(target_os = "macos", target_os = "ios"))] +mod os { + use libc; + + #[cfg(target_arch = "x86_64")] + const __PTHREAD_MUTEX_SIZE__: uint = 56; + #[cfg(any(target_arch = "x86", + target_arch = "arm"))] + const __PTHREAD_MUTEX_SIZE__: uint = 40; + + #[cfg(target_arch = "x86_64")] + const __PTHREAD_COND_SIZE__: uint = 40; + #[cfg(any(target_arch = "x86", + target_arch = "arm"))] + const __PTHREAD_COND_SIZE__: uint = 24; + + #[cfg(target_arch = "x86_64")] + const __PTHREAD_RWLOCK_SIZE__: uint = 192; + #[cfg(any(target_arch = "x86", + target_arch = "arm"))] + const __PTHREAD_RWLOCK_SIZE__: uint = 124; + + const _PTHREAD_MUTEX_SIG_INIT: libc::c_long = 0x32AAABA7; + const _PTHREAD_COND_SIG_INIT: libc::c_long = 0x3CB0B1BB; + const _PTHREAD_RWLOCK_SIG_INIT: libc::c_long = 0x2DA8B3B4; + + #[repr(C)] + pub struct pthread_mutex_t { + __sig: libc::c_long, + __opaque: [u8, ..__PTHREAD_MUTEX_SIZE__], + } + #[repr(C)] + pub struct pthread_cond_t { + __sig: libc::c_long, + __opaque: [u8, ..__PTHREAD_COND_SIZE__], + } + #[repr(C)] + pub struct pthread_rwlock_t { + __sig: libc::c_long, + __opaque: [u8, ..__PTHREAD_RWLOCK_SIZE__], + } + + pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t { + __sig: _PTHREAD_MUTEX_SIG_INIT, + __opaque: [0, ..__PTHREAD_MUTEX_SIZE__], + }; + pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t { + __sig: _PTHREAD_COND_SIG_INIT, + __opaque: [0, ..__PTHREAD_COND_SIZE__], + }; + pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = pthread_rwlock_t { + __sig: _PTHREAD_RWLOCK_SIG_INIT, + __opaque: [0, ..__PTHREAD_RWLOCK_SIZE__], + }; +} + +#[cfg(target_os = "linux")] +mod os { + use libc; + + // minus 8 because we have an 'align' field + #[cfg(target_arch = "x86_64")] + const __SIZEOF_PTHREAD_MUTEX_T: uint = 40 - 8; + #[cfg(any(target_arch = "x86", + target_arch = "arm", + target_arch = "mips", + target_arch = "mipsel"))] + const __SIZEOF_PTHREAD_MUTEX_T: uint = 24 - 8; + + #[cfg(any(target_arch = "x86_64", + target_arch = "x86", + target_arch = "arm", + target_arch = "mips", + target_arch = "mipsel"))] + const __SIZEOF_PTHREAD_COND_T: uint = 48 - 8; + + #[cfg(target_arch = "x86_64")] + const __SIZEOF_PTHREAD_RWLOCK_T: uint = 56 - 8; + + #[cfg(any(target_arch = "x86", + target_arch = "arm", + target_arch = "mips", + target_arch = "mipsel"))] + const __SIZEOF_PTHREAD_RWLOCK_T: uint = 32 - 8; + + #[repr(C)] + pub struct pthread_mutex_t { + __align: libc::c_longlong, + size: [u8, ..__SIZEOF_PTHREAD_MUTEX_T], + } + #[repr(C)] + pub struct pthread_cond_t { + __align: libc::c_longlong, + size: [u8, ..__SIZEOF_PTHREAD_COND_T], + } + #[repr(C)] + pub struct pthread_rwlock_t { + __align: libc::c_longlong, + size: [u8, ..__SIZEOF_PTHREAD_RWLOCK_T], + } + + pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t { + __align: 0, + size: [0, ..__SIZEOF_PTHREAD_MUTEX_T], + }; + pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t { + __align: 0, + size: [0, ..__SIZEOF_PTHREAD_COND_T], + }; + pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = pthread_rwlock_t { + __align: 0, + size: [0, ..__SIZEOF_PTHREAD_RWLOCK_T], + }; +} +#[cfg(target_os = "android")] +mod os { + use libc; + + #[repr(C)] + pub struct pthread_mutex_t { value: libc::c_int } + #[repr(C)] + pub struct pthread_cond_t { value: libc::c_int } + #[repr(C)] + pub struct pthread_rwlock_t { + lock: pthread_mutex_t, + cond: pthread_cond_t, + numLocks: libc::c_int, + writerThreadId: libc::c_int, + pendingReaders: libc::c_int, + pendingWriters: libc::c_int, + reserved: [*mut libc::c_void, ..4], + } + + pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t { + value: 0, + }; + pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t { + value: 0, + }; + pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = pthread_rwlock_t { + lock: PTHREAD_MUTEX_INITIALIZER, + cond: PTHREAD_COND_INITIALIZER, + numLocks: 0, + writerThreadId: 0, + pendingReaders: 0, + pendingWriters: 0, + reserved: [0 as *mut _, ..4], + }; +} diff --git a/src/libstd/sys/unix/tcp.rs b/src/libstd/sys/unix/tcp.rs index 00643ac0a79..5c99ad1e0ce 100644 --- a/src/libstd/sys/unix/tcp.rs +++ b/src/libstd/sys/unix/tcp.rs @@ -20,7 +20,8 @@ use sys::fs::FileDesc; use sys::{set_nonblocking, wouldblock}; use sys; use sys_common; -use sys_common::net::*; +use sys_common::net; +use sys_common::net::SocketStatus::Readable; pub use sys_common::net::TcpStream; @@ -34,17 +35,19 @@ pub struct TcpListener { impl TcpListener { pub fn bind(addr: ip::SocketAddr) -> IoResult<TcpListener> { - let fd = try!(socket(addr, libc::SOCK_STREAM)); + let fd = try!(net::socket(addr, libc::SOCK_STREAM)); let ret = TcpListener { inner: FileDesc::new(fd, true) }; let mut storage = unsafe { mem::zeroed() }; - let len = addr_to_sockaddr(addr, &mut storage); + let len = net::addr_to_sockaddr(addr, &mut storage); let addrp = &storage as *const _ as *const libc::sockaddr; // On platforms with Berkeley-derived sockets, this allows // to quickly rebind a socket, without needing to wait for // the OS to clean up the previous one. - try!(setsockopt(fd, libc::SOL_SOCKET, libc::SO_REUSEADDR, 1 as libc::c_int)); + try!(net::setsockopt(fd, libc::SOL_SOCKET, + libc::SO_REUSEADDR, + 1 as libc::c_int)); match unsafe { libc::bind(fd, addrp, len) } { @@ -77,7 +80,7 @@ impl TcpListener { } pub fn socket_name(&mut self) -> IoResult<ip::SocketAddr> { - sockname(self.fd(), libc::getsockname) + net::sockname(self.fd(), libc::getsockname) } } @@ -121,7 +124,7 @@ impl TcpAcceptor { -1 => return Err(last_net_error()), fd => return Ok(TcpStream::new(fd as sock_t)), } - try!(await(&[self.fd(), self.inner.reader.fd()], + try!(net::await(&[self.fd(), self.inner.reader.fd()], deadline, Readable)); } @@ -129,7 +132,7 @@ impl TcpAcceptor { } pub fn socket_name(&mut self) -> IoResult<ip::SocketAddr> { - sockname(self.fd(), libc::getsockname) + net::sockname(self.fd(), libc::getsockname) } pub fn set_timeout(&mut self, timeout: Option<u64>) { diff --git a/src/libstd/sys/unix/thread.rs b/src/libstd/sys/unix/thread.rs new file mode 100644 index 00000000000..2416b64f98f --- /dev/null +++ b/src/libstd/sys/unix/thread.rs @@ -0,0 +1,271 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::prelude::*; + +use boxed::Box; +use cmp; +use mem; +use ptr; +use libc::consts::os::posix01::{PTHREAD_CREATE_JOINABLE, PTHREAD_STACK_MIN}; +use libc; +use thunk::Thunk; + +use sys_common::stack::RED_ZONE; +use sys_common::thread::*; + +pub type rust_thread = libc::pthread_t; +pub type rust_thread_return = *mut u8; +pub type StartFn = extern "C" fn(*mut libc::c_void) -> rust_thread_return; + +#[no_stack_check] +pub extern fn thread_start(main: *mut libc::c_void) -> rust_thread_return { + return start_thread(main); +} + +#[cfg(all(not(target_os = "linux"), not(target_os = "macos")))] +pub mod guard { + pub unsafe fn current() -> uint { + 0 + } + + pub unsafe fn main() -> uint { + 0 + } + + pub unsafe fn init() { + } +} + +#[cfg(any(target_os = "linux", target_os = "macos"))] +pub mod guard { + use super::*; + #[cfg(any(target_os = "linux", target_os = "android"))] + use mem; + #[cfg(any(target_os = "linux", target_os = "android"))] + use ptr; + use libc; + use libc::funcs::posix88::mman::{mmap}; + use libc::consts::os::posix88::{PROT_NONE, + MAP_PRIVATE, + MAP_ANON, + MAP_FAILED, + MAP_FIXED}; + + // These are initialized in init() and only read from after + static mut PAGE_SIZE: uint = 0; + static mut GUARD_PAGE: uint = 0; + + #[cfg(target_os = "macos")] + unsafe fn get_stack_start() -> *mut libc::c_void { + current() as *mut libc::c_void + } + + #[cfg(any(target_os = "linux", target_os = "android"))] + unsafe fn get_stack_start() -> *mut libc::c_void { + let mut attr: libc::pthread_attr_t = mem::zeroed(); + if pthread_getattr_np(pthread_self(), &mut attr) != 0 { + panic!("failed to get thread attributes"); + } + let mut stackaddr = ptr::null_mut(); + let mut stacksize = 0; + if pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize) != 0 { + panic!("failed to get stack information"); + } + if pthread_attr_destroy(&mut attr) != 0 { + panic!("failed to destroy thread attributes"); + } + stackaddr + } + + pub unsafe fn init() { + let psize = libc::sysconf(libc::consts::os::sysconf::_SC_PAGESIZE); + if psize == -1 { + panic!("failed to get page size"); + } + + PAGE_SIZE = psize as uint; + + let stackaddr = get_stack_start(); + + // Rellocate the last page of the stack. + // This ensures SIGBUS will be raised on + // stack overflow. + let result = mmap(stackaddr, + PAGE_SIZE as libc::size_t, + PROT_NONE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED, + -1, + 0); + + if result != stackaddr || result == MAP_FAILED { + panic!("failed to allocate a guard page"); + } + + let offset = if cfg!(target_os = "linux") { + 2 + } else { + 1 + }; + + GUARD_PAGE = stackaddr as uint + offset * PAGE_SIZE; + } + + pub unsafe fn main() -> uint { + GUARD_PAGE + } + + #[cfg(target_os = "macos")] + pub unsafe fn current() -> uint { + (pthread_get_stackaddr_np(pthread_self()) as libc::size_t - + pthread_get_stacksize_np(pthread_self())) as uint + } + + #[cfg(any(target_os = "linux", target_os = "android"))] + pub unsafe fn current() -> uint { + let mut attr: libc::pthread_attr_t = mem::zeroed(); + if pthread_getattr_np(pthread_self(), &mut attr) != 0 { + panic!("failed to get thread attributes"); + } + let mut guardsize = 0; + if pthread_attr_getguardsize(&attr, &mut guardsize) != 0 { + panic!("failed to get stack guard page"); + } + if guardsize == 0 { + panic!("there is no guard page"); + } + let mut stackaddr = ptr::null_mut(); + let mut stacksize = 0; + if pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize) != 0 { + panic!("failed to get stack information"); + } + if pthread_attr_destroy(&mut attr) != 0 { + panic!("failed to destroy thread attributes"); + } + + stackaddr as uint + guardsize as uint + } +} + +pub unsafe fn create(stack: uint, p: Thunk) -> rust_thread { + let mut native: libc::pthread_t = mem::zeroed(); + let mut attr: libc::pthread_attr_t = mem::zeroed(); + assert_eq!(pthread_attr_init(&mut attr), 0); + assert_eq!(pthread_attr_setdetachstate(&mut attr, + PTHREAD_CREATE_JOINABLE), 0); + + // Reserve room for the red zone, the runtime's stack of last resort. + let stack_size = cmp::max(stack, RED_ZONE + min_stack_size(&attr) as uint); + match pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t) { + 0 => { + }, + libc::EINVAL => { + // EINVAL means |stack_size| is either too small or not a + // multiple of the system page size. Because it's definitely + // >= PTHREAD_STACK_MIN, it must be an alignment issue. + // Round up to the nearest page and try again. + let page_size = libc::sysconf(libc::_SC_PAGESIZE) as uint; + let stack_size = (stack_size + page_size - 1) & + (-(page_size as int - 1) as uint - 1); + assert_eq!(pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t), 0); + }, + errno => { + // This cannot really happen. + panic!("pthread_attr_setstacksize() error: {}", errno); + }, + }; + + let arg: *mut libc::c_void = mem::transmute(box p); // must box since sizeof(p)=2*uint + let ret = pthread_create(&mut native, &attr, thread_start, arg); + assert_eq!(pthread_attr_destroy(&mut attr), 0); + + if ret != 0 { + // be sure to not leak the closure + let _p: Box<Box<FnOnce()+Send>> = mem::transmute(arg); + panic!("failed to spawn native thread: {}", ret); + } + native +} + +pub unsafe fn join(native: rust_thread) { + assert_eq!(pthread_join(native, ptr::null_mut()), 0); +} + +pub unsafe fn detach(native: rust_thread) { + assert_eq!(pthread_detach(native), 0); +} + +pub unsafe fn yield_now() { assert_eq!(sched_yield(), 0); } +// glibc >= 2.15 has a __pthread_get_minstack() function that returns +// PTHREAD_STACK_MIN plus however many bytes are needed for thread-local +// storage. We need that information to avoid blowing up when a small stack +// is created in an application with big thread-local storage requirements. +// See #6233 for rationale and details. +// +// Link weakly to the symbol for compatibility with older versions of glibc. +// Assumes that we've been dynamically linked to libpthread but that is +// currently always the case. Note that you need to check that the symbol +// is non-null before calling it! +#[cfg(target_os = "linux")] +fn min_stack_size(attr: *const libc::pthread_attr_t) -> libc::size_t { + type F = unsafe extern "C" fn(*const libc::pthread_attr_t) -> libc::size_t; + extern { + #[linkage = "extern_weak"] + static __pthread_get_minstack: *const (); + } + if __pthread_get_minstack.is_null() { + PTHREAD_STACK_MIN + } else { + unsafe { mem::transmute::<*const (), F>(__pthread_get_minstack)(attr) } + } +} + +// __pthread_get_minstack() is marked as weak but extern_weak linkage is +// not supported on OS X, hence this kludge... +#[cfg(not(target_os = "linux"))] +fn min_stack_size(_: *const libc::pthread_attr_t) -> libc::size_t { + PTHREAD_STACK_MIN +} + +#[cfg(any(target_os = "linux"))] +extern { + pub fn pthread_self() -> libc::pthread_t; + pub fn pthread_getattr_np(native: libc::pthread_t, + attr: *mut libc::pthread_attr_t) -> libc::c_int; + pub fn pthread_attr_getguardsize(attr: *const libc::pthread_attr_t, + guardsize: *mut libc::size_t) -> libc::c_int; + pub fn pthread_attr_getstack(attr: *const libc::pthread_attr_t, + stackaddr: *mut *mut libc::c_void, + stacksize: *mut libc::size_t) -> libc::c_int; +} + +#[cfg(target_os = "macos")] +extern { + pub fn pthread_self() -> libc::pthread_t; + pub fn pthread_get_stackaddr_np(thread: libc::pthread_t) -> *mut libc::c_void; + pub fn pthread_get_stacksize_np(thread: libc::pthread_t) -> libc::size_t; +} + +extern { + fn pthread_create(native: *mut libc::pthread_t, + attr: *const libc::pthread_attr_t, + f: StartFn, + value: *mut libc::c_void) -> libc::c_int; + fn pthread_join(native: libc::pthread_t, + value: *mut *mut libc::c_void) -> libc::c_int; + fn pthread_attr_init(attr: *mut libc::pthread_attr_t) -> libc::c_int; + pub fn pthread_attr_destroy(attr: *mut libc::pthread_attr_t) -> libc::c_int; + fn pthread_attr_setstacksize(attr: *mut libc::pthread_attr_t, + stack_size: libc::size_t) -> libc::c_int; + fn pthread_attr_setdetachstate(attr: *mut libc::pthread_attr_t, + state: libc::c_int) -> libc::c_int; + fn pthread_detach(thread: libc::pthread_t) -> libc::c_int; + fn sched_yield() -> libc::c_int; +} diff --git a/src/libstd/sys/unix/thread_local.rs b/src/libstd/sys/unix/thread_local.rs new file mode 100644 index 00000000000..b300e93eeb6 --- /dev/null +++ b/src/libstd/sys/unix/thread_local.rs @@ -0,0 +1,52 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use prelude::*; +use libc::c_int; + +pub type Key = pthread_key_t; + +#[inline] +pub unsafe fn create(dtor: Option<unsafe extern fn(*mut u8)>) -> Key { + let mut key = 0; + assert_eq!(pthread_key_create(&mut key, dtor), 0); + return key; +} + +#[inline] +pub unsafe fn set(key: Key, value: *mut u8) { + let r = pthread_setspecific(key, value); + debug_assert_eq!(r, 0); +} + +#[inline] +pub unsafe fn get(key: Key) -> *mut u8 { + pthread_getspecific(key) +} + +#[inline] +pub unsafe fn destroy(key: Key) { + let r = pthread_key_delete(key); + debug_assert_eq!(r, 0); +} + +#[cfg(target_os = "macos")] +type pthread_key_t = ::libc::c_ulong; + +#[cfg(not(target_os = "macos"))] +type pthread_key_t = ::libc::c_uint; + +extern { + fn pthread_key_create(key: *mut pthread_key_t, + dtor: Option<unsafe extern fn(*mut u8)>) -> c_int; + fn pthread_key_delete(key: pthread_key_t) -> c_int; + fn pthread_getspecific(key: pthread_key_t) -> *mut u8; + fn pthread_setspecific(key: pthread_key_t, value: *mut u8) -> c_int; +} diff --git a/src/libstd/sys/unix/timer.rs b/src/libstd/sys/unix/timer.rs index 6ebbedb8e90..fe393b81e3d 100644 --- a/src/libstd/sys/unix/timer.rs +++ b/src/libstd/sys/unix/timer.rs @@ -46,7 +46,7 @@ //! //! Note that all time units in this file are in *milliseconds*. -pub use self::Req::*; +use self::Req::*; use libc; use mem; @@ -60,7 +60,7 @@ use sys_common::helper_thread::Helper; use prelude::*; use io::IoResult; -helper_init!(static HELPER: Helper<Req>) +helper_init! { static HELPER: Helper<Req> } pub trait Callback { fn call(&mut self); diff --git a/src/libstd/sys/windows/backtrace.rs b/src/libstd/sys/windows/backtrace.rs new file mode 100644 index 00000000000..42c8f7705e1 --- /dev/null +++ b/src/libstd/sys/windows/backtrace.rs @@ -0,0 +1,371 @@ +// 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. +/// As always, windows has something very different than unix, we mainly want +/// to avoid having to depend too much on libunwind for windows. +/// +/// If you google around, you'll find a fair bit of references to built-in +/// functions to get backtraces on windows. It turns out that most of these are +/// in an external library called dbghelp. I was unable to find this library +/// via `-ldbghelp`, but it is apparently normal to do the `dlopen` equivalent +/// of it. +/// +/// You'll also find that there's a function called CaptureStackBackTrace +/// mentioned frequently (which is also easy to use), but sadly I didn't have a +/// copy of that function in my mingw install (maybe it was broken?). Instead, +/// this takes the route of using StackWalk64 in order to walk the stack. + +use c_str::CString; +use intrinsics; +use io::{IoResult, Writer}; +use libc; +use mem; +use ops::Drop; +use option::Option::{Some, None}; +use path::Path; +use result::Result::{Ok, Err}; +use sync::{StaticMutex, MUTEX_INIT}; +use slice::SliceExt; +use str::StrExt; +use dynamic_lib::DynamicLibrary; + +use sys_common::backtrace::*; + +#[allow(non_snake_case)] +extern "system" { + fn GetCurrentProcess() -> libc::HANDLE; + fn GetCurrentThread() -> libc::HANDLE; + fn RtlCaptureContext(ctx: *mut arch::CONTEXT); +} + +type SymFromAddrFn = + extern "system" fn(libc::HANDLE, u64, *mut u64, + *mut SYMBOL_INFO) -> libc::BOOL; +type SymInitializeFn = + extern "system" fn(libc::HANDLE, *mut libc::c_void, + libc::BOOL) -> libc::BOOL; +type SymCleanupFn = + extern "system" fn(libc::HANDLE) -> libc::BOOL; + +type StackWalk64Fn = + extern "system" fn(libc::DWORD, libc::HANDLE, libc::HANDLE, + *mut STACKFRAME64, *mut arch::CONTEXT, + *mut libc::c_void, *mut libc::c_void, + *mut libc::c_void, *mut libc::c_void) -> libc::BOOL; + +const MAX_SYM_NAME: uint = 2000; +const IMAGE_FILE_MACHINE_I386: libc::DWORD = 0x014c; +const IMAGE_FILE_MACHINE_IA64: libc::DWORD = 0x0200; +const IMAGE_FILE_MACHINE_AMD64: libc::DWORD = 0x8664; + +#[repr(C)] +struct SYMBOL_INFO { + SizeOfStruct: libc::c_ulong, + TypeIndex: libc::c_ulong, + Reserved: [u64, ..2], + Index: libc::c_ulong, + Size: libc::c_ulong, + ModBase: u64, + Flags: libc::c_ulong, + Value: u64, + Address: u64, + Register: libc::c_ulong, + Scope: libc::c_ulong, + Tag: libc::c_ulong, + NameLen: libc::c_ulong, + MaxNameLen: libc::c_ulong, + // note that windows has this as 1, but it basically just means that + // the name is inline at the end of the struct. For us, we just bump + // the struct size up to MAX_SYM_NAME. + Name: [libc::c_char, ..MAX_SYM_NAME], +} + + +#[repr(C)] +enum ADDRESS_MODE { + AddrMode1616, + AddrMode1632, + AddrModeReal, + AddrModeFlat, +} + +struct ADDRESS64 { + Offset: u64, + Segment: u16, + Mode: ADDRESS_MODE, +} + +struct STACKFRAME64 { + AddrPC: ADDRESS64, + AddrReturn: ADDRESS64, + AddrFrame: ADDRESS64, + AddrStack: ADDRESS64, + AddrBStore: ADDRESS64, + FuncTableEntry: *mut libc::c_void, + Params: [u64, ..4], + Far: libc::BOOL, + Virtual: libc::BOOL, + Reserved: [u64, ..3], + KdHelp: KDHELP64, +} + +struct KDHELP64 { + Thread: u64, + ThCallbackStack: libc::DWORD, + ThCallbackBStore: libc::DWORD, + NextCallback: libc::DWORD, + FramePointer: libc::DWORD, + KiCallUserMode: u64, + KeUserCallbackDispatcher: u64, + SystemRangeStart: u64, + KiUserExceptionDispatcher: u64, + StackBase: u64, + StackLimit: u64, + Reserved: [u64, ..5], +} + +#[cfg(target_arch = "x86")] +mod arch { + use libc; + + const MAXIMUM_SUPPORTED_EXTENSION: uint = 512; + + #[repr(C)] + pub struct CONTEXT { + ContextFlags: libc::DWORD, + Dr0: libc::DWORD, + Dr1: libc::DWORD, + Dr2: libc::DWORD, + Dr3: libc::DWORD, + Dr6: libc::DWORD, + Dr7: libc::DWORD, + FloatSave: FLOATING_SAVE_AREA, + SegGs: libc::DWORD, + SegFs: libc::DWORD, + SegEs: libc::DWORD, + SegDs: libc::DWORD, + Edi: libc::DWORD, + Esi: libc::DWORD, + Ebx: libc::DWORD, + Edx: libc::DWORD, + Ecx: libc::DWORD, + Eax: libc::DWORD, + Ebp: libc::DWORD, + Eip: libc::DWORD, + SegCs: libc::DWORD, + EFlags: libc::DWORD, + Esp: libc::DWORD, + SegSs: libc::DWORD, + ExtendedRegisters: [u8, ..MAXIMUM_SUPPORTED_EXTENSION], + } + + #[repr(C)] + pub struct FLOATING_SAVE_AREA { + ControlWord: libc::DWORD, + StatusWord: libc::DWORD, + TagWord: libc::DWORD, + ErrorOffset: libc::DWORD, + ErrorSelector: libc::DWORD, + DataOffset: libc::DWORD, + DataSelector: libc::DWORD, + RegisterArea: [u8, ..80], + Cr0NpxState: libc::DWORD, + } + + pub fn init_frame(frame: &mut super::STACKFRAME64, + ctx: &CONTEXT) -> libc::DWORD { + frame.AddrPC.Offset = ctx.Eip as u64; + frame.AddrPC.Mode = super::ADDRESS_MODE::AddrModeFlat; + frame.AddrStack.Offset = ctx.Esp as u64; + frame.AddrStack.Mode = super::ADDRESS_MODE::AddrModeFlat; + frame.AddrFrame.Offset = ctx.Ebp as u64; + frame.AddrFrame.Mode = super::ADDRESS_MODE::AddrModeFlat; + super::IMAGE_FILE_MACHINE_I386 + } +} + +#[cfg(target_arch = "x86_64")] +mod arch { + use libc::{c_longlong, c_ulonglong}; + use libc::types::os::arch::extra::{WORD, DWORD, DWORDLONG}; + use simd; + + #[repr(C)] + pub struct CONTEXT { + _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte + P1Home: DWORDLONG, + P2Home: DWORDLONG, + P3Home: DWORDLONG, + P4Home: DWORDLONG, + P5Home: DWORDLONG, + P6Home: DWORDLONG, + + ContextFlags: DWORD, + MxCsr: DWORD, + + SegCs: WORD, + SegDs: WORD, + SegEs: WORD, + SegFs: WORD, + SegGs: WORD, + SegSs: WORD, + EFlags: DWORD, + + Dr0: DWORDLONG, + Dr1: DWORDLONG, + Dr2: DWORDLONG, + Dr3: DWORDLONG, + Dr6: DWORDLONG, + Dr7: DWORDLONG, + + Rax: DWORDLONG, + Rcx: DWORDLONG, + Rdx: DWORDLONG, + Rbx: DWORDLONG, + Rsp: DWORDLONG, + Rbp: DWORDLONG, + Rsi: DWORDLONG, + Rdi: DWORDLONG, + R8: DWORDLONG, + R9: DWORDLONG, + R10: DWORDLONG, + R11: DWORDLONG, + R12: DWORDLONG, + R13: DWORDLONG, + R14: DWORDLONG, + R15: DWORDLONG, + + Rip: DWORDLONG, + + FltSave: FLOATING_SAVE_AREA, + + VectorRegister: [M128A, .. 26], + VectorControl: DWORDLONG, + + DebugControl: DWORDLONG, + LastBranchToRip: DWORDLONG, + LastBranchFromRip: DWORDLONG, + LastExceptionToRip: DWORDLONG, + LastExceptionFromRip: DWORDLONG, + } + + #[repr(C)] + pub struct M128A { + _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte + Low: c_ulonglong, + High: c_longlong + } + + #[repr(C)] + pub struct FLOATING_SAVE_AREA { + _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte + _Dummy: [u8, ..512] // FIXME: Fill this out + } + + pub fn init_frame(frame: &mut super::STACKFRAME64, + ctx: &CONTEXT) -> DWORD { + frame.AddrPC.Offset = ctx.Rip as u64; + frame.AddrPC.Mode = super::ADDRESS_MODE::AddrModeFlat; + frame.AddrStack.Offset = ctx.Rsp as u64; + frame.AddrStack.Mode = super::ADDRESS_MODE::AddrModeFlat; + frame.AddrFrame.Offset = ctx.Rbp as u64; + frame.AddrFrame.Mode = super::ADDRESS_MODE::AddrModeFlat; + super::IMAGE_FILE_MACHINE_AMD64 + } +} + +#[repr(C)] +struct Cleanup { + handle: libc::HANDLE, + SymCleanup: SymCleanupFn, +} + +impl Drop for Cleanup { + fn drop(&mut self) { (self.SymCleanup)(self.handle); } +} + +pub fn write(w: &mut Writer) -> IoResult<()> { + // According to windows documentation, all dbghelp functions are + // single-threaded. + static LOCK: StaticMutex = MUTEX_INIT; + let _g = unsafe { LOCK.lock() }; + + // Open up dbghelp.dll, we don't link to it explicitly because it can't + // always be found. Additionally, it's nice having fewer dependencies. + let path = Path::new("dbghelp.dll"); + let lib = match DynamicLibrary::open(Some(&path)) { + Ok(lib) => lib, + Err(..) => return Ok(()), + }; + + macro_rules! sym{ ($e:expr, $t:ident) => (unsafe { + match lib.symbol($e) { + Ok(f) => mem::transmute::<*mut u8, $t>(f), + Err(..) => return Ok(()) + } + }) } + + // Fetch the symbols necessary from dbghelp.dll + let SymFromAddr = sym!("SymFromAddr", SymFromAddrFn); + let SymInitialize = sym!("SymInitialize", SymInitializeFn); + let SymCleanup = sym!("SymCleanup", SymCleanupFn); + let StackWalk64 = sym!("StackWalk64", StackWalk64Fn); + + // Allocate necessary structures for doing the stack walk + let process = unsafe { GetCurrentProcess() }; + let thread = unsafe { GetCurrentThread() }; + let mut context: arch::CONTEXT = unsafe { intrinsics::init() }; + unsafe { RtlCaptureContext(&mut context); } + let mut frame: STACKFRAME64 = unsafe { intrinsics::init() }; + let image = arch::init_frame(&mut frame, &context); + + // Initialize this process's symbols + let ret = SymInitialize(process, 0 as *mut libc::c_void, libc::TRUE); + if ret != libc::TRUE { return Ok(()) } + let _c = Cleanup { handle: process, SymCleanup: SymCleanup }; + + // And now that we're done with all the setup, do the stack walking! + let mut i = 0i; + try!(write!(w, "stack backtrace:\n")); + while StackWalk64(image, process, thread, &mut frame, &mut context, + 0 as *mut libc::c_void, + 0 as *mut libc::c_void, + 0 as *mut libc::c_void, + 0 as *mut libc::c_void) == libc::TRUE{ + let addr = frame.AddrPC.Offset; + if addr == frame.AddrReturn.Offset || addr == 0 || + frame.AddrReturn.Offset == 0 { break } + + i += 1; + try!(write!(w, " {:2}: {:#2$x}", i, addr, HEX_WIDTH)); + let mut info: SYMBOL_INFO = unsafe { intrinsics::init() }; + info.MaxNameLen = MAX_SYM_NAME as libc::c_ulong; + // the struct size in C. the value is different to + // `size_of::<SYMBOL_INFO>() - MAX_SYM_NAME + 1` (== 81) + // due to struct alignment. + info.SizeOfStruct = 88; + + let mut displacement = 0u64; + let ret = SymFromAddr(process, addr as u64, &mut displacement, + &mut info); + + if ret == libc::TRUE { + try!(write!(w, " - ")); + let cstr = unsafe { CString::new(info.Name.as_ptr(), false) }; + let bytes = cstr.as_bytes(); + match cstr.as_str() { + Some(s) => try!(demangle(w, s)), + None => try!(w.write(bytes[..bytes.len()-1])), + } + } + try!(w.write(&['\n' as u8])); + } + + Ok(()) +} diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs index b8e9b1dca3a..d1cb91bcdb3 100644 --- a/src/libstd/sys/windows/c.rs +++ b/src/libstd/sys/windows/c.rs @@ -131,7 +131,7 @@ extern "system" { pub mod compat { use intrinsics::{atomic_store_relaxed, transmute}; - use iter::Iterator; + use iter::IteratorExt; use libc::types::os::arch::extra::{LPCWSTR, HMODULE, LPCSTR, LPVOID}; use prelude::*; @@ -169,7 +169,7 @@ pub mod compat { /// /// 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( + macro_rules! compat_fn { ($module:ident::$symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty $fallback:block) => ( #[inline(always)] @@ -195,7 +195,7 @@ pub mod compat { ($module:ident::$symbol:ident($($argname:ident: $argtype:ty),*) $fallback:block) => ( compat_fn!($module::$symbol($($argname: $argtype),*) -> () $fallback) ) - ) + } /// Compatibility layer for functions in `kernel32.dll` /// @@ -211,20 +211,20 @@ pub mod compat { fn SetLastError(dwErrCode: DWORD); } - compat_fn!(kernel32::CreateSymbolicLinkW(_lpSymlinkFileName: LPCWSTR, + 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, + compat_fn! { kernel32::GetFinalPathNameByHandleW(_hFile: HANDLE, _lpszFilePath: LPCWSTR, _cchFilePath: DWORD, _dwFlags: DWORD) -> DWORD { unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); } 0 - }) + } } } } diff --git a/src/libstd/sys/windows/condvar.rs b/src/libstd/sys/windows/condvar.rs new file mode 100644 index 00000000000..7f9d669c447 --- /dev/null +++ b/src/libstd/sys/windows/condvar.rs @@ -0,0 +1,62 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use cell::UnsafeCell; +use libc::{mod, DWORD}; +use os; +use sys::mutex::{mod, Mutex}; +use sys::sync as ffi; +use time::Duration; + +pub struct Condvar { inner: UnsafeCell<ffi::CONDITION_VARIABLE> } + +pub const CONDVAR_INIT: Condvar = Condvar { + inner: UnsafeCell { value: ffi::CONDITION_VARIABLE_INIT } +}; + +impl Condvar { + #[inline] + pub unsafe fn new() -> Condvar { CONDVAR_INIT } + + #[inline] + pub unsafe fn wait(&self, mutex: &Mutex) { + let r = ffi::SleepConditionVariableCS(self.inner.get(), + mutex::raw(mutex), + libc::INFINITE); + debug_assert!(r != 0); + } + + pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { + let r = ffi::SleepConditionVariableCS(self.inner.get(), + mutex::raw(mutex), + dur.num_milliseconds() as DWORD); + if r == 0 { + const ERROR_TIMEOUT: DWORD = 0x5B4; + debug_assert_eq!(os::errno() as uint, ERROR_TIMEOUT as uint); + false + } else { + true + } + } + + #[inline] + pub unsafe fn notify_one(&self) { + ffi::WakeConditionVariable(self.inner.get()) + } + + #[inline] + pub unsafe fn notify_all(&self) { + ffi::WakeAllConditionVariable(self.inner.get()) + } + + pub unsafe fn destroy(&self) { + // ... + } +} diff --git a/src/libstd/sys/windows/ext.rs b/src/libstd/sys/windows/ext.rs new file mode 100644 index 00000000000..049aca3f590 --- /dev/null +++ b/src/libstd/sys/windows/ext.rs @@ -0,0 +1,100 @@ +// 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. + +//! Experimental extensions to `std` for Windows. +//! +//! For now, this module is limited to extracting handles, file +//! descriptors, and sockets, but its functionality will grow over +//! time. + +#![experimental] + +use sys_common::AsInner; +use libc; + +use io; + +/// Raw HANDLEs. +pub type Handle = libc::HANDLE; + +/// Raw SOCKETs. +pub type Socket = libc::SOCKET; + +/// Extract raw handles. +pub trait AsRawHandle { + /// Extract the raw handle, without taking any ownership. + fn as_raw_handle(&self) -> Handle; +} + +impl AsRawHandle for io::fs::File { + fn as_raw_handle(&self) -> Handle { + self.as_inner().handle() + } +} + +impl AsRawHandle for io::pipe::PipeStream { + fn as_raw_handle(&self) -> Handle { + self.as_inner().handle() + } +} + +impl AsRawHandle for io::net::pipe::UnixStream { + fn as_raw_handle(&self) -> Handle { + self.as_inner().handle() + } +} + +impl AsRawHandle for io::net::pipe::UnixListener { + fn as_raw_handle(&self) -> Handle { + self.as_inner().handle() + } +} + +impl AsRawHandle for io::net::pipe::UnixAcceptor { + fn as_raw_handle(&self) -> Handle { + self.as_inner().handle() + } +} + +/// Extract raw sockets. +pub trait AsRawSocket { + fn as_raw_socket(&self) -> Socket; +} + +impl AsRawSocket for io::net::tcp::TcpStream { + fn as_raw_socket(&self) -> Socket { + self.as_inner().fd() + } +} + +impl AsRawSocket for io::net::tcp::TcpListener { + fn as_raw_socket(&self) -> Socket { + self.as_inner().socket() + } +} + +impl AsRawSocket for io::net::tcp::TcpAcceptor { + fn as_raw_socket(&self) -> Socket { + self.as_inner().socket() + } +} + +impl AsRawSocket for io::net::udp::UdpSocket { + fn as_raw_socket(&self) -> Socket { + self.as_inner().fd() + } +} + +/// A prelude for conveniently writing platform-specific code. +/// +/// Includes all extension traits, and some important type definitions. +pub mod prelude { + pub use super::{Socket, Handle, AsRawSocket, AsRawHandle}; +} diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs index b881eb2d495..15eddd569be 100644 --- a/src/libstd/sys/windows/fs.rs +++ b/src/libstd/sys/windows/fs.rs @@ -15,7 +15,7 @@ use libc::{mod, c_int}; use c_str::CString; use mem; -use os::windows::fill_utf16_buf_and_decode; +use sys::os::fill_utf16_buf_and_decode; use path; use ptr; use str; @@ -23,13 +23,13 @@ use io; use prelude::*; use sys; +use sys::os; use sys_common::{keep_going, eof, mkerr_libc}; use io::{FilePermission, Write, UnstableFileStat, Open, FileAccess, FileMode}; -use io::{IoResult, IoError, FileStat, SeekStyle, Seek, Writer, Reader}; +use io::{IoResult, IoError, FileStat, SeekStyle}; use io::{Read, Truncate, SeekCur, SeekSet, ReadWrite, SeekEnd, Append}; -pub use path::WindowsPath as Path; pub type fd_t = libc::c_int; pub struct FileDesc { @@ -131,7 +131,7 @@ impl FileDesc { return ret; } - pub fn fstat(&mut self) -> IoResult<io::FileStat> { + pub fn fstat(&self) -> IoResult<io::FileStat> { let mut stat: libc::stat = unsafe { mem::zeroed() }; match unsafe { libc::fstat(self.fd(), &mut stat) } { 0 => Ok(mkstat(&stat)), @@ -263,7 +263,7 @@ pub fn readdir(p: &Path) -> IoResult<Vec<Path>> { let mut more_files = 1 as libc::BOOL; while more_files != 0 { { - let filename = str::truncate_utf16_at_nul(&wfd.cFileName); + let filename = os::truncate_utf16_at_nul(&wfd.cFileName); match String::from_utf16(filename) { Some(filename) => paths.push(Path::new(filename)), None => { @@ -376,8 +376,8 @@ pub fn readlink(p: &Path) -> IoResult<Path> { 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))) + Some(ref s) if s.starts_with(r"\\?\") => { // " + Ok(Path::new(s.slice_from(4))) } Some(s) => Ok(Path::new(s)), None => Err(super::last_error()), @@ -407,12 +407,12 @@ fn mkstat(stat: &libc::stat) -> FileStat { FileStat { size: stat.st_size as u64, kind: match (stat.st_mode as libc::c_int) & libc::S_IFMT { - libc::S_IFREG => io::TypeFile, - libc::S_IFDIR => io::TypeDirectory, - libc::S_IFIFO => io::TypeNamedPipe, - libc::S_IFBLK => io::TypeBlockSpecial, - libc::S_IFLNK => io::TypeSymlink, - _ => io::TypeUnknown, + libc::S_IFREG => io::FileType::RegularFile, + libc::S_IFDIR => io::FileType::Directory, + libc::S_IFIFO => io::FileType::NamedPipe, + libc::S_IFBLK => io::FileType::BlockSpecial, + libc::S_IFLNK => io::FileType::Symlink, + _ => io::FileType::Unknown, }, perm: FilePermission::from_bits_truncate(stat.st_mode as u32), created: stat.st_ctime as u64, diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index 815ace21f87..6924687d8c4 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -24,25 +24,36 @@ use prelude::*; use io::{mod, IoResult, IoError}; use sync::{Once, ONCE_INIT}; -macro_rules! helper_init( (static $name:ident: Helper<$m:ty>) => ( +macro_rules! helper_init { (static $name:ident: Helper<$m:ty>) => ( static $name: Helper<$m> = Helper { - lock: ::rustrt::mutex::NATIVE_MUTEX_INIT, + lock: ::sync::MUTEX_INIT, + cond: ::sync::CONDVAR_INIT, chan: ::cell::UnsafeCell { value: 0 as *mut Sender<$m> }, signal: ::cell::UnsafeCell { value: 0 }, initialized: ::cell::UnsafeCell { value: false }, + shutdown: ::cell::UnsafeCell { value: false }, }; -) ) +) } +pub mod backtrace; pub mod c; +pub mod ext; +pub mod condvar; pub mod fs; +pub mod helper_signal; +pub mod mutex; pub mod os; -pub mod tcp; -pub mod udp; pub mod pipe; -pub mod helper_signal; pub mod process; +pub mod rwlock; +pub mod sync; +pub mod stack_overflow; +pub mod tcp; +pub mod thread; +pub mod thread_local; pub mod timer; pub mod tty; +pub mod udp; pub mod addrinfo { pub use sys_common::net::get_host_addresses; @@ -130,7 +141,7 @@ pub fn decode_error_detailed(errno: i32) -> IoError { } #[inline] -pub fn retry<I> (f: || -> I) -> I { f() } // PR rust-lang/rust/#17020 +pub fn retry<I, F>(f: F) -> I where F: FnOnce() -> I { f() } // PR rust-lang/rust/#17020 pub fn ms_to_timeval(ms: u64) -> libc::timeval { libc::timeval { diff --git a/src/libstd/sys/windows/mutex.rs b/src/libstd/sys/windows/mutex.rs new file mode 100644 index 00000000000..ddd89070ed5 --- /dev/null +++ b/src/libstd/sys/windows/mutex.rs @@ -0,0 +1,78 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use prelude::*; + +use sync::atomic; +use alloc::{mod, heap}; + +use libc::DWORD; +use sys::sync as ffi; + +const SPIN_COUNT: DWORD = 4000; + +pub struct Mutex { inner: atomic::AtomicUint } + +pub const MUTEX_INIT: Mutex = Mutex { inner: atomic::INIT_ATOMIC_UINT }; + +#[inline] +pub unsafe fn raw(m: &Mutex) -> ffi::LPCRITICAL_SECTION { + m.get() +} + +impl Mutex { + #[inline] + pub unsafe fn new() -> Mutex { + Mutex { inner: atomic::AtomicUint::new(init_lock() as uint) } + } + #[inline] + pub unsafe fn lock(&self) { + ffi::EnterCriticalSection(self.get()) + } + #[inline] + pub unsafe fn try_lock(&self) -> bool { + ffi::TryEnterCriticalSection(self.get()) != 0 + } + #[inline] + pub unsafe fn unlock(&self) { + ffi::LeaveCriticalSection(self.get()) + } + pub unsafe fn destroy(&self) { + let lock = self.inner.swap(0, atomic::SeqCst); + if lock != 0 { free_lock(lock as ffi::LPCRITICAL_SECTION) } + } + + unsafe fn get(&self) -> ffi::LPCRITICAL_SECTION { + match self.inner.load(atomic::SeqCst) { + 0 => {} + n => return n as ffi::LPCRITICAL_SECTION + } + let lock = init_lock(); + match self.inner.compare_and_swap(0, lock as uint, atomic::SeqCst) { + 0 => return lock as ffi::LPCRITICAL_SECTION, + _ => {} + } + free_lock(lock); + return self.inner.load(atomic::SeqCst) as ffi::LPCRITICAL_SECTION; + } +} + +unsafe fn init_lock() -> ffi::LPCRITICAL_SECTION { + let block = heap::allocate(ffi::CRITICAL_SECTION_SIZE, 8) + as ffi::LPCRITICAL_SECTION; + if block.is_null() { alloc::oom() } + ffi::InitializeCriticalSectionAndSpinCount(block, SPIN_COUNT); + return block; +} + +unsafe fn free_lock(h: ffi::LPCRITICAL_SECTION) { + ffi::DeleteCriticalSection(h); + heap::deallocate(h as *mut _, ffi::CRITICAL_SECTION_SIZE, 8); +} diff --git a/src/libstd/sys/windows/os.rs b/src/libstd/sys/windows/os.rs index aa43b42e746..e007b46b261 100644 --- a/src/libstd/sys/windows/os.rs +++ b/src/libstd/sys/windows/os.rs @@ -8,17 +8,38 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +//! Implementation of `std::os` functionality for Windows + // FIXME: move various extern bindings from here into liblibc or // something similar -use libc; -use libc::{c_int, c_char, c_void}; use prelude::*; + +use fmt; use io::{IoResult, IoError}; -use sys::fs::FileDesc; +use libc::{c_int, c_char, c_void}; +use libc; +use os; +use path::BytesContainer; use ptr; +use sync::atomic::{AtomicInt, INIT_ATOMIC_INT, SeqCst}; +use sys::fs::FileDesc; +use slice; use os::TMPBUF_SZ; +use libc::types::os::arch::extra::DWORD; + +const BUF_BYTES : uint = 2048u; + +/// Return a slice of `v` ending at (and not including) the first NUL +/// (0). +pub fn truncate_utf16_at_nul<'a>(v: &'a [u16]) -> &'a [u16] { + match v.iter().position(|c| *c == 0) { + // don't include the 0 + Some(i) => v[..i], + None => v + } +} pub fn errno() -> uint { use libc::types::os::arch::extra::DWORD; @@ -76,7 +97,7 @@ pub fn error_string(errnum: i32) -> String { return format!("OS Error {} (FormatMessageW() returned error {})", errnum, fm_err); } - let msg = String::from_utf16(::str::truncate_utf16_at_nul(&buf)); + let msg = String::from_utf16(truncate_utf16_at_nul(&buf)); match msg { Some(msg) => format!("OS Error {}: {}", errnum, msg), None => format!("OS Error {} (FormatMessageW() returned invalid UTF-16)", errnum), @@ -101,3 +122,212 @@ pub unsafe fn pipe() -> IoResult<(FileDesc, FileDesc)> { _ => Err(IoError::last_error()), } } + +pub fn fill_utf16_buf_and_decode(f: |*mut u16, DWORD| -> DWORD) -> Option<String> { + unsafe { + let mut n = TMPBUF_SZ as DWORD; + let mut res = None; + let mut done = false; + while !done { + let mut buf = Vec::from_elem(n as uint, 0u16); + let k = f(buf.as_mut_ptr(), n); + if k == (0 as DWORD) { + done = true; + } else if k == n && + libc::GetLastError() == + libc::ERROR_INSUFFICIENT_BUFFER as DWORD { + n *= 2 as DWORD; + } else if k >= n { + n = k; + } else { + done = true; + } + if k != 0 && done { + let sub = buf.slice(0, k as uint); + // We want to explicitly catch the case when the + // closure returned invalid UTF-16, rather than + // set `res` to None and continue. + let s = String::from_utf16(sub) + .expect("fill_utf16_buf_and_decode: closure created invalid UTF-16"); + res = Some(s) + } + } + return res; + } +} + +pub fn getcwd() -> IoResult<Path> { + use libc::DWORD; + use libc::GetCurrentDirectoryW; + use io::OtherIoError; + + let mut buf = [0 as u16, ..BUF_BYTES]; + unsafe { + if libc::GetCurrentDirectoryW(buf.len() as DWORD, buf.as_mut_ptr()) == 0 as DWORD { + return Err(IoError::last_error()); + } + } + + match String::from_utf16(truncate_utf16_at_nul(&buf)) { + Some(ref cwd) => Ok(Path::new(cwd)), + None => Err(IoError { + kind: OtherIoError, + desc: "GetCurrentDirectoryW returned invalid UTF-16", + detail: None, + }), + } +} + +pub unsafe fn get_env_pairs() -> Vec<Vec<u8>> { + use libc::funcs::extra::kernel32::{ + GetEnvironmentStringsW, + FreeEnvironmentStringsW + }; + let ch = GetEnvironmentStringsW(); + if ch as uint == 0 { + panic!("os::env() failure getting env string from OS: {}", + os::last_os_error()); + } + // Here, we lossily decode the string as UTF16. + // + // The docs suggest that the result should be in Unicode, but + // Windows doesn't guarantee it's actually UTF16 -- it doesn't + // validate the environment string passed to CreateProcess nor + // SetEnvironmentVariable. Yet, it's unlikely that returning a + // raw u16 buffer would be of practical use since the result would + // be inherently platform-dependent and introduce additional + // complexity to this code. + // + // Using the non-Unicode version of GetEnvironmentStrings is even + // worse since the result is in an OEM code page. Characters that + // can't be encoded in the code page would be turned into question + // marks. + let mut result = Vec::new(); + let mut i = 0; + while *ch.offset(i) != 0 { + let p = &*ch.offset(i); + let mut len = 0; + while *(p as *const _).offset(len) != 0 { + len += 1; + } + let p = p as *const u16; + let s = slice::from_raw_buf(&p, len as uint); + result.push(String::from_utf16_lossy(s).into_bytes()); + i += len as int + 1; + } + FreeEnvironmentStringsW(ch); + result +} + +pub fn split_paths(unparsed: &[u8]) -> Vec<Path> { + // On Windows, the PATH environment variable is semicolon separated. Double + // quotes are used as a way of introducing literal semicolons (since + // c:\some;dir is a valid Windows path). Double quotes are not themselves + // permitted in path names, so there is no way to escape a double quote. + // Quoted regions can appear in arbitrary locations, so + // + // c:\foo;c:\som"e;di"r;c:\bar + // + // Should parse as [c:\foo, c:\some;dir, c:\bar]. + // + // (The above is based on testing; there is no clear reference available + // for the grammar.) + + let mut parsed = Vec::new(); + let mut in_progress = Vec::new(); + let mut in_quote = false; + + for b in unparsed.iter() { + match *b { + b';' if !in_quote => { + parsed.push(Path::new(in_progress.as_slice())); + in_progress.truncate(0) + } + b'"' => { + in_quote = !in_quote; + } + _ => { + in_progress.push(*b); + } + } + } + parsed.push(Path::new(in_progress)); + parsed +} + +pub fn join_paths<T: BytesContainer>(paths: &[T]) -> Result<Vec<u8>, &'static str> { + let mut joined = Vec::new(); + let sep = b';'; + + for (i, path) in paths.iter().map(|p| p.container_as_bytes()).enumerate() { + if i > 0 { joined.push(sep) } + if path.contains(&b'"') { + return Err("path segment contains `\"`"); + } else if path.contains(&sep) { + joined.push(b'"'); + joined.push_all(path); + joined.push(b'"'); + } else { + joined.push_all(path); + } + } + + Ok(joined) +} + +pub fn load_self() -> Option<Vec<u8>> { + unsafe { + fill_utf16_buf_and_decode(|buf, sz| { + libc::GetModuleFileNameW(0u as libc::DWORD, buf, sz) + }).map(|s| s.to_string().into_bytes()) + } +} + +pub fn chdir(p: &Path) -> IoResult<()> { + let mut p = p.as_str().unwrap().utf16_units().collect::<Vec<u16>>(); + p.push(0); + + unsafe { + match libc::SetCurrentDirectoryW(p.as_ptr()) != (0 as libc::BOOL) { + true => Ok(()), + false => Err(IoError::last_error()), + } + } +} + +pub fn page_size() -> uint { + use mem; + unsafe { + let mut info = mem::zeroed(); + libc::GetSystemInfo(&mut info); + + return info.dwPageSize as uint; + } +} + +#[cfg(test)] +mod tests { + use super::truncate_utf16_at_nul; + + #[test] + fn test_truncate_utf16_at_nul() { + let v = []; + let b: &[u16] = &[]; + assert_eq!(truncate_utf16_at_nul(&v), b); + + let v = [0, 2, 3]; + assert_eq!(truncate_utf16_at_nul(&v), b); + + let v = [1, 0, 3]; + let b: &[u16] = &[1]; + assert_eq!(truncate_utf16_at_nul(&v), b); + + let v = [1, 2, 0]; + let b: &[u16] = &[1, 2]; + assert_eq!(truncate_utf16_at_nul(&v), b); + + let v = [1, 2, 3]; + let b: &[u16] = &[1, 2, 3]; + assert_eq!(truncate_utf16_at_nul(&v), b); + } +} diff --git a/src/libstd/sys/windows/pipe.rs b/src/libstd/sys/windows/pipe.rs index a623c2cd8e2..bf658d0efd0 100644 --- a/src/libstd/sys/windows/pipe.rs +++ b/src/libstd/sys/windows/pipe.rs @@ -89,8 +89,7 @@ use libc; use c_str::CString; use mem; use ptr; -use sync::atomic; -use rustrt::mutex; +use sync::{atomic, Mutex}; use io::{mod, IoError, IoResult}; use prelude::*; @@ -126,7 +125,7 @@ impl Drop for Event { struct Inner { handle: libc::HANDLE, - lock: mutex::NativeMutex, + lock: Mutex<()>, read_closed: atomic::AtomicBool, write_closed: atomic::AtomicBool, } @@ -135,7 +134,7 @@ impl Inner { fn new(handle: libc::HANDLE) -> Inner { Inner { handle: handle, - lock: unsafe { mutex::NativeMutex::new() }, + lock: Mutex::new(()), read_closed: atomic::AtomicBool::new(false), write_closed: atomic::AtomicBool::new(false), } @@ -329,7 +328,7 @@ impl UnixStream { } } - fn handle(&self) -> libc::HANDLE { self.inner.handle } + pub fn handle(&self) -> libc::HANDLE { self.inner.handle } fn read_closed(&self) -> bool { self.inner.read_closed.load(atomic::SeqCst) @@ -585,6 +584,10 @@ impl UnixListener { }), }) } + + pub fn handle(&self) -> libc::HANDLE { + self.handle + } } impl Drop for UnixListener { @@ -729,6 +732,10 @@ impl UnixAcceptor { Ok(()) } } + + pub fn handle(&self) -> libc::HANDLE { + self.listener.handle() + } } impl Clone for UnixAcceptor { diff --git a/src/libstd/sys/windows/process.rs b/src/libstd/sys/windows/process.rs index 3fb5ee34356..0c2c76077dd 100644 --- a/src/libstd/sys/windows/process.rs +++ b/src/libstd/sys/windows/process.rs @@ -26,20 +26,17 @@ use sys::fs; use sys::{mod, retry, c, wouldblock, set_nonblocking, ms_to_timeval, timer}; use sys::fs::FileDesc; use sys_common::helper_thread::Helper; -use sys_common::{AsFileDesc, mkerr_libc, timeout}; +use sys_common::{AsInner, mkerr_libc, timeout}; use io::fs::PathExtensions; -use string::String; pub use sys_common::ProcessConfig; -/** - * A value representing a child process. - * - * The lifetime of this value is linked to the lifetime of the actual - * process - the Process destructor calls self.finish() which waits - * for the process to terminate. - */ +/// A value representing a child process. +/// +/// The lifetime of this value is linked to the lifetime of the actual +/// process - the Process destructor calls self.finish() which waits +/// for the process to terminate. pub struct Process { /// The unique id of the process (this should never be negative). pid: pid_t, @@ -105,7 +102,7 @@ impl Process { pub fn spawn<K, V, C, P>(cfg: &C, in_fd: Option<P>, out_fd: Option<P>, err_fd: Option<P>) -> IoResult<Process> - where C: ProcessConfig<K, V>, P: AsFileDesc, + where C: ProcessConfig<K, V>, P: AsInner<FileDesc>, K: BytesContainer + Eq + Hash, V: BytesContainer { use libc::types::os::arch::extra::{DWORD, HANDLE, STARTUPINFO}; @@ -124,8 +121,8 @@ impl Process { use libc::funcs::extra::msvcrt::get_osfhandle; use mem; - use iter::Iterator; - use str::StrPrelude; + use iter::{Iterator, IteratorExt}; + use str::StrExt; if cfg.gid().is_some() || cfg.uid().is_some() { return Err(IoError { @@ -195,7 +192,7 @@ impl Process { } } Some(ref fd) => { - let orig = get_osfhandle(fd.as_fd().fd()) as HANDLE; + let orig = get_osfhandle(fd.as_inner().fd()) as HANDLE; if orig == INVALID_HANDLE_VALUE { return Err(super::last_error()) } @@ -225,7 +222,7 @@ impl Process { with_envp(cfg.env(), |envp| { with_dirp(cfg.cwd(), |dirp| { - let mut cmd_str: Vec<u16> = cmd_str.as_slice().utf16_units().collect(); + let mut cmd_str: Vec<u16> = cmd_str.utf16_units().collect(); cmd_str.push(0); let created = CreateProcessW(ptr::null(), cmd_str.as_mut_ptr(), @@ -263,16 +260,14 @@ impl Process { } } - /** - * Waits for a process to exit and returns the exit code, failing - * if there is no process with the specified id. - * - * Note that this is private to avoid race conditions on unix where if - * a user calls waitpid(some_process.get_id()) then some_process.finish() - * and some_process.destroy() and some_process.finalize() will then either - * operate on a none-existent process or, even worse, on a newer process - * with the same id. - */ + /// Waits for a process to exit and returns the exit code, failing + /// if there is no process with the specified id. + /// + /// Note that this is private to avoid race conditions on unix where if + /// a user calls waitpid(some_process.get_id()) then some_process.finish() + /// and some_process.destroy() and some_process.finalize() will then either + /// operate on a none-existent process or, even worse, on a newer process + /// with the same id. pub fn wait(&self, deadline: u64) -> IoResult<ProcessExit> { use libc::types::os::arch::extra::DWORD; use libc::consts::os::extra::{ @@ -422,9 +417,8 @@ fn make_command_line(prog: &CString, args: &[CString]) -> String { } } -fn with_envp<K, V, T>(env: Option<&collections::HashMap<K, V>>, - cb: |*mut c_void| -> T) -> T - where K: BytesContainer + Eq + Hash, V: BytesContainer +fn with_envp<K, V, T, F>(env: Option<&collections::HashMap<K, V>>, cb: F) -> T where + K: BytesContainer + Eq + Hash, V: BytesContainer, F: FnOnce(*mut c_void) -> T, { // 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 @@ -435,9 +429,9 @@ fn with_envp<K, V, T>(env: Option<&collections::HashMap<K, V>>, for pair in env.iter() { let kv = format!("{}={}", - pair.ref0().container_as_str().unwrap(), - pair.ref1().container_as_str().unwrap()); - blk.extend(kv.as_slice().utf16_units()); + pair.0.container_as_str().unwrap(), + pair.1.container_as_str().unwrap()); + blk.extend(kv.utf16_units()); blk.push(0); } @@ -449,7 +443,9 @@ fn with_envp<K, V, T>(env: Option<&collections::HashMap<K, V>>, } } -fn with_dirp<T>(d: Option<&CString>, cb: |*const u16| -> T) -> T { +fn with_dirp<T, F>(d: Option<&CString>, cb: F) -> T where + F: FnOnce(*const u16) -> T, +{ match d { Some(dir) => { let dir_str = dir.as_str() @@ -488,24 +484,24 @@ mod tests { assert_eq!( test_wrapper("prog", &["aaa", "bbb", "ccc"]), - "prog aaa bbb ccc".to_string() + "prog aaa bbb ccc" ); assert_eq!( test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa"]), - "\"C:\\Program Files\\blah\\blah.exe\" aaa".to_string() + "\"C:\\Program Files\\blah\\blah.exe\" aaa" ); assert_eq!( test_wrapper("C:\\Program Files\\test", &["aa\"bb"]), - "\"C:\\Program Files\\test\" aa\\\"bb".to_string() + "\"C:\\Program Files\\test\" aa\\\"bb" ); assert_eq!( test_wrapper("echo", &["a b c"]), - "echo \"a b c\"".to_string() + "echo \"a b c\"" ); assert_eq!( - test_wrapper("\u03c0\u042f\u97f3\u00e6\u221e", &[]), - "\u03c0\u042f\u97f3\u00e6\u221e".to_string() + test_wrapper("\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}", &[]), + "\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}" ); } } diff --git a/src/libstd/sys/windows/rwlock.rs b/src/libstd/sys/windows/rwlock.rs new file mode 100644 index 00000000000..88ce85c39f6 --- /dev/null +++ b/src/libstd/sys/windows/rwlock.rs @@ -0,0 +1,53 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use cell::UnsafeCell; +use sys::sync as ffi; + +pub struct RWLock { inner: UnsafeCell<ffi::SRWLOCK> } + +pub const RWLOCK_INIT: RWLock = RWLock { + inner: UnsafeCell { value: ffi::SRWLOCK_INIT } +}; + +impl RWLock { + #[inline] + pub unsafe fn new() -> RWLock { RWLOCK_INIT } + + #[inline] + pub unsafe fn read(&self) { + ffi::AcquireSRWLockShared(self.inner.get()) + } + #[inline] + pub unsafe fn try_read(&self) -> bool { + ffi::TryAcquireSRWLockShared(self.inner.get()) != 0 + } + #[inline] + pub unsafe fn write(&self) { + ffi::AcquireSRWLockExclusive(self.inner.get()) + } + #[inline] + pub unsafe fn try_write(&self) -> bool { + ffi::TryAcquireSRWLockExclusive(self.inner.get()) != 0 + } + #[inline] + pub unsafe fn read_unlock(&self) { + ffi::ReleaseSRWLockShared(self.inner.get()) + } + #[inline] + pub unsafe fn write_unlock(&self) { + ffi::ReleaseSRWLockExclusive(self.inner.get()) + } + + #[inline] + pub unsafe fn destroy(&self) { + // ... + } +} diff --git a/src/libstd/sys/windows/stack_overflow.rs b/src/libstd/sys/windows/stack_overflow.rs new file mode 100644 index 00000000000..bdf2e0bccb1 --- /dev/null +++ b/src/libstd/sys/windows/stack_overflow.rs @@ -0,0 +1,115 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rt::util::report_overflow; +use core::prelude::*; +use ptr; +use mem; +use libc; +use libc::types::os::arch::extra::{LPVOID, DWORD, LONG, BOOL}; +use sys_common::{stack, thread_info}; + +pub struct Handler { + _data: *mut libc::c_void +} + +impl Handler { + pub unsafe fn new() -> Handler { + make_handler() + } +} + +impl Drop for Handler { + fn drop(&mut self) {} +} + +// get_task_info is called from an exception / signal handler. +// It returns the guard page of the current task or 0 if that +// guard page doesn't exist. None is returned if there's currently +// no local task. +unsafe fn get_task_guard_page() -> uint { + thread_info::stack_guard() +} + +// This is initialized in init() and only read from after +static mut PAGE_SIZE: uint = 0; + +#[no_stack_check] +extern "system" fn vectored_handler(ExceptionInfo: *mut EXCEPTION_POINTERS) -> LONG { + unsafe { + let rec = &(*(*ExceptionInfo).ExceptionRecord); + let code = rec.ExceptionCode; + + if code != EXCEPTION_STACK_OVERFLOW { + return EXCEPTION_CONTINUE_SEARCH; + } + + // We're calling into functions with stack checks, + // however stack checks by limit should be disabled on Windows + stack::record_sp_limit(0); + + report_overflow(); + + EXCEPTION_CONTINUE_SEARCH + } +} + +pub unsafe fn init() { + let mut info = mem::zeroed(); + libc::GetSystemInfo(&mut info); + PAGE_SIZE = info.dwPageSize as uint; + + if AddVectoredExceptionHandler(0, vectored_handler) == ptr::null_mut() { + panic!("failed to install exception handler"); + } + + mem::forget(make_handler()); +} + +pub unsafe fn cleanup() { +} + +pub unsafe fn make_handler() -> Handler { + if SetThreadStackGuarantee(&mut 0x5000) == 0 { + panic!("failed to reserve stack space for exception handling"); + } + + Handler { _data: 0i as *mut libc::c_void } +} + +pub struct EXCEPTION_RECORD { + pub ExceptionCode: DWORD, + pub ExceptionFlags: DWORD, + pub ExceptionRecord: *mut EXCEPTION_RECORD, + pub ExceptionAddress: LPVOID, + pub NumberParameters: DWORD, + pub ExceptionInformation: [LPVOID, ..EXCEPTION_MAXIMUM_PARAMETERS] +} + +pub struct EXCEPTION_POINTERS { + pub ExceptionRecord: *mut EXCEPTION_RECORD, + pub ContextRecord: LPVOID +} + +pub type PVECTORED_EXCEPTION_HANDLER = extern "system" + fn(ExceptionInfo: *mut EXCEPTION_POINTERS) -> LONG; + +pub type ULONG = libc::c_ulong; + +const EXCEPTION_CONTINUE_SEARCH: LONG = 0; +const EXCEPTION_MAXIMUM_PARAMETERS: uint = 15; +const EXCEPTION_STACK_OVERFLOW: DWORD = 0xc00000fd; + +extern "system" { + fn AddVectoredExceptionHandler(FirstHandler: ULONG, + VectoredHandler: PVECTORED_EXCEPTION_HANDLER) + -> LPVOID; + fn SetThreadStackGuarantee(StackSizeInBytes: *mut ULONG) -> BOOL; +} diff --git a/src/libstd/sys/windows/sync.rs b/src/libstd/sys/windows/sync.rs new file mode 100644 index 00000000000..cbca47912b5 --- /dev/null +++ b/src/libstd/sys/windows/sync.rs @@ -0,0 +1,58 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use libc::{BOOL, DWORD, c_void, LPVOID}; +use libc::types::os::arch::extra::BOOLEAN; + +pub type LPCRITICAL_SECTION = *mut c_void; +pub type LPCONDITION_VARIABLE = *mut CONDITION_VARIABLE; +pub type LPSRWLOCK = *mut SRWLOCK; + +#[cfg(target_arch = "x86")] +pub const CRITICAL_SECTION_SIZE: uint = 24; +#[cfg(target_arch = "x86_64")] +pub const CRITICAL_SECTION_SIZE: uint = 40; + +#[repr(C)] +pub struct CONDITION_VARIABLE { pub ptr: LPVOID } +#[repr(C)] +pub struct SRWLOCK { pub ptr: LPVOID } + +pub const CONDITION_VARIABLE_INIT: CONDITION_VARIABLE = CONDITION_VARIABLE { + ptr: 0 as *mut _, +}; +pub const SRWLOCK_INIT: SRWLOCK = SRWLOCK { ptr: 0 as *mut _ }; + +extern "system" { + // critical sections + pub fn InitializeCriticalSectionAndSpinCount( + lpCriticalSection: LPCRITICAL_SECTION, + dwSpinCount: DWORD) -> BOOL; + pub fn DeleteCriticalSection(lpCriticalSection: LPCRITICAL_SECTION); + pub fn EnterCriticalSection(lpCriticalSection: LPCRITICAL_SECTION); + pub fn LeaveCriticalSection(lpCriticalSection: LPCRITICAL_SECTION); + pub fn TryEnterCriticalSection(lpCriticalSection: LPCRITICAL_SECTION) -> BOOL; + + // condition variables + pub fn SleepConditionVariableCS(ConditionVariable: LPCONDITION_VARIABLE, + CriticalSection: LPCRITICAL_SECTION, + dwMilliseconds: DWORD) -> BOOL; + pub fn WakeConditionVariable(ConditionVariable: LPCONDITION_VARIABLE); + pub fn WakeAllConditionVariable(ConditionVariable: LPCONDITION_VARIABLE); + + // slim rwlocks + pub fn AcquireSRWLockExclusive(SRWLock: LPSRWLOCK); + pub fn AcquireSRWLockShared(SRWLock: LPSRWLOCK); + pub fn ReleaseSRWLockExclusive(SRWLock: LPSRWLOCK); + pub fn ReleaseSRWLockShared(SRWLock: LPSRWLOCK); + pub fn TryAcquireSRWLockExclusive(SRWLock: LPSRWLOCK) -> BOOLEAN; + pub fn TryAcquireSRWLockShared(SRWLock: LPSRWLOCK) -> BOOLEAN; +} + diff --git a/src/libstd/sys/windows/tcp.rs b/src/libstd/sys/windows/tcp.rs index 3baf2be08d2..505e6137bf9 100644 --- a/src/libstd/sys/windows/tcp.rs +++ b/src/libstd/sys/windows/tcp.rs @@ -18,8 +18,7 @@ use super::{last_error, last_net_error, retry, sock_t}; use sync::{Arc, atomic}; use sys::fs::FileDesc; use sys::{mod, c, set_nonblocking, wouldblock, timer}; -use sys_common::{mod, timeout, eof}; -use sys_common::net::*; +use sys_common::{mod, timeout, eof, net}; pub use sys_common::net::TcpStream; @@ -48,37 +47,35 @@ impl Drop for Event { // TCP listeners //////////////////////////////////////////////////////////////////////////////// -pub struct TcpListener { - inner: FileDesc, -} +pub struct TcpListener { sock: sock_t } impl TcpListener { pub fn bind(addr: ip::SocketAddr) -> IoResult<TcpListener> { sys::init_net(); - let fd = try!(socket(addr, libc::SOCK_STREAM)); - let ret = TcpListener { inner: FileDesc::new(fd as libc::c_int, true) }; + let sock = try!(net::socket(addr, libc::SOCK_STREAM)); + let ret = TcpListener { sock: sock }; let mut storage = unsafe { mem::zeroed() }; - let len = addr_to_sockaddr(addr, &mut storage); + let len = net::addr_to_sockaddr(addr, &mut storage); let addrp = &storage as *const _ as *const libc::sockaddr; - match unsafe { libc::bind(fd, addrp, len) } { + match unsafe { libc::bind(sock, addrp, len) } { -1 => Err(last_net_error()), _ => Ok(ret), } } - pub fn fd(&self) -> sock_t { self.inner.fd as sock_t } + pub fn socket(&self) -> sock_t { self.sock } pub fn listen(self, backlog: int) -> IoResult<TcpAcceptor> { - match unsafe { libc::listen(self.fd(), backlog as libc::c_int) } { + match unsafe { libc::listen(self.socket(), backlog as libc::c_int) } { -1 => Err(last_net_error()), _ => { let accept = try!(Event::new()); let ret = unsafe { - c::WSAEventSelect(self.fd(), accept.handle(), c::FD_ACCEPT) + c::WSAEventSelect(self.socket(), accept.handle(), c::FD_ACCEPT) }; if ret != 0 { return Err(last_net_error()) @@ -97,7 +94,13 @@ impl TcpListener { } pub fn socket_name(&mut self) -> IoResult<ip::SocketAddr> { - sockname(self.fd(), libc::getsockname) + net::sockname(self.socket(), libc::getsockname) + } +} + +impl Drop for TcpListener { + fn drop(&mut self) { + unsafe { super::close_sock(self.sock); } } } @@ -114,7 +117,7 @@ struct AcceptorInner { } impl TcpAcceptor { - pub fn fd(&self) -> sock_t { self.inner.listener.fd() } + pub fn socket(&self) -> sock_t { self.inner.listener.socket() } pub fn accept(&mut self) -> IoResult<TcpStream> { // Unlink unix, windows cannot invoke `select` on arbitrary file @@ -161,13 +164,13 @@ impl TcpAcceptor { let mut wsaevents: c::WSANETWORKEVENTS = unsafe { mem::zeroed() }; let ret = unsafe { - c::WSAEnumNetworkEvents(self.fd(), events[1], &mut wsaevents) + c::WSAEnumNetworkEvents(self.socket(), events[1], &mut wsaevents) }; if ret != 0 { return Err(last_net_error()) } if wsaevents.lNetworkEvents & c::FD_ACCEPT == 0 { continue } match unsafe { - libc::accept(self.fd(), ptr::null_mut(), ptr::null_mut()) + libc::accept(self.socket(), ptr::null_mut(), ptr::null_mut()) } { -1 if wouldblock() => {} -1 => return Err(last_net_error()), @@ -175,13 +178,13 @@ impl TcpAcceptor { // Accepted sockets inherit the same properties as the caller, // so we need to deregister our event and switch the socket back // to blocking mode - fd => { - let stream = TcpStream::new(fd); + socket => { + let stream = TcpStream::new(socket); let ret = unsafe { - c::WSAEventSelect(fd, events[1], 0) + c::WSAEventSelect(socket, events[1], 0) }; if ret != 0 { return Err(last_net_error()) } - try!(set_nonblocking(fd, false)); + try!(set_nonblocking(socket, false)); return Ok(stream) } } @@ -191,7 +194,7 @@ impl TcpAcceptor { } pub fn socket_name(&mut self) -> IoResult<ip::SocketAddr> { - sockname(self.fd(), libc::getsockname) + net::sockname(self.socket(), libc::getsockname) } pub fn set_timeout(&mut self, timeout: Option<u64>) { diff --git a/src/libstd/sys/windows/thread.rs b/src/libstd/sys/windows/thread.rs new file mode 100644 index 00000000000..4498f56c00a --- /dev/null +++ b/src/libstd/sys/windows/thread.rs @@ -0,0 +1,96 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::prelude::*; + +use boxed::Box; +use cmp; +use mem; +use ptr; +use libc; +use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES, SIZE_T, BOOL, + LPVOID, DWORD, LPDWORD, HANDLE}; +use thunk::Thunk; +use sys_common::stack::RED_ZONE; +use sys_common::thread::*; + +pub type rust_thread = HANDLE; +pub type rust_thread_return = DWORD; + +pub type StartFn = extern "system" fn(*mut libc::c_void) -> rust_thread_return; + +#[no_stack_check] +pub extern "system" fn thread_start(main: *mut libc::c_void) -> rust_thread_return { + return start_thread(main); +} + +pub mod guard { + pub unsafe fn main() -> uint { + 0 + } + + pub unsafe fn current() -> uint { + 0 + } + + pub unsafe fn init() { + } +} + +pub unsafe fn create(stack: uint, p: Thunk) -> rust_thread { + let arg: *mut libc::c_void = mem::transmute(box p); + // FIXME On UNIX, we guard against stack sizes that are too small but + // that's because pthreads enforces that stacks are at least + // PTHREAD_STACK_MIN bytes big. Windows has no such lower limit, it's + // just that below a certain threshold you can't do anything useful. + // That threshold is application and architecture-specific, however. + // For now, the only requirement is that it's big enough to hold the + // red zone. Round up to the next 64 kB because that's what the NT + // kernel does, might as well make it explicit. With the current + // 20 kB red zone, that makes for a 64 kB minimum stack. + let stack_size = (cmp::max(stack, RED_ZONE) + 0xfffe) & (-0xfffe - 1); + let ret = CreateThread(ptr::null_mut(), stack_size as libc::size_t, + thread_start, arg, 0, ptr::null_mut()); + + if ret as uint == 0 { + // be sure to not leak the closure + let _p: Box<Thunk> = mem::transmute(arg); + panic!("failed to spawn native thread: {}", ret); + } + return ret; +} + +pub unsafe fn join(native: rust_thread) { + use libc::consts::os::extra::INFINITE; + WaitForSingleObject(native, INFINITE); +} + +pub unsafe fn detach(native: rust_thread) { + assert!(libc::CloseHandle(native) != 0); +} + +pub unsafe fn yield_now() { + // This function will return 0 if there are no other threads to execute, + // but this also means that the yield was useless so this isn't really a + // case that needs to be worried about. + SwitchToThread(); +} + +#[allow(non_snake_case)] +extern "system" { + fn CreateThread(lpThreadAttributes: LPSECURITY_ATTRIBUTES, + dwStackSize: SIZE_T, + lpStartAddress: StartFn, + lpParameter: LPVOID, + dwCreationFlags: DWORD, + lpThreadId: LPDWORD) -> HANDLE; + fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD; + fn SwitchToThread() -> BOOL; +} diff --git a/src/libstd/sys/windows/thread_local.rs b/src/libstd/sys/windows/thread_local.rs new file mode 100644 index 00000000000..60b0d584db3 --- /dev/null +++ b/src/libstd/sys/windows/thread_local.rs @@ -0,0 +1,259 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use prelude::*; + +use libc::types::os::arch::extra::{DWORD, LPVOID, BOOL}; + +use mem; +use rt; +use sys_common::mutex::{MUTEX_INIT, Mutex}; + +pub type Key = DWORD; +pub type Dtor = unsafe extern fn(*mut u8); + +// Turns out, like pretty much everything, Windows is pretty close the +// functionality that Unix provides, but slightly different! In the case of +// TLS, Windows does not provide an API to provide a destructor for a TLS +// variable. This ends up being pretty crucial to this implementation, so we +// need a way around this. +// +// The solution here ended up being a little obscure, but fear not, the +// internet has informed me [1][2] that this solution is not unique (no way +// I could have thought of it as well!). The key idea is to insert some hook +// somewhere to run arbitrary code on thread termination. With this in place +// we'll be able to run anything we like, including all TLS destructors! +// +// To accomplish this feat, we perform a number of tasks, all contained +// within this module: +// +// * All TLS destructors are tracked by *us*, not the windows runtime. This +// means that we have a global list of destructors for each TLS key that +// we know about. +// * When a TLS key is destroyed, we're sure to remove it from the dtor list +// if it's in there. +// * When a thread exits, we run over the entire list and run dtors for all +// non-null keys. This attempts to match Unix semantics in this regard. +// +// This ends up having the overhead of using a global list, having some +// locks here and there, and in general just adding some more code bloat. We +// attempt to optimize runtime by forgetting keys that don't have +// destructors, but this only gets us so far. +// +// For more details and nitty-gritty, see the code sections below! +// +// [1]: http://www.codeproject.com/Articles/8113/Thread-Local-Storage-The-C-Way +// [2]: https://github.com/ChromiumWebApps/chromium/blob/master/base +// /threading/thread_local_storage_win.cc#L42 + +// NB these are specifically not types from `std::sync` as they currently rely +// on poisoning and this module needs to operate at a lower level than requiring +// the thread infrastructure to be in place (useful on the borders of +// initialization/destruction). +static DTOR_LOCK: Mutex = MUTEX_INIT; +static mut DTORS: *mut Vec<(Key, Dtor)> = 0 as *mut _; + +// ------------------------------------------------------------------------- +// Native bindings +// +// This section is just raw bindings to the native functions that Windows +// provides, There's a few extra calls to deal with destructors. + +#[inline] +pub unsafe fn create(dtor: Option<Dtor>) -> Key { + const TLS_OUT_OF_INDEXES: DWORD = 0xFFFFFFFF; + let key = TlsAlloc(); + assert!(key != TLS_OUT_OF_INDEXES); + match dtor { + Some(f) => register_dtor(key, f), + None => {} + } + return key; +} + +#[inline] +pub unsafe fn set(key: Key, value: *mut u8) { + let r = TlsSetValue(key, value as LPVOID); + debug_assert!(r != 0); +} + +#[inline] +pub unsafe fn get(key: Key) -> *mut u8 { + TlsGetValue(key) as *mut u8 +} + +#[inline] +pub unsafe fn destroy(key: Key) { + if unregister_dtor(key) { + // FIXME: Currently if a key has a destructor associated with it we + // can't actually ever unregister it. If we were to + // unregister it, then any key destruction would have to be + // serialized with respect to actually running destructors. + // + // We want to avoid a race where right before run_dtors runs + // some destructors TlsFree is called. Allowing the call to + // TlsFree would imply that the caller understands that *all + // known threads* are not exiting, which is quite a difficult + // thing to know! + // + // For now we just leak all keys with dtors to "fix" this. + // Note that source [2] above shows precedent for this sort + // of strategy. + } else { + let r = TlsFree(key); + debug_assert!(r != 0); + } +} + +extern "system" { + fn TlsAlloc() -> DWORD; + fn TlsFree(dwTlsIndex: DWORD) -> BOOL; + fn TlsGetValue(dwTlsIndex: DWORD) -> LPVOID; + fn TlsSetValue(dwTlsIndex: DWORD, lpTlsvalue: LPVOID) -> BOOL; +} + +// ------------------------------------------------------------------------- +// Dtor registration +// +// These functions are associated with registering and unregistering +// destructors. They're pretty simple, they just push onto a vector and scan +// a vector currently. +// +// FIXME: This could probably be at least a little faster with a BTree. + +unsafe fn init_dtors() { + if !DTORS.is_null() { return } + + let dtors = box Vec::<(Key, Dtor)>::new(); + DTORS = mem::transmute(dtors); + + rt::at_exit(move|| { + DTOR_LOCK.lock(); + let dtors = DTORS; + DTORS = 0 as *mut _; + mem::transmute::<_, Box<Vec<(Key, Dtor)>>>(dtors); + assert!(DTORS.is_null()); // can't re-init after destructing + DTOR_LOCK.unlock(); + }); +} + +unsafe fn register_dtor(key: Key, dtor: Dtor) { + DTOR_LOCK.lock(); + init_dtors(); + (*DTORS).push((key, dtor)); + DTOR_LOCK.unlock(); +} + +unsafe fn unregister_dtor(key: Key) -> bool { + DTOR_LOCK.lock(); + init_dtors(); + let ret = { + let dtors = &mut *DTORS; + let before = dtors.len(); + dtors.retain(|&(k, _)| k != key); + dtors.len() != before + }; + DTOR_LOCK.unlock(); + ret +} + +// ------------------------------------------------------------------------- +// Where the Magic (TM) Happens +// +// If you're looking at this code, and wondering "what is this doing?", +// you're not alone! I'll try to break this down step by step: +// +// # What's up with CRT$XLB? +// +// For anything about TLS destructors to work on Windows, we have to be able +// to run *something* when a thread exits. To do so, we place a very special +// static in a very special location. If this is encoded in just the right +// way, the kernel's loader is apparently nice enough to run some function +// of ours whenever a thread exits! How nice of the kernel! +// +// Lots of detailed information can be found in source [1] above, but the +// gist of it is that this is leveraging a feature of Microsoft's PE format +// (executable format) which is not actually used by any compilers today. +// This apparently translates to any callbacks in the ".CRT$XLB" section +// being run on certain events. +// +// So after all that, we use the compiler's #[link_section] feature to place +// a callback pointer into the magic section so it ends up being called. +// +// # What's up with this callback? +// +// The callback specified receives a number of parameters from... someone! +// (the kernel? the runtime? I'm not qute sure!) There are a few events that +// this gets invoked for, but we're currentl only interested on when a +// thread or a process "detaches" (exits). The process part happens for the +// last thread and the thread part happens for any normal thread. +// +// # Ok, what's up with running all these destructors? +// +// This will likely need to be improved over time, but this function +// attempts a "poor man's" destructor callback system. To do this we clone a +// local copy of the dtor list to start out with. This is our fudgy attempt +// to not hold the lock while destructors run and not worry about the list +// changing while we're looking at it. +// +// Once we've got a list of what to run, we iterate over all keys, check +// their values, and then run destructors if the values turn out to be non +// null (setting them to null just beforehand). We do this a few times in a +// loop to basically match Unix semantics. If we don't reach a fixed point +// after a short while then we just inevitably leak something most likely. +// +// # The article mentions crazy stuff about "/INCLUDE"? +// +// It sure does! This seems to work for now, so maybe we'll just run into +// that if we start linking with msvc? + +#[link_section = ".CRT$XLB"] +#[linkage = "external"] +#[allow(warnings)] +pub static p_thread_callback: unsafe extern "system" fn(LPVOID, DWORD, + LPVOID) = + on_tls_callback; + +#[allow(warnings)] +unsafe extern "system" fn on_tls_callback(h: LPVOID, + dwReason: DWORD, + pv: LPVOID) { + const DLL_THREAD_DETACH: DWORD = 3; + const DLL_PROCESS_DETACH: DWORD = 0; + if dwReason == DLL_THREAD_DETACH || dwReason == DLL_PROCESS_DETACH { + run_dtors(); + } +} + +unsafe fn run_dtors() { + let mut any_run = true; + for _ in range(0, 5i) { + if !any_run { break } + any_run = false; + let dtors = { + DTOR_LOCK.lock(); + let ret = if DTORS.is_null() { + Vec::new() + } else { + (*DTORS).iter().map(|s| *s).collect() + }; + DTOR_LOCK.unlock(); + ret + }; + for &(key, dtor) in dtors.iter() { + let ptr = TlsGetValue(key); + if !ptr.is_null() { + TlsSetValue(key, 0 as *mut _); + dtor(ptr as *mut _); + any_run = true; + } + } + } +} diff --git a/src/libstd/sys/windows/timer.rs b/src/libstd/sys/windows/timer.rs index 9af3a7c8b6e..7e4dd768aa9 100644 --- a/src/libstd/sys/windows/timer.rs +++ b/src/libstd/sys/windows/timer.rs @@ -20,7 +20,7 @@ //! Other than that, the implementation is pretty straightforward in terms of //! the other two implementations of timers with nothing *that* new showing up. -pub use self::Req::*; +use self::Req::*; use libc; use ptr; @@ -32,7 +32,7 @@ use sys_common::helper_thread::Helper; use prelude::*; use io::IoResult; -helper_init!(static HELPER: Helper<Req>) +helper_init! { static HELPER: Helper<Req> } pub trait Callback { fn call(&mut self); diff --git a/src/libstd/sys/windows/tty.rs b/src/libstd/sys/windows/tty.rs index 0e7b06cbb94..f793de5bb57 100644 --- a/src/libstd/sys/windows/tty.rs +++ b/src/libstd/sys/windows/tty.rs @@ -111,9 +111,9 @@ impl TTY { } pub fn write(&mut self, buf: &[u8]) -> IoResult<()> { - let utf16 = match from_utf8(buf) { + let utf16 = match from_utf8(buf).ok() { Some(utf8) => { - utf8.as_slice().utf16_units().collect::<Vec<u16>>() + utf8.utf16_units().collect::<Vec<u16>>() } None => return Err(invalid_encoding()), }; |
