What I cannot create, I do not understand

Rust 中的 runtime 到底是咋回事, 为了彻底搞懂它, 我在尽量不借助第三方 crate 的情况下实现了一个玩具 runtime, 之所以说是玩具,因为它没有复杂的调度算法(只有一个全局 task queue)

代码除了 mpmc(multi-producer, multi-consumer) 使用第三方 crate crossbeam 之外, 其余代码一律手撸

可以这么玩

fn main() {
let toy = Toy::new(); for i in 1..=20 {
toy.spawn(async move {
let ret = FakeIO::new(Duration::from_secs(i)).await;
println!("{:?}: {:?}", thread::current().id(), ret);
})
} toy.run(4); // 4 threads
}

其中 FakeIO 也是足够单纯

pub struct FakeIO {
finished: Arc<AtomicBool>,
duration: Duration,
} impl Future for FakeIO {
type Output = Duration; fn poll(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
if self.finished.load(Ordering::Acquire) {
return Poll::Ready(self.duration);
} let finished = self.finished.clone();
let waker = cx.waker().clone();
let duration = self.duration; thread::spawn(move || {
thread::sleep(duration); finished.store(true, Ordering::Release); waker.wake();
}); Poll::Pending
}
}

数据结构

数据结构就下面几个(参考了 tokio 的设计)

struct Task {
raw: RawTask,
} unsafe impl Send for Task {}
unsafe impl Sync for Task {} struct RawTask {
ptr: NonNull<Header>, // pointer to Cell<T> where T: Future
} struct Header {
// todo: maybe replace the Mutex<State> with AtomicUsize
state: Mutex<State>,
vtable: &'static Vtable,
sender: crossbeam::channel::Sender<Task>,
} #[derive(Default)]
struct State {
running: bool,
notified: bool,
completed: bool,
} /// #[repr(C)] make sure `*mut Cell<T>` can cast to valid `*mut Header`, and backwards.
/// In the default situation, the data layout may not be the same as the order in which the fields are specified in the declaration of the type
/// 默认情况下 Rust 的数据布局不一定会按照 field 的声明顺序排列
/// [The Default Representation](https://doc.rust-lang.org/reference/type-layout.html?#the-default-representation)
///
/// [playground link](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=39ac84782d121970598b91201b168f82)
///
/// you can easilly view the data layout with this crate https://github.com/hangj/layout-rs
#[repr(C)]
struct Cell<T: Future> {
header: Header,
future: T,
output: Option<T::Output>,
} struct Vtable {
poll_task: unsafe fn(NonNull<Header>),
clone_task: unsafe fn(NonNull<Header>) -> NonNull<Header>,
drop_task: unsafe fn(NonNull<Header>),
}

其中值得注意的是:

  • RawTask 内的 ptr 实际上指向的是 NonNull<Cell<T: Future>>
  • Cell<T: Future> 被标记了 #repr(C), 原因已在注释中说明
  • vtable 的设计参考了 Waker 中的 vtable, 相当于利用泛型函数保存了类型信息, 便于后面从裸指针恢复到原始类型

点击「阅读原文」直达 toy-runtime 仓库

Have fun!

async-await Rust: 200 多行代码实现一个极简 runtime的更多相关文章

  1. Async/Await是这样简化JavaScript代码的

    译者按: 在Async/Await替代Promise的6个理由中,我们比较了两种不同的异步编程方法:Async/Await和Promise,这篇博客将通过示例代码介绍Async/Await是如何简化J ...

  2. [.NET] 怎样使用 async & await 一步步将同步代码转换为异步编程

    怎样使用 async & await 一步步将同步代码转换为异步编程 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6079707.html  ...

  3. 【TypeScript】如何在TypeScript中使用async/await,让你的代码更像C#。

    [TypeScript]如何在TypeScript中使用async/await,让你的代码更像C#. async/await 提到这个东西,大家应该都很熟悉.最出名的可能就是C#中的,但也有其它语言也 ...

  4. [译]async/await中使用阻塞式代码导致死锁 百万数据排序:优化的选择排序(堆排序)

    [译]async/await中使用阻塞式代码导致死锁 这篇博文主要是讲解在async/await中使用阻塞式代码导致死锁的问题,以及如何避免出现这种死锁.内容主要是从作者Stephen Cleary的 ...

  5. [译]async/await中使用阻塞式代码导致死锁

    原文:[译]async/await中使用阻塞式代码导致死锁 这篇博文主要是讲解在async/await中使用阻塞式代码导致死锁的问题,以及如何避免出现这种死锁.内容主要是从作者Stephen Clea ...

  6. 通过 Mesos、Docker 和 Go,使用 300 行代码创建一个分布式系统

    [摘要]虽然 Docker 和 Mesos 已成为不折不扣的 Buzzwords ,但是对于大部分人来说它们仍然是陌生的,下面我们就一起领略 Mesos .Docker 和 Go 配合带来的强大破坏力 ...

  7. 通过Mesos、Docker和Go,使用300行代码创建一个分布式系统

    [摘要]虽然 Docker 和 Mesos 已成为不折不扣的 Buzzwords ,但是对于大部分人来说它们仍然是陌生的,下面我们就一起领略 Mesos .Docker 和 Go 配合带来的强大破坏力 ...

  8. SpringBoot,用200行代码完成一个一二级分布式缓存

    缓存系统的用来代替直接访问数据库,用来提升系统性能,减小数据库复杂.早期缓存跟系统在一个虚拟机里,这样内存访问,速度最快. 后来应用系统水平扩展,缓存作为一个独立系统存在,如redis,但是每次从缓存 ...

  9. 不到50行代码实现一个能对请求并发数做限制的通用RequestDecorator

    使用场景 在开发中,我们可能会遇到一些对异步请求数做并发量限制的场景,比如说微信小程序的request并发最多为5个,又或者我们需要做一些批量处理的工作,可是我们又不想同时对服务器发出太多请求(可能会 ...

  10. 37行代码实现一个简单的打游戏AI

    不废话,直接上码,跟神经网络一点关系都没有,这37行代码只能保证电脑的对敌牺牲率是1:10左右,如果想手动操控,注释掉autopilot后边的代码即可. 哪个大神有兴趣可以用tensorflow或者s ...

随机推荐

  1. .NET CORE 部署到IIS上,HTTP 错误 500.19 - Internal Server Error

    经排查,是因为项目中web.config的rewrite节点不支持,注释掉此节点即可,或者尝试下载相关依赖以支持此节点

  2. python从shp文件中读取经纬度数据

    python从shp文件中读取经纬度数据 没有接触过GIS的人来说shp文件很陌生而且很难打开查看,好在python可以从中提取出自己想要的数据 pyshp库的安装 python的pyshp库可以实现 ...

  3. SpringBoot 配置CORS处理前后端分离跨域配置无效问题解析

    前言 浏览器有跨域限制,非同源策略(协议.主机名或端口不同)被视为跨域请求,解决跨域有跨域资源共享(CORS).反向代理和 JSONP的方式.本篇通过 SpringBoot 的资源共享配置(CORS) ...

  4. ES6之数组的Array.from()方法

    Array.from()方法就是构造函数本身的方法 将一个类数组对象或者可遍历对象转换成一个真正的数组. 那么什么是类数组对象呢?所谓类数组对象,最基本的要求就是具有length属性的对象. 1.将类 ...

  5. 2020-03-02:在无序数组中,如何求第K小的数?

    2020-03-02:在无序数组中,如何求第K小的数? 福哥答案2021-03-02: 1.堆排序.时间复杂度:O(N*lgK).有代码. 2.单边快排.时间复杂度:O(N).有代码. 3.bfprt ...

  6. 2021-05-23:给定一个字符串str,str表示一个公式,公式里可能有整数、加减乘除符号和左右括号。返回公式的计算结果,难点在于括号可能嵌套很多层。str=“48*((70-65)-43)+8*

    2021-05-23:给定一个字符串str,str表示一个公式,公式里可能有整数.加减乘除符号和左右括号.返回公式的计算结果,难点在于括号可能嵌套很多层.str="48*((70-65)-4 ...

  7. openstack部署2

    检查服务,查看dashboard页面有哪些功能 检查服务状态 检查计算节点,控制节点服务是up状态 检查网络节点是True的状态.这里的每个计算节点,都是一个neutron的客户端. 查看dashbo ...

  8. 解决pytest+allure报告中文乱码

    在conftest文件里添加钩子函数: def pytest_collection_modifyitems(items): """ 测试用例收集完成时,将收集到的item ...

  9. 2023-05-20:go语言的slice和rust语言的Vec的扩容流程是什么?

    2023-05-20:go语言的slice和rust语言的Vec的扩容流程是什么? 答案2023-05-20: go语言的slice扩容流程 go版本是1.20.4. 扩容流程见源码见runtime/ ...

  10. 代码随想录算法训练营Day48 动态规划

    代码随想录算法训练营 代码随想录算法训练营Day48 动态规划|198.打家劫舍 213.打家劫舍II 337.打家劫舍III 198.打家劫舍 题目链接:198.打家劫舍 你是一个专业的小偷,计划偷 ...