假设 tokio 存在下面这种 scope
// mod task
async fn scope<'env, R>(f: Future<'env, Output=R>) -> R;
意味着可以写出
async fn parent_fut() {
// lifetime: 'parent_fut
let x = 1;
let x_ref = &x;
task::scope(async {
// 这个 Future 绑定了 'parent_fut 的 scope,
// 所以它的运行一定要保证 'parent_fut 还活着
println!(x_ref);
}).await;
// 这么写确实保证了,因为这种写法 future 不会有 cancel
}
// 但假设
async fn parent_fut() {
// lifetime: 'parent_fut
let x = 1;
let x_ref = &x;
let sleep = tokio::sleep(1);
scoped_fut = task::scope(async {
// 这个 Future 绑定了 'parent_fut 的 scope,
// 所以它的运行一定要保证 'parent_fut 还活着
// 而这里 spawn 另一个 fut,同样绑定了 scope
// P.S. 这里如果保留 spawn 原有的 'static 绑定就失去了各种意义,
// 意味着在 scoped future 里只能干单线程持有 scope 的事情,
// e.g. some(x_ref).await but not concurrently
// 所以那个 proposal 里提出在这里要自动绑定 parent scope.
let outlive_fut = tokio::spawn(async {
println!(x_ref);
})
outlive_fut.await.unwrap();
}).await;
select! {
sleep => {
// 如果这里先结束,意味着 scoped_fut 不会再继续推进了
// 而 scoped_fut 不会推进意味着 outlive_fut 可能在 parent_fut
// 执行结束完,还在继续执行
//
// parent_fut 执行结束意味着 Drop 立刻就会发生,那么 'parent_fut
// 就挂了,outlive_fut 的执行一定会出问题
//
// 为了避免这个问题,parent_fut 结束前一定要 cancel outlive_fut, 而 outlive_fut
// 跟 parent_fut 其实并没有 scope 上的直接关系,aka. 合理的方案是:
// parent_fut 一定要 cancel scoped_fut, scoped_fut 去 cancel outlive_fut
//
// 这个也是原 proposal 里的,scoped future 一定要 cancel (either force or gratefully)
// 它里面的所有还没执行完的 future。
//
// 所以要实现这个 cancel,怎么实现?目前只有 Drop,假设用 Drop 能实现 cancel。
// Attention:
// 1. Drop 是立即执行,且占用当前线程。
// 2. Future 无法被立即 cancel(可能在运行中),一定存在等待的情况。
// => Drop Cancel 一定存在等待,且阻塞
// 假设 Drop 实现了一个 blocking thread 并等待里面的 future 结束,确实就实现了,但意味着这个线程就被吃掉了,而
// 单线程运行时此时就意味着其他所有的 future 无法推进,死锁。多线程运行时虽然可以缓解这个问题,
// 但是并不能解决 (e.g. 所有 thread 都 block on cancel)。
//
// 所以一定要 Async Cancel。
// 没想明白怎么实现。
return;
}
scoped_fut => {
return;
}
}
}