|
22 | 22 | import tornado.locks
|
23 | 23 | from jupyter_server.utils import ensure_async
|
24 | 24 | from nbdime import diff_notebooks, merge_notebooks
|
| 25 | +import signal |
25 | 26 |
|
26 | 27 | from .log import get_logger
|
27 | 28 |
|
|
55 | 56 | execution_lock = tornado.locks.Lock()
|
56 | 57 |
|
57 | 58 |
|
| 59 | +def sigchld_handler(signum, frame): |
| 60 | + """Handle SIGCHLD to reap zombie processes from git operations. |
| 61 | +
|
| 62 | + Git commands often spawn helper processes (git-credential-helper, git-remote-https, |
| 63 | + ssh, git-upload-pack, git-receive-pack) that can become zombie processes when the |
| 64 | + main git process exits before its children complete. |
| 65 | +
|
| 66 | + This is particularly problematic in cloud/container environments where: |
| 67 | + - The application runs as PID 1 or under minimal init systems |
| 68 | + - Standard init process zombie reaping may be unreliable or slow |
| 69 | + - Resource constraints can cause zombie accumulation |
| 70 | +
|
| 71 | + This handler automatically reaps any zombie processes to prevent system resource leaks. |
| 72 | + """ |
| 73 | + while True: |
| 74 | + try: |
| 75 | + # Reap child processes non-blockingly |
| 76 | + pid, status = os.waitpid(-1, os.WNOHANG) |
| 77 | + if pid == 0: # No more children to reap |
| 78 | + break |
| 79 | + get_logger().debug(f"Reaped child process {pid} with status {status}") |
| 80 | + except OSError: |
| 81 | + # No child processes or other error |
| 82 | + break |
| 83 | + |
| 84 | + |
| 85 | +# Set up SIGCHLD handler for automatic zombie reaping |
| 86 | +signal.signal(signal.SIGCHLD, sigchld_handler) |
| 87 | + |
| 88 | + |
58 | 89 | class State(IntEnum):
|
59 | 90 | """Git repository state."""
|
60 | 91 |
|
|
0 commit comments