// 独立 Tokio 参照补齐当前阻塞工作负载的收尾合同;不调用 Dever 运行库。 // 原始轻量参照继续保留,不将本组结果冒充原参照的性能提升。 use futures_util::FutureExt; use std::panic::{AssertUnwindSafe, catch_unwind}; use tokio_util::task::TaskTracker; impl NativeTasks { fn worker_scope( &self, call: impl AsyncFnOnce(&TaskTracker) -> Value, ) -> Result { let tasks = TaskTracker::new(); self.runtime.block_on(async { let result = AssertUnwindSafe(async { call(&tasks).await }).catch_unwind().await; tasks.close(); tasks.wait().await; result.map_err(task_fault) }) } async fn tracked_blocking( &self, tasks: &TaskTracker, call: impl FnOnce() -> Value + Send + 'static, ) -> Result { if tasks.is_closed() { return Err(task_fault("closed worker scope")); } let permit = self.workers.clone().acquire_owned().await.unwrap(); let token = tasks.token(); let (result, _token) = tokio::task::spawn_blocking(move || { let _permit = permit; let result = catch_unwind(AssertUnwindSafe(call)).map_err(task_fault); // 输出先析构,跟踪才结束;panic 载荷也在通知排空前释放。 (result, token) }).await.map_err(task_fault)?; result } } fn reference_worker_scoped( values: &[TaskCase], ) -> Result { if BLOCKING { reference_worker_calls::(values, |owned| { native_blocking_sleep(0)?; native_total(&owned).map_err(NativeAsyncFailure::from) }) } else { reference_worker_calls::(values, |owned| native_total(&owned)) } } fn native_blocking_sleep(millis: i64) -> Result<(), NativeAsyncFailure> { let fail = |message: &str| NativeAsyncFailure::Sleep(message.to_owned(), task_fault("sleep")); let millis = u64::try_from(millis).map_err(|_| fail("sleep duration must be nonnegative"))?; let duration = Duration::from_millis(millis); std::time::Instant::now().checked_add(duration) .ok_or_else(|| fail("sleep deadline is out of range"))?; std::thread::sleep(duration); Ok(()) } // 保留实际业务错误及文本负载;不以填充字段强凑与生成代码相同的布局。 fn reference_worker_calls + Into + Send + 'static>( values: &[TaskCase], call: impl FnOnce(Vec) -> Result + Copy + Send + 'static, ) -> Result { let Some(value) = values.first() else { return Ok(0); }; value.native.worker_scope(async |tasks| { let mut total = 0i64; for _ in 0..CALLS { let owned = value.values.clone(); let result = value.native.tracked_blocking(tasks, move || call(owned)) .await.map_err(Failure::from)??; total = total.checked_add(result) .ok_or_else(|| Failure::from(task_fault("integer overflow")))?; } Ok::<_, Failure>(total) })?.map_err(Into::into) } fn worker_scoped(cases: &[TaskCase]) { let expected = native_total(&cases[0].values).unwrap(); assert_eq!(reference_worker_scoped::(cases).unwrap(), expected); assert_eq!(reference_worker_scoped::(cases).unwrap(), expected); assert_eq!(reference_worker_scoped::(cases).unwrap(), expected * CONTINUOUS_CALLS as i64); assert_eq!(reference_worker_scoped::(cases).unwrap(), expected * CONTINUOUS_CALLS as i64); let failures = [TaskCase { generated: new_runtime(), native: NativeTasks::new(), values: vec![i64::MAX, 1], }]; assert!(reference_worker_scoped::(&failures).is_err()); assert!(reference_worker_scoped::(&failures).is_err()); assert!(reference_worker_scoped::(&failures).is_err()); assert!(reference_worker_scoped::(&failures).is_err()); compare_named("task_parallel_scoped", cases, 500, generated_parallel, reference_worker_scoped::, |value| *value as u64, "rust_scoped"); compare_named("task_blocking_scoped", cases, 500, generated_blocking, reference_worker_scoped::, |value| *value as u64, "rust_scoped"); compare_named("task_parallel_scoped_steady", cases, 64, generated_worker_steady::, reference_worker_scoped::, |value| *value as u64, "rust_scoped"); compare_named("task_blocking_scoped_steady", cases, 64, generated_worker_steady::, reference_worker_scoped::, |value| *value as u64, "rust_scoped"); }