Skip to main content

broot/git/
status_computer.rs

1use {
2    super::TreeGitStatus,
3    crate::{
4        git,
5        task_sync::{
6            Computation,
7            ComputationResult,
8            Dam,
9        },
10    },
11    git2::Repository,
12    once_cell::sync::Lazy,
13    rustc_hash::FxHashMap,
14    std::{
15        path::{
16            Path,
17            PathBuf,
18        },
19        sync::Mutex,
20    },
21    termimad::crossbeam::channel::bounded,
22};
23
24fn compute_tree_status(root_path: &Path) -> ComputationResult<TreeGitStatus> {
25    match Repository::open(root_path) {
26        Ok(git_repo) => {
27            let tree_git_status = time!(TreeGitStatus::from(&git_repo),);
28            match tree_git_status {
29                Some(gs) => ComputationResult::Done(gs),
30                None => ComputationResult::None,
31            }
32        }
33        Err(e) => {
34            debug!("failed to discover repo: {:?}", e);
35            ComputationResult::None
36        }
37    }
38}
39
40// the key is the path of the repository
41static TS_CACHE_MX: Lazy<Mutex<FxHashMap<PathBuf, Computation<TreeGitStatus>>>> =
42    Lazy::new(|| Mutex::new(FxHashMap::default()));
43
44/// try to get the result of the computation of the tree git status.
45/// This may be immediate if a previous computation was finished.
46/// This may wait for the result of a new computation or of a previously
47/// launched one.
48/// In any case:
49/// - this function returns as soon as the dam asks for it (ie when there's an event)
50/// - computations are never dropped unless the program ends: they continue in background
51///   and the result may be available for following queries
52pub fn get_tree_status(
53    root_path: &Path,
54    dam: &mut Dam,
55) -> ComputationResult<TreeGitStatus> {
56    match git::closest_repo_dir(root_path) {
57        None => ComputationResult::None,
58        Some(repo_path) => {
59            let comp = TS_CACHE_MX
60                .lock()
61                .unwrap()
62                .get(&repo_path)
63                .map(|c| (*c).clone());
64            match comp {
65                Some(Computation::Finished(comp_res)) => {
66                    // already computed
67                    comp_res
68                }
69                Some(Computation::InProgress(comp_receiver)) => {
70                    // computation in progress
71                    // We do a select! to wait for either the dam
72                    // or the receiver
73                    debug!("start select on in progress computation");
74                    dam.select(comp_receiver)
75                }
76                None => {
77                    // not yet started. We launch the computation and store
78                    // the receiver immediately.
79                    // We use the dam to return from this function when
80                    // needed (while letting the underlying thread finish
81                    // the job)
82                    //
83                    // note: must also update the TS_CACHE entry at end
84                    let (s, r) = bounded(1);
85                    TS_CACHE_MX
86                        .lock()
87                        .unwrap()
88                        .insert(repo_path.clone(), Computation::InProgress(r));
89                    dam.try_compute(move || {
90                        let comp_res = compute_tree_status(&repo_path);
91                        TS_CACHE_MX
92                            .lock()
93                            .unwrap()
94                            .insert(repo_path.clone(), Computation::Finished(comp_res.clone()));
95                        if let Err(e) = s.send(comp_res.clone()) {
96                            debug!("error while sending comp result: {:?}", e);
97                        }
98                        comp_res
99                    })
100                }
101            }
102        }
103    }
104}
105
106/// clear the finished or in progress computation.
107/// Limit: we may receive in cache the result of a computation
108/// which started before the clear (if this is a problem we could
109/// store a cleaning counter alongside the cache to prevent insertions)
110pub fn clear_status_computer_cache() {
111    let mut ts_cache = TS_CACHE_MX.lock().unwrap();
112    ts_cache.clear();
113}