view rust/hg-cpython/src/update.rs @ 52213:96b113d22b34 stable

rust-update: handle SIGINT from long-running update threads The current code does not respond to ^C until after the Rust bit is finished doing its work. This is expected, since Rust holds the GIL for the duration of the call and does not call `PyErr_CheckSignals`. Freeing the GIL to do our work does not really improve anything since the Rust threads are still going, and the only way of cancelling a thread is by making it cooperate. So we do the following: - remember the SIGINT handler in hg-cpython and reset it after the call into core (see inline comment in `update.rs` about this) - make all update threads watch for a global `AtomicBool` being `true`, and if so stop their work - reset the global bool and exit early (i.e. before writing the dirstate) - raise SIGINT from `hg-cpython` if update returns `InterruptReceived`
author Rapha?l Gom?s <rgomes@octobus.net>
date Tue, 12 Nov 2024 12:52:13 +0100
parents e6a44bc91bc2
children
line wrap: on
line source

// debug.rs
//
// Copyright 2024 Mercurial developers
//
// This software may be used and distributed according to the terms of the
// GNU General Public License version 2 or any later version.

//! Module for updating a repository.
use cpython::{PyDict, PyModule, PyObject, PyResult, Python};
use hg::{
    progress::{HgProgressBar, Progress},
    update::update_from_null,
    BaseRevision,
};

use crate::{
    exceptions::FallbackError,
    utils::{hgerror_to_pyerr, repo_from_path, with_sigint_wrapper},
};

pub fn update_from_null_fast_path(
    py: Python,
    repo_path: PyObject,
    to: BaseRevision,
    num_cpus: Option<usize>,
) -> PyResult<usize> {
    log::trace!("Using update from null fastpath");
    let repo = repo_from_path(py, repo_path)?;
    let progress: &dyn Progress = &HgProgressBar::new("updating");

    let res = with_sigint_wrapper(py, || {
        update_from_null(&repo, to.into(), progress, num_cpus)
    })?;

    hgerror_to_pyerr(py, res)
}

pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
    let dotted_name = &format!("{}.update", package);
    let m = PyModule::new(py, dotted_name)?;

    m.add(py, "__package__", package)?;
    m.add(py, "__doc__", "Rust module for updating a repository")?;
    m.add(py, "FallbackError", py.get_type::<FallbackError>())?;
    m.add(
        py,
        "update_from_null",
        py_fn!(
            py,
            update_from_null_fast_path(
                repo_path: PyObject,
                to: BaseRevision,
                num_cpus: Option<usize>
            )
        ),
    )?;

    let sys = PyModule::import(py, "sys")?;
    let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
    sys_modules.set_item(py, dotted_name, &m)?;

    Ok(m)
}