Skip to content

Commit

Permalink
interrupt support for pretty plumbing
Browse files Browse the repository at this point in the history
  • Loading branch information
Byron committed Aug 6, 2020
1 parent 2106c64 commit bca7ce2
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 18 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ small = ["lean-cli"]
fast = ["git-features/parallel", "git-features/fast-sha1"]
pretty-cli = ["structopt",
"git-features/progress-prodash",
"git-features/interuptible",
"gitoxide-core/serde1",
"prodash/log-renderer",
"prodash-tui-renderer",
"prodash-line-renderer",
"ctrlc",
"prodash/localtime",
"env_logger",
"futures-lite"]
Expand All @@ -68,7 +68,6 @@ atty = { version = "0.2.14", optional = true, default-features = false }
env_logger = { version = "0.7.1", optional = true, default-features = false, features = ["humantime", "termcolor", "atty"] }
crosstermion = { version = "0.3.0", optional = true, default-features = false }
futures-lite = { version = "0.1.10", optional = true, default-features = false }
ctrlc = { version = "3.1.4", optional = true, default-features = false, features = ['termination'] }

[profile.release]
overflow-checks = false
Expand Down
8 changes: 6 additions & 2 deletions git-features/src/interuptible.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,20 @@ mod _impl {
R: io::Read,
{
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
if is_interrupted() {
if is_interupted() {
return Err(io::Error::new(io::ErrorKind::Other, "interrupted by user"));
}
self.inner.read(buf)
}
}

pub fn is_interrupted() -> bool {
pub fn is_interupted() -> bool {
IS_INTERRUPTED.load(Ordering::Relaxed)
}

pub fn interupt() {
IS_INTERRUPTED.store(true, Ordering::Relaxed);
}
}

#[cfg(not(feature = "interuptible"))]
Expand Down
6 changes: 6 additions & 0 deletions gitoxide-core/src/pack/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ pub enum IterationMode {
Restore,
}

impl IterationMode {
pub fn variants() -> &'static [&'static str] {
&["as-is", "verify", "restore"]
}
}

impl Default for IterationMode {
fn default() -> Self {
IterationMode::Verify
Expand Down
2 changes: 1 addition & 1 deletion src/plumbing/lean.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ mod options {
/// Valid values are
/// - as-is
/// * do not do anything and expect the pack file to be valid as per the trailing hash
/// - verifyhash
/// - verify
/// * the input ourselves and validate that it matches with the hash provided in the pack
/// - restore
/// * hash the input ourselves and ignore failing entries, instead finish the pack with the hash we computed
Expand Down
106 changes: 95 additions & 11 deletions src/plumbing/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,54 @@ mod options {

#[derive(Debug, StructOpt)]
pub enum Subcommands {
/// Create an index from a packfile.
///
/// This command can also be used to stream packs to standard input or to repair partial packs.
#[structopt(setting = AppSettings::ColoredHelp)]
IndexFromPack {
/// Display verbose messages and progress information
#[structopt(long, short = "v")]
verbose: bool,

/// Bring up a terminal user interface displaying progress visually
#[structopt(long, conflicts_with("verbose"))]
progress: bool,

/// The progress TUI will stay up even though the work is already completed.
///
/// Use this to be able to read progress messages or additional information visible in the TUI log pane.
#[structopt(long, conflicts_with("verbose"), requires("progress"))]
progress_keep_open: bool,

/// Specify how to iterate the pack, defaults to 'verify'
///
/// Valid values are
/// - as-is
/// * do not do anything and expect the pack file to be valid as per the trailing hash
/// - verify
/// * the input ourselves and validate that it matches with the hash provided in the pack
/// - restore
/// * hash the input ourselves and ignore failing entries, instead finish the pack with the hash we computed
#[structopt(
long,
short = "i",
default_value = "verify",
possible_values(core::pack::index::IterationMode::variants())
)]
iteration_mode: core::pack::index::IterationMode,

/// Path to the pack file to read (with .pack extension).
///
/// If unset, the pack file is expected on stdin.
#[structopt(long, short = "p")]
pack_path: Option<PathBuf>,

/// The folder into which to place the pack and the generated index file
///
/// If unset, only informational output will be provided to standard output.
#[structopt(parse(from_os_str))]
directory: Option<PathBuf>,
},
/// Verify the integrity of a pack or index file
#[structopt(setting = AppSettings::ColoredHelp)]
PackExplode {
Expand Down Expand Up @@ -150,6 +198,7 @@ fn prepare_and_run<T: Send + 'static>(
+ 'static,
) -> Result<T> {
super::init_env_logger(false);
use git_features::interuptible::{interupt, is_interupted};
match (verbose, progress) {
(false, false) => run(None, &mut stdout(), &mut stderr()),
(true, false) => {
Expand All @@ -161,12 +210,16 @@ fn prepare_and_run<T: Send + 'static>(
let sub_progress = progress.add_child(name);
let (tx, rx) = std::sync::mpsc::sync_channel::<Event<T>>(1);
let ui_handle = crate::shared::setup_line_renderer(progress, 2, true);
ctrlc::set_handler({
std::thread::spawn({
let tx = tx.clone();
move || {
tx.send(Event::UIDone).ok();
move || loop {
std::thread::sleep(std::time::Duration::from_millis(500));
if is_interupted() {
tx.send(Event::UIDone).ok();
break;
}
}
})?;
});
std::thread::spawn(move || {
let res = run(Some(sub_progress), &mut stdout(), &mut stderr());
tx.send(Event::ComputationDone(res)).ok();
Expand Down Expand Up @@ -216,13 +269,20 @@ fn prepare_and_run<T: Send + 'static>(
let res = run(Some(sub_progress), &mut out, &mut err);
tx.send(Event::ComputationDone(res, out, err)).ok();
});
match rx.recv()? {
Event::UIDone => Err(anyhow!("Operation cancelled by user")),
Event::ComputationDone(res, out, err) => {
ui_handle.join().ok();
stdout().write_all(&out)?;
stderr().write_all(&err)?;
res
loop {
match rx.recv()? {
Event::UIDone => {
// We don't know why the UI is done, usually it's the user aborting.
// We need the computation to stop as well so let's wait for that to happen
interupt();
continue;
}
Event::ComputationDone(res, out, err) => {
ui_handle.join().ok();
stdout().write_all(&out)?;
stderr().write_all(&err)?;
break res;
}
}
}
}
Expand All @@ -233,6 +293,30 @@ pub fn main() -> Result<()> {
let args = Args::from_args();
let thread_limit = args.threads;
match args.cmd {
Subcommands::IndexFromPack {
verbose,
iteration_mode,
pack_path,
directory,
progress,
progress_keep_open,
} => prepare_and_run(
"index-from-pack",
verbose,
progress,
progress_keep_open,
move |progress, _out, _err| {
core::pack::index::from_pack(
pack_path,
directory,
git_features::progress::DoOrDiscard::from(progress),
core::pack::index::Context {
thread_limit,
iteration_mode: iteration_mode,
},
)
},
),
Subcommands::PackExplode {
verbose,
check,
Expand Down
5 changes: 4 additions & 1 deletion tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@
* [ ] handle ctrl+c similarly to the pretty version to prevent leakage (or find a way to use
system temp files)
* [x] for lean mode
* [ ] for pretty mode
* [x] for pretty mode
* [ ] allow interrupting the resolution phase too
* [ ] fix typo :D - thanks IJ for confusing me
* [ ] move --verbose, --progress and --progress-keep-open to the top-level
* [ ] unit tests for bundle index write
* [ ] journey test for command-line capabilities
* [ ] nicer errors with descriptive messages
Expand Down

0 comments on commit bca7ce2

Please sign in to comment.