1 个不稳定版本
0.1.0 | 2022年7月25日 |
---|
#5 在 #schedules
20KB
412 行
schedwalk
在所有可能的轮询调度下测试 futures。
并发系统很复杂。很容易错误地假设进度以某种特定的顺序发生(即竞态条件)。对于 Rust 中的异步系统,这可能是对 futures 轮询顺序的假设——对轮询调度的假设。
大多数异步测试运行时只会为您的测试执行一个调度,永远不会覆盖所有可能的调度。 schedwalk
是一个异步测试框架,允许您可靠地测试所有可能的调度。
示例
假设我们正在开发一个 Web 应用程序,并希望计算平均响应时间。我们可能会用以下两个任务来模拟
# use std::convert::identity as spawn;
# futures::executor::block_on(async {
use futures::{channel::mpsc, join};
let (sender, mut receiver) = mpsc::unbounded::<u32>();
let send_task = spawn(async move {
sender.unbounded_send(23).unwrap();
sender.unbounded_send(20).unwrap();
sender.unbounded_send(54).unwrap();
});
let avg_task = spawn(async move {
let mut sum = 0;
let mut count = 0;
while let Some(num) = receiver.try_next().unwrap() {
sum += num;
count += 1;
}
println!("average is {}", sum / count)
});
join!(send_task, avg_task);
# })
但是这个有竞态条件错误。如果 avg_task
在 send_task
之前执行怎么办?那么 count
将为 0,然后我们将除以 0!我们隐含地假设一个任务在另一个任务之前执行。
那么我们如何创建一个触发上述竞态条件的测试呢?我们可以尝试在一个异步运行时(如 Tokio)下执行,但问题是它并不能保证失败的调度会被执行。事实上,在编写本文时,似乎单线程执行器 永远不会 触发失败。使用多线程执行器 可能会 触发失败,但没有保证。最好的情况是,我们创建了一个不可靠的测试。
理想情况下,我们希望在出现此类错误时,代码每次都确定性地失败。
现在介绍 schedwalk
:一个用于在所有可能的调度下测试 futures 的库。使用 schedwalk
我们可以创建这样的测试
use schedwalk::{for_all_schedules, spawn};
use futures::{channel::mpsc, join};
for_all_schedules(|| async {
let (sender, mut receiver) = mpsc::unbounded::<u32>();
let send_task = spawn(async move {
sender.unbounded_send(23).unwrap();
sender.unbounded_send(20).unwrap();
sender.unbounded_send(54).unwrap();
});
let avg_task = spawn(async move {
let mut sum = 0;
let mut count = 0;
while let Some(num) = receiver.try_next().unwrap() {
sum += num;
count += 1;
}
println!("average is {}", sum / count)
});
join!(send_task, avg_task);
})
schedwalk
将在所有可能的调度下执行 future。在这种情况下,有两个:一个是 send_task
先执行,另一个是 avg_task
先执行。这将可靠地在我们的测试中触发错误。
为了便于调试,panic 和死锁将把轮询调度作为字符串打印到标准错误。将环境变量 SCHEDULE
设置为这将只执行确切的失败的调度。上面的例子将打印 panic in SCHEDULE=01
。再次使用 SCHEDULE=01 cargo test example
执行测试时,将只执行那个确切的调度。
注意事项
关于 schedwalk
有一些建议需要注意。
schedwalk
假设确定性。每次必须以相同的顺序创建和轮询未来。也就是说,不能有影响轮询未来的顺序的线程局部或全局状态,也不能有外部IO影响系统。schedwalk
将遍历所有可能的调度。在高并发轮询大量未来的情况下,这可能会迅速变得难以处理。
依赖项
~97KB