diff options
| author | Nick Cameron <ncameron@mozilla.com> | 2015-05-12 12:48:14 +1200 |
|---|---|---|
| committer | Nick Cameron <ncameron@mozilla.com> | 2015-05-12 12:48:14 +1200 |
| commit | e0216fcc42d5f4961d07378a783c87814097015f (patch) | |
| tree | e5f503c129942a2b7c2815b1c8e4db85b5bbcf1b /src/libstd/sync | |
| parent | aa5ca282b20391c6df51fdfa94e0de5672ccfac1 (diff) | |
| parent | 750f2c63f2737305d33288303cdecb7a470a7922 (diff) | |
| download | rust-e0216fcc42d5f4961d07378a783c87814097015f.tar.gz rust-e0216fcc42d5f4961d07378a783c87814097015f.zip | |
Merge branch 'master' into
Diffstat (limited to 'src/libstd/sync')
| -rw-r--r-- | src/libstd/sync/barrier.rs | 4 | ||||
| -rw-r--r-- | src/libstd/sync/mpsc/mod.rs | 26 | ||||
| -rw-r--r-- | src/libstd/sync/mpsc/mpsc_queue.rs | 2 | ||||
| -rw-r--r-- | src/libstd/sync/mpsc/oneshot.rs | 14 | ||||
| -rw-r--r-- | src/libstd/sync/mpsc/select.rs | 32 | ||||
| -rw-r--r-- | src/libstd/sync/mpsc/shared.rs | 6 | ||||
| -rw-r--r-- | src/libstd/sync/mpsc/spsc_queue.rs | 2 | ||||
| -rw-r--r-- | src/libstd/sync/mpsc/stream.rs | 8 | ||||
| -rw-r--r-- | src/libstd/sync/mpsc/sync.rs | 18 | ||||
| -rw-r--r-- | src/libstd/sync/mutex.rs | 56 | ||||
| -rw-r--r-- | src/libstd/sync/once.rs | 4 | ||||
| -rw-r--r-- | src/libstd/sync/rwlock.rs | 103 |
12 files changed, 168 insertions, 107 deletions
diff --git a/src/libstd/sync/barrier.rs b/src/libstd/sync/barrier.rs index 34fcf6cdadd..8360620c345 100644 --- a/src/libstd/sync/barrier.rs +++ b/src/libstd/sync/barrier.rs @@ -10,7 +10,7 @@ use sync::{Mutex, Condvar}; -/// A barrier enables multiple tasks to synchronize the beginning +/// A barrier enables multiple threads to synchronize the beginning /// of some computation. /// /// ``` @@ -128,7 +128,7 @@ mod tests { }); } - // At this point, all spawned tasks should be blocked, + // At this point, all spawned threads should be blocked, // so we shouldn't get anything from the port assert!(match rx.try_recv() { Err(TryRecvError::Empty) => true, diff --git a/src/libstd/sync/mpsc/mod.rs b/src/libstd/sync/mpsc/mod.rs index 61932225d79..77aeeca7968 100644 --- a/src/libstd/sync/mpsc/mod.rs +++ b/src/libstd/sync/mpsc/mod.rs @@ -107,7 +107,7 @@ //! //! let (tx, rx) = sync_channel::<i32>(0); //! thread::spawn(move|| { -//! // This will wait for the parent task to start receiving +//! // This will wait for the parent thread to start receiving //! tx.send(53).unwrap(); //! }); //! rx.recv().unwrap(); @@ -253,7 +253,7 @@ // blocking. The implementation is essentially the entire blocking procedure // followed by an increment as soon as its woken up. The cancellation procedure // involves an increment and swapping out of to_wake to acquire ownership of the -// task to unblock. +// thread to unblock. // // Sadly this current implementation requires multiple allocations, so I have // seen the throughput of select() be much worse than it should be. I do not @@ -272,6 +272,7 @@ use error; use fmt; use mem; use cell::UnsafeCell; +use marker::Reflect; pub use self::select::{Select, Handle}; use self::select::StartResult; @@ -288,7 +289,7 @@ mod mpsc_queue; mod spsc_queue; /// The receiving-half of Rust's channel type. This half can only be owned by -/// one task +/// one thread #[stable(feature = "rust1", since = "1.0.0")] pub struct Receiver<T> { inner: UnsafeCell<Flavor<T>>, @@ -315,7 +316,7 @@ pub struct IntoIter<T> { } /// The sending-half of Rust's asynchronous channel type. This half can only be -/// owned by one task, but it can be cloned to send to other tasks. +/// owned by one thread, but it can be cloned to send to other threads. #[stable(feature = "rust1", since = "1.0.0")] pub struct Sender<T> { inner: UnsafeCell<Flavor<T>>, @@ -326,7 +327,7 @@ pub struct Sender<T> { unsafe impl<T: Send> Send for Sender<T> { } /// The sending-half of Rust's synchronous channel type. This half can only be -/// owned by one task, but it can be cloned to send to other tasks. +/// owned by one thread, but it can be cloned to send to other threads. #[stable(feature = "rust1", since = "1.0.0")] pub struct SyncSender<T> { inner: Arc<UnsafeCell<sync::Packet<T>>>, @@ -420,7 +421,7 @@ impl<T> UnsafeFlavor<T> for Receiver<T> { /// Creates a new asynchronous channel, returning the sender/receiver halves. /// /// All data sent on the sender will become available on the receiver, and no -/// send will block the calling task (this channel has an "infinite buffer"). +/// send will block the calling thread (this channel has an "infinite buffer"). /// /// # Examples /// @@ -955,8 +956,7 @@ impl<T> fmt::Display for SendError<T> { } #[stable(feature = "rust1", since = "1.0.0")] -impl<T: Send> error::Error for SendError<T> { - +impl<T: Send + Reflect> error::Error for SendError<T> { fn description(&self) -> &str { "sending on a closed channel" } @@ -991,7 +991,7 @@ impl<T> fmt::Display for TrySendError<T> { } #[stable(feature = "rust1", since = "1.0.0")] -impl<T: Send> error::Error for TrySendError<T> { +impl<T: Send + Reflect> error::Error for TrySendError<T> { fn description(&self) -> &str { match *self { @@ -1596,7 +1596,7 @@ mod tests { drop(rx); // destroy a shared tx2.send(()).unwrap(); }); - // make sure the other task has gone to sleep + // make sure the other thread has gone to sleep for _ in 0..5000 { thread::yield_now(); } // upgrade to a shared chan and send a message @@ -1604,7 +1604,7 @@ mod tests { drop(tx); t.send(()).unwrap(); - // wait for the child task to exit before we exit + // wait for the child thread to exit before we exit rx2.recv().unwrap(); } } @@ -2060,7 +2060,7 @@ mod sync_tests { drop(rx); // destroy a shared tx2.send(()).unwrap(); }); - // make sure the other task has gone to sleep + // make sure the other thread has gone to sleep for _ in 0..5000 { thread::yield_now(); } // upgrade to a shared chan and send a message @@ -2068,7 +2068,7 @@ mod sync_tests { drop(tx); t.send(()).unwrap(); - // wait for the child task to exit before we exit + // wait for the child thread to exit before we exit rx2.recv().unwrap(); } diff --git a/src/libstd/sync/mpsc/mpsc_queue.rs b/src/libstd/sync/mpsc/mpsc_queue.rs index 4ab5a796fcb..2c0da938cbf 100644 --- a/src/libstd/sync/mpsc/mpsc_queue.rs +++ b/src/libstd/sync/mpsc/mpsc_queue.rs @@ -28,7 +28,7 @@ //! A mostly lock-free multi-producer, single consumer queue. //! //! This module contains an implementation of a concurrent MPSC queue. This -//! queue can be used to share data between tasks, and is also used as the +//! queue can be used to share data between threads, and is also used as the //! building block of channels in rust. //! //! Note that the current implementation of this queue has a caveat of the `pop` diff --git a/src/libstd/sync/mpsc/oneshot.rs b/src/libstd/sync/mpsc/oneshot.rs index ab45b722c45..7e9c017617d 100644 --- a/src/libstd/sync/mpsc/oneshot.rs +++ b/src/libstd/sync/mpsc/oneshot.rs @@ -23,7 +23,7 @@ /// # Implementation /// /// Oneshots are implemented around one atomic usize variable. This variable -/// indicates both the state of the port/chan but also contains any tasks +/// indicates both the state of the port/chan but also contains any threads /// blocked on the port. All atomic operations happen on this one word. /// /// In order to upgrade a oneshot channel, an upgrade is considered a disconnect @@ -55,7 +55,7 @@ const DISCONNECTED: usize = 2; // channel is disconnected OR upgraded // whoever changed the state. pub struct Packet<T> { - // Internal state of the chan/port pair (stores the blocked task as well) + // Internal state of the chan/port pair (stores the blocked thread as well) state: AtomicUsize, // One-shot data slot location data: Option<T>, @@ -139,7 +139,7 @@ impl<T> Packet<T> { } pub fn recv(&mut self) -> Result<T, Failure<T>> { - // Attempt to not block the task (it's a little expensive). If it looks + // Attempt to not block the thread (it's a little expensive). If it looks // like we're not empty, then immediately go through to `try_recv`. if self.state.load(Ordering::SeqCst) == EMPTY { let (wait_token, signal_token) = blocking::tokens(); @@ -317,8 +317,8 @@ impl<T> Packet<T> { } } - // Remove a previous selecting task from this port. This ensures that the - // blocked task will no longer be visible to any other threads. + // Remove a previous selecting thread from this port. This ensures that the + // blocked thread will no longer be visible to any other threads. // // The return value indicates whether there's data on this port. pub fn abort_selection(&mut self) -> Result<bool, Receiver<T>> { @@ -329,7 +329,7 @@ impl<T> Packet<T> { s @ DATA | s @ DISCONNECTED => s, - // If we've got a blocked task, then use an atomic to gain ownership + // If we've got a blocked thread, then use an atomic to gain ownership // of it (may fail) ptr => self.state.compare_and_swap(ptr, EMPTY, Ordering::SeqCst) }; @@ -338,7 +338,7 @@ impl<T> Packet<T> { // about it. match state { EMPTY => unreachable!(), - // our task used for select was stolen + // our thread used for select was stolen DATA => Ok(true), // If the other end has hung up, then we have complete ownership diff --git a/src/libstd/sync/mpsc/select.rs b/src/libstd/sync/mpsc/select.rs index fde99e11040..679cc550454 100644 --- a/src/libstd/sync/mpsc/select.rs +++ b/src/libstd/sync/mpsc/select.rs @@ -58,7 +58,7 @@ use core::prelude::*; -use core::cell::Cell; +use core::cell::{Cell, UnsafeCell}; use core::marker; use core::mem; use core::ptr; @@ -70,9 +70,13 @@ use sync::mpsc::blocking::{self, SignalToken}; /// The "receiver set" of the select interface. This structure is used to manage /// a set of receivers which are being selected over. pub struct Select { + inner: UnsafeCell<SelectInner>, + next_id: Cell<usize>, +} + +struct SelectInner { head: *mut Handle<'static, ()>, tail: *mut Handle<'static, ()>, - next_id: Cell<usize>, } impl !marker::Send for Select {} @@ -84,7 +88,7 @@ pub struct Handle<'rx, T:Send+'rx> { /// The ID of this handle, used to compare against the return value of /// `Select::wait()` id: usize, - selector: &'rx Select, + selector: *mut SelectInner, next: *mut Handle<'static, ()>, prev: *mut Handle<'static, ()>, added: bool, @@ -127,8 +131,10 @@ impl Select { /// ``` pub fn new() -> Select { Select { - head: ptr::null_mut(), - tail: ptr::null_mut(), + inner: UnsafeCell::new(SelectInner { + head: ptr::null_mut(), + tail: ptr::null_mut(), + }), next_id: Cell::new(1), } } @@ -141,7 +147,7 @@ impl Select { self.next_id.set(id + 1); Handle { id: id, - selector: self, + selector: self.inner.get(), next: ptr::null_mut(), prev: ptr::null_mut(), added: false, @@ -223,7 +229,7 @@ impl Select { // woken us up (although the wakeup is guaranteed to fail). // // This situation happens in the window of where a sender invokes - // increment(), sees -1, and then decides to wake up the task. After + // increment(), sees -1, and then decides to wake up the thread. After // all this is done, the sending thread will set `selecting` to // `false`. Until this is done, we cannot return. If we were to // return, then a sender could wake up a receiver which has gone @@ -250,7 +256,7 @@ impl Select { } } - fn iter(&self) -> Packets { Packets { cur: self.head } } + fn iter(&self) -> Packets { Packets { cur: unsafe { &*self.inner.get() }.head } } } impl<'rx, T: Send> Handle<'rx, T> { @@ -271,7 +277,7 @@ impl<'rx, T: Send> Handle<'rx, T> { /// while it is added to the `Select` set. pub unsafe fn add(&mut self) { if self.added { return } - let selector: &mut Select = mem::transmute(&*self.selector); + let selector = &mut *self.selector; let me: *mut Handle<'static, ()> = mem::transmute(&*self); if selector.head.is_null() { @@ -292,7 +298,7 @@ impl<'rx, T: Send> Handle<'rx, T> { pub unsafe fn remove(&mut self) { if !self.added { return } - let selector: &mut Select = mem::transmute(&*self.selector); + let selector = &mut *self.selector; let me: *mut Handle<'static, ()> = mem::transmute(&*self); if self.prev.is_null() { @@ -317,8 +323,10 @@ impl<'rx, T: Send> Handle<'rx, T> { impl Drop for Select { fn drop(&mut self) { - assert!(self.head.is_null()); - assert!(self.tail.is_null()); + unsafe { + assert!((&*self.inner.get()).head.is_null()); + assert!((&*self.inner.get()).tail.is_null()); + } } } diff --git a/src/libstd/sync/mpsc/shared.rs b/src/libstd/sync/mpsc/shared.rs index 09a02923f14..41c79dd52c8 100644 --- a/src/libstd/sync/mpsc/shared.rs +++ b/src/libstd/sync/mpsc/shared.rs @@ -91,8 +91,8 @@ impl<T> Packet<T> { } // This function is used at the creation of a shared packet to inherit a - // previously blocked task. This is done to prevent spurious wakeups of - // tasks in select(). + // previously blocked thread. This is done to prevent spurious wakeups of + // threads in select(). // // This can only be called at channel-creation time pub fn inherit_blocker(&mut self, @@ -424,7 +424,7 @@ impl<T> Packet<T> { } } - // Cancels a previous task waiting on this port, returning whether there's + // Cancels a previous thread waiting on this port, returning whether there's // data on the port. // // This is similar to the stream implementation (hence fewer comments), but diff --git a/src/libstd/sync/mpsc/spsc_queue.rs b/src/libstd/sync/mpsc/spsc_queue.rs index f4b9c7d45fd..b72da91c0a0 100644 --- a/src/libstd/sync/mpsc/spsc_queue.rs +++ b/src/libstd/sync/mpsc/spsc_queue.rs @@ -30,7 +30,7 @@ //! A single-producer single-consumer concurrent queue //! //! This module contains the implementation of an SPSC queue which can be used -//! concurrently between two tasks. This data structure is safe to use and +//! concurrently between two threads. This data structure is safe to use and //! enforces the semantics that there is one pusher and one popper. #![unstable(feature = "std_misc")] diff --git a/src/libstd/sync/mpsc/stream.rs b/src/libstd/sync/mpsc/stream.rs index 1200e71d9af..404814b4cd4 100644 --- a/src/libstd/sync/mpsc/stream.rs +++ b/src/libstd/sync/mpsc/stream.rs @@ -181,7 +181,7 @@ impl<T> Packet<T> { data => return data, } - // Welp, our channel has no data. Deschedule the current task and + // Welp, our channel has no data. Deschedule the current thread and // initiate the blocking protocol. let (wait_token, signal_token) = blocking::tokens(); if self.decrement(signal_token).is_ok() { @@ -385,7 +385,7 @@ impl<T> Packet<T> { } } - // Removes a previous task from being blocked in this port + // Removes a previous thread from being blocked in this port pub fn abort_selection(&mut self, was_upgrade: bool) -> Result<bool, Receiver<T>> { // If we're aborting selection after upgrading from a oneshot, then @@ -414,7 +414,7 @@ impl<T> Packet<T> { let prev = self.bump(steals + 1); // If we were previously disconnected, then we know for sure that there - // is no task in to_wake, so just keep going + // is no thread in to_wake, so just keep going let has_data = if prev == DISCONNECTED { assert_eq!(self.to_wake.load(Ordering::SeqCst), 0); true // there is data, that data is that we're disconnected @@ -428,7 +428,7 @@ impl<T> Packet<T> { // // If the previous count was positive then we're in a tougher // situation. A possible race is that a sender just incremented - // through -1 (meaning it's going to try to wake a task up), but it + // through -1 (meaning it's going to try to wake a thread up), but it // hasn't yet read the to_wake. In order to prevent a future recv() // from waking up too early (this sender picking up the plastered // over to_wake), we spin loop here waiting for to_wake to be 0. diff --git a/src/libstd/sync/mpsc/sync.rs b/src/libstd/sync/mpsc/sync.rs index 4687df107f6..904eab1fd7e 100644 --- a/src/libstd/sync/mpsc/sync.rs +++ b/src/libstd/sync/mpsc/sync.rs @@ -19,7 +19,7 @@ /// which means that every successful send is paired with a successful recv. /// /// This flavor of channels defines a new `send_opt` method for channels which -/// is the method by which a message is sent but the task does not panic if it +/// is the method by which a message is sent but the thread does not panic if it /// cannot be delivered. /// /// Another major difference is that send() will *always* return back the data @@ -62,12 +62,12 @@ unsafe impl<T: Send> Sync for Packet<T> { } struct State<T> { disconnected: bool, // Is the channel disconnected yet? queue: Queue, // queue of senders waiting to send data - blocker: Blocker, // currently blocked task on this channel + blocker: Blocker, // currently blocked thread on this channel buf: Buffer<T>, // storage for buffered messages cap: usize, // capacity of this channel /// A curious flag used to indicate whether a sender failed or succeeded in - /// blocking. This is used to transmit information back to the task that it + /// blocking. This is used to transmit information back to the thread that it /// must dequeue its message from the buffer because it was not received. /// This is only relevant in the 0-buffer case. This obviously cannot be /// safely constructed, but it's guaranteed to always have a valid pointer @@ -84,7 +84,7 @@ enum Blocker { NoneBlocked } -/// Simple queue for threading tasks together. Nodes are stack-allocated, so +/// Simple queue for threading threads together. Nodes are stack-allocated, so /// this structure is not safe at all struct Queue { head: *mut Node, @@ -130,7 +130,7 @@ fn wait<'a, 'b, T>(lock: &'a Mutex<State<T>>, /// Wakes up a thread, dropping the lock at the correct time fn wakeup<T>(token: SignalToken, guard: MutexGuard<State<T>>) { - // We need to be careful to wake up the waiting task *outside* of the mutex + // We need to be careful to wake up the waiting thread *outside* of the mutex // in case it incurs a context switch. drop(guard); token.signal(); @@ -298,7 +298,7 @@ impl<T> Packet<T> { }; mem::drop(guard); - // only outside of the lock do we wake up the pending tasks + // only outside of the lock do we wake up the pending threads pending_sender1.map(|t| t.signal()); pending_sender2.map(|t| t.signal()); } @@ -394,8 +394,8 @@ impl<T> Packet<T> { } } - // Remove a previous selecting task from this port. This ensures that the - // blocked task will no longer be visible to any other threads. + // Remove a previous selecting thread from this port. This ensures that the + // blocked thread will no longer be visible to any other threads. // // The return value indicates whether there's data on this port. pub fn abort_selection(&self) -> bool { @@ -446,7 +446,7 @@ impl<T> Buffer<T> { } //////////////////////////////////////////////////////////////////////////////// -// Queue, a simple queue to enqueue tasks with (stack-allocated nodes) +// Queue, a simple queue to enqueue threads with (stack-allocated nodes) //////////////////////////////////////////////////////////////////////////////// impl Queue { diff --git a/src/libstd/sync/mutex.rs b/src/libstd/sync/mutex.rs index 30c7407a96d..febf5f1b183 100644 --- a/src/libstd/sync/mutex.rs +++ b/src/libstd/sync/mutex.rs @@ -30,7 +30,7 @@ use sys_common::poison::{self, TryLockError, TryLockResult, LockResult}; /// /// The mutexes in this module implement a strategy called "poisoning" where a /// mutex is considered poisoned whenever a thread panics while holding the -/// lock. Once a mutex is poisoned, all other tasks are unable to access the +/// lock. Once a mutex is poisoned, all other threads are unable to access the /// data by default as it is likely tainted (some invariant is not being /// upheld). /// @@ -56,7 +56,7 @@ use sys_common::poison::{self, TryLockError, TryLockResult, LockResult}; /// // Spawn a few threads to increment a shared variable (non-atomically), and /// // let the main thread know once all increments are done. /// // -/// // Here we're using an Arc to share memory among tasks, and the data inside +/// // Here we're using an Arc to share memory among threads, and the data inside /// // the Arc is protected with a mutex. /// let data = Arc::new(Mutex::new(0)); /// @@ -69,7 +69,7 @@ use sys_common::poison::{self, TryLockError, TryLockResult, LockResult}; /// // which can access the shared state when the lock is held. /// // /// // We unwrap() the return value to assert that we are not expecting -/// // tasks to ever fail while holding the lock. +/// // threads to ever fail while holding the lock. /// let mut data = data.lock().unwrap(); /// *data += 1; /// if *data == N { @@ -112,7 +112,7 @@ use sys_common::poison::{self, TryLockError, TryLockResult, LockResult}; /// *guard += 1; /// ``` #[stable(feature = "rust1", since = "1.0.0")] -pub struct Mutex<T> { +pub struct Mutex<T: ?Sized> { // Note that this static mutex is in a *box*, not inlined into the struct // itself. Once a native mutex has been used once, its address can never // change (it can't be moved). This mutex type can be safely moved at any @@ -124,9 +124,9 @@ pub struct Mutex<T> { // these are the only places where `T: Send` matters; all other // functionality works fine on a single thread. -unsafe impl<T: Send> Send for Mutex<T> { } +unsafe impl<T: ?Sized + Send> Send for Mutex<T> { } -unsafe impl<T: Send> Sync for Mutex<T> { } +unsafe impl<T: ?Sized + Send> Sync for Mutex<T> { } /// The static mutex type is provided to allow for static allocation of mutexes. /// @@ -164,7 +164,7 @@ pub struct StaticMutex { /// `Deref` and `DerefMut` implementations #[must_use] #[stable(feature = "rust1", since = "1.0.0")] -pub struct MutexGuard<'a, T: 'a> { +pub struct MutexGuard<'a, T: ?Sized + 'a> { // funny underscores due to how Deref/DerefMut currently work (they // disregard field privacy). __lock: &'a StaticMutex, @@ -172,7 +172,7 @@ pub struct MutexGuard<'a, T: 'a> { __poison: poison::Guard, } -impl<'a, T> !marker::Send for MutexGuard<'a, T> {} +impl<'a, T: ?Sized> !marker::Send for MutexGuard<'a, T> {} /// Static initialization of a mutex. This constant can be used to initialize /// other mutex constants. @@ -192,11 +192,13 @@ impl<T> Mutex<T> { data: UnsafeCell::new(t), } } +} - /// Acquires a mutex, blocking the current task until it is able to do so. +impl<T: ?Sized> Mutex<T> { + /// Acquires a mutex, blocking the current thread until it is able to do so. /// - /// This function will block the local task until it is available to acquire - /// the mutex. Upon returning, the task is the only task with the mutex + /// This function will block the local thread until it is available to acquire + /// the mutex. Upon returning, the thread is the only thread with the mutex /// held. An RAII guard is returned to allow scoped unlock of the lock. When /// the guard goes out of scope, the mutex will be unlocked. /// @@ -245,7 +247,7 @@ impl<T> Mutex<T> { } #[stable(feature = "rust1", since = "1.0.0")] -impl<T> Drop for Mutex<T> { +impl<T: ?Sized> Drop for Mutex<T> { fn drop(&mut self) { // This is actually safe b/c we know that there is no further usage of // this mutex (it's up to the user to arrange for a mutex to get @@ -255,12 +257,12 @@ impl<T> Drop for Mutex<T> { } #[stable(feature = "rust1", since = "1.0.0")] -impl<T: fmt::Debug + 'static> fmt::Debug for Mutex<T> { +impl<T: ?Sized + fmt::Debug + 'static> fmt::Debug for Mutex<T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.try_lock() { - Ok(guard) => write!(f, "Mutex {{ data: {:?} }}", *guard), + Ok(guard) => write!(f, "Mutex {{ data: {:?} }}", &*guard), Err(TryLockError::Poisoned(err)) => { - write!(f, "Mutex {{ data: Poisoned({:?}) }}", **err.get_ref()) + write!(f, "Mutex {{ data: Poisoned({:?}) }}", &**err.get_ref()) }, Err(TryLockError::WouldBlock) => write!(f, "Mutex {{ <locked> }}") } @@ -310,7 +312,7 @@ impl StaticMutex { } } -impl<'mutex, T> MutexGuard<'mutex, T> { +impl<'mutex, T: ?Sized> MutexGuard<'mutex, T> { fn new(lock: &'mutex StaticMutex, data: &'mutex UnsafeCell<T>) -> LockResult<MutexGuard<'mutex, T>> { @@ -325,7 +327,7 @@ impl<'mutex, T> MutexGuard<'mutex, T> { } #[stable(feature = "rust1", since = "1.0.0")] -impl<'mutex, T> Deref for MutexGuard<'mutex, T> { +impl<'mutex, T: ?Sized> Deref for MutexGuard<'mutex, T> { type Target = T; fn deref<'a>(&'a self) -> &'a T { @@ -333,14 +335,14 @@ impl<'mutex, T> Deref for MutexGuard<'mutex, T> { } } #[stable(feature = "rust1", since = "1.0.0")] -impl<'mutex, T> DerefMut for MutexGuard<'mutex, T> { +impl<'mutex, T: ?Sized> DerefMut for MutexGuard<'mutex, T> { fn deref_mut<'a>(&'a mut self) -> &'a mut T { unsafe { &mut *self.__data.get() } } } #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> Drop for MutexGuard<'a, T> { +impl<'a, T: ?Sized> Drop for MutexGuard<'a, T> { #[inline] fn drop(&mut self) { unsafe { @@ -350,11 +352,11 @@ impl<'a, T> Drop for MutexGuard<'a, T> { } } -pub fn guard_lock<'a, T>(guard: &MutexGuard<'a, T>) -> &'a sys::Mutex { +pub fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a sys::Mutex { &guard.__lock.lock } -pub fn guard_poison<'a, T>(guard: &MutexGuard<'a, T>) -> &'a poison::Flag { +pub fn guard_poison<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a poison::Flag { &guard.__lock.poison } @@ -528,4 +530,16 @@ mod tests { let lock = arc.lock().unwrap(); assert_eq!(*lock, 2); } + + #[test] + fn test_mutex_unsized() { + let mutex: &Mutex<[i32]> = &Mutex::new([1, 2, 3]); + { + let b = &mut *mutex.lock().unwrap(); + b[0] = 4; + b[2] = 5; + } + let comp: &[i32] = &[4, 2, 5]; + assert_eq!(&*mutex.lock().unwrap(), comp); + } } diff --git a/src/libstd/sync/once.rs b/src/libstd/sync/once.rs index 2d712369228..57baedaad9c 100644 --- a/src/libstd/sync/once.rs +++ b/src/libstd/sync/once.rs @@ -55,13 +55,13 @@ impl Once { /// will be executed if this is the first time `call_once` has been called, /// and otherwise the routine will *not* be invoked. /// - /// This method will block the calling task if another initialization + /// This method will block the calling thread if another initialization /// routine is currently running. /// /// When this function returns, it is guaranteed that some initialization /// has run and completed (it may not be the closure specified). It is also /// guaranteed that any memory writes performed by the executed closure can - /// be reliably observed by other tasks at this point (there is a + /// be reliably observed by other threads at this point (there is a /// happens-before relation between the closure and code executing after the /// return). #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libstd/sync/rwlock.rs b/src/libstd/sync/rwlock.rs index a133bb01b61..625377df7d6 100644 --- a/src/libstd/sync/rwlock.rs +++ b/src/libstd/sync/rwlock.rs @@ -25,7 +25,7 @@ use sys_common::rwlock as sys; /// typically allows for read-only access (shared access). /// /// The type parameter `T` represents the data that this lock protects. It is -/// required that `T` satisfies `Send` to be shared across tasks and `Sync` to +/// required that `T` satisfies `Send` to be shared across threads and `Sync` to /// allow concurrent access through readers. The RAII guards returned from the /// locking methods implement `Deref` (and `DerefMut` for the `write` methods) /// to allow access to the contained of the lock. @@ -60,13 +60,13 @@ use sys_common::rwlock as sys; /// } // write lock is dropped here /// ``` #[stable(feature = "rust1", since = "1.0.0")] -pub struct RwLock<T> { +pub struct RwLock<T: ?Sized> { inner: Box<StaticRwLock>, data: UnsafeCell<T>, } -unsafe impl<T: Send + Sync> Send for RwLock<T> {} -unsafe impl<T: Send + Sync> Sync for RwLock<T> {} +unsafe impl<T: ?Sized + Send + Sync> Send for RwLock<T> {} +unsafe impl<T: ?Sized + Send + Sync> Sync for RwLock<T> {} /// Structure representing a statically allocated RwLock. /// @@ -111,24 +111,24 @@ pub const RW_LOCK_INIT: StaticRwLock = StaticRwLock { /// dropped. #[must_use] #[stable(feature = "rust1", since = "1.0.0")] -pub struct RwLockReadGuard<'a, T: 'a> { +pub struct RwLockReadGuard<'a, T: ?Sized + 'a> { __lock: &'a StaticRwLock, __data: &'a UnsafeCell<T>, } -impl<'a, T> !marker::Send for RwLockReadGuard<'a, T> {} +impl<'a, T: ?Sized> !marker::Send for RwLockReadGuard<'a, T> {} /// RAII structure used to release the exclusive write access of a lock when /// dropped. #[must_use] #[stable(feature = "rust1", since = "1.0.0")] -pub struct RwLockWriteGuard<'a, T: 'a> { +pub struct RwLockWriteGuard<'a, T: ?Sized + 'a> { __lock: &'a StaticRwLock, __data: &'a UnsafeCell<T>, __poison: poison::Guard, } -impl<'a, T> !marker::Send for RwLockWriteGuard<'a, T> {} +impl<'a, T: ?Sized> !marker::Send for RwLockWriteGuard<'a, T> {} impl<T> RwLock<T> { /// Creates a new instance of an `RwLock<T>` which is unlocked. @@ -144,7 +144,9 @@ impl<T> RwLock<T> { pub fn new(t: T) -> RwLock<T> { RwLock { inner: box RW_LOCK_INIT, data: UnsafeCell::new(t) } } +} +impl<T: ?Sized> RwLock<T> { /// Locks this rwlock with shared read access, blocking the current thread /// until it can be acquired. /// @@ -169,14 +171,16 @@ impl<T> RwLock<T> { RwLockReadGuard::new(&*self.inner, &self.data) } - /// Attempts to acquire this lock with shared read access. + /// Attempts to acquire this rwlock with shared read access. + /// + /// If the access could not be granted at this time, then `Err` is returned. + /// Otherwise, an RAII guard is returned which will release the shared access + /// when it is dropped. /// - /// This function will never block and will return immediately if `read` - /// would otherwise succeed. Returns `Some` of an RAII guard which will - /// release the shared access of this thread when dropped, or `None` if the - /// access could not be granted. This method does not provide any - /// guarantees with respect to the ordering of whether contentious readers - /// or writers will acquire the lock first. + /// This function does not block. + /// + /// This function does not provide any guarantees with respect to the ordering + /// of whether contentious readers or writers will acquire the lock first. /// /// # Failure /// @@ -217,9 +221,14 @@ impl<T> RwLock<T> { /// Attempts to lock this rwlock with exclusive write access. /// - /// This function does not ever block, and it will return `None` if a call - /// to `write` would otherwise block. If successful, an RAII guard is - /// returned. + /// If the lock could not be acquired at this time, then `Err` is returned. + /// Otherwise, an RAII guard is returned which will release the lock when + /// it is dropped. + /// + /// This function does not block. + /// + /// This function does not provide any guarantees with respect to the ordering + /// of whether contentious readers or writers will acquire the lock first. /// /// # Failure /// @@ -230,7 +239,7 @@ impl<T> RwLock<T> { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn try_write(&self) -> TryLockResult<RwLockWriteGuard<T>> { - if unsafe { self.inner.lock.try_read() } { + if unsafe { self.inner.lock.try_write() } { Ok(try!(RwLockWriteGuard::new(&*self.inner, &self.data))) } else { Err(TryLockError::WouldBlock) @@ -250,19 +259,19 @@ impl<T> RwLock<T> { } #[stable(feature = "rust1", since = "1.0.0")] -impl<T> Drop for RwLock<T> { +impl<T: ?Sized> Drop for RwLock<T> { fn drop(&mut self) { unsafe { self.inner.lock.destroy() } } } #[stable(feature = "rust1", since = "1.0.0")] -impl<T: fmt::Debug> fmt::Debug for RwLock<T> { +impl<T: ?Sized + fmt::Debug> fmt::Debug for RwLock<T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.try_read() { - Ok(guard) => write!(f, "RwLock {{ data: {:?} }}", *guard), + Ok(guard) => write!(f, "RwLock {{ data: {:?} }}", &*guard), Err(TryLockError::Poisoned(err)) => { - write!(f, "RwLock {{ data: Poisoned({:?}) }}", **err.get_ref()) + write!(f, "RwLock {{ data: Poisoned({:?}) }}", &**err.get_ref()) }, Err(TryLockError::WouldBlock) => write!(f, "RwLock {{ <locked> }}") } @@ -341,8 +350,7 @@ impl StaticRwLock { } } -impl<'rwlock, T> RwLockReadGuard<'rwlock, T> { - +impl<'rwlock, T: ?Sized> RwLockReadGuard<'rwlock, T> { fn new(lock: &'rwlock StaticRwLock, data: &'rwlock UnsafeCell<T>) -> LockResult<RwLockReadGuard<'rwlock, T>> { poison::map_result(lock.poison.borrow(), |_| { @@ -353,8 +361,8 @@ impl<'rwlock, T> RwLockReadGuard<'rwlock, T> { }) } } -impl<'rwlock, T> RwLockWriteGuard<'rwlock, T> { +impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { fn new(lock: &'rwlock StaticRwLock, data: &'rwlock UnsafeCell<T>) -> LockResult<RwLockWriteGuard<'rwlock, T>> { poison::map_result(lock.poison.borrow(), |guard| { @@ -368,33 +376,35 @@ impl<'rwlock, T> RwLockWriteGuard<'rwlock, T> { } #[stable(feature = "rust1", since = "1.0.0")] -impl<'rwlock, T> Deref for RwLockReadGuard<'rwlock, T> { +impl<'rwlock, T: ?Sized> Deref for RwLockReadGuard<'rwlock, T> { type Target = T; fn deref(&self) -> &T { unsafe { &*self.__data.get() } } } + #[stable(feature = "rust1", since = "1.0.0")] -impl<'rwlock, T> Deref for RwLockWriteGuard<'rwlock, T> { +impl<'rwlock, T: ?Sized> Deref for RwLockWriteGuard<'rwlock, T> { type Target = T; fn deref(&self) -> &T { unsafe { &*self.__data.get() } } } + #[stable(feature = "rust1", since = "1.0.0")] -impl<'rwlock, T> DerefMut for RwLockWriteGuard<'rwlock, T> { +impl<'rwlock, T: ?Sized> DerefMut for RwLockWriteGuard<'rwlock, T> { fn deref_mut(&mut self) -> &mut T { unsafe { &mut *self.__data.get() } } } #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> Drop for RwLockReadGuard<'a, T> { +impl<'a, T: ?Sized> Drop for RwLockReadGuard<'a, T> { fn drop(&mut self) { unsafe { self.__lock.lock.read_unlock(); } } } #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> Drop for RwLockWriteGuard<'a, T> { +impl<'a, T: ?Sized> Drop for RwLockWriteGuard<'a, T> { fn drop(&mut self) { self.__lock.poison.done(&self.__poison); unsafe { self.__lock.lock.write_unlock(); } @@ -410,7 +420,7 @@ mod tests { use rand::{self, Rng}; use sync::mpsc::channel; use thread; - use sync::{Arc, RwLock, StaticRwLock, RW_LOCK_INIT}; + use sync::{Arc, RwLock, StaticRwLock, TryLockError, RW_LOCK_INIT}; #[test] fn smoke() { @@ -562,4 +572,33 @@ mod tests { let lock = arc.read().unwrap(); assert_eq!(*lock, 2); } + + #[test] + fn test_rwlock_unsized() { + let rw: &RwLock<[i32]> = &RwLock::new([1, 2, 3]); + { + let b = &mut *rw.write().unwrap(); + b[0] = 4; + b[2] = 5; + } + let comp: &[i32] = &[4, 2, 5]; + assert_eq!(&*rw.read().unwrap(), comp); + } + + #[test] + fn test_rwlock_try_write() { + use mem::drop; + + let lock = RwLock::new(0isize); + let read_guard = lock.read().unwrap(); + + let write_result = lock.try_write(); + match write_result { + Err(TryLockError::WouldBlock) => (), + Ok(_) => assert!(false, "try_write should not succeed while read_guard is in scope"), + Err(_) => assert!(false, "unexpected error"), + } + + drop(read_guard); + } } |
