view rust/chg/src/clientext.rs @ 48178:f12a19d03d2c

fix: reduce number of tool executions By grouping together (path, ctx) pairs according to the inputs they would provide to fixer tools, we can deduplicate executions of fixer tools to significantly reduce the amount of time spent running slow tools. This change does not handle clean files in the working copy, which could still be deduplicated against the files in the checked out commit. It's a little harder to do that because the filerev is not available in the workingfilectx (and it doesn't exist for added files). Anecdotally, this change makes some real uses cases at Google 10x faster. I think we were originally hesitant to do this because the benefits weren't obvious, and implementing it efficiently is kind of tricky. If we simply memoized the formatter execution function, we would be keeping tons of file content in memory. Also included is a regression test for a corner case that I broke with my first attempt at optimizing this code. Differential Revision: https://phab.mercurial-scm.org/D11280
author Danny Hooper <hooper@google.com>
date Thu, 02 Sep 2021 14:08:45 -0700
parents 426294d06ddc
children
line wrap: on
line source

// Copyright 2018 Yuya Nishihara <yuya@tcha.org>
//
// This software may be used and distributed according to the terms of the
// GNU General Public License version 2 or any later version.

//! cHg extensions to command server client.

use bytes::{BufMut, BytesMut};
use std::ffi::OsStr;
use std::io;
use std::mem;
use std::os::unix::ffi::OsStrExt;
use std::os::unix::io::AsRawFd;
use std::path::Path;
use tokio_hglib::UnixClient;

use crate::attachio;
use crate::message::{self, Instruction, ServerSpec};
use crate::runcommand;
use crate::uihandler::SystemHandler;

/// Command-server client that also supports cHg extensions.
pub struct ChgClient {
    client: UnixClient,
}

impl ChgClient {
    /// Connects to a command server listening at the specified socket path.
    pub async fn connect(path: impl AsRef<Path>) -> io::Result<Self> {
        let client = UnixClient::connect(path).await?;
        Ok(ChgClient { client })
    }

    /// Server capabilities, encoding, etc.
    pub fn server_spec(&self) -> &ServerSpec {
        self.client.server_spec()
    }

    /// Attaches the client file descriptors to the server.
    pub async fn attach_io(
        &mut self,
        stdin: &impl AsRawFd,
        stdout: &impl AsRawFd,
        stderr: &impl AsRawFd,
    ) -> io::Result<()> {
        attachio::attach_io(
            self.client.borrow_protocol_mut(),
            stdin,
            stdout,
            stderr,
        )
        .await
    }

    /// Changes the working directory of the server.
    pub async fn set_current_dir(
        &mut self,
        dir: impl AsRef<Path>,
    ) -> io::Result<()> {
        let dir_bytes = dir.as_ref().as_os_str().as_bytes().to_owned();
        self.client
            .borrow_protocol_mut()
            .send_command_with_args("chdir", dir_bytes)
            .await
    }

    /// Updates the environment variables of the server.
    pub async fn set_env_vars_os(
        &mut self,
        vars: impl IntoIterator<Item = (impl AsRef<OsStr>, impl AsRef<OsStr>)>,
    ) -> io::Result<()> {
        self.client
            .borrow_protocol_mut()
            .send_command_with_args("setenv", message::pack_env_vars_os(vars))
            .await
    }

    /// Changes the process title of the server.
    pub async fn set_process_name(
        &mut self,
        name: impl AsRef<OsStr>,
    ) -> io::Result<()> {
        let name_bytes = name.as_ref().as_bytes().to_owned();
        self.client
            .borrow_protocol_mut()
            .send_command_with_args("setprocname", name_bytes)
            .await
    }

    /// Changes the umask of the server process.
    pub async fn set_umask(&mut self, mask: u32) -> io::Result<()> {
        let mut mask_bytes = BytesMut::with_capacity(mem::size_of_val(&mask));
        mask_bytes.put_u32(mask);
        self.client
            .borrow_protocol_mut()
            .send_command_with_args("setumask2", mask_bytes)
            .await
    }

    /// Runs the specified Mercurial command with cHg extension.
    pub async fn run_command_chg(
        &mut self,
        handler: &mut impl SystemHandler,
        args: impl IntoIterator<Item = impl AsRef<OsStr>>,
    ) -> io::Result<i32> {
        runcommand::run_command(
            self.client.borrow_protocol_mut(),
            handler,
            message::pack_args_os(args),
        )
        .await
    }

    /// Validates if the server can run Mercurial commands with the expected
    /// configuration.
    ///
    /// The `args` should contain early command arguments such as `--config`
    /// and `-R`.
    ///
    /// Client-side environment must be sent prior to this request, by
    /// `set_current_dir()` and `set_env_vars_os()`.
    pub async fn validate(
        &mut self,
        args: impl IntoIterator<Item = impl AsRef<OsStr>>,
    ) -> io::Result<Vec<Instruction>> {
        let data = self
            .client
            .borrow_protocol_mut()
            .query_with_args("validate", message::pack_args_os(args))
            .await?;
        message::parse_instructions(data)
    }
}