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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use {
crate::{
git,
git_status::*,
task_sync::{
Dam,
ComputationResult,
Computation,
},
},
git2::{
Repository,
},
crossbeam::channel::bounded,
std::{
collections::HashMap,
path::{Path, PathBuf},
sync::Mutex,
},
};
fn compute_tree_status(root_path: &Path) -> ComputationResult<TreeGitStatus> {
match Repository::open(root_path) {
Ok(git_repo) => {
let tree_git_status = time!(
Debug,
"compute_tree_status",
TreeGitStatus::from(&git_repo),
);
match tree_git_status {
Some(gs) => ComputationResult::Done(gs),
None => ComputationResult::None,
}
}
Err(e) => {
debug!("failed to discover repo: {:?}", e);
ComputationResult::None
}
}
}
lazy_static! {
// the key is the path of the repository
static ref TS_CACHE_MX: Mutex<HashMap<PathBuf, Computation<TreeGitStatus>>> =
Mutex::new(HashMap::new());
}
/// try to get the result of the computation of the tree git status.
/// This may be immediate if a previous computation was finished.
/// This may wait for the result of a new computation or of a previously
/// launched one.
/// In any case:
/// - this function returns as soon as the dam asks for it (ie when there's an event)
/// - computations are never dropped unless the program ends: they continue in background
/// and the result may be available for following queries
pub fn get_tree_status(root_path: &Path, dam: &mut Dam) -> ComputationResult<TreeGitStatus> {
match git::closest_repo_dir(root_path) {
None => ComputationResult::None,
Some(repo_path) => {
let comp = TS_CACHE_MX.lock().unwrap().get(&repo_path).map(|c| (*c).clone());
match comp {
Some(Computation::Finished(comp_res)) => {
// already computed
comp_res.clone()
}
Some(Computation::InProgress(comp_receiver)) => {
// computation in progress
// We do a select! to wait for either the dam
// or the receiver
debug!("start select on in progress computation");
dam.select(comp_receiver.clone())
}
None => {
// not yet started. We launch the computation and store
// the receiver immediately.
// We use the dam to return from this function when
// needed (while letting the underlying thread finish
// the job)
//
// note: must also update the TS_CACHE entry at end
let (s, r) = bounded(1);
TS_CACHE_MX.lock().unwrap().insert(
repo_path.clone(),
Computation::InProgress(r),
);
dam.try_compute(move||{
debug!("start computation");
let comp_res = compute_tree_status(&repo_path);
debug!("comp finished - try inserting");
TS_CACHE_MX.lock().unwrap().insert(
repo_path.clone(),
Computation::Finished(comp_res.clone()),
);
debug!("result stored in cache, now sending to receiver");
if let Err(e) = s.send(comp_res.clone()) {
debug!("error while sending comp result: {:?}", e);
}
debug!("result sent to receiver - now returning");
comp_res
})
}
}
}
}
}
/// clear the finished or in progress computation.
/// Limit: we may receive in cache the result of a computation
/// which started before the clear (if this is a problem we could
/// store a cleaning counter alongside the cache to prevent insertions)
pub fn clear_cache() {
let mut ts_cache = TS_CACHE_MX.lock().unwrap();
ts_cache.clear();
}