#![cfg_attr(feature = "cargo-clippy", allow(doc_markdown))]
#![cfg_attr(not(feature = "std"), no_std)]
#![feature(alloc, allocator_api)]
#![feature(plugin)]
#![cfg_attr(test, feature(test))]
#![plugin(interpolate_idents)]
mod aligned;
mod backing;
mod init;
mod large;
mod ptr_map;
mod stack;
#[cfg(test)]
mod tests;
mod util;
extern crate alloc;
#[cfg(feature = "std")]
extern crate core;
#[macro_use]
extern crate lazy_static;
extern crate object_alloc;
#[cfg(test)]
#[macro_use]
extern crate object_alloc_test;
extern crate sysconf;
use core::marker::PhantomData;
use core::default::Default;
use core::mem;
use self::util::list::*;
use util::workingset::WorkingSet;
use init::*;
use self::init::InitSystem;
use self::object_alloc::{Exhausted, ObjectAlloc, UntypedObjectAlloc};
use self::alloc::allocator::Layout;
pub use backing::BackingAlloc;
#[cfg(feature = "std")]
use backing::heap::HeapBackingAlloc;
#[cfg(feature = "os")]
use backing::mmap::MmapBackingAlloc;
use init::NopInitSystem;
type DefaultInitSystem<T> = init::InitInitSystem<T, init::DefaultInitializer<T>>;
type FnInitSystem<T, F> = init::InitInitSystem<T, init::FnInitializer<T, F>>;
type UnsafeFnInitSystem<T, F> = init::InitInitSystem<T, init::UnsafeFnInitializer<T, F>>;
lazy_static!{
static ref PAGE_SIZE: usize = self::sysconf::page::pagesize();
static ref PAGE_ALIGN_MASK: usize = !(*PAGE_SIZE - 1);
}
const WORKING_PERIOD_SECONDS: u64 = 15;
const OBJECTS_PER_SLAB: usize = 8;
pub struct SlabAlloc<T, I: InitSystem, B: BackingAlloc> {
alloc: PrivateSlabAlloc<I, B>,
_marker: PhantomData<T>,
}
pub struct UntypedSlabAlloc<I: InitSystem, B: BackingAlloc> {
alloc: PrivateUntypedSlabAlloc<I, B>,
}
enum PrivateSlabAlloc<I: InitSystem, B: BackingAlloc> {
Aligned(SizedSlabAlloc<I, aligned::System<B::Aligned>>),
Large(SizedSlabAlloc<I, large::System<B::Large>>),
}
enum PrivateUntypedSlabAlloc<I: InitSystem, B: BackingAlloc> {
Aligned(SizedSlabAlloc<I, aligned::System<B::Aligned>>),
Large(SizedSlabAlloc<I, large::System<B::Large>>),
}
pub struct SlabAllocBuilder<T, I: InitSystem> {
init: I,
layout: Layout,
_marker: PhantomData<T>,
}
impl<T, I: InitSystem> SlabAllocBuilder<T, I> {
pub fn align(mut self, align: usize) -> SlabAllocBuilder<T, I> {
assert!(align.is_power_of_two());
assert!(align <= mem::size_of::<T>());
assert_eq!(mem::size_of::<T>() % align, 0);
assert!(align <= *PAGE_SIZE);
self.layout = self.layout.align_to(align);
self
}
#[cfg(feature = "std")]
pub fn build(self) -> SlabAlloc<T, I, HeapBackingAlloc> {
use backing::heap::{get_aligned, get_large};
self.build_backing(get_aligned, get_large)
}
#[cfg(feature = "os")]
pub fn build_mmap(self) -> SlabAlloc<T, I, MmapBackingAlloc> {
use backing::mmap::{get_aligned, get_large};
self.build_backing(get_aligned, get_large)
}
#[cfg(feature = "std")]
pub fn build_untyped(self) -> UntypedSlabAlloc<I, HeapBackingAlloc> {
use backing::heap::{get_aligned, get_large};
self.build_untyped_backing(get_aligned, get_large)
}
#[cfg(feature = "os")]
pub fn build_untyped_mmap(self) -> UntypedSlabAlloc<I, MmapBackingAlloc> {
use backing::mmap::{get_aligned, get_large};
self.build_untyped_backing(get_aligned, get_large)
}
pub fn build_backing<B, A, L>(self, get_aligned: A, get_large: L) -> SlabAlloc<T, I, B>
where B: BackingAlloc,
A: Fn(Layout) -> Option<B::Aligned>,
L: Fn(Layout) -> B::Large
{
let layout = util::misc::satisfy_min_align(self.layout.clone(), I::min_align());
let aligned_backing_size = aligned::backing_size_for::<I>(&layout);
let aligned_slab_layout =
Layout::from_size_align(aligned_backing_size, aligned_backing_size).unwrap();
SlabAlloc {
alloc: if let Some(alloc) = get_aligned(aligned_slab_layout) {
let data = aligned::System::new(layout, alloc).unwrap();
PrivateSlabAlloc::Aligned(SizedSlabAlloc::new(self.init, self.layout, data))
} else {
let backing_size = large::backing_size_for::<I>(&layout);
let slab_layout = Layout::from_size_align(backing_size, *PAGE_SIZE).unwrap();
let data = large::System::new(layout, get_large(slab_layout)).unwrap();
PrivateSlabAlloc::Large(SizedSlabAlloc::new(self.init, self.layout, data))
},
_marker: PhantomData,
}
}
pub fn build_untyped_backing<B, A, L>(self,
get_aligned: A,
get_large: L)
-> UntypedSlabAlloc<I, B>
where B: BackingAlloc,
A: Fn(Layout) -> Option<B::Aligned>,
L: Fn(Layout) -> B::Large
{
let layout = util::misc::satisfy_min_align(self.layout.clone(), I::min_align());
let aligned_backing_size = aligned::backing_size_for::<I>(&layout);
let aligned_slab_layout =
Layout::from_size_align(aligned_backing_size, aligned_backing_size).unwrap();
UntypedSlabAlloc {
alloc: if let Some(alloc) = get_aligned(aligned_slab_layout) {
let data = aligned::System::new(layout, alloc).unwrap();
PrivateUntypedSlabAlloc::Aligned(SizedSlabAlloc::new(self.init, self.layout, data))
} else {
let backing_size = large::backing_size_for::<I>(&layout);
let slab_layout = Layout::from_size_align(backing_size, *PAGE_SIZE).unwrap();
let data = large::System::new(layout, get_large(slab_layout)).unwrap();
PrivateUntypedSlabAlloc::Large(SizedSlabAlloc::new(self.init, self.layout, data))
},
}
}
}
impl<T: Default> SlabAllocBuilder<T, DefaultInitSystem<T>> {
pub fn default() -> SlabAllocBuilder<T, DefaultInitSystem<T>> {
SlabAllocBuilder {
init: DefaultInitSystem::new(DefaultInitializer::new()),
layout: Layout::new::<T>(),
_marker: PhantomData,
}
}
}
impl<T, F: Fn() -> T> SlabAllocBuilder<T, FnInitSystem<T, F>> {
pub fn func(f: F) -> SlabAllocBuilder<T, FnInitSystem<T, F>> {
SlabAllocBuilder {
init: FnInitSystem::new(FnInitializer::new(f)),
layout: Layout::new::<T>(),
_marker: PhantomData,
}
}
}
impl<T, F: Fn(*mut T)> SlabAllocBuilder<T, UnsafeFnInitSystem<T, F>> {
pub unsafe fn unsafe_func(f: F) -> SlabAllocBuilder<T, UnsafeFnInitSystem<T, F>> {
SlabAllocBuilder {
init: UnsafeFnInitSystem::new(UnsafeFnInitializer::new(f)),
layout: Layout::new::<T>(),
_marker: PhantomData,
}
}
}
impl<T> SlabAllocBuilder<T, NopInitSystem> {
pub unsafe fn no_initialize() -> SlabAllocBuilder<T, NopInitSystem> {
SlabAllocBuilder {
init: NopInitSystem,
layout: Layout::new::<T>(),
_marker: PhantomData,
}
}
}
pub struct UntypedSlabAllocBuilder<I: InitSystem> {
init: I,
layout: Layout,
}
impl<I: InitSystem> UntypedSlabAllocBuilder<I> {
pub fn align(mut self, align: usize) -> UntypedSlabAllocBuilder<I> {
assert!(align.is_power_of_two());
assert!(align <= self.layout.size());
assert_eq!(self.layout.size() % align, 0);
assert!(align <= *PAGE_SIZE);
self.layout = self.layout.align_to(align);
self
}
#[cfg(feature = "std")]
pub fn build(self) -> UntypedSlabAlloc<I, HeapBackingAlloc> {
use backing::heap::{get_aligned, get_large};
self.build_backing(get_aligned, get_large)
}
#[cfg(feature = "os")]
pub fn build_mmap(self) -> UntypedSlabAlloc<I, MmapBackingAlloc> {
use backing::mmap::{get_aligned, get_large};
self.build_backing(get_aligned, get_large)
}
pub fn build_backing<B, A, L>(self, get_aligned: A, get_large: L) -> UntypedSlabAlloc<I, B>
where B: BackingAlloc,
A: Fn(Layout) -> Option<B::Aligned>,
L: Fn(Layout) -> B::Large
{
let layout = util::misc::satisfy_min_align(self.layout.clone(), I::min_align());
let aligned_backing_size = aligned::backing_size_for::<I>(&layout);
let aligned_slab_layout =
Layout::from_size_align(aligned_backing_size, aligned_backing_size).unwrap();
UntypedSlabAlloc {
alloc: if let Some(alloc) = get_aligned(aligned_slab_layout) {
let data = aligned::System::new(layout, alloc).unwrap();
PrivateUntypedSlabAlloc::Aligned(SizedSlabAlloc::new(self.init, self.layout, data))
} else {
let backing_size = large::backing_size_for::<I>(&layout);
let slab_layout = Layout::from_size_align(backing_size, *PAGE_SIZE).unwrap();
let data = large::System::new(layout, get_large(slab_layout)).unwrap();
PrivateUntypedSlabAlloc::Large(SizedSlabAlloc::new(self.init, self.layout, data))
},
}
}
}
impl<F: Fn(*mut u8)> UntypedSlabAllocBuilder<UnsafeFnInitSystem<u8, F>> {
pub fn func(layout: Layout, f: F) -> UntypedSlabAllocBuilder<UnsafeFnInitSystem<u8, F>> {
UntypedSlabAllocBuilder {
init: UnsafeFnInitSystem::new(UnsafeFnInitializer::new(f)),
layout: layout,
}
}
}
impl UntypedSlabAllocBuilder<NopInitSystem> {
pub fn new(layout: Layout) -> UntypedSlabAllocBuilder<NopInitSystem> {
UntypedSlabAllocBuilder {
init: NopInitSystem,
layout: layout,
}
}
}
unsafe impl<T, I: InitSystem, B: BackingAlloc> ObjectAlloc<T> for SlabAlloc<T, I, B> {
unsafe fn alloc(&mut self) -> Result<*mut T, Exhausted> {
match self.alloc {
PrivateSlabAlloc::Aligned(ref mut alloc) => alloc.alloc(),
PrivateSlabAlloc::Large(ref mut alloc) => alloc.alloc(),
}
.map(|ptr| ptr as *mut T)
}
unsafe fn dealloc(&mut self, x: *mut T) {
match self.alloc {
PrivateSlabAlloc::Aligned(ref mut alloc) => alloc.dealloc(x as *mut u8),
PrivateSlabAlloc::Large(ref mut alloc) => alloc.dealloc(x as *mut u8),
}
}
}
unsafe impl<T, I: InitSystem, B: BackingAlloc> UntypedObjectAlloc for SlabAlloc<T, I, B> {
fn layout(&self) -> Layout {
match self.alloc {
PrivateSlabAlloc::Aligned(ref alloc) => alloc.layout.clone(),
PrivateSlabAlloc::Large(ref alloc) => alloc.layout.clone(),
}
}
unsafe fn alloc(&mut self) -> Result<*mut u8, Exhausted> {
match self.alloc {
PrivateSlabAlloc::Aligned(ref mut alloc) => alloc.alloc(),
PrivateSlabAlloc::Large(ref mut alloc) => alloc.alloc(),
}
}
unsafe fn dealloc(&mut self, x: *mut u8) {
match self.alloc {
PrivateSlabAlloc::Aligned(ref mut alloc) => alloc.dealloc(x),
PrivateSlabAlloc::Large(ref mut alloc) => alloc.dealloc(x),
}
}
}
unsafe impl<I: InitSystem, B: BackingAlloc> UntypedObjectAlloc for UntypedSlabAlloc<I, B> {
fn layout(&self) -> Layout {
match self.alloc {
PrivateUntypedSlabAlloc::Aligned(ref alloc) => alloc.layout.clone(),
PrivateUntypedSlabAlloc::Large(ref alloc) => alloc.layout.clone(),
}
}
unsafe fn alloc(&mut self) -> Result<*mut u8, Exhausted> {
match self.alloc {
PrivateUntypedSlabAlloc::Aligned(ref mut alloc) => alloc.alloc(),
PrivateUntypedSlabAlloc::Large(ref mut alloc) => alloc.alloc(),
}
}
unsafe fn dealloc(&mut self, x: *mut u8) {
match self.alloc {
PrivateUntypedSlabAlloc::Aligned(ref mut alloc) => alloc.dealloc(x),
PrivateUntypedSlabAlloc::Large(ref mut alloc) => alloc.dealloc(x),
}
}
}
struct SizedSlabAlloc<I: InitSystem, S: SlabSystem<I>> {
freelist: LinkedList<S::Slab>, total_slabs: usize,
num_full: usize, refcnt: usize,
full_slab_working_set: WorkingSet<usize>,
slab_system: S,
init_system: I,
layout: Layout,
}
impl<I: InitSystem, S: SlabSystem<I>> SizedSlabAlloc<I, S> {
fn new(init: I, layout: Layout, slabs: S) -> SizedSlabAlloc<I, S> {
SizedSlabAlloc {
freelist: LinkedList::new(),
total_slabs: 0,
num_full: 0,
refcnt: 0,
full_slab_working_set: WorkingSet::new(0),
slab_system: slabs,
init_system: init,
layout: layout,
}
}
fn alloc(&mut self) -> Result<*mut u8, Exhausted> {
if self.freelist.size() == 0 {
let ok = self.alloc_slab();
if !ok {
return Err(Exhausted);
}
}
let slab = self.freelist.peek_front();
if self.slab_system.is_full(slab) {
self.num_full -= 1;
self.full_slab_working_set.update_min(self.num_full);
}
let (obj, init_status) = self.slab_system.alloc(slab);
if self.slab_system.is_empty(slab) {
self.freelist.remove_front();
}
self.refcnt += 1;
debug_assert_eq!(obj as usize % self.layout.align(), 0);
self.init_system.init(obj, init_status);
Ok(obj)
}
fn alloc_slab(&mut self) -> bool {
let new = self.slab_system.alloc_slab();
if new.is_null() {
return false;
}
self.freelist.insert_back(new);
self.total_slabs += 1;
self.num_full += 1;
true
}
fn dealloc(&mut self, ptr: *mut u8) {
debug_assert_eq!(ptr as usize % self.layout.align(), 0);
let (slab, was_empty) = self.slab_system.dealloc(ptr, I::status_initialized());
let is_full = self.slab_system.is_full(slab);
match (was_empty, is_full) {
(false, true) => {
self.freelist.move_to_back(slab);
self.num_full += 1;
}
(true, true) => {
self.freelist.insert_back(slab);
self.num_full += 1;
}
(true, false) => self.freelist.insert_front(slab),
(false, false) => {}
}
if is_full {
self.garbage_collect_slabs();
}
self.refcnt -= 1;
}
fn garbage_collect_slabs(&mut self) {
if let Some(min_full) = self.full_slab_working_set
.refresh(WORKING_PERIOD_SECONDS) {
for _ in 0..min_full {
let slab = self.freelist.remove_back();
self.slab_system.dealloc_slab(slab);
self.total_slabs -= 1;
self.num_full -= 1;
}
self.full_slab_working_set.set(self.num_full);
}
}
}
impl<I: InitSystem, S: SlabSystem<I>> Drop for SizedSlabAlloc<I, S> {
fn drop(&mut self) {
if self.refcnt != 0 {
if std::thread::panicking() {
} else {
panic!("non-zero refcount when dropping slab allocator");
}
}
while self.freelist.size() > 0 {
let slab = self.freelist.remove_front();
self.slab_system.dealloc_slab(slab);
}
}
}
trait SlabSystem<I: InitSystem> {
type Slab: Linkable;
fn alloc_slab(&mut self) -> *mut Self::Slab;
fn dealloc_slab(&mut self, slab: *mut Self::Slab);
fn is_full(&self, slab: *mut Self::Slab) -> bool;
fn is_empty(&self, slab: *mut Self::Slab) -> bool;
fn alloc(&self, slab: *mut Self::Slab) -> (*mut u8, I::Status);
fn dealloc(&self, obj: *mut u8, init_status: I::Status) -> (*mut Self::Slab, bool);
}