如何在不使用任何外部依赖项的情况下执行异步/等待功能? [英] How do I execute an async/await function without using any external dependencies?

查看:115
本文介绍了如何在不使用任何外部依赖项的情况下执行异步/等待功能?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am attempting to create simplest possible example that can get async fn hello() to eventually print out Hello World!. This should happen without any external dependency like tokio, just plain Rust and std. Bonus points if we can get it done without ever using 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.
}

  • 我知道async/await仍然是夜间功能,并且在可预见的将来可能会发生变化.
  • 我知道有很多Future实现,我知道tokio的存在.
  • 我只是想教育自己了解标准图书馆期货的内部运作方式.
    • I know async/await is still a nightly feature, and it is subject to change in the foreseeable future.
    • I know there is a whole lot of Future implementations, I am aware of the existence of tokio.
    • I am just trying to educate myself on the inner workings of standard library futures.
    • 我模糊的理解是,首先,我需要完成Pin任务.所以我继续前进,

      My vague understanding is that, first off, I need to Pin task down. So I went ahead and

      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它,因此我确定它不会在内存中移动.出乎意料的是,我遇到了同样的错误.

      so I thought, of course, I probably need to Box it, so I'm sure it won't move around in memory. Somewhat surprisingly, I get the same error.

      到目前为止我能得到的是

      What I could get so far is

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

      这显然不是我应该做的.即使这样,也可以说我得到了Pin ned Future的帮助.现在,我需要以某种方式poll().为此,我需要一个Waker.

      which is obviously not something I should do. Even so, let's say I got my hands on the Pinned Future. Now I need to poll() it somehow. For that, I need a Waker.

      因此,我尝试四处寻找如何使用Waker的方法.在 doc 上,它看起来像是唯一的获取方法一个Waker与另一个接受RawWakernew_unchecked在一起.从那里我这里,然后从那里此处,我只是just缩在地板上,开始哭泣.

      So I tried to look around on how to get my hands on a Waker. On the doc it kinda looks like the only way to get a Waker is with another new_unchecked that accepts a RawWaker. From there I got here and from there here, where I just curled up on the floor and started crying.

      推荐答案

      期货堆栈的这一部分并不是很多人想要实现的.我看到的粗略估计可能会有10个左右的实际实现.

      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) -> 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 ,我们看到我们需要一个 Pin 确定未来和 Context . Context是从 Waker 创建的需要 RawWaker . RawWaker需要 RawWakerVTable .我们以最简单的方式创建所有这些片段:

      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:

      • 由于我们不打算支持NotReady案例,因此我们实际上不需要为该案例做任何事情,而可以惊慌失措.这也意味着wake的实现可以是无操作的.

      • 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.

      由于我们并不想提高效率,所以我们不需要为唤醒者存储任何数据,因此clonedrop基本上也可以是无操作的.

      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.

      确定未来的最简单方法是Box,但这不是最有效的方法.

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

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

      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.

      这篇关于如何在不使用任何外部依赖项的情况下执行异步/等待功能?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆