Skip to content

Commit 3e487b3

Browse files
authored
feat(runner): implement parallel execution (#34)
Replace sequential fallback with true parallel execution using std::thread::scope. Parallel blocks now run all steps concurrently and collect any errors.
1 parent 968bb51 commit 3e487b3

File tree

1 file changed

+36
-7
lines changed

1 file changed

+36
-7
lines changed

src/runner.rs

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,43 @@ fn execute_step(
8484
) -> Result<()> {
8585
match step {
8686
Step::Simple(step_def) => execute_step_def(step_def, default_dir, default_env, config),
87-
Step::Parallel { parallel } => {
88-
// TODO: Implement parallel execution with tokio
89-
// For now, run sequentially
90-
for step_def in parallel {
91-
execute_step_def(step_def, default_dir, default_env, config)?;
92-
}
93-
Ok(())
87+
Step::Parallel { parallel } => execute_parallel(parallel, default_dir, default_env, config),
88+
}
89+
}
90+
91+
/// Execute steps in parallel using scoped threads
92+
fn execute_parallel(
93+
steps: &[StepDef],
94+
default_dir: &Path,
95+
default_env: &HashMap<String, String>,
96+
config: &Config,
97+
) -> Result<()> {
98+
use std::sync::Mutex;
99+
use std::thread;
100+
101+
let errors: Mutex<Vec<anyhow::Error>> = Mutex::new(Vec::new());
102+
103+
thread::scope(|s| {
104+
for step_def in steps {
105+
s.spawn(|| {
106+
if let Err(e) = execute_step_def(step_def, default_dir, default_env, config) {
107+
errors.lock().unwrap().push(e);
108+
}
109+
});
94110
}
111+
});
112+
113+
let errors = errors.into_inner().unwrap();
114+
if errors.is_empty() {
115+
Ok(())
116+
} else {
117+
// Combine all errors into one message
118+
let error_messages: Vec<String> = errors.iter().map(|e| format!(" - {}", e)).collect();
119+
anyhow::bail!(
120+
"Parallel execution failed with {} error(s):\n{}",
121+
error_messages.len(),
122+
error_messages.join("\n")
123+
)
95124
}
96125
}
97126

0 commit comments

Comments
 (0)