Skip to content

Instantly share code, notes, and snippets.

@XMPPwocky
Created January 6, 2015 19:36
Show Gist options
  • Save XMPPwocky/ba7160d3240f4801a668 to your computer and use it in GitHub Desktop.
Save XMPPwocky/ba7160d3240f4801a668 to your computer and use it in GitHub Desktop.
use std::collections::{
RingBuf
};
use std::default::{
Default
};
use std::sync::{
Arc,
Mutex,
RWLock,
RWLockReadGuard,
RWLockWriteGuard,
};
pub struct Handle<Entity: Send> {
elist: EntityList<Entity>,
idx: u32
}
impl<Entity: Send> Clone for Handle<Entity> {
fn clone(&self) -> Handle<Entity> {
Handle {
elist: self.elist.clone(),
idx: self.idx
}
}
}
#[unsafe_destructor]
impl<Entity: Send> Drop for Handle<Entity> {
fn drop(&mut self) {
if let Ok(mut freelist) = self.elist.inner.freelist.lock() {
freelist.push_front(self.idx);
};
}
}
impl<Entity: Send + Sync> Handle<Entity> {
pub fn read(&self) -> RWLockReadGuard<Entity> {
self.elist.inner.entities[self.idx as uint].read().unwrap()
}
pub fn write(&self) -> RWLockWriteGuard<Entity> {
self.elist.inner.entities[self.idx as uint].write().unwrap()
}
}
pub const MAX_ENTITIES: uint = 2048;
struct EntityListInner<Entity> {
entities: Vec<RWLock<Entity>>,
freelist: Mutex<RingBuf<u32>>,
}
pub struct EntityList<Entity: Send> {
inner: Arc<EntityListInner<Entity>>
}
impl<Entity: Send> Clone for EntityList<Entity> {
fn clone(&self) -> EntityList<Entity> {
EntityList { inner: self.inner.clone() }
}
}
impl<Entity: Default + Send + Sync> EntityList<Entity> {
/// Inserts e into the entity list.
pub fn spawn(&self, e: Entity) -> Handle<Entity> {
let idx = self.inner.freelist.lock().unwrap().pop_front()
.expect("Out of room in EntityList!");
let hnd = Handle {
elist: self.clone(),
idx: idx
};
*hnd.write() = e;
hnd
}
/// Constructs a new, empty entity list.
pub fn new() -> EntityList<Entity> {
let entities = range(0u, MAX_ENTITIES)
.map(|_| RWLock::new(Default::default()))
.collect::<Vec<_>>();
let freelist = Mutex::new(
range(0, MAX_ENTITIES)
.map(|x| x as u32)
.collect::<RingBuf<_>>());
let inner = EntityListInner {
entities: entities,
freelist: freelist
};
EntityList {
inner: Arc::new(inner)
}
}
}
#[cfg(test)]
mod test {
use super::{
EntityList,
MAX_ENTITIES
};
use test::black_box;
#[test]
fn smoke_elist() {
let elist = EntityList::new();
let e1 = elist.spawn(0u32);
assert_eq!(*e1.read(), 0);
}
#[test]
fn read_aliasing() {
let elist = EntityList::new();
let e1 = elist.spawn(0u32);
assert_eq!(*e1.read(), *e1.read());
}
#[test]
fn mutation() {
let elist = EntityList::new();
let e1 = elist.spawn(0u32);
let e2 = elist.spawn(1u32);
*e1.write() = 42;
assert_eq!(*e1.read(), 42);
assert_eq!(*e2.read(), 1);
}
#[test]
fn index_reuse() {
let elist = EntityList::new();
{
black_box(elist.spawn(0u32));
}
let e1 = elist.spawn(1u32);
assert_eq!(e1.idx, 0);
}
#[test]
fn nearly_out_of_room() {
let elist = EntityList::new();
let mut handles = Vec::new();
for _ in range(0, MAX_ENTITIES) {
handles.push(elist.spawn(0u32));
}
black_box(handles);
}
#[test]
#[should_fail]
fn out_of_room() {
let elist = EntityList::new();
let mut handles = Vec::new();
for _ in range(0, MAX_ENTITIES + 1) {
handles.push(elist.spawn(0u32));
}
black_box(handles);
}
#[test]
// mostly just checking that things are Send, etc.
fn threading() {
use std::thread::Thread;
let elist = EntityList::new();
let e1 = Thread::spawn(move || {
elist.clone().spawn(0u32)
}).join().ok().unwrap();
let e1_ = e1.clone();
Thread::spawn(move |&:| {
*e1_.write() = 42;
}).join().ok().unwrap();
assert_eq!(*e1.read(), 42);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment