]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | // Copyright 2014 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
c34b1796 | 11 | use error::{Error}; |
1a4d82fc | 12 | use fmt; |
5bcae85e | 13 | use sync::atomic::{AtomicBool, Ordering}; |
85aaf69f | 14 | use thread; |
1a4d82fc | 15 | |
5bcae85e | 16 | pub struct Flag { failed: AtomicBool } |
c34b1796 | 17 | |
5bcae85e SL |
18 | // Note that the Ordering uses to access the `failed` field of `Flag` below is |
19 | // always `Relaxed`, and that's because this isn't actually protecting any data, | |
20 | // it's just a flag whether we've panicked or not. | |
21 | // | |
22 | // The actual location that this matters is when a mutex is **locked** which is | |
23 | // where we have external synchronization ensuring that we see memory | |
24 | // reads/writes to this flag. | |
25 | // | |
26 | // As a result, if it matters, we should see the correct value for `failed` in | |
27 | // all cases. | |
c34b1796 | 28 | |
1a4d82fc | 29 | impl Flag { |
62682a34 | 30 | pub const fn new() -> Flag { |
5bcae85e | 31 | Flag { failed: AtomicBool::new(false) } |
62682a34 SL |
32 | } |
33 | ||
1a4d82fc JJ |
34 | #[inline] |
35 | pub fn borrow(&self) -> LockResult<Guard> { | |
85aaf69f | 36 | let ret = Guard { panicking: thread::panicking() }; |
62682a34 | 37 | if self.get() { |
85aaf69f | 38 | Err(PoisonError::new(ret)) |
1a4d82fc JJ |
39 | } else { |
40 | Ok(ret) | |
41 | } | |
42 | } | |
43 | ||
44 | #[inline] | |
45 | pub fn done(&self, guard: &Guard) { | |
85aaf69f | 46 | if !guard.panicking && thread::panicking() { |
5bcae85e | 47 | self.failed.store(true, Ordering::Relaxed); |
1a4d82fc JJ |
48 | } |
49 | } | |
50 | ||
51 | #[inline] | |
52 | pub fn get(&self) -> bool { | |
5bcae85e | 53 | self.failed.load(Ordering::Relaxed) |
1a4d82fc JJ |
54 | } |
55 | } | |
56 | ||
1a4d82fc JJ |
57 | pub struct Guard { |
58 | panicking: bool, | |
59 | } | |
60 | ||
61 | /// A type of error which can be returned whenever a lock is acquired. | |
62 | /// | |
8bb4bdeb | 63 | /// Both [`Mutex`]es and [`RwLock`]s are poisoned whenever a thread fails while the lock |
1a4d82fc JJ |
64 | /// is held. The precise semantics for when a lock is poisoned is documented on |
65 | /// each lock, but once a lock is poisoned then all future acquisitions will | |
66 | /// return this error. | |
8bb4bdeb XL |
67 | /// |
68 | /// [`Mutex`]: ../../std/sync/struct.Mutex.html | |
69 | /// [`RwLock`]: ../../std/sync/struct.RwLock.html | |
85aaf69f | 70 | #[stable(feature = "rust1", since = "1.0.0")] |
1a4d82fc JJ |
71 | pub struct PoisonError<T> { |
72 | guard: T, | |
73 | } | |
74 | ||
75 | /// An enumeration of possible errors which can occur while calling the | |
76 | /// `try_lock` method. | |
85aaf69f | 77 | #[stable(feature = "rust1", since = "1.0.0")] |
1a4d82fc | 78 | pub enum TryLockError<T> { |
bd371182 | 79 | /// The lock could not be acquired because another thread failed while holding |
1a4d82fc | 80 | /// the lock. |
85aaf69f | 81 | #[stable(feature = "rust1", since = "1.0.0")] |
7453a54e | 82 | Poisoned(#[stable(feature = "rust1", since = "1.0.0")] PoisonError<T>), |
1a4d82fc JJ |
83 | /// The lock could not be acquired at this time because the operation would |
84 | /// otherwise block. | |
85aaf69f | 85 | #[stable(feature = "rust1", since = "1.0.0")] |
1a4d82fc JJ |
86 | WouldBlock, |
87 | } | |
88 | ||
89 | /// A type alias for the result of a lock method which can be poisoned. | |
90 | /// | |
8bb4bdeb XL |
91 | /// The [`Ok`] variant of this result indicates that the primitive was not |
92 | /// poisoned, and the `Guard` is contained within. The [`Err`] variant indicates | |
93 | /// that the primitive was poisoned. Note that the [`Err`] variant *also* carries | |
94 | /// the associated guard, and it can be acquired through the [`into_inner`] | |
1a4d82fc | 95 | /// method. |
8bb4bdeb XL |
96 | /// |
97 | /// [`Ok`]: ../../std/result/enum.Result.html#variant.Ok | |
98 | /// [`Err`]: ../../std/result/enum.Result.html#variant.Err | |
99 | /// [`into_inner`]: ../../std/sync/struct.Mutex.html#method.into_inner | |
85aaf69f | 100 | #[stable(feature = "rust1", since = "1.0.0")] |
1a4d82fc JJ |
101 | pub type LockResult<Guard> = Result<Guard, PoisonError<Guard>>; |
102 | ||
103 | /// A type alias for the result of a nonblocking locking method. | |
104 | /// | |
8bb4bdeb XL |
105 | /// For more information, see [`LockResult`]. A `TryLockResult` doesn't |
106 | /// necessarily hold the associated guard in the [`Err`] type as the lock may not | |
1a4d82fc | 107 | /// have been acquired for other reasons. |
8bb4bdeb XL |
108 | /// |
109 | /// [`LockResult`]: ../../std/sync/type.LockResult.html | |
110 | /// [`Err`]: ../../std/result/enum.Result.html#variant.Err | |
85aaf69f | 111 | #[stable(feature = "rust1", since = "1.0.0")] |
1a4d82fc JJ |
112 | pub type TryLockResult<Guard> = Result<Guard, TryLockError<Guard>>; |
113 | ||
85aaf69f SL |
114 | #[stable(feature = "rust1", since = "1.0.0")] |
115 | impl<T> fmt::Debug for PoisonError<T> { | |
1a4d82fc | 116 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
85aaf69f SL |
117 | "PoisonError { inner: .. }".fmt(f) |
118 | } | |
119 | } | |
120 | ||
121 | #[stable(feature = "rust1", since = "1.0.0")] | |
122 | impl<T> fmt::Display for PoisonError<T> { | |
123 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
c34b1796 | 124 | "poisoned lock: another task failed inside".fmt(f) |
85aaf69f SL |
125 | } |
126 | } | |
127 | ||
92a42be0 | 128 | #[stable(feature = "rust1", since = "1.0.0")] |
9e0c209e | 129 | impl<T> Error for PoisonError<T> { |
85aaf69f SL |
130 | fn description(&self) -> &str { |
131 | "poisoned lock: another task failed inside" | |
1a4d82fc JJ |
132 | } |
133 | } | |
134 | ||
135 | impl<T> PoisonError<T> { | |
9346a6ac | 136 | /// Creates a `PoisonError`. |
8bb4bdeb XL |
137 | /// |
138 | /// This is generally created by methods like [`Mutex::lock`] or [`RwLock::read`]. | |
139 | /// | |
140 | /// [`Mutex::lock`]: ../../std/sync/struct.Mutex.html#method.lock | |
141 | /// [`RwLock::read`]: ../../std/sync/struct.RwLock.html#method.read | |
62682a34 | 142 | #[stable(feature = "sync_poison", since = "1.2.0")] |
85aaf69f SL |
143 | pub fn new(guard: T) -> PoisonError<T> { |
144 | PoisonError { guard: guard } | |
145 | } | |
146 | ||
85aaf69f SL |
147 | /// Consumes this error indicating that a lock is poisoned, returning the |
148 | /// underlying guard to allow access regardless. | |
62682a34 | 149 | #[stable(feature = "sync_poison", since = "1.2.0")] |
85aaf69f SL |
150 | pub fn into_inner(self) -> T { self.guard } |
151 | ||
152 | /// Reaches into this error indicating that a lock is poisoned, returning a | |
153 | /// reference to the underlying guard to allow access regardless. | |
62682a34 | 154 | #[stable(feature = "sync_poison", since = "1.2.0")] |
85aaf69f SL |
155 | pub fn get_ref(&self) -> &T { &self.guard } |
156 | ||
157 | /// Reaches into this error indicating that a lock is poisoned, returning a | |
158 | /// mutable reference to the underlying guard to allow access regardless. | |
62682a34 | 159 | #[stable(feature = "sync_poison", since = "1.2.0")] |
85aaf69f | 160 | pub fn get_mut(&mut self) -> &mut T { &mut self.guard } |
1a4d82fc JJ |
161 | } |
162 | ||
92a42be0 | 163 | #[stable(feature = "rust1", since = "1.0.0")] |
c34b1796 AL |
164 | impl<T> From<PoisonError<T>> for TryLockError<T> { |
165 | fn from(err: PoisonError<T>) -> TryLockError<T> { | |
1a4d82fc JJ |
166 | TryLockError::Poisoned(err) |
167 | } | |
168 | } | |
169 | ||
85aaf69f SL |
170 | #[stable(feature = "rust1", since = "1.0.0")] |
171 | impl<T> fmt::Debug for TryLockError<T> { | |
1a4d82fc JJ |
172 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
173 | match *self { | |
85aaf69f SL |
174 | TryLockError::Poisoned(..) => "Poisoned(..)".fmt(f), |
175 | TryLockError::WouldBlock => "WouldBlock".fmt(f) | |
1a4d82fc JJ |
176 | } |
177 | } | |
178 | } | |
179 | ||
85aaf69f | 180 | #[stable(feature = "rust1", since = "1.0.0")] |
7453a54e | 181 | impl<T> fmt::Display for TryLockError<T> { |
85aaf69f | 182 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
7453a54e SL |
183 | match *self { |
184 | TryLockError::Poisoned(..) => "poisoned lock: another task failed inside", | |
185 | TryLockError::WouldBlock => "try_lock failed because the operation would block" | |
186 | }.fmt(f) | |
85aaf69f SL |
187 | } |
188 | } | |
189 | ||
92a42be0 | 190 | #[stable(feature = "rust1", since = "1.0.0")] |
9e0c209e | 191 | impl<T> Error for TryLockError<T> { |
85aaf69f SL |
192 | fn description(&self) -> &str { |
193 | match *self { | |
194 | TryLockError::Poisoned(ref p) => p.description(), | |
195 | TryLockError::WouldBlock => "try_lock failed because the operation would block" | |
196 | } | |
197 | } | |
198 | ||
199 | fn cause(&self) -> Option<&Error> { | |
200 | match *self { | |
201 | TryLockError::Poisoned(ref p) => Some(p), | |
202 | _ => None | |
203 | } | |
204 | } | |
1a4d82fc JJ |
205 | } |
206 | ||
207 | pub fn map_result<T, U, F>(result: LockResult<T>, f: F) | |
208 | -> LockResult<U> | |
209 | where F: FnOnce(T) -> U { | |
210 | match result { | |
211 | Ok(t) => Ok(f(t)), | |
85aaf69f | 212 | Err(PoisonError { guard }) => Err(PoisonError::new(f(guard))) |
1a4d82fc JJ |
213 | } |
214 | } |