首页 > 解决方案 > 如何在不使用任何外部依赖项的情况下执行 async/await 函数?

问题描述

我正在尝试创建可以async fn hello()最终打印出来的最简单的示例Hello World!。这应该在没有任何外部依赖的情况下发生tokio,例如简单的 Rust 和std. 如果我们可以在不使用unsafe.

#![feature(async_await)]

async fn hello() {
    println!("Hello, World!");
}

fn main() {
    let task = hello();

    // Something beautiful happens here, and `Hello, World!` is printed on screen.
}

我无助而笨拙的努力

我的模糊理解是,首先,我需要完成Pin任务。所以我继续前进

let pinned_task = Pin::new(&mut task);

the trait `std::marker::Unpin` is not implemented for `std::future::GenFuture<[static generator@src/main.rs:7:18: 9:2 {}]>`

所以我想,当然,我可能需要Box它,所以我确信它不会在内存中移动。有点令人惊讶的是,我得到了同样的错误。

到目前为止我能得到的是

let pinned_task = unsafe {
    Pin::new_unchecked(&mut task)
};

这显然不是我应该做的。即便如此,假设我得到了Pinned Future。现在我需要以poll()某种方式。为此,我需要一个Waker.

因此,我试图环顾四周,了解如何获得Waker. 在文档上,似乎获得 a 的唯一方法Waker是使用另一个new_unchecked接受 a 的方法RawWaker。从那里我到这里又从那里我只是蜷缩在地板上开始哭泣。

标签: asynchronousrustfuture

解决方案


期货堆栈的这一部分并不打算由许多人来实现。我所看到的粗略估计可能会有 10 个左右的实际实现。

也就是说,您可以通过遵循所需的函数签名来填写执行程序的基本方面:

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) -> 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()) }
}

从 开始Future::poll,我们看到我们需要一个Pinned future 和一个Context. Context是从Waker需要 a 的 a创建的RawWaker。ARawWaker需要一个RawWakerVTable. 我们以最简单的方式创建所有这些部分:

  • 由于我们不尝试支持NotReady案例,因此我们不需要为该案例实际做任何事情,而是可以恐慌。这也意味着 的实现wake可以是无操作的。

  • 由于我们没有试图提高效率,我们不需要为我们的唤醒器存储任何数据,因此基本上clonedrop可以是无操作的。

  • 确定未来的最简单方法就是确定未来Box,但这不是最有效的可能性。


如果你想支持NotReady,最简单的扩展是有一个繁忙的循环,永远轮询。一个稍微更有效的解决方案是有一个全局变量来指示有人调用wake并阻止它成为真的。


推荐阅读