1 use std::cmp;
2 use std::fmt;
3 
4 use errors::InvalidThreadAccess;
5 use fragile::Fragile;
6 use std::mem;
7 use sticky::Sticky;
8 
9 enum SemiStickyImpl<T> {
10     Fragile(Fragile<T>),
11     Sticky(Sticky<T>),
12 }
13 
14 /// A `SemiSticky<T>` keeps a value T stored in a thread if it has a drop.
15 ///
16 /// This is a combined version of `Fragile<T>` and `Sticky<T>`.  If the type
17 /// does not have a drop it will effectively be a `Fragile<T>`, otherwise it
18 /// will be internally behave like a `Sticky<T>`.
19 pub struct SemiSticky<T> {
20     inner: SemiStickyImpl<T>,
21 }
22 
23 impl<T> SemiSticky<T> {
24     /// Creates a new `SemiSticky` wrapping a `value`.
25     ///
26     /// The value that is moved into the `SemiSticky` can be non `Send` and
27     /// will be anchored to the thread that created the object.  If the
28     /// sticky wrapper type ends up being send from thread to thread
29     /// only the original thread can interact with the value.  In case the
30     /// value does not have `Drop` it will be stored in the `SemiSticky`
31     /// instead.
new(value: T) -> Self32     pub fn new(value: T) -> Self {
33         SemiSticky {
34             inner: if mem::needs_drop::<T>() {
35                 SemiStickyImpl::Sticky(Sticky::new(value))
36             } else {
37                 SemiStickyImpl::Fragile(Fragile::new(value))
38             },
39         }
40     }
41 
42     /// Returns `true` if the access is valid.
43     ///
44     /// This will be `false` if the value was sent to another thread.
is_valid(&self) -> bool45     pub fn is_valid(&self) -> bool {
46         match self.inner {
47             SemiStickyImpl::Fragile(ref inner) => inner.is_valid(),
48             SemiStickyImpl::Sticky(ref inner) => inner.is_valid(),
49         }
50     }
51 
52     /// Consumes the `SemiSticky`, returning the wrapped value.
53     ///
54     /// # Panics
55     ///
56     /// Panics if called from a different thread than the one where the
57     /// original value was created.
into_inner(self) -> T58     pub fn into_inner(self) -> T {
59         match self.inner {
60             SemiStickyImpl::Fragile(inner) => inner.into_inner(),
61             SemiStickyImpl::Sticky(inner) => inner.into_inner(),
62         }
63     }
64 
65     /// Consumes the `SemiSticky`, returning the wrapped value if successful.
66     ///
67     /// The wrapped value is returned if this is called from the same thread
68     /// as the one where the original value was created, otherwise the
69     /// `SemiSticky` is returned as `Err(self)`.
try_into_inner(self) -> Result<T, Self>70     pub fn try_into_inner(self) -> Result<T, Self> {
71         match self.inner {
72             SemiStickyImpl::Fragile(inner) => inner.try_into_inner().map_err(|inner| SemiSticky {
73                 inner: SemiStickyImpl::Fragile(inner),
74             }),
75             SemiStickyImpl::Sticky(inner) => inner.try_into_inner().map_err(|inner| SemiSticky {
76                 inner: SemiStickyImpl::Sticky(inner),
77             }),
78         }
79     }
80 
81     /// Immutably borrows the wrapped value.
82     ///
83     /// # Panics
84     ///
85     /// Panics if the calling thread is not the one that wrapped the value.
86     /// For a non-panicking variant, use [`try_get`](#method.try_get`).
get(&self) -> &T87     pub fn get(&self) -> &T {
88         match self.inner {
89             SemiStickyImpl::Fragile(ref inner) => inner.get(),
90             SemiStickyImpl::Sticky(ref inner) => inner.get(),
91         }
92     }
93 
94     /// Mutably borrows the wrapped value.
95     ///
96     /// # Panics
97     ///
98     /// Panics if the calling thread is not the one that wrapped the value.
99     /// For a non-panicking variant, use [`try_get_mut`](#method.try_get_mut`).
get_mut(&mut self) -> &mut T100     pub fn get_mut(&mut self) -> &mut T {
101         match self.inner {
102             SemiStickyImpl::Fragile(ref mut inner) => inner.get_mut(),
103             SemiStickyImpl::Sticky(ref mut inner) => inner.get_mut(),
104         }
105     }
106 
107     /// Tries to immutably borrow the wrapped value.
108     ///
109     /// Returns `None` if the calling thread is not the one that wrapped the value.
try_get(&self) -> Result<&T, InvalidThreadAccess>110     pub fn try_get(&self) -> Result<&T, InvalidThreadAccess> {
111         match self.inner {
112             SemiStickyImpl::Fragile(ref inner) => inner.try_get(),
113             SemiStickyImpl::Sticky(ref inner) => inner.try_get(),
114         }
115     }
116 
117     /// Tries to mutably borrow the wrapped value.
118     ///
119     /// Returns `None` if the calling thread is not the one that wrapped the value.
try_get_mut(&mut self) -> Result<&mut T, InvalidThreadAccess>120     pub fn try_get_mut(&mut self) -> Result<&mut T, InvalidThreadAccess> {
121         match self.inner {
122             SemiStickyImpl::Fragile(ref mut inner) => inner.try_get_mut(),
123             SemiStickyImpl::Sticky(ref mut inner) => inner.try_get_mut(),
124         }
125     }
126 }
127 
128 impl<T> From<T> for SemiSticky<T> {
129     #[inline]
from(t: T) -> SemiSticky<T>130     fn from(t: T) -> SemiSticky<T> {
131         SemiSticky::new(t)
132     }
133 }
134 
135 impl<T: Clone> Clone for SemiSticky<T> {
136     #[inline]
clone(&self) -> SemiSticky<T>137     fn clone(&self) -> SemiSticky<T> {
138         SemiSticky::new(self.get().clone())
139     }
140 }
141 
142 impl<T: Default> Default for SemiSticky<T> {
143     #[inline]
default() -> SemiSticky<T>144     fn default() -> SemiSticky<T> {
145         SemiSticky::new(T::default())
146     }
147 }
148 
149 impl<T: PartialEq> PartialEq for SemiSticky<T> {
150     #[inline]
eq(&self, other: &SemiSticky<T>) -> bool151     fn eq(&self, other: &SemiSticky<T>) -> bool {
152         *self.get() == *other.get()
153     }
154 }
155 
156 impl<T: Eq> Eq for SemiSticky<T> {}
157 
158 impl<T: PartialOrd> PartialOrd for SemiSticky<T> {
159     #[inline]
partial_cmp(&self, other: &SemiSticky<T>) -> Option<cmp::Ordering>160     fn partial_cmp(&self, other: &SemiSticky<T>) -> Option<cmp::Ordering> {
161         self.get().partial_cmp(&*other.get())
162     }
163 
164     #[inline]
lt(&self, other: &SemiSticky<T>) -> bool165     fn lt(&self, other: &SemiSticky<T>) -> bool {
166         *self.get() < *other.get()
167     }
168 
169     #[inline]
le(&self, other: &SemiSticky<T>) -> bool170     fn le(&self, other: &SemiSticky<T>) -> bool {
171         *self.get() <= *other.get()
172     }
173 
174     #[inline]
gt(&self, other: &SemiSticky<T>) -> bool175     fn gt(&self, other: &SemiSticky<T>) -> bool {
176         *self.get() > *other.get()
177     }
178 
179     #[inline]
ge(&self, other: &SemiSticky<T>) -> bool180     fn ge(&self, other: &SemiSticky<T>) -> bool {
181         *self.get() >= *other.get()
182     }
183 }
184 
185 impl<T: Ord> Ord for SemiSticky<T> {
186     #[inline]
cmp(&self, other: &SemiSticky<T>) -> cmp::Ordering187     fn cmp(&self, other: &SemiSticky<T>) -> cmp::Ordering {
188         self.get().cmp(&*other.get())
189     }
190 }
191 
192 impl<T: fmt::Display> fmt::Display for SemiSticky<T> {
fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>193     fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
194         fmt::Display::fmt(self.get(), f)
195     }
196 }
197 
198 impl<T: fmt::Debug> fmt::Debug for SemiSticky<T> {
fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>199     fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
200         match self.try_get() {
201             Ok(value) => f.debug_struct("SemiSticky").field("value", value).finish(),
202             Err(..) => {
203                 struct InvalidPlaceholder;
204                 impl fmt::Debug for InvalidPlaceholder {
205                     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
206                         f.write_str("<invalid thread>")
207                     }
208                 }
209 
210                 f.debug_struct("SemiSticky")
211                     .field("value", &InvalidPlaceholder)
212                     .finish()
213             }
214         }
215     }
216 }
217 
218 #[test]
test_basic()219 fn test_basic() {
220     use std::thread;
221     let val = SemiSticky::new(true);
222     assert_eq!(val.to_string(), "true");
223     assert_eq!(val.get(), &true);
224     assert!(val.try_get().is_ok());
225     thread::spawn(move || {
226         assert!(val.try_get().is_err());
227     }).join()
228         .unwrap();
229 }
230 
231 #[test]
test_mut()232 fn test_mut() {
233     let mut val = SemiSticky::new(true);
234     *val.get_mut() = false;
235     assert_eq!(val.to_string(), "false");
236     assert_eq!(val.get(), &false);
237 }
238 
239 #[test]
240 #[should_panic]
test_access_other_thread()241 fn test_access_other_thread() {
242     use std::thread;
243     let val = SemiSticky::new(true);
244     thread::spawn(move || {
245         val.get();
246     }).join()
247         .unwrap();
248 }
249 
250 #[test]
test_drop_same_thread()251 fn test_drop_same_thread() {
252     use std::sync::atomic::{AtomicBool, Ordering};
253     use std::sync::Arc;
254     let was_called = Arc::new(AtomicBool::new(false));
255     struct X(Arc<AtomicBool>);
256     impl Drop for X {
257         fn drop(&mut self) {
258             self.0.store(true, Ordering::SeqCst);
259         }
260     }
261     let val = SemiSticky::new(X(was_called.clone()));
262     mem::drop(val);
263     assert_eq!(was_called.load(Ordering::SeqCst), true);
264 }
265 
266 #[test]
test_noop_drop_elsewhere()267 fn test_noop_drop_elsewhere() {
268     use std::sync::atomic::{AtomicBool, Ordering};
269     use std::sync::Arc;
270     use std::thread;
271 
272     let was_called = Arc::new(AtomicBool::new(false));
273 
274     {
275         let was_called = was_called.clone();
276         thread::spawn(move || {
277             struct X(Arc<AtomicBool>);
278             impl Drop for X {
279                 fn drop(&mut self) {
280                     self.0.store(true, Ordering::SeqCst);
281                 }
282             }
283 
284             let val = SemiSticky::new(X(was_called.clone()));
285             assert!(
286                 thread::spawn(move || {
287                     // moves it here but do not deallocate
288                     val.try_get().ok();
289                 }).join()
290                     .is_ok()
291             );
292 
293             assert_eq!(was_called.load(Ordering::SeqCst), false);
294         }).join()
295             .unwrap();
296     }
297 
298     assert_eq!(was_called.load(Ordering::SeqCst), true);
299 }
300 
301 #[test]
test_rc_sending()302 fn test_rc_sending() {
303     use std::rc::Rc;
304     use std::thread;
305     let val = SemiSticky::new(Rc::new(true));
306     thread::spawn(move || {
307         assert!(val.try_get().is_err());
308     }).join().unwrap();
309 }
310