1#![allow(non_camel_case_types)]
5
6use std::cmp;
7use std::fmt;
8use std::mem;
9use std::sync::Mutex;
10use std::thread;
11use std::thread::ThreadId;
12use std::sync::Arc;
13
14use crate::errors::FailTakeOwnership;
15use crate::errors::InvalidThreadAccess;
16use std::mem::ManuallyDrop;
17
18pub struct CellGuard {
28 pub freeze: bool,
29 pub thread_id: ThreadId,
30}
31
32pub struct iCell<T> {
49 value: ManuallyDrop<T>,
50 guard: Arc<Mutex<CellGuard>>,
51}
52
53impl<T> iCell<T> {
54 pub fn new(value: T,freeze: bool) -> Self {
73 let guard = CellGuard {
74 freeze,
75 thread_id: thread::current().id(),
76 };
77
78 iCell {
79 value: ManuallyDrop::new(value),
80 guard: Arc::new(Mutex::new(guard)),
81 }
82 }
83
84 pub fn take_ownership(&self) -> Result<bool, FailTakeOwnership>{
108 let mut guard = self.guard.lock().unwrap();
109 if guard.freeze {
110 return Err(FailTakeOwnership);
111 }
112 guard.freeze = true;
113 guard.thread_id = thread::current().id();
114 Ok(true)
115 }
116
117 pub fn is_valid(&self) -> bool {
132 let owner = self.guard.lock().unwrap().thread_id;
133 thread::current().id() == owner
134 }
135
136 #[inline(always)]
137 fn assert_thread(&self) {
138 if !self.is_valid() {
139 panic!("trying to access wrapped value in fragile container from incorrect thread.");
140 }
141 }
142
143 pub fn into_inner(self) -> T {
159 self.assert_thread();
160 let mut this = ManuallyDrop::new(self);
161
162 unsafe { ManuallyDrop::take(&mut this.value) }
163 }
164
165 pub fn try_into_inner(self) -> Result<T, Self> {
190 if self.is_valid() {
191 Ok(self.into_inner())
192 } else {
193 Err(self)
194 }
195 }
196
197 fn get(&self) -> &T {
201 self.assert_thread();
202 &self.value
203 }
204
205 fn get_mut(&mut self) -> &mut T {
209 self.assert_thread();
210 &mut self.value
211 }
212
213 pub fn try_get(&self) -> Result<&T, InvalidThreadAccess> {
218 if self.is_valid() {
219 Ok(&*self.value)
220 } else {
221 Err(InvalidThreadAccess)
222 }
223 }
224
225 pub fn try_get_mut(&mut self) -> Result<&mut T, InvalidThreadAccess> {
230 if self.is_valid() {
231 Ok(self.get_mut())
232 } else {
233 Err(InvalidThreadAccess)
234 }
235 }
236}
237
238impl<T> Drop for iCell<T> {
239 #[track_caller]
240 fn drop(&mut self) {
241 if mem::needs_drop::<T>() {
242 if self.is_valid() {
243 unsafe { ManuallyDrop::drop(&mut self.value) };
244 } else {
245 panic!("destructor of fragile object ran on wrong thread");
246 }
247 }
248 }
249}
250
251impl<T> From<T> for iCell<T> {
252 #[inline]
253 fn from(t: T) -> iCell<T> {
254 iCell::new(t, false)
255 }
256}
257
258impl<T: Clone> Clone for iCell<T> {
259 #[inline]
260 fn clone(&self) -> iCell<T> {
261 iCell::new(self.get().clone(), false)
262 }
263}
264
265impl<T: Default> Default for iCell<T> {
266 #[inline]
267 fn default() -> iCell<T> {
268 iCell::new(T::default(), false)
269 }
270}
271
272impl<T: PartialEq> PartialEq for iCell<T> {
273 #[inline]
274 fn eq(&self, other: &iCell<T>) -> bool {
275 *self.get() == *other.get()
276 }
277}
278
279impl<T: Eq> Eq for iCell<T> {}
280
281impl<T: PartialOrd> PartialOrd for iCell<T> {
282 #[inline]
283 fn partial_cmp(&self, other: &iCell<T>) -> Option<cmp::Ordering> {
284 self.get().partial_cmp(other.get())
285 }
286
287 #[inline]
288 fn lt(&self, other: &iCell<T>) -> bool {
289 *self.get() < *other.get()
290 }
291
292 #[inline]
293 fn le(&self, other: &iCell<T>) -> bool {
294 *self.get() <= *other.get()
295 }
296
297 #[inline]
298 fn gt(&self, other: &iCell<T>) -> bool {
299 *self.get() > *other.get()
300 }
301
302 #[inline]
303 fn ge(&self, other: &iCell<T>) -> bool {
304 *self.get() >= *other.get()
305 }
306}
307
308impl<T: Ord> Ord for iCell<T> {
309 #[inline]
310 fn cmp(&self, other: &iCell<T>) -> cmp::Ordering {
311 self.get().cmp(other.get())
312 }
313}
314
315impl<T: fmt::Display> fmt::Display for iCell<T> {
316 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
317 fmt::Display::fmt(self.get(), f)
318 }
319}
320
321impl<T: fmt::Debug> fmt::Debug for iCell<T> {
322 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
323 match self.try_get() {
324 Ok(value) => f.debug_struct("Fragile").field("value", value).finish(),
325 Err(..) => {
326 struct InvalidPlaceholder;
327 impl fmt::Debug for InvalidPlaceholder {
328 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
329 f.write_str("<invalid thread>")
330 }
331 }
332
333 f.debug_struct("Fragile")
334 .field("value", &InvalidPlaceholder)
335 .finish()
336 }
337 }
338 }
339}
340
341unsafe impl<T> Sync for iCell<T> {}
345
346#[allow(clippy::non_send_fields_in_send_ty)]
348unsafe impl<T> Send for iCell<T> {}
349
350#[test]
351fn test_basic() {
352 use std::thread;
353 let val = iCell::new(true, false);
354 assert_eq!(val.to_string(), "true");
355 assert_eq!(val.get(), &true);
356 assert!(val.try_get().is_ok());
357 thread::spawn(move || {
358 assert!(val.try_get().is_err());
359 })
360 .join()
361 .unwrap();
362}
363
364#[test]
365fn test_mut() {
366 let mut val = iCell::new(true, false);
367 *val.get_mut() = false;
368 assert_eq!(val.to_string(), "false");
369 assert_eq!(val.get(), &false);
370}
371
372#[test]
373#[should_panic]
374fn test_access_other_thread() {
375 use std::thread;
376 let val = iCell::new(true, false);
377 thread::spawn(move || {
378 val.get();
379 })
380 .join()
381 .unwrap();
382}
383
384#[test]
385fn test_noop_drop_elsewhere() {
386 use std::thread;
387 let val = iCell::new(true, false);
388 thread::spawn(move || {
389 val.try_get().ok();
391 })
392 .join()
393 .unwrap();
394}
395
396#[test]
397fn test_panic_on_drop_elsewhere() {
398 use std::sync::atomic::{AtomicBool, Ordering};
399 use std::sync::Arc;
400 use std::thread;
401 let was_called = Arc::new(AtomicBool::new(false));
402 struct X(Arc<AtomicBool>);
403 impl Drop for X {
404 fn drop(&mut self) {
405 self.0.store(true, Ordering::SeqCst);
406 }
407 }
408 let val = iCell::new(X(was_called.clone()), false);
409 assert!(thread::spawn(move || {
410 val.try_get().ok();
411 })
412 .join()
413 .is_err());
414 assert!(!was_called.load(Ordering::SeqCst));
415}
416
417#[test]
418fn test_rc_sending() {
419 use std::rc::Rc;
420 use std::sync::mpsc::channel;
421 use std::thread;
422
423 let val = iCell::new(Rc::new(true), false);
424 let (tx, rx) = channel();
425
426 let thread = thread::spawn(move || {
427 assert!(val.try_get().is_err());
428 let here = val;
429 tx.send(here).unwrap();
430 });
431
432 let rv = rx.recv().unwrap();
433 assert!(**rv.get());
434
435 thread.join().unwrap();
436}
437
438#[test]
439fn test_rc_sending_take_ownership() {
440 use std::rc::Rc;
441 use std::sync::mpsc::channel;
442 use std::thread;
443
444 let val = iCell::new(Rc::new(true), false);
445 let (tx, rx) = channel();
446
447 let sender = thread::spawn(move || {
448 assert!(val.try_get().is_err());
449 let here = val;
450 tx.send(here).unwrap();
451 });
452
453 let recv = thread::spawn(move || {
454 let rv = rx.recv().unwrap();
455 let r = rv.take_ownership();
456 match r {
457 Ok(_) => {},
458 Err(_) => panic!("failed to take ownership"),
459 }
460 assert!(**rv.get());
461 });
462
463 recv.join().unwrap();
464 sender.join().unwrap();
465}
466
467#[test]
468fn test_use_after_free() {
469 use std::rc::Rc;
470 use std::sync::mpsc::channel;
471 use std::thread;
472
473 let val = iCell::new(Rc::new(true), false);
474 let (tx, rx) = channel();
475
476 let sender = thread::spawn(move || {
477 assert!(val.try_get().is_err());
478 let here = val;
479 tx.send(here).unwrap();
480 });
481
482 sender.join().unwrap();
483
484 let recv = thread::spawn(move || {
485 let rv = rx.recv().unwrap();
486 let r = rv.take_ownership();
487 match r {
488 Ok(_) => {},
489 Err(_) => panic!("failed to take ownership"),
490 }
491 assert!(**rv.get());
492 });
493
494 recv.join().unwrap();
495}