use std::thread;
use std::fmt;
use std::cmp;
use std::ops;
use std::hash::{Hash, Hasher};
pub struct SendCell<T> {
value: Option<T>,
thread_id: thread::ThreadId,
}
impl<T> SendCell<T> {
pub fn new(value: T) -> Self {
SendCell {
value: Some(value),
thread_id: thread::current().id(),
}
}
pub fn into_inner(mut self) -> T {
if thread::current().id() != self.thread_id {
panic!("trying to convert to inner value in invalid thread");
}
self.value.take().unwrap()
}
pub fn try_into_inner(mut self) -> Result<T, Self> {
if thread::current().id() == self.thread_id {
Ok(self.value.take().unwrap())
} else {
Err(self)
}
}
pub fn get(&self) -> &T {
if thread::current().id() != self.thread_id {
panic!("trying to convert to inner value in invalid thread");
}
self.value.as_ref().unwrap()
}
pub fn try_get(&self) -> Option<&T> {
if thread::current().id() == self.thread_id {
Some(self.value.as_ref().unwrap())
} else {
None
}
}
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> Drop for SendCell<T> {
fn drop(&mut self) {
if thread::current().id() != self.thread_id {
panic!("trying to convert to inner value in invalid thread");
}
}
}
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::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 _ = cell.get();
}
#[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);
}
#[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 _ = cell.borrow();
}
#[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);
}
#[test]
fn into_inner_success() {
let cell = SendCell::new(1);
assert_eq!(cell.try_into_inner().unwrap(), 1);
}
#[test]
#[should_panic]
fn into_inner_failure() {
let t = thread::spawn(move || SendCell::new(1));
let r = t.join();
let cell = r.unwrap();
let _ = cell.into_inner();
}
#[test]
fn try_into_inner_failure() {
let t = thread::spawn(move || SendCell::new(1));
let r = t.join();
let cell = r.unwrap();
assert!(cell.try_into_inner().is_err());
}
}