1use std::cmp;
2use std::fmt;
3
4use errors::InvalidThreadAccess;
5use fragile::Fragile;
6use std::mem;
7use sticky::Sticky;
8
9enum SemiStickyImpl<T> {
10 Fragile(Fragile<T>),
11 Sticky(Sticky<T>),
12}
13
14pub struct SemiSticky<T> {
20 inner: SemiStickyImpl<T>,
21}
22
23impl<T> SemiSticky<T> {
24 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 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 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 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 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 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 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 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
128impl<T> From<T> for SemiSticky<T> {
129 #[inline]
130 fn from(t: T) -> SemiSticky<T> {
131 SemiSticky::new(t)
132 }
133}
134
135impl<T: Clone> Clone for SemiSticky<T> {
136 #[inline]
137 fn clone(&self) -> SemiSticky<T> {
138 SemiSticky::new(self.get().clone())
139 }
140}
141
142impl<T: Default> Default for SemiSticky<T> {
143 #[inline]
144 fn default() -> SemiSticky<T> {
145 SemiSticky::new(T::default())
146 }
147}
148
149impl<T: PartialEq> PartialEq for SemiSticky<T> {
150 #[inline]
151 fn eq(&self, other: &SemiSticky<T>) -> bool {
152 *self.get() == *other.get()
153 }
154}
155
156impl<T: Eq> Eq for SemiSticky<T> {}
157
158impl<T: PartialOrd> PartialOrd for SemiSticky<T> {
159 #[inline]
160 fn partial_cmp(&self, other: &SemiSticky<T>) -> Option<cmp::Ordering> {
161 self.get().partial_cmp(&*other.get())
162 }
163
164 #[inline]
165 fn lt(&self, other: &SemiSticky<T>) -> bool {
166 *self.get() < *other.get()
167 }
168
169 #[inline]
170 fn le(&self, other: &SemiSticky<T>) -> bool {
171 *self.get() <= *other.get()
172 }
173
174 #[inline]
175 fn gt(&self, other: &SemiSticky<T>) -> bool {
176 *self.get() > *other.get()
177 }
178
179 #[inline]
180 fn ge(&self, other: &SemiSticky<T>) -> bool {
181 *self.get() >= *other.get()
182 }
183}
184
185impl<T: Ord> Ord for SemiSticky<T> {
186 #[inline]
187 fn cmp(&self, other: &SemiSticky<T>) -> cmp::Ordering {
188 self.get().cmp(&*other.get())
189 }
190}
191
192impl<T: fmt::Display> fmt::Display for SemiSticky<T> {
193 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
194 fmt::Display::fmt(self.get(), f)
195 }
196}
197
198impl<T: fmt::Debug> fmt::Debug for SemiSticky<T> {
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]
219fn 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]
232fn 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]
241fn 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]
251fn 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]
267fn 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 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]
302fn 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}