Rust asynchronous programming: official standard library

The standard library code is located at https://github.com/rust-lang/rust/tree/master/library
here using the latest version 1.45.2

Involved packages

std::future::*

Corresponding src/libstd/future.rs, mainly provide the following api

pub use core::future::Future;
pub use core::future::{
    
    from_generator, get_context, ResumeTy};
pub use core::future::{
    
    pending, ready, Pending, Ready};
pub use core::future::IntoFuture;

core::futureCorresponding src/libcore/futuredirectory, there are the following files

future.rs      
into_future.rs 
mod.rs         
pending.rs     
poll_fn.rs     
ready.rs

std::poll::* withstd::wake::*

Corresponding to src/libcore/task/mod.rs, mainly provide the following api

mod poll;
pub use self::poll::Poll;

mod wake;
pub use self::wake::{
    
    Context, RawWaker, RawWakerVTable, Waker};

src/libcore/task directory, there are the following files

mod.rs
poll.rs
wake.rs

Future

src/libcore/future/future.rs

#[lang = "future_trait"]
pub trait Future {
    
    
    type Output;
    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
}

The Future trait is the most basic trait of asynchronous programming. It has a poll method. The second parameter of poll is Context. Context contains a wake-up function. The poll method returns Poll::Ready or Poll::Pending.

Context

src/libcore/task/wake.rs

The context of asynchronous tasks is used by Future's poll method. Currently, only Waker is packaged internally. It is not ruled out that other things will be added in the future.

pub struct Context<'a> {
    
    
    waker: &'a Waker,
    _marker: PhantomData<fn(&'a ()) -> &'a ()>,
}

impl<'a> Context<'a> {
    
    
    #[inline]
    pub fn from_waker(waker: &'a Waker) -> Self {
    
    
        Context {
    
     waker, _marker: PhantomData }
    }
    #[inline]
    pub fn waker(&self) -> &'a Waker {
    
    
        &self.waker
    }
}

Waker

src/libcore/task/wake.rs

The wake-up function is used to notify the executor that the task can run. It implements Send, Sync, and Clone, and encapsulates the RawWaker instance. RawWaker defines the specific wake-up behavior of the executor (generally use condition variables and mutex locks to implement wait and notify)

pub struct Waker {
    
    
    waker: RawWaker,
}

impl Unpin for Waker {
    
    }
unsafe impl Send for Waker {
    
    }
unsafe impl Sync for Waker {
    
    }
impl Clone for Waker {
    
    
    fn clone(&self) -> Self {
    
    
        Waker {
    
    
            waker: unsafe {
    
     (self.waker.vtable.clone)(self.waker.data) },
        }
    }
}

RawWaker

src/libcore/task/wake.rs

#[derive(PartialEq, Debug)]
pub struct RawWaker {
    
    
    data: *const (),
    vtable: &'static RawWakerVTable,
}

RawWakerVTable

src/libcore/task/wake.rs

A virtual function pointer table (vtable), used in RawWaker

#[derive(PartialEq, Copy, Clone, Debug)]
pub struct RawWakerVTable {
    
    

    clone: unsafe fn(*const ()) -> RawWaker,

    wake: unsafe fn(*const ()),

    wake_by_ref: unsafe fn(*const ()),

    drop: unsafe fn(*const ()),
}

Poll

src/libcore/task/poll.rs

Future's poll method return value

#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Poll<T> {
    
    
    Ready(T),
    Pending,
}

Ready

src/libcore/future/ready.rs

Create a Future with values ​​ready immediately

#[derive(Debug, Clone)]
pub struct Ready<T>(Option<T>);

impl<T> Unpin for Ready<T> {
    
    }

impl<T> Future for Ready<T> {
    
    
    type Output = T;

    #[inline]
    fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<T> {
    
    
        Poll::Ready(self.0.take().expect("Ready polled after completion"))
    }
}
pub fn ready<T>(t: T) -> Ready<T> {
    
    
    Ready(Some(t))
}

eg

 #![feature(future_readiness_fns)]
 use core::future;
 async fn run() {
    
    
   let a = future::ready(1);
   assert_eq!(a.await, 1);
}

Pending

src/libcore/future/pending.rs

Create a Future that can never be completed (calculated)

#[derive(Debug)]
pub struct Pending<T> {
    
    
    _data: marker::PhantomData<T>,
}

pub fn pending<T>() -> Pending<T> {
    
    
    Pending {
    
     _data: marker::PhantomData }
}
impl<T> Future for Pending<T> {
    
    
    type Output = T;

    fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<T> {
    
    
        Poll::Pending
    }
}

eg

#![feature(future_readiness_fns)]
use core::future;
async fn run() {
    
    
	let future = future::pending();
	let () = future.await;
    unreachable!();
}

IntoFuture

src/libcore/future/into_future.rs

Convert a type to Future

pub trait IntoFuture {
    
    
    type Output;
    type Future: Future<Output = Self::Output>;
    fn into_future(self) -> Self::Future;
}

impl<F: Future> IntoFuture for F {
    
    
    type Output = F::Output;
    type Future = F;
    fn into_future(self) -> Self::Future {
    
    
        self
    }
}

from_generator

src/libcore/future/mod.rs

  • The ansyc code block is finally converted into a generator by the compiler. The generator will be wrapped in GenFuture. GenFuture implements Future. The resume method of the generator is called in poll. If it is in the state, it returns Poll::Pending, if it is Complete. (x), then return Poll::Ready(x)
  • The meaning of ResumeTy: The generator cannot be implemented for<'a, 'b> Generator<&'a mut Context<'b>>, so a raw pointer is needed, and the raw pointer or NonNull cannot be combined SendwithSync
#[derive(Debug, Copy, Clone)]
pub struct ResumeTy(NonNull<Context<'static>>);

unsafe impl Send for ResumeTy {
    
    }

unsafe impl Sync for ResumeTy {
    
    }

pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return>
where
    T: Generator<ResumeTy, Yield = ()>,
{
    
    
    struct GenFuture<T: Generator<ResumeTy, Yield = ()>>(T);
    impl<T: Generator<ResumeTy, Yield = ()>> !Unpin for GenFuture<T> {
    
    }

    impl<T: Generator<ResumeTy, Yield = ()>> Future for GenFuture<T> {
    
    
        type Output = T::Return;
        fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
    
    
            // Safety:!Unpin + !Drop
            let gen = unsafe {
    
     Pin::map_unchecked_mut(self, |s| &mut s.0) };
            match gen.resume(ResumeTy(NonNull::from(cx).cast::<Context<'static>>())) {
    
    
                GeneratorState::Yielded(()) => Poll::Pending,
                GeneratorState::Complete(x) => Poll::Ready(x),
            }
        }
    }
    GenFuture(gen)
}


pub unsafe fn get_context<'a, 'b>(cx: ResumeTy) -> &'a mut Context<'b> {
    
    
    &mut *cx.0.as_ptr().cast()
}

Guess you like

Origin blog.csdn.net/kk3909/article/details/108155264