| @@ -0,0 +1,358 @@ | |||
| // 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::{mod, AtomicUint}; | |||
| use sync::{mutex, StaticMutexGuard}; | |||
| use sys_common::condvar as sys; | |||
| use sys_common::mutex as sys_mutex; | |||
| use time::Duration; | |||
|
|
|||
| /// A Condition Variable | |||
| /// | |||
| /// Condition variables represent the ability to block a thread such that it | |||
| /// consumes no CPU time while waiting for an event to occur. Condition | |||
| /// variables are typically associated with a boolean predicate (a condition) | |||
| /// and a mutex. The predicate is always verified inside of the mutex before | |||
| /// determining that thread must block. | |||
| /// | |||
| /// Functions in this module will block the current **thread** of execution and | |||
| /// are bindings to system-provided condition variables where possible. Note | |||
| /// that this module places one additional restriction over the system condition | |||
| /// variables: each condvar can be used with precisely one mutex at runtime. Any | |||
| /// attempt to use multiple mutexes on the same condition variable will result | |||
| /// in a runtime panic. If this is not desired, then the unsafe primitives in | |||
| /// `sys` do not have this restriction but may result in undefined behavior. | |||
| /// | |||
| /// # Example | |||
| /// | |||
| /// ``` | |||
| /// use std::sync::{Arc, Mutex, Condvar}; | |||
| /// | |||
| /// let pair = Arc::new((Mutex::new(false), Condvar::new())); | |||
| /// let pair2 = pair.clone(); | |||
| /// | |||
| /// // Inside of our lock, spawn a new thread, and then wait for it to start | |||
| /// spawn(proc() { | |||
| /// let &(ref lock, ref cvar) = &*pair2; | |||
| /// let mut started = lock.lock(); | |||
| /// *started = true; | |||
| /// cvar.notify_one(); | |||
| /// }); | |||
| /// | |||
| /// // wait for the thread to start up | |||
| /// let &(ref lock, ref cvar) = &*pair; | |||
| /// let started = lock.lock(); | |||
| /// while !*started { | |||
| /// cvar.wait(&started); | |||
| /// } | |||
| /// ``` | |||
| pub struct Condvar { inner: Box<StaticCondvar> } | |||
|
|
|||
| /// Statically allocated condition variables. | |||
| /// | |||
| /// This structure is identical to `Condvar` except that it is suitable for use | |||
| /// in static initializers for other structures. | |||
| /// | |||
| /// # Example | |||
| /// | |||
| /// ``` | |||
| /// use std::sync::{StaticCondvar, CONDVAR_INIT}; | |||
| /// | |||
| /// static CVAR: StaticCondvar = CONDVAR_INIT; | |||
| /// ``` | |||
| pub struct StaticCondvar { | |||
| inner: sys::Condvar, | |||
| mutex: AtomicUint, | |||
| } | |||
|
|
|||
| /// Constant initializer for a statically allocated condition variable. | |||
| pub const CONDVAR_INIT: StaticCondvar = StaticCondvar { | |||
| inner: sys::CONDVAR_INIT, | |||
| mutex: atomic::INIT_ATOMIC_UINT, | |||
| }; | |||
|
|
|||
| /// A trait for vaules which can be passed to the waiting methods of condition | |||
| /// variables. This is implemented by the mutex guards in this module. | |||
| /// | |||
| /// Note that this trait should likely not be implemented manually unless you | |||
| /// really know what you're doing. | |||
| pub trait AsMutexGuard { | |||
| #[allow(missing_docs)] | |||
| unsafe fn as_mutex_guard(&self) -> &StaticMutexGuard; | |||
| } | |||
|
|
|||
| impl Condvar { | |||
| /// Creates a new condition variable which is ready to be waited on and | |||
| /// notified. | |||
| pub fn new() -> Condvar { | |||
| Condvar { | |||
| inner: box StaticCondvar { | |||
| inner: unsafe { sys::Condvar::new() }, | |||
| mutex: AtomicUint::new(0), | |||
| } | |||
| } | |||
| } | |||
|
|
|||
| /// Block the current thread until this condition variable receives a | |||
| /// notification. | |||
| /// | |||
| /// This function will atomically unlock the mutex specified (represented by | |||
| /// `guard`) and block the current thread. This means that any calls to | |||
| /// `notify_*()` which happen logically after the mutex is unlocked are | |||
| /// candidates to wake this thread up. When this function call returns, the | |||
| /// lock specified will have been re-acquired. | |||
| /// | |||
| /// Note that this function is susceptible to spurious wakeups. Condition | |||
| /// variables normally have a boolean predicate associated with them, and | |||
| /// the predicate must always be checked each time this function returns to | |||
| /// protect against spurious wakeups. | |||
| /// | |||
| /// # Panics | |||
| /// | |||
| /// This function will `panic!()` if it is used with more than one mutex | |||
| /// over time. Each condition variable is dynamically bound to exactly one | |||
| /// mutex to ensure defined behavior across platforms. If this functionality | |||
| /// is not desired, then unsafe primitives in `sys` are provided. | |||
| pub fn wait<T: AsMutexGuard>(&self, mutex_guard: &T) { | |||
| unsafe { | |||
| let me: &'static Condvar = &*(self as *const _); | |||
| me.inner.wait(mutex_guard) | |||
| } | |||
| } | |||
|
|
|||
| /// Wait on this condition variable for a notification, timing out after a | |||
| /// specified duration. | |||
| /// | |||
| /// The semantics of this function are equivalent to `wait()` except that | |||
| /// the thread will be blocked for roughly no longer than `dur`. This method | |||
| /// should not be used for precise timing due to anomalies such as | |||
| /// preemption or platform differences that may not cause the maximum amount | |||
| /// of time waited to be precisely `dur`. | |||
| /// | |||
| /// If the wait timed out, then `false` will be returned. Otherwise if a | |||
| /// notification was received then `true` will be returned. | |||
| /// | |||
| /// Like `wait`, the lock specified will be re-acquired when this function | |||
| /// returns, regardless of whether the timeout elapsed or not. | |||
| pub fn wait_timeout<T: AsMutexGuard>(&self, mutex_guard: &T, | |||
| dur: Duration) -> bool { | |||
| unsafe { | |||
| let me: &'static Condvar = &*(self as *const _); | |||
| me.inner.wait_timeout(mutex_guard, dur) | |||
| } | |||
| } | |||
|
|
|||
| /// Wake up one blocked thread on this condvar. | |||
| /// | |||
| /// If there is a blocked thread on this condition variable, then it will | |||
| /// be woken up from its call to `wait` or `wait_timeout`. Calls to | |||
| /// `notify_one` are not buffered in any way. | |||
| /// | |||
| /// To wake up all threads, see `notify_one()`. | |||
| pub fn notify_one(&self) { unsafe { self.inner.inner.notify_one() } } | |||
|
|
|||
| /// Wake up all blocked threads on this condvar. | |||
| /// | |||
| /// This method will ensure that any current waiters on the condition | |||
| /// variable are awoken. Calls to `notify_all()` are not buffered in any | |||
| /// way. | |||
| /// | |||
| /// To wake up only one thread, see `notify_one()`. | |||
| pub fn notify_all(&self) { unsafe { self.inner.inner.notify_all() } } | |||
| } | |||
|
|
|||
| impl Drop for Condvar { | |||
| fn drop(&mut self) { | |||
| unsafe { self.inner.inner.destroy() } | |||
| } | |||
| } | |||
|
|
|||
| impl StaticCondvar { | |||
| /// Block the current thread until this condition variable receives a | |||
| /// notification. | |||
| /// | |||
| /// See `Condvar::wait`. | |||
| pub fn wait<T: AsMutexGuard>(&'static self, mutex_guard: &T) { | |||
| unsafe { | |||
| let lock = mutex_guard.as_mutex_guard(); | |||
| let sys = mutex::guard_lock(lock); | |||
| self.verify(sys); | |||
| self.inner.wait(sys); | |||
| (*mutex::guard_poison(lock)).check("mutex"); | |||
| } | |||
| } | |||
|
|
|||
| /// Wait on this condition variable for a notification, timing out after a | |||
| /// specified duration. | |||
| /// | |||
| /// See `Condvar::wait_timeout`. | |||
| pub fn wait_timeout<T: AsMutexGuard>(&'static self, mutex_guard: &T, | |||
| dur: Duration) -> bool { | |||
| unsafe { | |||
| let lock = mutex_guard.as_mutex_guard(); | |||
| let sys = mutex::guard_lock(lock); | |||
| self.verify(sys); | |||
| let ret = self.inner.wait_timeout(sys, dur); | |||
| (*mutex::guard_poison(lock)).check("mutex"); | |||
| return ret; | |||
| } | |||
| } | |||
|
|
|||
| /// Wake up one blocked thread on this condvar. | |||
| /// | |||
| /// See `Condvar::notify_one`. | |||
| pub fn notify_one(&'static self) { unsafe { self.inner.notify_one() } } | |||
|
|
|||
| /// Wake up all blocked threads on this condvar. | |||
| /// | |||
| /// See `Condvar::notify_all`. | |||
| pub fn notify_all(&'static self) { unsafe { self.inner.notify_all() } } | |||
|
|
|||
| /// Deallocate all resources associated with this static condvar. | |||
| /// | |||
| /// This method is unsafe to call as there is no guarantee that there are no | |||
| /// active users of the condvar, and this also doesn't prevent any future | |||
| /// users of the condvar. This method is required to be called to not leak | |||
| /// memory on all platforms. | |||
| pub unsafe fn destroy(&'static self) { | |||
| self.inner.destroy() | |||
| } | |||
|
|
|||
| fn verify(&self, mutex: &sys_mutex::Mutex) { | |||
| let addr = mutex as *const _ as uint; | |||
| match self.mutex.compare_and_swap(0, addr, atomic::SeqCst) { | |||
| // If we got out 0, then we have successfully bound the mutex to | |||
| // this cvar. | |||
| 0 => {} | |||
|
|
|||
| // If we get out a value that's the same as `addr`, then someone | |||
| // already beat us to the punch. | |||
| n if n == addr => {} | |||
|
|
|||
| // Anything else and we're using more than one mutex on this cvar, | |||
| // which is currently disallowed. | |||
| _ => panic!("attempted to use a condition variable with two \ | |||
| mutexes"), | |||
| } | |||
| } | |||
| } | |||
|
|
|||
| #[cfg(test)] | |||
| mod tests { | |||
| use prelude::*; | |||
|
|
|||
| use time::Duration; | |||
| use super::{StaticCondvar, CONDVAR_INIT}; | |||
| use sync::{StaticMutex, MUTEX_INIT, Condvar, Mutex, Arc}; | |||
|
|
|||
| #[test] | |||
| fn smoke() { | |||
| let c = Condvar::new(); | |||
| c.notify_one(); | |||
| c.notify_all(); | |||
| } | |||
|
|
|||
| #[test] | |||
| fn static_smoke() { | |||
| static C: StaticCondvar = CONDVAR_INIT; | |||
| C.notify_one(); | |||
| C.notify_all(); | |||
| unsafe { C.destroy(); } | |||
| } | |||
|
|
|||
| #[test] | |||
| fn notify_one() { | |||
| static C: StaticCondvar = CONDVAR_INIT; | |||
| static M: StaticMutex = MUTEX_INIT; | |||
|
|
|||
| let g = M.lock(); | |||
| spawn(proc() { | |||
| let _g = M.lock(); | |||
| C.notify_one(); | |||
| }); | |||
| C.wait(&g); | |||
| drop(g); | |||
| unsafe { C.destroy(); M.destroy(); } | |||
| } | |||
|
|
|||
| #[test] | |||
| fn notify_all() { | |||
| const N: uint = 10; | |||
|
|
|||
| let data = Arc::new((Mutex::new(0), Condvar::new())); | |||
| let (tx, rx) = channel(); | |||
| for _ in range(0, N) { | |||
| let data = data.clone(); | |||
| let tx = tx.clone(); | |||
| spawn(proc() { | |||
| let &(ref lock, ref cond) = &*data; | |||
| let mut cnt = lock.lock(); | |||
| *cnt += 1; | |||
| if *cnt == N { | |||
| tx.send(()); | |||
| } | |||
| while *cnt != 0 { | |||
| cond.wait(&cnt); | |||
| } | |||
| tx.send(()); | |||
| }); | |||
| } | |||
| drop(tx); | |||
|
|
|||
| let &(ref lock, ref cond) = &*data; | |||
| rx.recv(); | |||
| let mut cnt = lock.lock(); | |||
| *cnt = 0; | |||
| cond.notify_all(); | |||
| drop(cnt); | |||
|
|
|||
| for _ in range(0, N) { | |||
| rx.recv(); | |||
| } | |||
| } | |||
|
|
|||
| #[test] | |||
| fn wait_timeout() { | |||
| static C: StaticCondvar = CONDVAR_INIT; | |||
| static M: StaticMutex = MUTEX_INIT; | |||
|
|
|||
| let g = M.lock(); | |||
| assert!(!C.wait_timeout(&g, Duration::nanoseconds(1000))); | |||
| spawn(proc() { | |||
| let _g = M.lock(); | |||
| C.notify_one(); | |||
| }); | |||
| assert!(C.wait_timeout(&g, Duration::days(1))); | |||
| drop(g); | |||
| unsafe { C.destroy(); M.destroy(); } | |||
| } | |||
|
|
|||
| #[test] | |||
| #[should_fail] | |||
| fn two_mutexes() { | |||
| static M1: StaticMutex = MUTEX_INIT; | |||
| static M2: StaticMutex = MUTEX_INIT; | |||
| static C: StaticCondvar = CONDVAR_INIT; | |||
|
|
|||
| let g = M1.lock(); | |||
| spawn(proc() { | |||
| let _g = M1.lock(); | |||
| C.notify_one(); | |||
| }); | |||
| C.wait(&g); | |||
| drop(g); | |||
|
|
|||
| C.wait(&M2.lock()); | |||
|
|
|||
| } | |||
| } | |||
|
|
|||
| @@ -0,0 +1,48 @@ | |||
| // 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 option::None; | |||
| use rustrt::task::Task; | |||
| use rustrt::local::Local; | |||
|
|
|||
| pub struct Flag { pub failed: bool } | |||
|
|
|||
| impl Flag { | |||
| pub fn borrow(&mut self) -> Guard { | |||
| Guard { flag: &mut self.failed, failing: failing() } | |||
| } | |||
| } | |||
|
|
|||
| pub struct Guard<'a> { | |||
| flag: &'a mut bool, | |||
| failing: bool, | |||
| } | |||
|
|
|||
| impl<'a> Guard<'a> { | |||
| pub fn check(&self, name: &str) { | |||
| if *self.flag { | |||
| panic!("poisoned {} - another task failed inside", name); | |||
| } | |||
| } | |||
|
|
|||
| pub fn done(&mut self) { | |||
| if !self.failing && failing() { | |||
| *self.flag = true; | |||
| } | |||
| } | |||
| } | |||
|
|
|||
| fn failing() -> bool { | |||
| if Local::exists(None::<Task>) { | |||
| Local::borrow(None::<Task>).unwinder.unwinding() | |||
| } else { | |||
| false | |||
| } | |||
| } | |||
| @@ -0,0 +1,195 @@ | |||
| // 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 ops::Drop; | |||
| use sync::{Mutex, Condvar}; | |||
|
|
|||
| /// A counting, blocking, semaphore. | |||
| /// | |||
| /// Semaphores are a form of atomic counter where access is only granted if the | |||
| /// counter is a positive value. Each acquisition will block the calling thread | |||
| /// until the counter is positive, and each release will increment the counter | |||
| /// and unblock any threads if necessary. | |||
| /// | |||
| /// # Example | |||
| /// | |||
| /// ``` | |||
| /// use std::sync::Semaphore; | |||
| /// | |||
| /// // Create a semaphore that represents 5 resources | |||
| /// let sem = Semaphore::new(5); | |||
| /// | |||
| /// // Acquire one of the resources | |||
| /// sem.acquire(); | |||
| /// | |||
| /// // Acquire one of the resources for a limited period of time | |||
| /// { | |||
| /// let _guard = sem.access(); | |||
| /// // ... | |||
| /// } // resources is released here | |||
| /// | |||
| /// // Release our initially acquired resource | |||
| /// sem.release(); | |||
| /// ``` | |||
| pub struct Semaphore { | |||
| lock: Mutex<int>, | |||
| cvar: Condvar, | |||
| } | |||
|
|
|||
| /// An RAII guard which will release a resource acquired from a semaphore when | |||
| /// dropped. | |||
| pub struct SemaphoreGuard<'a> { | |||
| sem: &'a Semaphore, | |||
| } | |||
|
|
|||
| impl Semaphore { | |||
| /// Creates a new semaphore with the initial count specified. | |||
| /// | |||
| /// The count specified can be thought of as a number of resources, and a | |||
| /// call to `acquire` or `access` will block until at least one resource is | |||
| /// available. It is valid to initialize a semaphore with a negative count. | |||
| pub fn new(count: int) -> Semaphore { | |||
| Semaphore { | |||
| lock: Mutex::new(count), | |||
| cvar: Condvar::new(), | |||
| } | |||
| } | |||
|
|
|||
| /// Acquires a resource of this semaphore, blocking the current thread until | |||
| /// it can do so. | |||
| /// | |||
| /// This method will block until the internal count of the semaphore is at | |||
| /// least 1. | |||
| pub fn acquire(&self) { | |||
| let mut count = self.lock.lock(); | |||
| while *count <= 0 { | |||
| self.cvar.wait(&count); | |||
| } | |||
| *count -= 1; | |||
| } | |||
|
|
|||
| /// Release a resource from this semaphore. | |||
| /// | |||
| /// This will increment the number of resources in this semaphore by 1 and | |||
| /// will notify any pending waiters in `acquire` or `access` if necessary. | |||
| pub fn release(&self) { | |||
| *self.lock.lock() += 1; | |||
| self.cvar.notify_one(); | |||
| } | |||
|
|
|||
| /// Acquires a resource of this semaphore, returning an RAII guard to | |||
| /// release the semaphore when dropped. | |||
| /// | |||
| /// This function is semantically equivalent to an `acquire` followed by a | |||
| /// `release` when the guard returned is dropped. | |||
| pub fn access(&self) -> SemaphoreGuard { | |||
| self.acquire(); | |||
| SemaphoreGuard { sem: self } | |||
| } | |||
| } | |||
|
|
|||
| #[unsafe_destructor] | |||
| impl<'a> Drop for SemaphoreGuard<'a> { | |||
| fn drop(&mut self) { | |||
| self.sem.release(); | |||
| } | |||
| } | |||
|
|
|||
| #[cfg(test)] | |||
| mod tests { | |||
| use prelude::*; | |||
|
|
|||
| use sync::Arc; | |||
| use super::Semaphore; | |||
|
|
|||
| #[test] | |||
| fn test_sem_acquire_release() { | |||
| let s = Semaphore::new(1); | |||
| s.acquire(); | |||
| s.release(); | |||
| s.acquire(); | |||
| } | |||
|
|
|||
| #[test] | |||
| fn test_sem_basic() { | |||
| let s = Semaphore::new(1); | |||
| let _g = s.access(); | |||
| } | |||
|
|
|||
| #[test] | |||
| fn test_sem_as_mutex() { | |||
| let s = Arc::new(Semaphore::new(1)); | |||
| let s2 = s.clone(); | |||
| spawn(proc() { | |||
| let _g = s2.access(); | |||
| }); | |||
| let _g = s.access(); | |||
| } | |||
|
|
|||
| #[test] | |||
| fn test_sem_as_cvar() { | |||
| /* Child waits and parent signals */ | |||
| let (tx, rx) = channel(); | |||
| let s = Arc::new(Semaphore::new(0)); | |||
| let s2 = s.clone(); | |||
| spawn(proc() { | |||
| s2.acquire(); | |||
| tx.send(()); | |||
| }); | |||
| s.release(); | |||
| let _ = rx.recv(); | |||
|
|
|||
| /* Parent waits and child signals */ | |||
| let (tx, rx) = channel(); | |||
| let s = Arc::new(Semaphore::new(0)); | |||
| let s2 = s.clone(); | |||
| spawn(proc() { | |||
| s2.release(); | |||
| let _ = rx.recv(); | |||
| }); | |||
| s.acquire(); | |||
| tx.send(()); | |||
| } | |||
|
|
|||
| #[test] | |||
| fn test_sem_multi_resource() { | |||
| // Parent and child both get in the critical section at the same | |||
| // time, and shake hands. | |||
| let s = Arc::new(Semaphore::new(2)); | |||
| let s2 = s.clone(); | |||
| let (tx1, rx1) = channel(); | |||
| let (tx2, rx2) = channel(); | |||
| spawn(proc() { | |||
| let _g = s2.access(); | |||
| let _ = rx2.recv(); | |||
| tx1.send(()); | |||
| }); | |||
| let _g = s.access(); | |||
| tx2.send(()); | |||
| let _ = rx1.recv(); | |||
| } | |||
|
|
|||
| #[test] | |||
| fn test_sem_runtime_friendly_blocking() { | |||
| let s = Arc::new(Semaphore::new(1)); | |||
| let s2 = s.clone(); | |||
| let (tx, rx) = channel(); | |||
| { | |||
| let _g = s.access(); | |||
| spawn(proc() { | |||
| tx.send(()); | |||
| drop(s2.access()); | |||
| tx.send(()); | |||
| }); | |||
| rx.recv(); // wait for child to come alive | |||
| } | |||
| rx.recv(); // wait for child to be done | |||
| } | |||
| } | |||
| @@ -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() } | |||
| } | |||
| @@ -0,0 +1,64 @@ | |||
| // 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. | |||
|
|
|||
| pub use sys::mutex::raw; | |||
|
|
|||
| 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 } | |||
| @@ -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() } | |||
| } | |||
| @@ -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); | |||
| } | |||
| } | |||
| @@ -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); | |||
| } | |||
| } | |||
| @@ -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); | |||
| } | |||
| } | |||
| @@ -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], | |||
| }; | |||
| } | |||
| @@ -0,0 +1,63 @@ | |||
| // 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 libc; | |||
| 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) { | |||
| // ... | |||
| } | |||
| } | |||
| @@ -0,0 +1,76 @@ | |||
| // 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 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: &super::Mutex) -> ffi::LPCRITICAL_SECTION { | |||
| m.0.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); | |||
| } | |||
| @@ -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) { | |||
| // ... | |||
| } | |||
| } | |||
| @@ -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; | |||
| } | |||
|
|
|||