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
//! What:
//! The removal of the entries a snapshot's leave-out moved into its trash, side by side.
//! Why:
//! The entries are separate directories that share only the held trash handle, each opened by its own name and removed only
//! when it is the recorded object, so they are independent items: a reason for one entry never stops the others. They run
//! within the budget the host allows (`parallel_budget.rs`), which inside the commit service's control group is the two
//! threads and the memory that unit is given, and the reasons come back in entry order (`parallel_runner.rs`).
//!
//! In TS you'd write (pseudocode):
//! ```ts
//! const left = await removeEntries({ inside, trash, manifest, host, remove }); // one reason per entry that stays
//! ```
use super::parallel_budget::{BudgetRequest, HostResources, worker_budget};
use super::parallel_runner::{Event, OrderedRun, run_ordered};
use super::worktree_trash_manifest::{TrashManifest, entry_name};
use super::worktree_trash_remove::Removal;
use std::fs::File;
use std::path::Path;
/// The bytes the removal of one trash entry holds at its peak, estimated for a flat directory of a million names: the listing of
/// a directory is held while its entries are removed, at most about sixty-four bytes a name (the name, its allocation and the
/// directory flag), so about 64 MiB.
const ENTRY_MEMORY: u64 = 64 << 20;
/// The removal of one entry beneath the trash handle, replaceable so a control can watch or delay it.
pub type RemoveOne<'a> = dyn Fn(&Path, &File, &std::ffi::OsStr, Option<super::fs_beneath::ObjectId>) -> Result<super::worktree_trash_remove::Removal, String> + Sync + 'a;
/// What:
/// What the removal of a trash's entries is told.
/// Why:
/// One value names the inputs: where the trash is, its manifest, the host the budget is derived from, and the removal of one entry.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// type EntryRemoval = { inside: string; trash: Dir; manifest: TrashManifest; host: HostResources; remove: RemoveOne };
/// ```
pub struct EntryRemoval<'a> {
/// The path of the trash directory, for messages.
pub inside: &'a Path,
/// The trash directory, held open; every entry is opened by name below it.
pub trash: &'a File,
/// What the leave-out recorded.
pub manifest: &'a TrashManifest,
/// The CPUs and memory the budget is derived from.
pub host: &'a HostResources,
/// The removal of one entry.
pub remove: &'a RemoveOne<'a>,
}
/// What:
/// Remove every entry the manifest records, side by side, and return the reason each entry that stays gives, in entry order.
/// Why:
/// The entries are separate directories moved into the trash, each removed only when it is the recorded object, and share no
/// state but the held trash handle, which opens each by its own name. A reason for one never stops the others.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// const left = await removeEntries({ inside, trash, manifest, host, remove }); // one reason per entry that stays
/// ```
pub fn remove_entries(request: &EntryRemoval<'_>) -> Vec<String> {
let EntryRemoval { inside, trash, manifest, host, remove } = *request;
let indices: Vec<usize> = (0..manifest.entries.len()).collect();
let lanes: Vec<Vec<usize>> = indices.iter().map(|index: &usize| { return vec![*index]; }).collect();
let workers: usize = worker_budget(&BudgetRequest { items: indices.len(), per_item_memory: ENTRY_MEMORY, ceiling: None, cap: None, host });
let mut left: Vec<String> = Vec::new();
run_ordered(OrderedRun {
items: indices.as_slice(),
lanes: lanes.as_slice(),
workers,
work: &|_slot: usize, index: &usize, _say: &mut dyn FnMut(&str)| { return remove(inside, trash, std::ffi::OsStr::new(entry_name(*index).as_str()), Some(manifest.entries[*index])); },
on_event: &mut |event: Event<'_, Result<Removal, String>>| {
if let Event::End(_slot, Err(reason)) = event {
left.push(reason);
}
},
});
return left;
}
/// The controls for the entries of a trash removed side by side stay out of the release executable.
#[cfg(test)]
#[path = "worktree_trash_entries_tests.rs"]
mod tests;