use std::mem;
use std::ptr;
use std::sync::atomic::{AtomicUsize, Ordering};
use super::worker::Worker;
const JOB_SIZE: usize = 256;
#[cfg(target_pointer_width = "32")]
const VTABLE_WIDTH: usize = 4;
#[cfg(target_pointer_width = "64")]
const VTABLE_WIDTH: usize = 8;
const PADDING_WIDTH: usize = 8 - VTABLE_WIDTH;
const ARR_SIZE: usize = ((JOB_SIZE - VTABLE_WIDTH) / 8) - 1;
pub trait Thunk {
unsafe fn call(&mut self, args: &Worker);
}
pub struct ThunkImpl<F> {
func: Option<F>,
counter: *const AtomicUsize,
}
impl<F> Thunk for ThunkImpl<F>
where F: Send + FnOnce(&Worker)
{
#[inline]
unsafe fn call(&mut self, args: &Worker) {
let f = self.func.take().expect("Job function called twice");
f(args);
if !self.counter.is_null() {
(*self.counter).fetch_sub(1, Ordering::Release);
}
}
}
#[repr(C)]
struct TraitObject {
data: *mut (),
vtable: *mut (),
}
pub enum Job {
Inline(InlineJob),
Heap(Box<Thunk>),
}
pub struct InlineJob {
raw_data: [u64; ARR_SIZE],
vtable: *mut (),
_padding: [u8; PADDING_WIDTH],
}
unsafe impl Send for Job {}
impl Job {
pub fn new<'a, F>(counter: *const AtomicUsize, f: F) -> Job
where F: 'a + Send + FnOnce(&Worker)
{
let code = ThunkImpl {
func: Some(f),
counter: counter,
};
if mem::align_of::<ThunkImpl<F>>() > mem::align_of::<[u64; ARR_SIZE]>()
|| mem::size_of::<ThunkImpl<F>>() > ARR_SIZE * 8 {
let static_code: Box<Thunk> = unsafe { mem::transmute(Box::new(code) as Box<Thunk + 'a>) };
return Job::Heap(static_code);
}
let mut raw_data = [0u64; ARR_SIZE];
unsafe {
let code_ptr: *mut ThunkImpl<F> = raw_data.as_mut_ptr() as *mut _;
ptr::write(code_ptr, code);
let fat_ptr: *mut Thunk = code_ptr;
let t_obj: TraitObject = mem::transmute(fat_ptr);
Job::Inline(InlineJob {
raw_data: raw_data,
vtable: t_obj.vtable,
_padding: [0; PADDING_WIDTH],
})
}
}
pub unsafe fn call(&mut self, args: &Worker) {
match self {
&mut Job::Inline(ref mut job) => {
let t_obj = TraitObject {
data: &mut job.raw_data[0] as *mut _ as *mut (),
vtable: job.vtable,
};
let code_ptr: *mut Thunk = mem::transmute(t_obj);
(*code_ptr).call(args);
}
&mut Job::Heap(ref mut thunk) => {
thunk.call(args);
}
}
}
}