comparison rust/hg-pyo3/src/utils.rs @ 52983:69d40a9778fe

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.
author Rapha?l Gom?s <rgomes@octobus.net>
date Tue, 18 Feb 2025 11:44:21 +0100
parents 0c7ac026ed63
children
comparison
equal deleted inserted replaced
52982:0c7ac026ed63 52983:69d40a9778fe
341 e => PyRuntimeError::new_err(e.to_string()), 341 e => PyRuntimeError::new_err(e.to_string()),
342 }) 342 })
343 } 343 }
344 } 344 }
345 345
346 /// Wrap a call to `func` so that Python's `SIGINT` handler is first stored,
347 /// then restored after the call to `func` and finally raised if
348 /// `func` returns a [`HgError::InterruptReceived`].
349 ///
350 /// We cannot use [`Python::check_signals`] because it only works from the main
351 /// thread of the main interpreter. To that end, long-running Rust functions
352 /// need to cooperate by listening to their own `SIGINT` signal and return
353 /// the appropriate error on catching that signal: this is especially helpful
354 /// in multithreaded operations.
355 pub fn with_sigint_wrapper<R>(
356 py: Python,
357 func: impl Fn() -> Result<R, HgError>,
358 ) -> PyResult<Result<R, HgError>> {
359 let signal_py_mod = py.import(intern!(py, "signal"))?;
360 let sigint_py_const = signal_py_mod.getattr(intern!(py, "SIGINT"))?;
361 let old_handler = signal_py_mod
362 .call_method1(intern!(py, "getsignal"), (sigint_py_const.clone(),))?;
363 let res = func();
364 // Reset the old signal handler in Python because we may have changed it
365 signal_py_mod.call_method1(
366 intern!(py, "signal"),
367 (sigint_py_const.clone(), old_handler),
368 )?;
369 if let Err(HgError::InterruptReceived) = res {
370 // Trigger the signal in Python
371 signal_py_mod
372 .call_method1(intern!(py, "raise_signal"), (sigint_py_const,))?;
373 }
374 Ok(res)
375 }