use fnv;
use libc;
pub mod ffi;
use self::ffi::*;
use libc::c_char;
use std::alloc::System as Allocator;
use fnv::FnvHashMap;
use std::ffi::CStr;
use std::fmt;
use std::ptr;
use std::sync::Once;
use std::alloc::{GlobalAlloc, Layout};
pub type Ptr = *mut u8;
static ONCE: Once = Once::new();
const DEFAULT_ALIGN: usize = 8;
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd)]
#[repr(u64)]
pub enum WeldRuntimeErrno {
Success = 0,
ConfigurationError,
LoadLibraryError,
CompileError,
ArrayOutOfBounds,
BadIteratorLength,
MismatchedZipSize,
OutOfMemory,
RunNotFound,
Unknown,
DeserializationError,
KeyNotFoundError,
AssertionError,
ErrnoMax,
}
impl fmt::Display for WeldRuntimeErrno {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
#[derive(Debug, PartialEq)]
pub struct WeldRuntimeContext {
allocations: FnvHashMap<Ptr, Layout>,
errno: WeldRuntimeErrno,
result: Ptr,
nworkers: i32,
memlimit: usize,
allocated: usize,
}
impl WeldRuntimeContext {
unsafe fn malloc(&mut self, size: i64) -> Ptr {
if size == 0 {
trace!("Alloc'd 0-size pointer (null)");
return ptr::null_mut();
}
let size = size as usize;
if self.allocated + size > self.memlimit {
self.errno = WeldRuntimeErrno::OutOfMemory;
panic!(
"Weld run ran out of memory (limit={}, attempted to allocate {}",
self.memlimit,
self.allocated + size
);
}
let layout = Layout::from_size_align_unchecked(size as usize, DEFAULT_ALIGN);
let mem = Allocator.alloc(layout);
self.allocated += layout.size();
trace!("Alloc'd pointer {:?} ({} bytes)", mem, layout.size());
self.allocations.insert(mem, layout);
mem
}
unsafe fn realloc(&mut self, pointer: Ptr, size: i64) -> Ptr {
if pointer.is_null() {
return self.malloc(size);
}
let size = size as usize;
let old_layout = self.allocations.remove(&pointer).unwrap();
if self.allocated - old_layout.size() + size > self.memlimit {
self.errno = WeldRuntimeErrno::OutOfMemory;
panic!(
"Weld run ran out of memory (limit={}, attempted to allocate {}",
self.memlimit,
self.allocated - old_layout.size() + size
);
}
let mem = Allocator.realloc(pointer, old_layout, size);
let new_layout = Layout::from_size_align_unchecked(size, DEFAULT_ALIGN);
self.allocated -= old_layout.size();
self.allocated += new_layout.size();
self.allocations.insert(mem, new_layout);
mem
}
fn set_errno(&mut self, errno: WeldRuntimeErrno) {
self.errno = errno;
panic!("Weld runtime threw error: {}", self.errno)
}
fn set_result(&mut self, result: Ptr) {
self.result = result;
}
fn result(&self) -> Ptr {
self.result
}
}
impl WeldRuntimeContext {
pub fn new(nworkers: i32, memlimit: i64) -> WeldRuntimeContext {
WeldRuntimeContext {
allocations: FnvHashMap::default(),
errno: WeldRuntimeErrno::Success,
result: ptr::null_mut(),
nworkers,
memlimit: memlimit as usize,
allocated: 0,
}
}
pub unsafe fn free(&mut self, pointer: Ptr) {
if pointer.is_null() {
trace!("Freed null pointer (no-op) in runst_free()");
return;
}
let layout = self.allocations.remove(&pointer).unwrap();
trace!(
"Freeing pointer {:?} ({} bytes) in runst_free()",
pointer,
layout.size()
);
Allocator.dealloc(pointer, layout);
self.allocated -= layout.size();
}
pub fn memory_usage(&self) -> i64 {
self.allocated as i64
}
pub fn run_id(&self) -> i64 {
0
}
fn errno(&self) -> WeldRuntimeErrno {
self.errno
}
pub fn threads(&self) -> i32 {
self.nworkers
}
pub fn memory_limit(&self) -> i64 {
self.memlimit as i64
}
}
impl Drop for WeldRuntimeContext {
fn drop(&mut self) {
trace!("Allocations: {}", self.allocations.len());
unsafe {
for (pointer, layout) in self.allocations.iter() {
trace!(
"Freeing pointer {:?} ({} bytes) in drop()",
*pointer,
layout.size()
);
Allocator.dealloc(*pointer, layout.clone());
}
}
}
}
unsafe fn initialize() {
ONCE.call_once(|| {
let mut x = weld_runst_init as usize;
x += weld_runst_set_result as usize;
x += weld_runst_get_result as usize;
x += weld_runst_malloc as usize;
x += weld_runst_realloc as usize;
x += weld_runst_free as usize;
x += weld_runst_get_errno as usize;
x += weld_runst_set_errno as usize;
x += weld_runst_assert as usize;
x += weld_runst_print as usize;
trace!("Runtime initialized with hashed values {}", x);
});
}