extern crate fragile;
use std::cmp;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::ops;
pub struct SendCell<T> {
value: fragile::Fragile<T>,
}
impl<T> SendCell<T> {
pub fn new(value: T) -> Self {
SendCell {
value: fragile::Fragile::new(value),
}
}
pub fn into_inner(self) -> T {
self.value.into_inner()
}
pub fn try_into_inner(self) -> Result<T, Self> {
self.value
.try_into_inner()
.map_err(|v| SendCell { value: v })
}
pub fn get(&self) -> &T {
self.value.get()
}
pub fn try_get(&self) -> Option<&T> {
self.value.try_get().ok()
}
pub fn borrow(&self) -> Ref<T> {
Ref { value: self.get() }
}
pub fn try_borrow(&self) -> Option<Ref<T>> {
self.try_get().map(|value| Ref { value: value })
}
}
impl<T> From<T> for SendCell<T> {
fn from(t: T) -> SendCell<T> {
SendCell::new(t)
}
}
impl<T: Default> Default for SendCell<T> {
fn default() -> SendCell<T> {
SendCell::new(T::default())
}
}
impl<T: Clone> Clone for SendCell<T> {
fn clone(&self) -> SendCell<T> {
SendCell::new(self.get().clone())
}
}
impl<T: fmt::Debug> fmt::Debug for SendCell<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
self.get().fmt(f)
}
}
impl<T: PartialEq> PartialEq<SendCell<T>> for SendCell<T> {
fn eq(&self, other: &Self) -> bool {
self.get().eq(other.get())
}
}
impl<T: Eq> Eq for SendCell<T> {}
impl<T: PartialOrd> PartialOrd<SendCell<T>> for SendCell<T> {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
self.get().partial_cmp(other.get())
}
}
impl<T: Ord> Ord for SendCell<T> {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.get().cmp(other.get())
}
}
impl<T: Hash> Hash for SendCell<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.get().hash(state)
}
}
unsafe impl<T> Send for SendCell<T> {}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Ref<'a, T: 'a> {
value: &'a T,
}
impl<'a, T: 'a> ops::Deref for Ref<'a, T> {
type Target = T;
fn deref(&self) -> &T {
self.value
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::mem;
use std::panic;
use std::thread;
#[test]
fn get_success() {
let cell = SendCell::new(1);
assert_eq!(cell.get(), &1);
assert_eq!(cell.try_get(), Some(&1));
}
#[test]
#[should_panic]
fn get_failure() {
let t = thread::spawn(move || {
let cell = SendCell::new(1);
assert_eq!(cell.get(), &1);
cell
});
let r = t.join();
let cell = r.unwrap();
let panic = {
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| cell.get()));
res.err()
};
mem::forget(cell);
if let Some(payload) = panic {
panic::resume_unwind(payload);
}
}
#[test]
fn try_get_failure() {
let t = thread::spawn(move || {
let cell = SendCell::new(1);
assert_eq!(cell.get(), &1);
cell
});
let r = t.join();
let cell = r.unwrap();
assert_eq!(cell.try_get(), None);
mem::forget(cell);
}
#[test]
fn borrow_success() {
let cell = SendCell::new(1);
assert_eq!(*cell.borrow(), 1);
assert_eq!(*cell.try_borrow().unwrap(), 1);
}
#[test]
#[should_panic]
fn borrow_failure() {
let t = thread::spawn(move || {
let cell = SendCell::new(1);
assert_eq!(*cell.borrow(), 1);
cell
});
let r = t.join();
let cell = r.unwrap();
let panic = {
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| cell.borrow()));
res.err()
};
mem::forget(cell);
if let Some(payload) = panic {
panic::resume_unwind(payload);
}
}
#[test]
fn try_borrow_failure() {
let t = thread::spawn(move || {
let cell = SendCell::new(1);
assert_eq!(*cell.borrow(), 1);
cell
});
let r = t.join();
let cell = r.unwrap();
assert_eq!(cell.try_borrow(), None);
mem::forget(cell);
}
#[test]
fn into_inner_success() {
let cell = SendCell::new(1);
assert_eq!(cell.try_into_inner().unwrap(), 1);
}
#[test]
fn try_into_inner_failure() {
let t = thread::spawn(move || SendCell::new(1));
let r = t.join();
let cell = r.unwrap();
let res = cell.try_into_inner();
assert!(res.is_err());
mem::forget(res);
}
struct Dummy(i32);
impl Drop for Dummy {
fn drop(&mut self) {}
}
#[test]
#[should_panic]
fn drop_panic() {
let t = thread::spawn(move || SendCell::new(Dummy(1)));
let r = t.join();
let _ = r.unwrap();
}
#[test]
fn drop_is_not_run_from_other_thread() {
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
struct MakeItTrueOnDrop(Arc<AtomicBool>);
impl Drop for MakeItTrueOnDrop {
fn drop(&mut self) {
self.0.swap(true, Ordering::SeqCst);
}
}
let is_dropped = Arc::new(AtomicBool::new(false));
let v = SendCell::new(MakeItTrueOnDrop(is_dropped.clone()));
let t = thread::spawn(move || {
let _ = v;
});
let error = t.join().expect_err("thread should have panicked");
assert_eq!(
error.downcast_ref::<&str>(),
Some(&"destructor of fragile object ran on wrong thread")
);
assert_eq!(
is_dropped.load(Ordering::SeqCst),
false,
"Drop impl should not have been executed"
);
}
}