Skip to content

Instantly share code, notes, and snippets.

@kabergstrom
Created June 3, 2019 22:39
Show Gist options
  • Save kabergstrom/a1f1e4b23b598364e22f5af6cea2d0ac to your computer and use it in GitHub Desktop.
Save kabergstrom/a1f1e4b23b598364e22f5af6cea2d0ac to your computer and use it in GitHub Desktop.
single threaded executor
use std::future::*;
use std::pin::Pin;
use std::ptr::NonNull;
use std::rc::Rc;
use std::task::*;
struct FatPtrData<T: ?Sized> {
vtable_ptr: usize,
is_waking: bool,
value: T,
}
/// Decompose a fat pointer into its constituent [pointer, extdata] pair
unsafe fn decomp_fat<T: ?Sized>(ptr: *const T) -> [usize; 2] {
let ptr_ref: *const *const T = &ptr;
let decomp_ref = ptr_ref as *const [usize; 2];
*decomp_ref
}
/// Recompose a fat pointer from its constituent [pointer, extdata] pair
unsafe fn recomp_fat<T: ?Sized>(components: [usize; 2]) -> *const T {
let component_ref: *const [usize; 2] = &components;
let ptr_ref = component_ref as *const *const T;
*ptr_ref
}
/// Recompose a mutable fat pointer from its constituent [pointer, extdata] pair
unsafe fn recomp_fat_mut<T: ?Sized>(components: [usize; 2]) -> *mut T {
let component_ref: *const [usize; 2] = &components;
let ptr_ref = component_ref as *const *mut T;
*ptr_ref
}
struct Exec {
poll_queue: Vec<NonNull<()>>,
requeue_buf: Vec<NonNull<()>>,
}
impl Exec {
const fn new() -> Self {
Exec {
poll_queue: Vec::new(),
requeue_buf: Vec::new(),
}
}
}
#[thread_local]
static mut EXECUTOR: Exec = Exec::new();
const NOOP_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop);
unsafe fn clone(f: *const ()) -> RawWaker {
let rc = ptr_to_rc(NonNull::new_unchecked(f as *mut ()));
let clone = rc.clone();
std::mem::forget(rc);
RawWaker::new(rc_to_ptr(clone).as_ptr(), &NOOP_WAKER_VTABLE)
}
unsafe fn wake(f: *const ()) {
let ptr = NonNull::new_unchecked(f as *mut ());
let rc = ptr_to_rc(ptr);
if !rc.is_waking {
*(&rc.is_waking as *const bool as *mut bool) = true;
EXECUTOR.poll_queue.push(ptr);
std::mem::forget(rc);
}
}
unsafe fn wake_by_ref(f: *const ()) {
let ptr = NonNull::new_unchecked(f as *mut ());
let rc = ptr_to_rc(ptr);
if !rc.is_waking {
*(&rc.is_waking as *const bool as *mut bool) = true;
EXECUTOR.poll_queue.push(rc_to_ptr(rc.clone()));
}
std::mem::forget(rc);
}
unsafe fn drop(f: *const ()) {
let rc = ptr_to_rc(NonNull::new_unchecked(f as *mut ()));
}
unsafe fn rc_to_ptr(rc: Rc<FatPtrData<dyn Future<Output = ()>>>) -> NonNull<()> {
NonNull::new_unchecked(decomp_fat(Rc::into_raw(rc))[0] as *mut ())
}
unsafe fn ptr_to_rc(ptr: NonNull<()>) -> Rc<FatPtrData<dyn Future<Output = ()>>> {
let fat_ptr = recomp_fat_mut([ptr.as_ptr() as usize, *ptr.cast::<usize>().as_ptr()]);
Rc::from_raw(fat_ptr)
}
pub fn poll_executors() -> bool {
unsafe {
EXECUTOR.requeue_buf.clear();
for f in EXECUTOR.poll_queue.drain(0..EXECUTOR.poll_queue.len()) {
let b = ptr_to_rc(f);
*(&b.is_waking as *const bool as *mut bool) = false;
let mut value_ptr = NonNull::new_unchecked(
&b.value as *const dyn Future<Output = ()> as *mut dyn Future<Output = ()>,
);
let waker = Waker::from_raw(RawWaker::new(f.as_ptr(), &NOOP_WAKER_VTABLE));
let mut ctx = Context::from_waker(&waker);
match Pin::new_unchecked(value_ptr.as_mut()).poll(&mut ctx) {
Poll::Pending => {
if !b.is_waking {
std::mem::forget(b);
EXECUTOR.requeue_buf.push(f);
}
}
Poll::Ready(_) => {
// future is dropped if it's the last ref, otherwise handled by Waker clone
}
}
std::mem::forget(waker);
}
EXECUTOR.poll_queue.extend(EXECUTOR.requeue_buf.iter());
!EXECUTOR.poll_queue.is_empty()
}
}
pub fn spawn_future<T: Future<Output = ()> + 'static>(f: T) {
unsafe {
let data = FatPtrData {
vtable_ptr: 0,
is_waking: true,
value: f,
};
let b = Rc::new(data);
let ptr = Rc::into_raw(b) as *const FatPtrData<dyn Future<Output = ()>>
as *mut FatPtrData<dyn Future<Output = ()>>;
let decomposed = decomp_fat(ptr);
(*ptr).vtable_ptr = decomposed[1];
EXECUTOR
.poll_queue
.push(NonNull::new_unchecked(decomposed[0] as *mut ()));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment