#![deny(missing_docs)]
#![cfg_attr(feature = "nightly", feature(test))]
use std::collections::hash_map::{HashMap, Entry};
use std::fmt;
use std::sync::Mutex;
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT};
use std::sync::atomic::Ordering::Relaxed;
static COUNTER: AtomicUsize = ATOMIC_USIZE_INIT;
thread_local!(static THREAD_ID: usize = COUNTER.fetch_add(1, Relaxed) + 1);
pub type CreateFn<T> = Box<Fn() -> T + Send + Sync + 'static>;
pub struct Pool<T: Send> {
create: CreateFn<T>,
owner: AtomicUsize,
owner_val: T,
global: Mutex<HashMap<usize, Box<T>>>,
}
unsafe impl<T: Send> Sync for Pool<T> {}
impl<T: fmt::Debug + Send + 'static> fmt::Debug for Pool<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Pool({:?})", self.owner_val)
}
}
impl<T: Send> Pool<T> {
pub fn new(create: CreateFn<T>) -> Pool<T> {
let owner_val = (create)();
Pool {
create: create,
owner: AtomicUsize::new(0),
owner_val: owner_val,
global: Mutex::new(HashMap::new()),
}
}
#[inline(always)]
pub fn get(&self) -> &T {
let id = THREAD_ID.with(|id| *id);
let owner = self.owner.load(Relaxed);
if owner == id {
return &self.owner_val;
}
self.get_slow(owner, id)
}
#[cold]
fn get_slow(&self, owner: usize, thread_id: usize) -> &T {
if owner == 0 {
if self.owner.compare_and_swap(0, thread_id, Relaxed) == 0 {
return &self.owner_val;
}
}
let mut global = self.global.lock().unwrap();
match global.entry(thread_id) {
Entry::Occupied(ref e) => {
let p: *const T = &**e.get();
unsafe { &*p }
}
Entry::Vacant(e) => {
let t = Box::new((self.create)());
let p: *const T = &*t;
e.insert(t);
unsafe { &*p }
}
}
}
}
#[cfg(test)]
#[cfg(feature = "nightly")]
mod bench;
#[cfg(test)]
mod tests {
use std::cell::RefCell;
use std::sync::Arc;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::SeqCst;
use std::thread;
use super::{CreateFn, Pool};
#[derive(Debug, Eq, PartialEq)]
struct Dummy(usize);
fn dummy() -> CreateFn<Dummy> {
let count = AtomicUsize::new(0);
Box::new(move || {
Dummy(count.fetch_add(1, SeqCst))
})
}
#[test]
fn empty() {
let pool = Pool::new(dummy());
assert_eq!(&Dummy(0), &*pool.get());
}
#[test]
fn reuse() {
let pool = Pool::new(dummy());
{
assert_eq!(&Dummy(0), &*pool.get());
}
assert_eq!(&Dummy(0), &*pool.get());
assert_eq!(&Dummy(0), &*pool.get());
}
#[test]
fn no_reuse() {
let pool = Arc::new(Pool::new(dummy()));
let val = pool.get();
assert_eq!(&Dummy(0), &*val);
let pool2 = pool.clone();
thread::spawn(move || {
assert_eq!(&Dummy(1), &*pool2.get());
}).join().unwrap();
}
#[test]
fn is_sync() {
fn foo<T: Sync>() {}
foo::<Pool<String>>();
foo::<Pool<RefCell<String>>>();
}
}