https://bz.mercurial-scm.org/show_bug.cgi?id=6961

            Bug ID: 6961
           Summary: fast update introduces hard incompatibility with
                    HGitaly
           Product: Mercurial
           Version: unspecified
          Hardware: PC
                OS: Linux
            Status: UNCONFIRMED
          Severity: bug
          Priority: wish
         Component: Mercurial
          Assignee: bugzi...@mercurial-scm.org
          Reporter: georges.raci...@octobus.net
                CC: mercurial-de...@mercurial-scm.org
    Python Version: ---

Seen on 6.9.3, as far as I can tell, the code is the same in 7.0rc0

This is a hard blocker for integration of Mercurial >= 6.9 in Heptapod

All operations involving a working directory update crash in this way:

  File
"/home/gracinet/heptapod/hdk/future/venv/lib64/python3.11/site-packages/mercurial/merge.py",
line 2063, in _update
    updated_count = rust_update_mod.update_from_null(
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.11/signal.py", line 58, in signal
    handler = _signal.signal(_enum_to_int(signalnum), _enum_to_int(handler))
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: signal only works in main thread of the main interpreter

The root cause is `update.rs` indeed installing a signal handler by calling
back in the Python interpreter:

/// 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)
}

The problem is that HGitaly services are never in the main thread of the main
interpreter, and that is forced on us by the gRPC server library itself:
HGitaly does not try to be really multithreaded, the server starts with a
threading of 1, but the server still runs all applicative code in a thread.

HGitaly does have its SIGINT/SIGTERM handlers, that it sets up before launching
the gRPC services. They are important for graceful shutdown, but I think we can
live without the code above if that just means that a fast Rust update cannot
be interrupted.

Obviously, this cannot just be monkey-patched, so I'd need Mercurial core to
provide more options (tweaking via environment variable would be just fine). If
we have a solution in 7.0 (final), I can try and skip 6.9.

-- 
You are receiving this mail because:
You are on the CC list for the bug.
_______________________________________________
Mercurial-devel mailing list
Mercurial-devel@lists.mercurial-scm.org
https://lists.mercurial-scm.org/mailman/listinfo/mercurial-devel

Reply via email to