summaryrefslogtreecommitdiffstats
path: root/src/executor.rs
blob: 814aa79a41104022812147d260312caac7851def (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
use std::collections::{HashMap, HashSet};

use crate::{runner, types::*};

#[derive(Debug)]
pub struct Executor<'a> {
	tasks: &'a TaskMap,
	tasks_blocked: HashSet<TaskRef>,
	tasks_runnable: Vec<TaskRef>,
	tasks_done: HashSet<TaskRef>,
	rdeps: HashMap<TaskRef, Vec<TaskRef>>,
}

impl<'a> Executor<'a> {
	pub fn new(tasks: &'a TaskMap, taskset: HashSet<TaskRef>) -> Self {
		let mut exc = Executor {
			tasks,
			tasks_blocked: HashSet::new(),
			tasks_runnable: Vec::new(),
			tasks_done: HashSet::new(),
			rdeps: HashMap::new(),
		};

		for task in taskset {
			let task_def = tasks.get(&task).expect("Invalid TaskRef");
			if task_def.depends.is_empty() {
				exc.tasks_runnable.push(task);
			} else {
				for dep in &task_def.depends {
					let rdep = exc.rdeps.entry(dep.clone()).or_default();
					rdep.push(task.clone());
				}

				exc.tasks_blocked.insert(task);
			}
		}

		exc
	}

	fn deps_satisfied(&self, task: &TaskRef) -> bool {
		let task_def = self.tasks.get(task).expect("Invalid TaskRef");

		task_def
			.depends
			.iter()
			.all(|dep| self.tasks_done.contains(dep))
	}

	fn run_one(&mut self, runner: &impl runner::Runner) -> runner::Result<()> {
		let task = self.tasks_runnable.pop().expect("No runnable tasks left");

		runner.run(&task)?;

		let rdeps = self.rdeps.get(&task);

		self.tasks_done.insert(task);

		for rdep in rdeps.unwrap_or(&Vec::new()) {
			if !self.tasks_blocked.contains(rdep) {
				continue;
			}
			if self.deps_satisfied(rdep) {
				self.tasks_blocked.remove(rdep);
				self.tasks_runnable.push(rdep.clone());
			}
		}

		Ok(())
	}

	pub fn run(&mut self) -> runner::Result<()> {
		let runner = runner::runc::RuncRunner::new(self.tasks);
		while !self.tasks_runnable.is_empty() {
			self.run_one(&runner)?;
		}

		assert!(self.tasks_blocked.is_empty(), "No runnable tasks left");
		Ok(())
	}
}