rust, future & generator

慕嘉茂
2023-12-01

探讨future的不同写法。以及其中的差异。

future trait

自定义实现一个 trait 特征。需要实现 poll 方法。该方法返回一个枚举值: Poll<T>, Ready 代表完成包裹了真正的返回数据;Pending 意味着任务还未完成。

pub trait Future {
    /// The type of value produced on completion.
    #[stable(feature = "futures_api", since = "1.36.0")]
    type Output;
	fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
}

pub enum Poll<T> {
    /// Represents that a value is immediately ready.
    #[stable(feature = "futures_api", since = "1.36.0")]
    Ready(#[stable(feature = "futures_api", since = "1.36.0")] T),

    /// Represents that a value is not ready yet.
    ///
    /// When a function returns `Pending`, the function *must* also
    /// ensure that the current task is scheduled to be awoken when
    /// progress can be made.
    #[stable(feature = "futures_api", since = "1.36.0")]
    Pending,
}

Executor 会来调用 Futurepoll 方法根据返回值的不同,决定下一步的动作。

fn() -> impl Future

显式返回一个future trait。

fn my_fut() -> impl Future<Output = ()> {
    future::ready(())
}

async fn() 以及闭包

代码示例如下:

async fn ss() -> u32 {
	0
}

async 关键字,将函数的原型修改为返回一个future trait。然后将执行的结果包装在一个新的future中返回。大致相当于:

fn ss() -> impl Future<Output = u32> {
    async { 0 }
}

从这里可以看到,async fn 永远是返回 Ready(T),而没有办法返回 Pending的。

async {}

async 代码块实现了一个匿名的 Future Trait,名为GenFuture,实现了 Future,并包裹一个 generator。如下所示:

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);

    // We rely on the fact that async/await futures are immovable in order to create
    // self-referential borrows in the underlying generator.
    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: Safe because we're !Unpin + !Drop, and this is just a field projection.
            let gen = unsafe { Pin::map_unchecked_mut(self, |s| &mut s.0) };

            // Resume the generator, turning the `&mut Context` into a `NonNull` raw pointer. The
            // `.await` lowering will safely cast that back to a `&mut Context`.
            match gen.resume(ResumeTy(NonNull::from(cx).cast::<Context<'static>>())) {
                GeneratorState::Yielded(()) => Poll::Pending,
                GeneratorState::Complete(x) => Poll::Ready(x),
            }
        }
    }

    GenFuture(gen)
}

GenFuture 是一个实现了 Future 的 generator。因此在 Executor 执行这样的 Future 会进入相应的 poll 方法,接下来 gen.resume 将会驱动 generator执行 async {} 的内容。如果 async {} 暂时无法完成,则返回 Yielded,进而 gen.resume 返回 Poll::Pending,暂停执行。一旦恢复执行,generator 返回 Complete,gen.resume 返回Poll::Ready(x),Future 任务完成。

此外,如果 async {} 执行中嵌套进入了下一级的 async{},则递进执行下一级 generator。

await

每一个await本身就像一个小型的执行器,在循环中查询任务的状态。
await 执行 generator 本身 async {} 代码。大致的逻辑就是循环调用Future的 poll 方法。如果返回 Pending,则 yield,否则退出循环,结束 Future。

示意代码如下:

loop {
	match polling_future_block() {
		Pending => yield,
		Ready(x) => break
	}
}
 类似资料: