How do I execute an async/await function without using any external dependencies?

后端 未结 1 1013
执念已碎
执念已碎 2021-01-20 05:24

I am attempting to create simplest possible example that can get async fn hello() to eventually print out Hello World!. This should happen without

1条回答
  •  萌比男神i
    2021-01-20 05:55

    This part of the futures stack is not intended to be implemented by many people. The rough estimate that I have seen in that maybe there will be 10 or so actual implementations.

    That said, you can fill in the basic aspects of an executor that is extremely limited by following the function signatures needed:

    async fn hello() {
        println!("Hello, World!");
    }
    
    fn main() {
        drive_to_completion(hello());
    }
    
    use std::{
        future::Future,
        ptr,
        task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
    };
    
    fn drive_to_completion(f: F) -> F::Output
    where
        F: Future,
    {
        let waker = my_waker();
        let mut context = Context::from_waker(&waker);
    
        let mut t = Box::pin(f);
        let t = t.as_mut();
    
        loop {
            match t.poll(&mut context) {
                Poll::Ready(v) => return v,
                Poll::Pending => panic!("This executor does not support futures that are not ready"),
            }
        }
    }
    
    type WakerData = *const ();
    
    unsafe fn clone(_: WakerData) -> RawWaker {
        my_raw_waker()
    }
    unsafe fn wake(_: WakerData) {}
    unsafe fn wake_by_ref(_: WakerData) {}
    unsafe fn drop(_: WakerData) {}
    
    static MY_VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop);
    
    fn my_raw_waker() -> RawWaker {
        RawWaker::new(ptr::null(), &MY_VTABLE)
    }
    
    fn my_waker() -> Waker {
        unsafe { Waker::from_raw(my_raw_waker()) }
    }
    

    Starting at Future::poll, we see we need a Pinned future and a Context. Context is created from a Waker which needs a RawWaker. A RawWaker needs a RawWakerVTable. We create all of those pieces in the simplest possible ways:

    • Since we aren't trying to support NotReady cases, we never need to actually do anything for that case and can instead panic. This also means that the implementations of wake can be no-ops.

    • Since we aren't trying to be efficient, we don't need to store any data for our waker, so clone and drop can basically be no-ops as well.

    • The easiest way to pin the future is to Box it, but this isn't the most efficient possibility.


    If you wanted to support NotReady, the simplest extension is to have a busy loop, polling forever. A slightly more efficient solution is to have a global variable that indicates that someone has called wake and block on that becoming true.

    0 讨论(0)
提交回复
热议问题