Address soundness hole in cycle prevention
This commit is contained in:
1 parent
4ad88bcd68
commit
f82205baf4
80 files changed
+730
-717
No files matched your search
+26
-24
@@ -29,7 +29,7 @@ async fn test_deep_chain() {
|
||||
let a = dag.add_task(task_fn(|_: ()| async { 1 }));
|
||||
let b = dag
|
||||
.add_task(task_fn(|x: i32| async move { x + 1 }))
|
||||
.depends_on(&a);
|
||||
.depends_on(a);
|
||||
let c = dag
|
||||
.add_task(task_fn(|x: i32| async move { x + 1 }))
|
||||
.depends_on(b);
|
||||
@@ -61,7 +61,7 @@ async fn test_wide_parallel() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
for (i, task) in tasks.iter().enumerate() {
|
||||
for (i, task) in tasks.into_iter().enumerate() {
|
||||
assert_eq!(dag.get(task).unwrap(), i * 2);
|
||||
}
|
||||
}
|
||||
@@ -70,13 +70,13 @@ async fn test_wide_parallel() {
|
||||
async fn test_diamond_dependency() {
|
||||
let dag = DagRunner::new();
|
||||
|
||||
let a = dag.add_task(task_fn(|_: ()| async { 10 }));
|
||||
let a: TaskHandle<_> = dag.add_task(task_fn(|_: ()| async { 10 })).into();
|
||||
let b = dag
|
||||
.add_task(task_fn(|x: i32| async move { x + 5 }))
|
||||
.depends_on(&a);
|
||||
.depends_on(a);
|
||||
let c = dag
|
||||
.add_task(task_fn(|x: i32| async move { x * 2 }))
|
||||
.depends_on(&a);
|
||||
.depends_on(a);
|
||||
let d = dag
|
||||
.add_task(task_fn(|(x, y): (i32, i32)| async move { x + y }))
|
||||
.depends_on((&b, &c));
|
||||
@@ -124,12 +124,12 @@ async fn test_different_output_types() {
|
||||
async fn test_multiple_sinks() {
|
||||
let dag = DagRunner::new();
|
||||
|
||||
let source = dag.add_task(task_fn(|_: ()| async { 10 }));
|
||||
let source: TaskHandle<_> = dag.add_task(task_fn(|_: ()| async { 10 })).into();
|
||||
|
||||
// Branch 1
|
||||
let branch1 = dag
|
||||
.add_task(task_fn(|x: i32| async move { x * 2 }))
|
||||
.depends_on(&source);
|
||||
.depends_on(source);
|
||||
let sink1 = dag
|
||||
.add_task(task_fn(|x: i32| async move { x + 1 }))
|
||||
.depends_on(branch1);
|
||||
@@ -137,7 +137,7 @@ async fn test_multiple_sinks() {
|
||||
// Branch 2
|
||||
let branch2 = dag
|
||||
.add_task(task_fn(|x: i32| async move { x + 5 }))
|
||||
.depends_on(&source);
|
||||
.depends_on(source);
|
||||
let sink2 = dag
|
||||
.add_task(task_fn(|x: i32| async move { x * 3 }))
|
||||
.depends_on(branch2);
|
||||
@@ -156,10 +156,10 @@ async fn test_single_task_inline_path() {
|
||||
let dag = DagRunner::new();
|
||||
|
||||
// Single task in its own layer
|
||||
let t1 = dag.add_task(task_fn(|_: ()| async { 10 }));
|
||||
let t1: TaskHandle<_> = dag.add_task(task_fn(|_: ()| async { 10 })).into();
|
||||
let t2 = dag
|
||||
.add_task(task_fn(|x: i32| async move { x * 2 }))
|
||||
.depends_on(&t1);
|
||||
.depends_on(t1);
|
||||
let t3 = dag
|
||||
.add_task(task_fn(|x: i32| async move { x + 5 }))
|
||||
.depends_on(t2);
|
||||
@@ -176,18 +176,18 @@ async fn test_multi_task_spawn_path() {
|
||||
// Test multi-task layer spawn path (line 411-456 in runner.rs)
|
||||
let dag = DagRunner::new();
|
||||
|
||||
let source = dag.add_task(task_fn(|_: ()| async { 100 }));
|
||||
let source: TaskHandle<_> = dag.add_task(task_fn(|_: ()| async { 100 })).into();
|
||||
|
||||
// Create multiple tasks in the same layer (all depend on source)
|
||||
let t1 = dag
|
||||
.add_task(task_fn(|x: i32| async move { x + 1 }))
|
||||
.depends_on(&source);
|
||||
.depends_on(source);
|
||||
let t2 = dag
|
||||
.add_task(task_fn(|x: i32| async move { x + 2 }))
|
||||
.depends_on(&source);
|
||||
.depends_on(source);
|
||||
let t3 = dag
|
||||
.add_task(task_fn(|x: i32| async move { x + 3 }))
|
||||
.depends_on(&source);
|
||||
.depends_on(source);
|
||||
|
||||
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
|
||||
.await
|
||||
@@ -204,14 +204,16 @@ async fn test_producer_consumer_channels() {
|
||||
let dag = DagRunner::new();
|
||||
|
||||
// Producer
|
||||
let producer = dag.add_task(task_fn(|_: ()| async { vec![1, 2, 3, 4, 5] }));
|
||||
let producer: TaskHandle<_> = dag
|
||||
.add_task(task_fn(|_: ()| async { vec![1, 2, 3, 4, 5] }))
|
||||
.into();
|
||||
|
||||
// Consumer
|
||||
let consumer = dag
|
||||
.add_task(task_fn(
|
||||
|data: Vec<i32>| async move { data.iter().sum::<i32>() },
|
||||
))
|
||||
.depends_on(&producer);
|
||||
.depends_on(producer);
|
||||
|
||||
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
|
||||
.await
|
||||
@@ -225,18 +227,18 @@ async fn test_multi_consumer_fanout() {
|
||||
// Test fanout with multiple consumers from one producer
|
||||
let dag = DagRunner::new();
|
||||
|
||||
let producer = dag.add_task(task_fn(|_: ()| async { 42 }));
|
||||
let producer: TaskHandle<_> = dag.add_task(task_fn(|_: ()| async { 42 })).into();
|
||||
|
||||
// Multiple consumers
|
||||
let c1 = dag
|
||||
.add_task(task_fn(|x: i32| async move { x * 2 }))
|
||||
.depends_on(&producer);
|
||||
.depends_on(producer);
|
||||
let c2 = dag
|
||||
.add_task(task_fn(|x: i32| async move { x + 10 }))
|
||||
.depends_on(&producer);
|
||||
.depends_on(producer);
|
||||
let c3 = dag
|
||||
.add_task(task_fn(|x: i32| async move { x - 5 }))
|
||||
.depends_on(&producer);
|
||||
.depends_on(producer);
|
||||
|
||||
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
|
||||
.await
|
||||
@@ -253,14 +255,14 @@ async fn test_compute_layers_with_dependents_tracking() {
|
||||
let dag = DagRunner::new();
|
||||
|
||||
// Create diamond pattern to ensure dependents are tracked correctly
|
||||
let source = dag.add_task(task_fn(|_: ()| async { 1 }));
|
||||
let source: TaskHandle<_> = dag.add_task(task_fn(|_: ()| async { 1 })).into();
|
||||
|
||||
let left = dag
|
||||
.add_task(task_fn(|x: i32| async move { x + 1 }))
|
||||
.depends_on(&source);
|
||||
.depends_on(source);
|
||||
let right = dag
|
||||
.add_task(task_fn(|x: i32| async move { x + 2 }))
|
||||
.depends_on(&source);
|
||||
.depends_on(source);
|
||||
|
||||
let sink = dag
|
||||
.add_task(task_fn(|(l, r): (i32, i32)| async move { l + r }))
|
||||
@@ -301,7 +303,7 @@ async fn test_layer_execution_order() {
|
||||
x + 1
|
||||
}
|
||||
}))
|
||||
.depends_on(&t1);
|
||||
.depends_on(t1);
|
||||
|
||||
let order3 = execution_order.clone();
|
||||
let t3 = dag
|
||||
|
||||
@@ -9,9 +9,9 @@ use futures::FutureExt;
|
||||
async fn test_example_a_fan_out() {
|
||||
let dag = DagRunner::new();
|
||||
|
||||
let base = dag.add_task(Seed::new(10)); // Input=() → no depends_on, returns TaskHandle
|
||||
let plus1 = dag.add_task(Inc::new()).depends_on(&base); // 11
|
||||
let plus2 = dag.add_task(Inc::new()).depends_on(&base); // 11
|
||||
let base: TaskHandle<_> = dag.add_task(Seed::new(10)).into(); // Input=() → no depends_on, returns TaskHandle
|
||||
let plus1 = dag.add_task(Inc::new()).depends_on(base); // 11
|
||||
let plus2 = dag.add_task(Inc::new()).depends_on(base); // 11
|
||||
|
||||
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
|
||||
.await
|
||||
@@ -29,7 +29,7 @@ async fn test_example_b_fan_in() {
|
||||
let n = dag.add_task(task_fn(|_: ()| async { 42usize }));
|
||||
let f = dag.add_task(task_fn(|_: ()| async { true }));
|
||||
|
||||
let output = dag.add_task(OutputResult::new()).depends_on((&s, &n, &f));
|
||||
let output = dag.add_task(OutputResult::new()).depends_on((s, n, f));
|
||||
|
||||
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
|
||||
.await
|
||||
@@ -42,11 +42,11 @@ async fn test_example_c_many_to_many() {
|
||||
let dag = DagRunner::new();
|
||||
|
||||
let x = dag.add_task(Seed::new(2));
|
||||
let y = dag.add_task(Seed::new(3));
|
||||
let y = dag.add_task(Seed::new(3)).into();
|
||||
let z = dag.add_task(Seed::new(5));
|
||||
|
||||
let sum_xy = dag.add_task(Add::new()).depends_on((&x, &y)); // 5
|
||||
let prod_yz = dag.add_task(Mul::new()).depends_on((&y, &z)); // 15
|
||||
let sum_xy = dag.add_task(Add::new()).depends_on((x, &y)); // 5
|
||||
let prod_yz = dag.add_task(Mul::new()).depends_on((&y, z)); // 15
|
||||
let total = dag.add_task(Add::new()).depends_on((&sum_xy, &prod_yz)); // 20
|
||||
|
||||
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
|
||||
@@ -59,12 +59,14 @@ async fn test_example_c_many_to_many() {
|
||||
async fn test_single_input_forms() {
|
||||
let dag = DagRunner::new();
|
||||
|
||||
let s = dag.add_task(task_fn(|_: ()| async { "hello".to_owned() }));
|
||||
let s = dag
|
||||
.add_task(task_fn(|_: ()| async { "hello".to_owned() }))
|
||||
.into();
|
||||
|
||||
// Preferred form: &node
|
||||
let upper1 = dag
|
||||
.add_task(task_fn(|s: String| async move { s.to_uppercase() }))
|
||||
.depends_on(&s);
|
||||
.depends_on(s);
|
||||
|
||||
// Also allowed: (&node,)
|
||||
let upper2 = dag
|
||||
|
||||
@@ -59,9 +59,9 @@ async fn integration_test_etl_pipeline() -> DagResult<()> {
|
||||
let source3 = dag.add_task(LoadValue::new(30));
|
||||
|
||||
// Transform: Process each source
|
||||
let transform1 = dag.add_task(Multiply::new(2)).depends_on(&source1);
|
||||
let transform2 = dag.add_task(Multiply::new(3)).depends_on(&source2);
|
||||
let transform3 = dag.add_task(Multiply::new(4)).depends_on(&source3);
|
||||
let transform1 = dag.add_task(Multiply::new(2)).depends_on(source1);
|
||||
let transform2 = dag.add_task(Multiply::new(3)).depends_on(source2);
|
||||
let transform3 = dag.add_task(Multiply::new(4)).depends_on(source3);
|
||||
|
||||
// Load: Aggregate results
|
||||
let combine12 = dag.add_task(Add).depends_on((&transform1, &transform2));
|
||||
@@ -115,9 +115,9 @@ async fn integration_test_multiple_sinks() -> DagResult<()> {
|
||||
let source = dag.add_task(LoadValue::new(10));
|
||||
|
||||
// Multiple independent branches
|
||||
let branch1 = dag.add_task(Multiply::new(2)).depends_on(&source);
|
||||
let branch2 = dag.add_task(Multiply::new(3)).depends_on(&source);
|
||||
let branch3 = dag.add_task(Multiply::new(4)).depends_on(&source);
|
||||
let branch1 = dag.add_task(Multiply::new(2)).depends_on(source);
|
||||
let branch2 = dag.add_task(Multiply::new(3)).depends_on(source);
|
||||
let branch3 = dag.add_task(Multiply::new(4)).depends_on(source);
|
||||
|
||||
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await?;
|
||||
|
||||
@@ -136,8 +136,8 @@ async fn integration_test_diamond_cascade() -> DagResult<()> {
|
||||
|
||||
// Diamond 1
|
||||
let source1 = dag.add_task(LoadValue::new(10));
|
||||
let left1 = dag.add_task(Multiply::new(2)).depends_on(&source1);
|
||||
let right1 = dag.add_task(Multiply::new(3)).depends_on(&source1);
|
||||
let left1 = dag.add_task(Multiply::new(2)).depends_on(source1);
|
||||
let right1 = dag.add_task(Multiply::new(3)).depends_on(source1);
|
||||
let sink1 = dag.add_task(Add).depends_on((&left1, &right1));
|
||||
|
||||
// Diamond 2 (depends on Diamond 1)
|
||||
@@ -193,7 +193,7 @@ async fn integration_test_error_recovery() -> DagResult<()> {
|
||||
let dag = DagRunner::new();
|
||||
|
||||
let source = dag.add_task(LoadValue::new(42));
|
||||
let transform = dag.add_task(Multiply::new(2)).depends_on(&source);
|
||||
let transform = dag.add_task(Multiply::new(2)).depends_on(source);
|
||||
|
||||
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await?;
|
||||
|
||||
@@ -248,9 +248,9 @@ async fn integration_test_reused_values() -> DagResult<()> {
|
||||
let source = dag.add_task(LoadValue::new(100));
|
||||
|
||||
// Use source in multiple different ways
|
||||
let double = dag.add_task(Multiply::new(2)).depends_on(&source);
|
||||
let triple = dag.add_task(Multiply::new(3)).depends_on(&source);
|
||||
let quadruple = dag.add_task(Multiply::new(4)).depends_on(&source);
|
||||
let double = dag.add_task(Multiply::new(2)).depends_on(source);
|
||||
let triple = dag.add_task(Multiply::new(3)).depends_on(source);
|
||||
let quadruple = dag.add_task(Multiply::new(4)).depends_on(source);
|
||||
|
||||
// Combine some of the results
|
||||
let combined = dag.add_task(Add).depends_on((&double, &triple));
|
||||
@@ -328,7 +328,7 @@ async fn integration_test_parallel_vs_sequential_timing() -> DagResult<()> {
|
||||
sleep(sleep_duration).await;
|
||||
x + 1
|
||||
}))
|
||||
.depends_on(¶llel_tasks[0]);
|
||||
.depends_on(parallel_tasks[0]);
|
||||
|
||||
let seq2 = dag
|
||||
.add_task(task_fn(move |x: i32| async move {
|
||||
|
||||
@@ -85,7 +85,7 @@ async fn test_stateful_tasks() {
|
||||
let dag = DagRunner::new();
|
||||
|
||||
let input = dag.add_task(task_fn(|_: ()| async { 5 }));
|
||||
let counter = dag.add_task(Counter::new(10)).depends_on(&input);
|
||||
let counter = dag.add_task(Counter::new(10)).depends_on(input);
|
||||
|
||||
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
|
||||
.await
|
||||
@@ -105,7 +105,7 @@ async fn test_task_fn_with_captured_state() {
|
||||
let base = dag.add_task(task_fn(|_: ()| async { 5 }));
|
||||
let scaled = dag
|
||||
.add_task(task_fn(move |x: i32| async move { x * multiplier }))
|
||||
.depends_on(&base);
|
||||
.depends_on(base);
|
||||
|
||||
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
|
||||
.await
|
||||
|
||||
Reference in new issue
Block a user