Skip to content
Snippets Groups Projects
Commit 69d40a97 authored by Raphaël Gomès's avatar Raphaël Gomès
Browse files

pyo3: add a util to handle SIGINT from a long-running Rust function

This is going to be useful for the upcoming `update` module, and is the
transliteration of the util of the same name in `hg-cpython`. Explanations
are inline.
parent 0c7ac026
No related branches found
No related tags found
3 merge requests!1292Draft: 7.0rc preparation,!1273diff: add a --ignore-changes-from-ancestors option,!1265Rewrite the remaining modules from `rust-cpython` to `PyO3`
......@@ -343,3 +343,33 @@
}
}
/// Wrap a call to `func` so that Python's `SIGINT` handler is first stored,
/// then restored after the call to `func` and finally raised if
/// `func` returns a [`HgError::InterruptReceived`].
///
/// We cannot use [`Python::check_signals`] because it only works from the main
/// thread of the main interpreter. To that end, long-running Rust functions
/// need to cooperate by listening to their own `SIGINT` signal and return
/// the appropriate error on catching that signal: this is especially helpful
/// in multithreaded operations.
pub fn with_sigint_wrapper<R>(
py: Python,
func: impl Fn() -> Result<R, HgError>,
) -> PyResult<Result<R, HgError>> {
let signal_py_mod = py.import(intern!(py, "signal"))?;
let sigint_py_const = signal_py_mod.getattr(intern!(py, "SIGINT"))?;
let old_handler = signal_py_mod
.call_method1(intern!(py, "getsignal"), (sigint_py_const.clone(),))?;
let res = func();
// Reset the old signal handler in Python because we may have changed it
signal_py_mod.call_method1(
intern!(py, "signal"),
(sigint_py_const.clone(), old_handler),
)?;
if let Err(HgError::InterruptReceived) = res {
// Trigger the signal in Python
signal_py_mod
.call_method1(intern!(py, "raise_signal"), (sigint_py_const,))?;
}
Ok(res)
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment