// 独立使用 Tokio 的取消与排空参照;不调用 Dever 的 Scope、Task 或生成函数。 use tokio_util::task::{AbortOnDropHandle, task_tracker::TaskTrackerToken}; impl NativeTasks { fn tracked_leaf( &self, tasks: &TaskTracker, call: impl FnOnce() -> Work + Send + 'static, ) -> Result, Fault> where Value: Send + 'static, Work: std::future::Future + 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 { 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"); }