From e59e82b1060cd34bdf09bb1dabf610d724181897 Mon Sep 17 00:00:00 2001 From: Neha Singla Date: Wed, 16 Jul 2025 13:00:10 -0700 Subject: [PATCH] Fix zombie process accumulation from git operations in cloud environment Git commands spawn helper processes (git-credential-helper, git-remote-https, ssh) that become zombies when the main git process exits before children complete. This is problematic in cloud/container environments where the application runs as PID 1 or under minimal init systems that don't reliably reap orphaned processes. Added SIGCHLD signal handler to automatically reap zombie processes system-wide using non-blocking waitpid(), preventing resource leaks without affecting normal git operations. --- jupyterlab_git/git.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/jupyterlab_git/git.py b/jupyterlab_git/git.py index a4ae5e9e..dceb8d2c 100644 --- a/jupyterlab_git/git.py +++ b/jupyterlab_git/git.py @@ -22,6 +22,7 @@ import tornado.locks from jupyter_server.utils import ensure_async from nbdime import diff_notebooks, merge_notebooks +import signal from .log import get_logger @@ -55,6 +56,36 @@ execution_lock = tornado.locks.Lock() +def sigchld_handler(signum, frame): + """Handle SIGCHLD to reap zombie processes from git operations. + + Git commands often spawn helper processes (git-credential-helper, git-remote-https, + ssh, git-upload-pack, git-receive-pack) that can become zombie processes when the + main git process exits before its children complete. + + This is particularly problematic in cloud/container environments where: + - The application runs as PID 1 or under minimal init systems + - Standard init process zombie reaping may be unreliable or slow + - Resource constraints can cause zombie accumulation + + This handler automatically reaps any zombie processes to prevent system resource leaks. + """ + while True: + try: + # Reap child processes non-blockingly + pid, status = os.waitpid(-1, os.WNOHANG) + if pid == 0: # No more children to reap + break + get_logger().debug(f"Reaped child process {pid} with status {status}") + except OSError: + # No child processes or other error + break + + +# Set up SIGCHLD handler for automatic zombie reaping +signal.signal(signal.SIGCHLD, sigchld_handler) + + class State(IntEnum): """Git repository state."""