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).
This commit is contained in:
Thomas Otto 2021-12-22 23:19:04 +01:00 committed by Dan Davison
parent 15d7bb8cf0
commit 3a3f597704
5 changed files with 60 additions and 31 deletions

View File

@ -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();

View File

@ -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,
]

View File

@ -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")

View File

@ -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(|| {})

View File

@ -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 CACHED_CALLING_PROCESS: Option<CallingProcess> = determine_calling_process();
static ref CALLER: Arc<(Mutex<CallingProcess>, Condvar)> =
Arc::new((Mutex::new(CallingProcess::Pending), Condvar::new()));
}
fn determine_calling_process() -> Option<CallingProcess> {
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() -> 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