103 lines
4.3 KiB
Rust
103 lines
4.3 KiB
Rust
// 独立使用 Tokio 的取消与排空参照;不调用 Dever 的 Scope、Task 或生成函数。
|
|
use tokio_util::task::{AbortOnDropHandle, task_tracker::TaskTrackerToken};
|
|
|
|
impl NativeTasks {
|
|
fn tracked_leaf<Value, Work>(
|
|
&self,
|
|
tasks: &TaskTracker,
|
|
call: impl FnOnce() -> Work + Send + 'static,
|
|
) -> Result<AbortOnDropHandle<(Value, TaskTrackerToken)>, Fault>
|
|
where
|
|
Value: Send + 'static,
|
|
Work: std::future::Future<Output = Value> + Send + 'static,
|
|
{
|
|
if tasks.is_closed() {
|
|
return Err(task_fault("closed task scope"));
|
|
}
|
|
let permit = self.tasks.clone().try_acquire_owned().map_err(task_fault)?;
|
|
let tracking = tasks.token();
|
|
let future = async move {
|
|
let result = call().await;
|
|
drop(permit);
|
|
result
|
|
};
|
|
// Future 先释放;未领取的输出也必须先于跟踪令牌释放。
|
|
Ok(AbortOnDropHandle::new(tokio::spawn(
|
|
future.map(move |result| (result, tracking)),
|
|
)))
|
|
}
|
|
}
|
|
|
|
fn reference_cancel_scoped(values: &[TaskCase]) -> Result<i64, Fault> {
|
|
let Some(value) = values.first() else { return Ok(0); };
|
|
value.native.worker_scope(async |tasks| {
|
|
let task = value.native.tracked_leaf(tasks, || native_fallible_sleep(60000))?;
|
|
task.abort();
|
|
match task.await {
|
|
Err(error) if error.is_cancelled() => (),
|
|
Err(error) => return Err(NativeAsyncFailure::Fault(task_fault(error))),
|
|
Ok((result, _tracking)) => result?,
|
|
}
|
|
Ok::<_, NativeAsyncFailure>(1)
|
|
})?.map_err(Into::into)
|
|
}
|
|
|
|
fn cancel_scoped(cases: &[TaskCase]) {
|
|
compare_named("task_cancel_scoped", cases, 1000,
|
|
generated_cancel, reference_cancel_scoped, |value| *value as u64, "rust_scoped");
|
|
}
|
|
|
|
fn check_cancel_cleanup() {
|
|
let native = NativeTasks::new();
|
|
native.worker_scope(async |tasks| {
|
|
let task = native.tracked_leaf(tasks, || async { 42 }).unwrap();
|
|
let (value, tracking) = task.await.unwrap();
|
|
assert_eq!(value, 42);
|
|
drop(tracking);
|
|
let permits = native.tasks.clone().acquire_many_owned(8).await.unwrap();
|
|
assert!(native.tracked_leaf(tasks, || async {}).is_err());
|
|
drop(permits);
|
|
tasks.close();
|
|
assert!(native.tracked_leaf(tasks, || async {}).is_err());
|
|
}).unwrap();
|
|
|
|
// 立即放弃、已启动 Future、未领取输出分别验证,根 panic 也必须排空。
|
|
for stage in 0..3 {
|
|
for root_panics in [false, true] {
|
|
let dropped = Arc::new(AtomicBool::new(false));
|
|
let observed = dropped.clone();
|
|
let owned = WorkerOutput(dropped);
|
|
let outcome = native.worker_scope(async |tasks| {
|
|
let (started, ready) = tokio::sync::oneshot::channel();
|
|
let task = native.tracked_leaf(tasks, move || async move {
|
|
if stage != 0 { started.send(()).unwrap(); }
|
|
if stage != 2 { std::future::pending::<()>().await; }
|
|
owned
|
|
}).unwrap();
|
|
if stage != 0 { ready.await.unwrap(); }
|
|
if stage == 2 {
|
|
while !task.is_finished() { tokio::task::yield_now().await; }
|
|
assert!(!observed.load(Ordering::SeqCst));
|
|
}
|
|
if root_panics { panic!("root failure after leaf submission"); }
|
|
drop(task);
|
|
});
|
|
assert_eq!(outcome.is_err(), root_panics);
|
|
assert!(observed.load(Ordering::SeqCst));
|
|
assert_eq!(native.tasks.available_permits(), 8);
|
|
}
|
|
}
|
|
|
|
native.worker_scope(async |tasks| {
|
|
let task = native.tracked_leaf(tasks, || async { Err::<(), _>("business failure") }).unwrap();
|
|
while !task.is_finished() { tokio::task::yield_now().await; }
|
|
task.abort();
|
|
let (result, tracking) = task.await.unwrap();
|
|
assert_eq!(result, Err("business failure"));
|
|
drop(tracking);
|
|
let task = native.tracked_leaf(tasks, || async { panic!("leaf failure") }).unwrap();
|
|
assert!(task.await.unwrap_err().is_panic());
|
|
}).unwrap();
|
|
println!("leaf cleanup: capacity, closed scope, inputs, future, unclaimed output, root panic, business error, task panic and re-entry passed");
|
|
}
|