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

+2 -2
View File
@@ -83,8 +83,8 @@ async fn main() {
// Step 4: Add source tasks (tasks with no dependencies)
//
// add_task() returns a TaskHandle<T> where T is the task's output type
let x = dag.add_task(Value::new(2)); // TaskHandle<i32>
let y = dag.add_task(Value::new(3)); // TaskHandle<i32>
let x = dag.add_task(Value::new(2)).into(); // TaskHandle<i32>
let y = dag.add_task(Value::new(3)).into(); // TaskHandle<i32>
// Step 5: Add a task with dependencies
//
+5 -5
View File
@@ -55,7 +55,7 @@
//! Squared: 100
//! ```
use dagx::{task, DagRunner};
use dagx::{task, DagRunner, TaskHandle};
use futures::FutureExt;
// Source task with a value field
@@ -131,13 +131,13 @@ async fn main() {
let dag = DagRunner::new();
// Single source task - constructed with ::new()
let source = dag.add_task(Source::new(10));
let source: TaskHandle<_> = dag.add_task(Source::new(10)).into();
// Multiple downstream tasks consuming the same value
// Each constructed differently to show flexibility
let plus_one = dag.add_task(AddOffset::new(1)).depends_on(&source);
let times_two = dag.add_task(Multiply::new(2)).depends_on(&source);
let squared = dag.add_task(Power { exponent: 2 }).depends_on(&source);
let plus_one = dag.add_task(AddOffset::new(1)).depends_on(source);
let times_two = dag.add_task(Multiply::new(2)).depends_on(source);
let squared = dag.add_task(Power { exponent: 2 }).depends_on(source);
// Run the DAG
println!("Running fan-out DAG...\n");
+1 -1
View File
@@ -130,7 +130,7 @@ async fn main() {
// Single downstream task consuming all values
let profile = dag
.add_task(BuildProfile)
.depends_on((&name, &age, &city, &active));
.depends_on((name, age, city, active));
// Run the DAG
println!("Running fan-in DAG...\n");
+1 -3
View File
@@ -123,9 +123,7 @@ async fn main() -> DagResult<()> {
let sum3 = dag.add_task(ComputeSum::new(501, 751, WORK_DELAY));
let sum4 = dag.add_task(ComputeSum::new(751, 1001, WORK_DELAY));
let total = dag
.add_task(Aggregate)
.depends_on((&sum1, &sum2, &sum3, &sum4));
let total = dag.add_task(Aggregate).depends_on((sum1, sum2, sum3, sum4));
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap)).await?;
+1 -1
View File
@@ -448,7 +448,7 @@ async fn main() {
let fallback5 = dag
.add_task(FallbackService::new("ServiceCall-5"))
.depends_on(&call5);
.depends_on(call5);
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
+2 -2
View File
@@ -184,8 +184,8 @@ async fn main() -> DagResult<()> {
let source_b = dag.add_task(LoadData::new("B"));
// Transform: Process each source (parallel)
let transform_a = dag.add_task(Transform::new(2)).depends_on(&source_a);
let transform_b = dag.add_task(Transform::new(3)).depends_on(&source_b);
let transform_a = dag.add_task(Transform::new(2)).depends_on(source_a);
let transform_b = dag.add_task(Transform::new(3)).depends_on(source_b);
// Combine: Merge the transformed datasets
let combined = dag
+3 -3
View File
@@ -128,9 +128,9 @@ async fn main() -> DagResult<()> {
let processed3 = dag.add_task(ValidateAndDouble::new(20)); // Valid: 40
// Categorize each result (also parallel, threshold = 25)
let cat1 = dag.add_task(Categorize::new(25)).depends_on(&processed1); // 20 < 25 = "low"
let cat2 = dag.add_task(Categorize::new(25)).depends_on(&processed2); // 0 = "invalid"
let cat3 = dag.add_task(Categorize::new(25)).depends_on(&processed3); // 40 >= 25 = "high"
let cat1 = dag.add_task(Categorize::new(25)).depends_on(processed1); // 20 < 25 = "low"
let cat2 = dag.add_task(Categorize::new(25)).depends_on(processed2); // 0 = "invalid"
let cat3 = dag.add_task(Categorize::new(25)).depends_on(processed3); // 40 >= 25 = "high"
// Combine results
let summary = dag
+9 -7
View File
@@ -36,7 +36,7 @@
//! Validation: Alice is valid (age 30 >= 18)
//! ```
use dagx::{task, DagRunner};
use dagx::{task, DagRunner, TaskHandle};
use futures::FutureExt;
// Custom type - just derive Clone!
@@ -97,15 +97,17 @@ async fn main() {
let dag = DagRunner::new();
// Create a task that produces a custom User type
let user_task = dag.add_task(CreateUser {
name: "Alice".to_string(),
age: 30,
});
let user_task: TaskHandle<_> = dag
.add_task(CreateUser {
name: "Alice".to_string(),
age: 30,
})
.into();
// Two different tasks consume the same custom User type
// dagx automatically handles Arc wrapping, so both tasks get &User
let greeting = dag.add_task(GreetUser).depends_on(&user_task);
let validation = dag.add_task(ValidateUser).depends_on(&user_task);
let greeting = dag.add_task(GreetUser).depends_on(user_task);
let validation = dag.add_task(ValidateUser).depends_on(user_task);
// Execute the DAG
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
+3 -3
View File
@@ -191,15 +191,15 @@ async fn main() {
// Stage 2: Data transformation (with stateful tasks)
let transformed_1 = dag
.add_task(DataTransformer::new(1.5))
.depends_on(&raw_value_1);
.depends_on(raw_value_1);
let transformed_2 = dag
.add_task(DataTransformer::new(2.0))
.depends_on(&raw_value_2);
.depends_on(raw_value_2);
let transformed_3 = dag
.add_task(DataTransformer::new(0.5))
.depends_on(&raw_value_3);
.depends_on(raw_value_3);
// Stage 3: Statistical analysis (parallel operations on transformed data)
let sum = dag
+1 -1
View File
@@ -398,7 +398,7 @@ async fn main() {
// Aggregate traces
let summary = dag
.add_task(TraceAggregator::new(correlation_id))
.depends_on((&inventory, &payment, &shipping));
.depends_on((inventory, payment, shipping));
// Run the DAG
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
+4 -4
View File
@@ -214,7 +214,7 @@ async fn main() {
let input = dag.add_task(StringSource("42"));
let parsed = dag.add_task(ParseInt).depends_on(&input);
let parsed = dag.add_task(ParseInt).depends_on(input);
let validated = dag
.add_task(ValidateRange { min: 0, max: 100 })
@@ -240,7 +240,7 @@ async fn main() {
let input = dag.add_task(StringSource("not a number"));
let parsed = dag.add_task(ParseInt).depends_on(&input);
let parsed = dag.add_task(ParseInt).depends_on(input);
let validated = dag
.add_task(ValidateRange { min: 0, max: 100 })
@@ -266,7 +266,7 @@ async fn main() {
let input = dag.add_task(StringSource("150"));
let parsed = dag.add_task(ParseInt).depends_on(&input);
let parsed = dag.add_task(ParseInt).depends_on(input);
let validated = dag
.add_task(ValidateRange { min: 0, max: 100 })
@@ -292,7 +292,7 @@ async fn main() {
let input = dag.add_task(StringSource("invalid"));
let parsed = dag.add_task(ParseInt).depends_on(&input);
let parsed = dag.add_task(ParseInt).depends_on(input);
let with_fallback = dag
.add_task(WithFallback { fallback: 0 })
+6 -6
View File
@@ -192,7 +192,7 @@ async fn main() {
// Verify a few results
let sum: i32 = tasks
.iter()
.into_iter()
.take(10)
.map(|t| dag.get(t).unwrap())
.fold(0i32, |acc, x| acc.wrapping_add(x));
@@ -208,7 +208,7 @@ async fn main() {
// Create a deep chain by combining adjacent task pairs
let tasks: Vec<_> = (0..100)
.map(|i| dag.add_task(Compute { value: i }))
.map(|i| dag.add_task(Compute { value: i }).into())
.collect();
let mut chain_tasks = vec![];
for i in 0..99 {
@@ -246,7 +246,7 @@ async fn main() {
// Process all levels reducing by combining pairs
let mut level = 0;
let mut layer: Vec<dagx::TaskHandle<i32>> =
current_level.iter().map(|b| b.into()).collect();
current_level.into_iter().map(|b| b.into()).collect();
while layer.len() > 1 {
level += 1;
@@ -291,7 +291,7 @@ async fn main() {
// Layer 1: 50 source tasks
let sources: Vec<_> = (0..50)
.map(|i| dag.add_task(Compute { value: i }))
.map(|i| dag.add_task(Compute { value: i }).into())
.collect();
// Layer 2: 50 tasks, each depends on all sources
@@ -361,11 +361,11 @@ async fn main() {
// Verify results are accessible
let i32_sum: i32 = small_tasks
.iter()
.into_iter()
.map(|t| dag.get(t).unwrap())
.fold(0i32, |acc, x| acc.wrapping_add(x));
let string_count = string_tasks
.iter()
.into_iter()
.map(|t| dag.get(t).unwrap().len())
.sum::<usize>();
+1 -1
View File
@@ -93,7 +93,7 @@ async fn main() {
let elapsed = start.elapsed();
// Verify all tasks completed correctly
for (i, task) in tasks.iter().enumerate() {
for (i, task) in tasks.into_iter().enumerate() {
assert_eq!(dag.get(task).unwrap(), i as u32);
}
+10 -8
View File
@@ -98,7 +98,7 @@
//! === Timeout Example Complete ===
//! ```
use dagx::{task, DagRunner};
use dagx::{task, DagRunner, TaskHandle};
use futures::FutureExt;
use tokio::time::{sleep, timeout, Duration};
@@ -196,7 +196,7 @@ async fn main() {
timeout_ms: 200,
});
let processed = dag.add_task(ProcessResult).depends_on(&timed);
let processed = dag.add_task(ProcessResult).depends_on(timed);
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
@@ -241,13 +241,15 @@ async fn main() {
{
let dag = DagRunner::new();
let source = dag.add_task(TaskWithTimeout {
work_duration_ms: 500,
timeout_ms: 100,
});
let source: TaskHandle<_> = dag
.add_task(TaskWithTimeout {
work_duration_ms: 500,
timeout_ms: 100,
})
.into();
let processed1 = dag.add_task(ProcessResult).depends_on(&source);
let processed2 = dag.add_task(ProcessResult).depends_on(&source);
let processed1 = dag.add_task(ProcessResult).depends_on(source);
let processed2 = dag.add_task(ProcessResult).depends_on(source);
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
.await
+6 -6
View File
@@ -87,7 +87,7 @@ async fn main() {
let dag = DagRunner::new();
// Layer 0: Source
let source = dag.add_task(Value(10));
let source = dag.add_task(Value(10)).into();
// Layer 1: Two parallel computations
let left = dag.add_task(Add).depends_on((&source, &source));
@@ -112,7 +112,7 @@ async fn main() {
// Create a linear chain - each layer has only one task
// This should trigger inline execution optimization
let a = dag.add_task(Value(1));
let a = dag.add_task(Value(1)).into();
let b = dag.add_task(Add).depends_on((&a, &a));
let c = dag.add_task(Multiply).depends_on((&b, &b));
let d = dag.add_task(Add).depends_on((&c, &c));
@@ -132,10 +132,10 @@ async fn main() {
let dag = DagRunner::new();
// Layer 0: Multiple sources
let v1 = dag.add_task(Value(1));
let v2 = dag.add_task(Value(2));
let v3 = dag.add_task(Value(3));
let v4 = dag.add_task(Value(4));
let v1 = dag.add_task(Value(1)).into();
let v2 = dag.add_task(Value(2)).into();
let v3 = dag.add_task(Value(3)).into();
let v4 = dag.add_task(Value(4)).into();
// Layer 1: Multiple parallel computations
let sum12 = dag.add_task(Add).depends_on((&v1, &v2));