async-await Rust: 200 多行代码实现一个极简 runtime
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的更多相关文章
- Async/Await是这样简化JavaScript代码的
译者按: 在Async/Await替代Promise的6个理由中,我们比较了两种不同的异步编程方法:Async/Await和Promise,这篇博客将通过示例代码介绍Async/Await是如何简化J ...
- [.NET] 怎样使用 async & await 一步步将同步代码转换为异步编程
怎样使用 async & await 一步步将同步代码转换为异步编程 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6079707.html ...
- 【TypeScript】如何在TypeScript中使用async/await,让你的代码更像C#。
[TypeScript]如何在TypeScript中使用async/await,让你的代码更像C#. async/await 提到这个东西,大家应该都很熟悉.最出名的可能就是C#中的,但也有其它语言也 ...
- [译]async/await中使用阻塞式代码导致死锁 百万数据排序:优化的选择排序(堆排序)
[译]async/await中使用阻塞式代码导致死锁 这篇博文主要是讲解在async/await中使用阻塞式代码导致死锁的问题,以及如何避免出现这种死锁.内容主要是从作者Stephen Cleary的 ...
- [译]async/await中使用阻塞式代码导致死锁
原文:[译]async/await中使用阻塞式代码导致死锁 这篇博文主要是讲解在async/await中使用阻塞式代码导致死锁的问题,以及如何避免出现这种死锁.内容主要是从作者Stephen Clea ...
- 通过 Mesos、Docker 和 Go,使用 300 行代码创建一个分布式系统
[摘要]虽然 Docker 和 Mesos 已成为不折不扣的 Buzzwords ,但是对于大部分人来说它们仍然是陌生的,下面我们就一起领略 Mesos .Docker 和 Go 配合带来的强大破坏力 ...
- 通过Mesos、Docker和Go,使用300行代码创建一个分布式系统
[摘要]虽然 Docker 和 Mesos 已成为不折不扣的 Buzzwords ,但是对于大部分人来说它们仍然是陌生的,下面我们就一起领略 Mesos .Docker 和 Go 配合带来的强大破坏力 ...
- SpringBoot,用200行代码完成一个一二级分布式缓存
缓存系统的用来代替直接访问数据库,用来提升系统性能,减小数据库复杂.早期缓存跟系统在一个虚拟机里,这样内存访问,速度最快. 后来应用系统水平扩展,缓存作为一个独立系统存在,如redis,但是每次从缓存 ...
- 不到50行代码实现一个能对请求并发数做限制的通用RequestDecorator
使用场景 在开发中,我们可能会遇到一些对异步请求数做并发量限制的场景,比如说微信小程序的request并发最多为5个,又或者我们需要做一些批量处理的工作,可是我们又不想同时对服务器发出太多请求(可能会 ...
- 37行代码实现一个简单的打游戏AI
不废话,直接上码,跟神经网络一点关系都没有,这37行代码只能保证电脑的对敌牺牲率是1:10左右,如果想手动操控,注释掉autopilot后边的代码即可. 哪个大神有兴趣可以用tensorflow或者s ...
随机推荐
- .NET CORE 部署到IIS上,HTTP 错误 500.19 - Internal Server Error
经排查,是因为项目中web.config的rewrite节点不支持,注释掉此节点即可,或者尝试下载相关依赖以支持此节点
- python从shp文件中读取经纬度数据
python从shp文件中读取经纬度数据 没有接触过GIS的人来说shp文件很陌生而且很难打开查看,好在python可以从中提取出自己想要的数据 pyshp库的安装 python的pyshp库可以实现 ...
- SpringBoot 配置CORS处理前后端分离跨域配置无效问题解析
前言 浏览器有跨域限制,非同源策略(协议.主机名或端口不同)被视为跨域请求,解决跨域有跨域资源共享(CORS).反向代理和 JSONP的方式.本篇通过 SpringBoot 的资源共享配置(CORS) ...
- ES6之数组的Array.from()方法
Array.from()方法就是构造函数本身的方法 将一个类数组对象或者可遍历对象转换成一个真正的数组. 那么什么是类数组对象呢?所谓类数组对象,最基本的要求就是具有length属性的对象. 1.将类 ...
- 2020-03-02:在无序数组中,如何求第K小的数?
2020-03-02:在无序数组中,如何求第K小的数? 福哥答案2021-03-02: 1.堆排序.时间复杂度:O(N*lgK).有代码. 2.单边快排.时间复杂度:O(N).有代码. 3.bfprt ...
- 2021-05-23:给定一个字符串str,str表示一个公式,公式里可能有整数、加减乘除符号和左右括号。返回公式的计算结果,难点在于括号可能嵌套很多层。str=“48*((70-65)-43)+8*
2021-05-23:给定一个字符串str,str表示一个公式,公式里可能有整数.加减乘除符号和左右括号.返回公式的计算结果,难点在于括号可能嵌套很多层.str="48*((70-65)-4 ...
- openstack部署2
检查服务,查看dashboard页面有哪些功能 检查服务状态 检查计算节点,控制节点服务是up状态 检查网络节点是True的状态.这里的每个计算节点,都是一个neutron的客户端. 查看dashbo ...
- 解决pytest+allure报告中文乱码
在conftest文件里添加钩子函数: def pytest_collection_modifyitems(items): """ 测试用例收集完成时,将收集到的item ...
- 2023-05-20:go语言的slice和rust语言的Vec的扩容流程是什么?
2023-05-20:go语言的slice和rust语言的Vec的扩容流程是什么? 答案2023-05-20: go语言的slice扩容流程 go版本是1.20.4. 扩容流程见源码见runtime/ ...
- 代码随想录算法训练营Day48 动态规划
代码随想录算法训练营 代码随想录算法训练营Day48 动态规划|198.打家劫舍 213.打家劫舍II 337.打家劫舍III 198.打家劫舍 题目链接:198.打家劫舍 你是一个专业的小偷,计划偷 ...