Address soundness hole in cycle prevention

This commit is contained in:
TechnoPorg committed 2026-01-29 06:56:17 +01:00
1 parent 4ad88bcd68
commit f82205baf4
80 files changed
+730 -717

No files matched your search

+13 -6
View File
@@ -1,7 +1,7 @@
//! Butterfly pattern DAG tests
use crate::common::task_fn;
use dagx::{DagResult, DagRunner};
use dagx::{DagResult, DagRunner, TaskHandle};
use futures::FutureExt;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
@@ -13,7 +13,10 @@ async fn test_butterfly_network() -> DagResult<()> {
// Simplified butterfly network - avoid tuples entirely
// Layer 0: inputs
let inputs: Vec<_> = (0..8)
.map(|i| dag.add_task(task_fn(move |_: ()| async move { i as f64 })))
.map(|i| {
dag.add_task(task_fn(move |_: ()| async move { i as f64 }))
.into()
})
.collect();
// Layer 1: butterfly operations (compute pairs separately)
@@ -92,7 +95,7 @@ async fn test_recursive_butterfly() -> DagResult<()> {
.into_iter()
.map(|val| {
let task = dag.add_task(task_fn(move |_: ()| async move { val }));
(&task).into()
task.into()
})
.collect();
@@ -115,7 +118,10 @@ async fn test_benes_network() -> DagResult<()> {
// 4 inputs
let inputs: Vec<_> = (0..4)
.map(|i| dag.add_task(task_fn(move |_: ()| async move { 1 << i }))) // Powers of 2
.map(|i| {
dag.add_task(task_fn(move |_: ()| async move { 1 << i }))
.into()
}) // Powers of 2
.collect();
// First stage: sort pairs
@@ -153,7 +159,7 @@ async fn test_shuffle_exchange_network() -> DagResult<()> {
let counter = Arc::new(AtomicUsize::new(0));
// Create 8 inputs
let inputs: Vec<_> = (0..8)
let inputs: Vec<TaskHandle<_>> = (0..8)
.map(|i| {
let counter = counter.clone();
dag.add_task(task_fn(move |_: ()| {
@@ -163,6 +169,7 @@ async fn test_shuffle_exchange_network() -> DagResult<()> {
i
}
}))
.into()
})
.collect();
@@ -172,7 +179,7 @@ async fn test_shuffle_exchange_network() -> DagResult<()> {
// Shuffle pattern: rotate bits
let source = ((i << 1) | (i >> 2)) & 0x7;
dag.add_task(task_fn(move |x: i32| async move { x }))
.depends_on(&inputs[source])
.depends_on(inputs[source])
})
.collect();
+12 -10
View File
@@ -1,7 +1,7 @@
//! Complex DAG pattern tests
use crate::common::task_fn;
use dagx::{DagResult, DagRunner};
use dagx::{DagResult, DagRunner, TaskHandle};
use futures::FutureExt;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
@@ -21,7 +21,7 @@ async fn test_workflow_with_conditional_paths() -> DagResult<()> {
Err("Invalid input")
}
}))
.depends_on(&input);
.depends_on(input);
// Process valid input
let process = dag
@@ -31,7 +31,7 @@ async fn test_workflow_with_conditional_paths() -> DagResult<()> {
Err(_) => -1,
}
}))
.depends_on(&validate);
.depends_on(validate);
// Parallel enhancement steps
let enhance1 = dag
@@ -80,7 +80,7 @@ async fn test_map_reduce_pattern() -> DagResult<()> {
// Simulate word count
vec![("word1", i % 3), ("word2", i % 5), ("word3", i % 7)]
}));
(&task).into()
task.into()
})
.collect();
@@ -166,7 +166,7 @@ async fn test_pipeline_with_feedback() -> DagResult<()> {
}
}
}))
.depends_on(&init);
.depends_on(init);
// Stage 2: Filter
let stage2 = dag
@@ -209,9 +209,11 @@ async fn test_scatter_gather_pattern() -> DagResult<()> {
let dag = DagRunner::new();
// Scatter: distribute work to multiple workers
let source = dag.add_task(task_fn(|_: ()| async {
vec![10, 20, 30, 40, 50, 60, 70, 80]
}));
let source: TaskHandle<_> = dag
.add_task(task_fn(|_: ()| async {
vec![10, 20, 30, 40, 50, 60, 70, 80]
}))
.into();
// Workers process chunks
let workers: Vec<_> = (0..4)
@@ -228,7 +230,7 @@ async fn test_scatter_gather_pattern() -> DagResult<()> {
data[start..end].iter().sum::<i32>()
}))
.depends_on(&source)
.depends_on(source)
})
.collect();
@@ -266,7 +268,7 @@ async fn test_fork_join_with_barriers() -> DagResult<()> {
// Multiple parallel paths
let mut path1 = Vec::new();
let parent_handle: dagx::TaskHandle<i32> = (&parent).into();
let parent_handle: dagx::TaskHandle<i32> = parent.into();
let mut prev1 = parent_handle;
for i in 0..3 {
let counter = phase_counter.clone();
+7 -7
View File
@@ -15,10 +15,10 @@ async fn test_2d_grid_pattern() -> DagResult<()> {
#[allow(clippy::needless_range_loop)]
for i in 0..4 {
let task = dag.add_task(task_fn(move |_: ()| async move { i as i32 }));
grid[0][i] = Some((&task).into());
grid[0][i] = Some(task.into());
if i > 0 {
let task = dag.add_task(task_fn(move |_: ()| async move { i as i32 * 10 }));
grid[i][0] = Some((&task).into());
grid[i][0] = Some(task.into());
}
}
@@ -55,7 +55,7 @@ async fn test_3d_grid_pattern() -> DagResult<()> {
// Initialize origin
let origin = dag.add_task(task_fn(|_: ()| async { 1 }));
grid[0][0][0] = Some((&origin).into());
grid[0][0][0] = Some(origin.into());
// Fill the 3D grid
for x in 0..size {
@@ -77,7 +77,7 @@ async fn test_3d_grid_pattern() -> DagResult<()> {
let task_handle: dagx::TaskHandle<usize> = match deps.len() {
0 => {
let t = dag.add_task(task_fn(move |_: ()| async move { x + y + z }));
(&t).into()
t.into()
}
1 => dag
.add_task(task_fn(move |prev: usize| async move { prev + 1 }))
@@ -121,7 +121,7 @@ async fn test_hexagonal_grid() -> DagResult<()> {
// Center hex
let center_builder = dag.add_task(task_fn(|_: ()| async { 100 }));
let center: dagx::TaskHandle<i32> = (&center_builder).into();
let center: dagx::TaskHandle<i32> = center_builder.into();
hex_grid.insert((0, 0), center);
// Ring 1 (6 hexes around center)
@@ -168,7 +168,7 @@ async fn test_hexagonal_grid() -> DagResult<()> {
.depends_on(neighbors[0])
} else {
let t = dag.add_task(task_fn(move |_: ()| async move { q + r }));
(&t).into()
t.into()
};
hex_grid.insert((q, r), task);
@@ -194,7 +194,7 @@ async fn test_toroidal_grid() -> DagResult<()> {
for i in 0..SIZE {
for j in 0..SIZE {
let task = dag.add_task(task_fn(move |_: ()| async move { (i * SIZE + j) as i32 }));
grid[i][j] = Some((&task).into());
grid[i][j] = Some(task.into());
}
}
+16 -14
View File
@@ -1,7 +1,7 @@
//! Tree pattern DAG tests
use crate::common::task_fn;
use dagx::{DagResult, DagRunner};
use dagx::{DagResult, DagRunner, TaskHandle};
use futures::FutureExt;
#[tokio::test]
@@ -15,7 +15,7 @@ async fn test_binary_tree_reduction() -> DagResult<()> {
// Build binary tree reduction
let mut current_level: Vec<dagx::TaskHandle<i32>> =
leaves.into_iter().map(|t| (&t).into()).collect();
leaves.into_iter().map(|t| t.into()).collect();
while current_level.len() > 1 {
let mut next_level = Vec::new();
@@ -52,7 +52,7 @@ async fn test_n_ary_tree() -> DagResult<()> {
fn build_tree(dag: &DagRunner, depth: usize, value: i32) -> dagx::TaskHandle<i32> {
if depth == 0 {
let task = dag.add_task(task_fn(move |_: ()| async move { value }));
return (&task).into();
return task.into();
}
let children: Vec<_> = (0..3)
@@ -88,19 +88,19 @@ async fn test_unbalanced_tree() -> DagResult<()> {
// / \
// f g
let f = dag.add_task(task_fn(|_: ()| async { 1 }));
let f: TaskHandle<_> = dag.add_task(task_fn(|_: ()| async { 1 })).into();
let g = dag.add_task(task_fn(|_: ()| async { 2 }));
let c = dag
.add_task(task_fn(|x: i32| async move { x * 2 }))
.depends_on(&f);
.depends_on(f);
let d_builder = dag.add_task(task_fn(|_: ()| async { 3 }));
let d: dagx::TaskHandle<i32> = (&d_builder).into();
let d: dagx::TaskHandle<i32> = d_builder.into();
let e = dag
.add_task(task_fn(|x: i32| async move { x * 3 }))
.depends_on(&g);
.depends_on(g);
let a = dag
.add_task(task_fn(|(c, d): (i32, i32)| async move { c + d }))
@@ -116,7 +116,7 @@ async fn test_unbalanced_tree() -> DagResult<()> {
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await?;
assert_eq!(dag.get(&f)?, 1);
assert_eq!(dag.get(f)?, 1);
assert_eq!(dag.get(c)?, 2);
assert_eq!(dag.get(a)?, 5); // 2 + 3
assert_eq!(dag.get(b)?, 16); // 6 + 10
@@ -130,20 +130,22 @@ async fn test_trie_like_structure() -> DagResult<()> {
let dag = DagRunner::new();
// Build a trie-like structure for paths
let root = dag.add_task(task_fn(|_: ()| async { "root".to_string() }));
let root: TaskHandle<_> = dag
.add_task(task_fn(|_: ()| async { "root".to_string() }))
.into();
// First level
let usr = dag
.add_task(task_fn(|r: String| async move { format!("{}/usr", r) }))
.depends_on(&root);
.depends_on(root);
let etc = dag
.add_task(task_fn(|r: String| async move { format!("{}/etc", r) }))
.depends_on(&root);
.depends_on(root);
let var = dag
.add_task(task_fn(|r: String| async move { format!("{}/var", r) }))
.depends_on(&root);
.depends_on(root);
// Second level
let usr_bin = dag
@@ -188,7 +190,7 @@ async fn test_balanced_k_ary_tree() -> DagResult<()> {
) -> dagx::TaskHandle<usize> {
if depth == 0 {
let task = dag.add_task(task_fn(move |_: ()| async move { node_id }));
return (&task).into();
return task.into();
}
let children: Vec<_> = (0..k)
@@ -243,7 +245,7 @@ async fn test_merkle_tree_pattern() -> DagResult<()> {
// Convert to TaskHandles
let mut current_level: Vec<dagx::TaskHandle<String>> =
data_blocks.into_iter().map(|t| (&t).into()).collect();
data_blocks.into_iter().map(|t| t.into()).collect();
while current_level.len() > 1 {
let mut next_level = Vec::new();