Initial Dever 0.0.1
This commit is contained in:
commit
81233d098c
1479 files changed
+163782
No files matched your search
@@ -0,0 +1,404 @@
|
||||
// 保留原单次入口参照,另外区分入口成本和同一入口内连续执行的任务成本。
|
||||
// 每个 steady 样本仍逐次创建、等待并清理真实任务,只复用父入口。
|
||||
|
||||
const CONTINUOUS_CALLS: usize = 16;
|
||||
|
||||
// 与 Dever async_total 保持同样的可失败计时分支和携带文本的错误合同,不加填充字段。
|
||||
enum NativeAsyncFailure {
|
||||
Fault(Fault),
|
||||
Sleep(String, Fault),
|
||||
}
|
||||
|
||||
impl From<Fault> for NativeAsyncFailure {
|
||||
fn from(value: Fault) -> Self {
|
||||
Self::Fault(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NativeAsyncFailure> for Fault {
|
||||
fn from(value: NativeAsyncFailure) -> Self {
|
||||
match value {
|
||||
NativeAsyncFailure::Fault(fault) => fault,
|
||||
NativeAsyncFailure::Sleep(message, fault) => {
|
||||
drop(message);
|
||||
fault
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn native_fallible_sleep(millis: i64) -> Result<(), NativeAsyncFailure> {
|
||||
let fail = |message: &str| NativeAsyncFailure::Sleep(message.to_owned(), task_fault("timer"));
|
||||
let millis = u64::try_from(millis).map_err(|_| fail("sleep duration must be nonnegative"))?;
|
||||
let deadline = tokio::time::Instant::now()
|
||||
.checked_add(Duration::from_millis(millis))
|
||||
.ok_or_else(|| fail("sleep deadline is out of range"))?;
|
||||
tokio::time::sleep_until(deadline).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn native_async_total(values: Vec<i64>, delayed: bool) -> Result<i64, NativeAsyncFailure> {
|
||||
if delayed {
|
||||
native_fallible_sleep(0).await?;
|
||||
}
|
||||
native_total(&values).map_err(Into::into)
|
||||
}
|
||||
|
||||
// 仅作成本诊断:保留原始 Rust 参照,另测附带完成跟踪的相同双任务负载。
|
||||
// 元组先放返回值再放令牌,未领取结果的析构顺序与生产运行库相同。
|
||||
fn native_tracked<Failure, Work>(
|
||||
native: &NativeTasks,
|
||||
work: Work,
|
||||
cancel: CancellationToken,
|
||||
tracking: tokio_util::task::task_tracker::TaskTrackerToken,
|
||||
) -> JoinHandle<(
|
||||
Result<i64, Failure>,
|
||||
tokio_util::task::task_tracker::TaskTrackerToken,
|
||||
)>
|
||||
where
|
||||
Failure: From<Fault> + Send + 'static,
|
||||
Work: std::future::Future<Output = Result<i64, Failure>> + Send + 'static,
|
||||
{
|
||||
let admission = native.tasks.clone().try_acquire_owned().unwrap();
|
||||
let workers = native.workers.clone();
|
||||
tokio::spawn(async move {
|
||||
let tracking = tracking;
|
||||
let result = tokio::select! {
|
||||
biased;
|
||||
result = work => result,
|
||||
() = cancel.cancelled() => Err(task_fault("cancelled").into()),
|
||||
};
|
||||
drop(workers);
|
||||
drop(admission);
|
||||
(result, tracking)
|
||||
})
|
||||
}
|
||||
|
||||
fn reference_async_tracked(values: &[TaskCase]) -> Result<i64, Fault> {
|
||||
reference_tracked(values, |values| async move { native_total(&values) })
|
||||
}
|
||||
|
||||
fn reference_async_typed(values: &[TaskCase]) -> Result<i64, Fault> {
|
||||
reference_tracked(values, |values| native_async_total(values, false))
|
||||
}
|
||||
|
||||
fn reference_tracked<Failure, Work>(
|
||||
values: &[TaskCase],
|
||||
call: impl Fn(Vec<i64>) -> Work,
|
||||
) -> Result<i64, Fault>
|
||||
where
|
||||
Failure: From<Fault> + Into<Fault> + Send + 'static,
|
||||
Work: std::future::Future<Output = Result<i64, Failure>> + Send + 'static,
|
||||
{
|
||||
let Some(value) = values.first() else {
|
||||
return Ok(0);
|
||||
};
|
||||
value.native.runtime.block_on(async {
|
||||
let cancel = CancellationToken::new();
|
||||
let tasks = tokio_util::task::TaskTracker::new();
|
||||
let outcome = async {
|
||||
let mut total = 0i64;
|
||||
for _ in 0..CONTINUOUS_CALLS {
|
||||
let first = native_tracked(
|
||||
&value.native,
|
||||
call(value.values.clone()),
|
||||
cancel.child_token(),
|
||||
tasks.token(),
|
||||
);
|
||||
let second = native_tracked(
|
||||
&value.native,
|
||||
call(value.values.clone()),
|
||||
cancel.child_token(),
|
||||
tasks.token(),
|
||||
);
|
||||
let first = first
|
||||
.await
|
||||
.map_err(task_fault)
|
||||
.and_then(|(result, _tracking)| result.map_err(Into::into));
|
||||
if first.is_err() {
|
||||
cancel.cancel();
|
||||
}
|
||||
let second = second
|
||||
.await
|
||||
.map_err(task_fault)
|
||||
.and_then(|(result, _tracking)| result.map_err(Into::into));
|
||||
let result = first?
|
||||
.checked_add(second?)
|
||||
.ok_or_else(|| task_fault("integer overflow"))?;
|
||||
total = total
|
||||
.checked_add(result)
|
||||
.ok_or_else(|| task_fault("integer overflow"))?;
|
||||
}
|
||||
Ok(total)
|
||||
}
|
||||
.await;
|
||||
cancel.cancel();
|
||||
tasks.close();
|
||||
tasks.wait().await;
|
||||
outcome
|
||||
})
|
||||
}
|
||||
|
||||
fn generated_entry(values: &[TaskCase]) -> Result<i64, Fault> {
|
||||
let Some(value) = values.first() else {
|
||||
return Ok(0);
|
||||
};
|
||||
value.generated.run(async |_| 1i64).map_err(task_fault)
|
||||
}
|
||||
|
||||
fn reference_entry(values: &[TaskCase]) -> Result<i64, Fault> {
|
||||
let Some(value) = values.first() else {
|
||||
return Ok(0);
|
||||
};
|
||||
Ok(value.native.runtime.block_on(async { 1i64 }))
|
||||
}
|
||||
|
||||
fn generated_leaf_entry(values: &[TaskCase]) -> Result<i64, Fault> {
|
||||
let Some(value) = values.first() else { return Ok(0); };
|
||||
value.generated.run_leaf(async { 1i64 }).map_err(task_fault)
|
||||
}
|
||||
|
||||
fn generated_async_steady(values: &[TaskCase]) -> Result<i64, Fault> {
|
||||
let Some(value) = values.first() else {
|
||||
return Ok(0);
|
||||
};
|
||||
value
|
||||
.generated
|
||||
.run(async |scope| {
|
||||
let mut total = 0i64;
|
||||
for _ in 0..CONTINUOUS_CALLS {
|
||||
let result = GENERATED_ASYNC(&value.values, scope)
|
||||
.await
|
||||
.map_err(task_fault)?;
|
||||
total = total
|
||||
.checked_add(result)
|
||||
.ok_or_else(|| task_fault("integer overflow"))?;
|
||||
}
|
||||
Ok(total)
|
||||
})
|
||||
.map_err(task_fault)?
|
||||
}
|
||||
|
||||
fn reference_async_steady(values: &[TaskCase]) -> Result<i64, Fault> {
|
||||
let Some(value) = values.first() else {
|
||||
return Ok(0);
|
||||
};
|
||||
value.native.runtime.block_on(async {
|
||||
let cancel = CancellationToken::new();
|
||||
let mut total = 0i64;
|
||||
for _ in 0..CONTINUOUS_CALLS {
|
||||
let first = value
|
||||
.native
|
||||
.start(value.values.clone(), true, cancel.child_token());
|
||||
let second = value
|
||||
.native
|
||||
.start(value.values.clone(), true, cancel.child_token());
|
||||
let first = first.await.map_err(task_fault).and_then(|result| result);
|
||||
if first.is_err() {
|
||||
cancel.cancel();
|
||||
}
|
||||
let second = second.await.map_err(task_fault).and_then(|result| result);
|
||||
let result = first?
|
||||
.checked_add(second?)
|
||||
.ok_or_else(|| task_fault("integer overflow"))?;
|
||||
total = total
|
||||
.checked_add(result)
|
||||
.ok_or_else(|| task_fault("integer overflow"))?;
|
||||
}
|
||||
cancel.cancel();
|
||||
Ok(total)
|
||||
})
|
||||
}
|
||||
|
||||
fn generated_cancel_steady(values: &[TaskCase]) -> Result<i64, Fault> {
|
||||
let Some(value) = values.first() else {
|
||||
return Ok(0);
|
||||
};
|
||||
value
|
||||
.generated
|
||||
.run(async |scope| {
|
||||
let mut total = 0i64;
|
||||
for _ in 0..CONTINUOUS_CALLS {
|
||||
let result = GENERATED_CANCEL(scope).await.map_err(task_fault)?;
|
||||
total = total
|
||||
.checked_add(result)
|
||||
.ok_or_else(|| task_fault("integer overflow"))?;
|
||||
}
|
||||
Ok(total)
|
||||
})
|
||||
.map_err(task_fault)?
|
||||
}
|
||||
|
||||
fn reference_cancel_steady(values: &[TaskCase]) -> Result<i64, Fault> {
|
||||
let Some(value) = values.first() else {
|
||||
return Ok(0);
|
||||
};
|
||||
value.native.runtime.block_on(async {
|
||||
let root = CancellationToken::new();
|
||||
let mut total = 0i64;
|
||||
for _ in 0..CONTINUOUS_CALLS {
|
||||
let admission = value.native.tasks.clone().try_acquire_owned().unwrap();
|
||||
let cancel = root.child_token();
|
||||
let child = cancel.clone();
|
||||
let task = tokio::spawn(async move {
|
||||
let deadline = tokio::time::Instant::now()
|
||||
.checked_add(Duration::from_millis(60000))
|
||||
.unwrap();
|
||||
tokio::select! {
|
||||
biased;
|
||||
() = tokio::time::sleep_until(deadline) => panic!("cancelled timer completed"),
|
||||
() = child.cancelled() => (),
|
||||
}
|
||||
drop(admission);
|
||||
});
|
||||
cancel.cancel();
|
||||
task.await.map_err(task_fault)?;
|
||||
total = total
|
||||
.checked_add(1)
|
||||
.ok_or_else(|| task_fault("integer overflow"))?;
|
||||
}
|
||||
root.cancel();
|
||||
Ok(total)
|
||||
})
|
||||
}
|
||||
|
||||
// 取消机制采用标准 JoinHandle::abort 的独立 Rust 对照;原 token 对照继续保留。
|
||||
fn reference_cancel_abort(values: &[TaskCase]) -> Result<i64, Fault> {
|
||||
let Some(value) = values.first() else { return Ok(0); };
|
||||
value.native.runtime.block_on(async {
|
||||
let admission = value.native.tasks.clone().try_acquire_owned().unwrap();
|
||||
let task = tokio::spawn(async move {
|
||||
let _admission = admission;
|
||||
let deadline = tokio::time::Instant::now()
|
||||
.checked_add(Duration::from_millis(60000))
|
||||
.ok_or_else(|| task_fault("sleep deadline"))?;
|
||||
tokio::time::sleep_until(deadline).await;
|
||||
Ok::<(), Fault>(())
|
||||
});
|
||||
task.abort();
|
||||
match task.await {
|
||||
Err(error) if error.is_cancelled() => Ok(1),
|
||||
Err(error) => Err(task_fault(error)),
|
||||
Ok(result) => { result?; Err(task_fault("cancelled timer completed")) }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn lifecycle(cases: &[TaskCase]) {
|
||||
let expected = native_total(&cases[0].values).unwrap() * 2 * CONTINUOUS_CALLS as i64;
|
||||
assert_eq!(generated_async_steady(cases).unwrap(), expected);
|
||||
assert_eq!(reference_async_steady(cases).unwrap(), expected);
|
||||
assert_eq!(reference_async_tracked(cases).unwrap(), expected);
|
||||
assert_eq!(reference_async_typed(cases).unwrap(), expected);
|
||||
let native = &cases[0].native;
|
||||
assert_eq!(
|
||||
native
|
||||
.runtime
|
||||
.block_on(native_async_total(cases[0].values.clone(), true))
|
||||
.map_err(Fault::from)
|
||||
.unwrap(),
|
||||
expected / CONTINUOUS_CALLS as i64 / 2
|
||||
);
|
||||
assert!(native.runtime.block_on(native_fallible_sleep(-1)).is_err());
|
||||
eprintln!(
|
||||
"result-layout: rust_typed_async={}",
|
||||
std::mem::size_of::<Result<i64, NativeAsyncFailure>>()
|
||||
);
|
||||
assert_eq!(
|
||||
generated_cancel_steady(cases).unwrap(),
|
||||
CONTINUOUS_CALLS as i64
|
||||
);
|
||||
assert_eq!(
|
||||
reference_cancel_steady(cases).unwrap(),
|
||||
CONTINUOUS_CALLS as i64
|
||||
);
|
||||
cases[0]
|
||||
.generated
|
||||
.run(async |scope| {
|
||||
eprintln!(
|
||||
"task-layout: task={} optional_task={}",
|
||||
std::mem::size_of::<dever_runtime::task::Task<()>>(),
|
||||
std::mem::size_of::<Option<dever_runtime::task::Task<()>>>(),
|
||||
);
|
||||
eprintln!(
|
||||
"future-layout: fault={} dever_pair={} rust_pair={} dever_cancel={} rust_cancel={}",
|
||||
std::mem::size_of::<Fault>(),
|
||||
std::mem::size_of_val(&GENERATED_ASYNC(&cases[0].values, scope)),
|
||||
std::mem::size_of_val(&handwritten_pair(scope, &cases[0].values)),
|
||||
std::mem::size_of_val(&GENERATED_CANCEL(scope)),
|
||||
std::mem::size_of_val(&handwritten_cancel(scope)),
|
||||
);
|
||||
let result = GENERATED_ASYNC(&cases[0].values, scope).await;
|
||||
eprintln!(
|
||||
"result-layout: dever_async={} rust_async={}",
|
||||
std::mem::size_of_val(&result),
|
||||
std::mem::size_of::<Result<i64, Fault>>()
|
||||
);
|
||||
let result = GENERATED_CANCEL(scope).await;
|
||||
eprintln!(
|
||||
"result-layout: dever_cancel={} rust_cancel={}",
|
||||
std::mem::size_of_val(&result),
|
||||
std::mem::size_of::<Result<i64, Fault>>()
|
||||
);
|
||||
})
|
||||
.unwrap();
|
||||
compare(
|
||||
"task_entry_ready",
|
||||
cases,
|
||||
100000,
|
||||
generated_entry,
|
||||
reference_entry,
|
||||
|value| *value as u64,
|
||||
);
|
||||
compare(
|
||||
"task_leaf_entry_ready",
|
||||
cases,
|
||||
100000,
|
||||
generated_leaf_entry,
|
||||
reference_entry,
|
||||
|value| *value as u64,
|
||||
);
|
||||
compare(
|
||||
"task_async_steady",
|
||||
cases,
|
||||
64,
|
||||
generated_async_steady,
|
||||
reference_async_steady,
|
||||
|value| *value as u64,
|
||||
);
|
||||
compare(
|
||||
"task_cancel_steady",
|
||||
cases,
|
||||
64,
|
||||
generated_cancel_steady,
|
||||
reference_cancel_steady,
|
||||
|value| *value as u64,
|
||||
);
|
||||
compare_named(
|
||||
"task_async_tracked_diagnostic",
|
||||
cases,
|
||||
64,
|
||||
generated_async_steady,
|
||||
reference_async_tracked,
|
||||
|value| *value as u64,
|
||||
"rust_tracked",
|
||||
);
|
||||
compare_named(
|
||||
"task_async_typed_diagnostic",
|
||||
cases,
|
||||
64,
|
||||
generated_async_steady,
|
||||
reference_async_typed,
|
||||
|value| *value as u64,
|
||||
"rust_typed",
|
||||
);
|
||||
compare_named(
|
||||
"task_cancel_abort_diagnostic",
|
||||
cases,
|
||||
1000,
|
||||
generated_cancel,
|
||||
reference_cancel_abort,
|
||||
|value| *value as u64,
|
||||
"rust_abort",
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user