#![feature(const_fn)]
use std::any::Any;
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::mem;
use std::sync::atomic;
static ID_COUNTER: atomic::AtomicUsize = atomic::AtomicUsize::new(0);
thread_local! {
static THREAD_OBJECTS: RefCell<BTreeMap<usize, Box<Any>>> = RefCell::new(BTreeMap::new());
}
#[derive(Copy, Clone)]
pub struct Object<T> {
initial: T,
id: usize,
}
impl<T> Object<T> {
pub fn new(initial: T) -> Object<T> {
Object {
initial: initial,
id: ID_COUNTER.fetch_add(1, atomic::Ordering::Relaxed),
}
}
}
impl<T: Clone + Any> Object<T> {
pub fn with<F, R>(&self, f: F) -> R where F: FnOnce(&mut T) -> R {
THREAD_OBJECTS.with(|map| {
let mut guard = map.borrow_mut();
let ptr = guard.entry(self.id).or_insert_with(|| Box::new(self.initial.clone()));
f(ptr.downcast_mut().unwrap())
})
}
pub fn replace(&self, new: T) -> T {
self.with(|x| mem::replace(x, new))
}
pub fn get(&self) -> T where T: Copy {
self.with(|x| *x)
}
}
impl<T: Default> Default for Object<T> {
fn default() -> Object<T> {
Object::new(T::default())
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::thread;
use std::sync::{Mutex, Arc};
#[test]
fn initial_value() {
let obj = Object::new(23);
obj.with(|&mut x| assert_eq!(x, 23));
assert_eq!(obj.with(|&mut x| x), 23);
}
#[test]
fn string() {
let obj = Object::new(String::new());
obj.with(|x| {
assert!(x.is_empty());
x.push('b');
});
obj.with(|x| {
assert_eq!(x, "b");
x.push('a');
});
obj.with(|x| {
assert_eq!(x, "ba");
});
}
#[test]
fn multiple_objects() {
let obj1 = Object::new(0);
let obj2 = Object::new(0);
obj2.with(|x| *x = 1);
obj1.with(|&mut x| assert_eq!(x, 0));
obj2.with(|&mut x| assert_eq!(x, 1));
}
#[test]
fn multi_thread() {
let obj = Object::new(0);
thread::spawn(move || {
obj.with(|x| *x = 1);
}).join().unwrap();
obj.with(|&mut x| assert_eq!(x, 0));
thread::spawn(move || {
obj.with(|&mut x| assert_eq!(x, 0));
obj.with(|x| *x = 2);
}).join().unwrap();
obj.with(|&mut x| assert_eq!(x, 0));
}
#[test]
fn replace() {
let obj = Object::new(420); assert_eq!(obj.replace(42), 420);
assert_eq!(obj.replace(32), 42);
assert_eq!(obj.replace(0), 32);
}
#[test]
fn default() {
assert_eq!(Object::<usize>::default().get(), 0);
}
#[derive(Clone)]
struct Dropper {
is_dropped: Arc<Mutex<bool>>,
}
impl Drop for Dropper {
fn drop(&mut self) {
*self.is_dropped.lock().unwrap() = true;
}
}
#[test]
fn drop() {
let is_dropped = Arc::new(Mutex::new(false));
let arc = is_dropped.clone();
thread::spawn(move || {
let obj = Object::new(Dropper {
is_dropped: arc,
});
obj.with(|_| {});
mem::forget(obj);
}).join().unwrap();
assert!(*is_dropped.lock().unwrap());
}
}