summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Otto <th1000s@posteo.net>2021-12-22 23:19:04 +0100
committerDan Davison <dandavison7@gmail.com>2022-01-03 17:28:27 -0500
commit3a3f59770416bad40e9f4ecc6cc8b260e3a4537d (patch)
tree7b3aff6e4968fe0cf6a0487ba2c7564d7f9a0467
parent15d7bb8cf0b3973719a97fa206ac94e5b46e0ede (diff)
Move parent process query into a thread
Start the query even before determining if information about the parent process is required (which are most invocations anyhow).
-rw-r--r--src/handlers/git_show_file.rs4
-rw-r--r--src/handlers/grep.rs11
-rw-r--r--src/handlers/hunk.rs12
-rw-r--r--src/main.rs6
-rw-r--r--src/utils/process.rs56
5 files changed, 59 insertions, 30 deletions
diff --git a/src/handlers/git_show_file.rs b/src/handlers/git_show_file.rs
index d5eeb512..eb261203 100644
--- a/src/handlers/git_show_file.rs
+++ b/src/handlers/git_show_file.rs
@@ -9,9 +9,7 @@ impl<'a> StateMachine<'a> {
self.painter.emit()?;
let mut handled_line = false;
if matches!(self.state, State::Unknown) {
- if let Some(process::CallingProcess::GitShow(_, extension)) =
- process::calling_process().as_deref()
- {
+ if let process::CallingProcess::GitShow(_, extension) = &*process::calling_process() {
self.state = State::GitShowFile;
self.painter.set_syntax(extension.as_deref());
self.painter.set_highlighter();
diff --git a/src/handlers/grep.rs b/src/handlers/grep.rs
index 32e1463c..c3d6da6c 100644
--- a/src/handlers/grep.rs
+++ b/src/handlers/grep.rs
@@ -246,8 +246,8 @@ fn get_code_style_sections<'b>(
}
fn make_output_config() -> GrepOutputConfig {
- match process::calling_process().as_deref() {
- Some(process::CallingProcess::GitGrep(command_line))
+ match &*process::calling_process() {
+ process::CallingProcess::GitGrep(command_line)
if command_line.short_options.contains("-W")
|| command_line.long_options.contains("--function-context") =>
{
@@ -265,7 +265,7 @@ fn make_output_config() -> GrepOutputConfig {
pad_line_number: true,
}
}
- Some(process::CallingProcess::GitGrep(command_line))
+ process::CallingProcess::GitGrep(command_line)
if command_line.short_options.contains("-p")
|| command_line.long_options.contains("--show-function") =>
{
@@ -380,9 +380,8 @@ pub fn parse_grep_line(line: &str) -> Option<GrepLine> {
if line.starts_with('{') {
ripgrep_json::parse_line(line)
} else {
- match process::calling_process().as_deref() {
- Some(process::CallingProcess::GitGrep(_))
- | Some(process::CallingProcess::OtherGrep) => [
+ match &*process::calling_process() {
+ process::CallingProcess::GitGrep(_) | process::CallingProcess::OtherGrep => [
&*GREP_LINE_REGEX_ASSUMING_FILE_EXTENSION,
&*GREP_LINE_REGEX_ASSUMING_NO_INTERNAL_SEPARATOR_CHARS,
]
diff --git a/src/handlers/hunk.rs b/src/handlers/hunk.rs
index 2acf2eae..b541d326 100644
--- a/src/handlers/hunk.rs
+++ b/src/handlers/hunk.rs
@@ -27,13 +27,11 @@ lazy_static! {
}
fn compute_is_word_diff() -> bool {
- match process::calling_process().as_deref() {
- Some(
- CallingProcess::GitDiff(cmd_line)
- | CallingProcess::GitShow(cmd_line, _)
- | CallingProcess::GitLog(cmd_line)
- | CallingProcess::GitReflog(cmd_line),
- ) => {
+ match &*process::calling_process() {
+ CallingProcess::GitDiff(cmd_line)
+ | CallingProcess::GitShow(cmd_line, _)
+ | CallingProcess::GitLog(cmd_line)
+ | CallingProcess::GitReflog(cmd_line) => {
cmd_line.long_options.contains("--word-diff")
|| cmd_line.long_options.contains("--word-diff-regex")
|| cmd_line.long_options.contains("--color-words")
diff --git a/src/main.rs b/src/main.rs
index 5e6eab5e..1de9375b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -65,6 +65,12 @@ pub mod errors {
#[cfg(not(tarpaulin_include))]
fn main() -> std::io::Result<()> {
+ // Do this first because both parsing all the input in `run_app()` and
+ // listing all processes takes about 50ms on Linux.
+ // It also improves the chance that the calling process is still around when
+ // input is piped into delta (e.g. `git show --word-diff=color | delta`).
+ utils::process::start_determining_calling_process_in_thread();
+
// Ignore ctrl-c (SIGINT) to avoid leaving an orphaned pager process.
// See https://github.com/dandavison/delta/issues/681
ctrlc::set_handler(|| {})
diff --git a/src/utils/process.rs b/src/utils/process.rs
index ed281d8d..26d68d68 100644
--- a/src/utils/process.rs
+++ b/src/utils/process.rs
@@ -1,9 +1,9 @@
-use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
use std::path::Path;
-use sysinfo::{Pid, Process, ProcessExt, ProcessRefreshKind, SystemExt};
+use std::sync::{Arc, Condvar, Mutex, MutexGuard};
use lazy_static::lazy_static;
+use sysinfo::{Pid, Process, ProcessExt, ProcessRefreshKind, SystemExt};
#[derive(Clone, Debug, PartialEq)]
pub enum CallingProcess {
@@ -13,6 +13,8 @@ pub enum CallingProcess {
GitReflog(CommandLine),
GitGrep(CommandLine),
OtherGrep, // rg, grep, ag, ack, etc
+ None, // no matching process could be found
+ Pending, // calling process is currently being determined
}
// TODO: Git blame is currently handled differently
@@ -23,23 +25,49 @@ pub struct CommandLine {
last_arg: Option<String>,
}
-pub fn calling_process() -> Option<Cow<'static, CallingProcess>> {
- #[cfg(not(test))]
- {
- CACHED_CALLING_PROCESS.as_ref().map(Cow::Borrowed)
- }
- #[cfg(test)]
- {
- determine_calling_process().map(Cow::Owned)
- }
+lazy_static! {
+ static ref CALLER: Arc<(Mutex<CallingProcess>, Condvar)> =
+ Arc::new((Mutex::new(CallingProcess::Pending), Condvar::new()));
}
-lazy_static! {
- static ref CACHED_CALLING_PROCESS: Option<CallingProcess> = determine_calling_process();
+pub fn start_determining_calling_process_in_thread() {
+ // The handle is neither kept nor returned nor joined but dropped, so the main
+ // thread can exit early if it does not need to know its parent process.
+ std::thread::Builder::new()
+ .name("find_calling_process".into())
+ .spawn(move || {
+ let calling_process = determine_calling_process();
+
+ let (caller_mutex, determine_done) = &**CALLER;
+
+ let mut caller = caller_mutex.lock().unwrap();
+ *caller = calling_process;
+ determine_done.notify_all();
+ })
+ .unwrap();
+}
+
+#[cfg(not(test))]
+pub fn calling_process() -> MutexGuard<'static, CallingProcess> {
+ let (caller_mutex, determine_done) = &**CALLER;
+
+ determine_done
+ .wait_while(caller_mutex.lock().unwrap(), |caller| {
+ *caller == CallingProcess::Pending
+ })
+ .unwrap()
+}
+
+// The return value is duck-typed to work in place of a MutexGuard when testing.
+#[cfg(test)]
+pub fn calling_process() -> Box<CallingProcess> {
+ type _UnusedImport = MutexGuard<'static, i8>;
+ Box::new(determine_calling_process())
}
-fn determine_calling_process() -> Option<CallingProcess> {
+fn determine_calling_process() -> CallingProcess {
calling_process_cmdline(ProcInfo::new(), describe_calling_process)
+ .unwrap_or(CallingProcess::None)
}
// Return value of `extract_args(args: &[String]) -> ProcessArgs<T>` function which is