Created
June 3, 2019 22:39
-
-
Save kabergstrom/a1f1e4b23b598364e22f5af6cea2d0ac to your computer and use it in GitHub Desktop.
single threaded executor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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