fix: prevent orphaned processes from consuming 100% CPU when terminal closes#16965
Conversation
Summary of ChangesHello @yuvrajangadsingh, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request delivers a critical fix for issues where the Gemini CLI could become an orphaned process, consuming 100% CPU, after a terminal window is closed. It introduces a comprehensive two-pronged approach to ensure the CLI gracefully exits under various termination scenarios, significantly improving system stability and resource management by preventing runaway processes. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces robust signal handling and a TTY check to prevent orphaned processes, which is a great improvement for stability. The implementation is mostly solid, with good use of flags to prevent some race conditions. However, I've identified a critical race condition between the new TTY check and the signal handlers that could lead to concurrent cleanup execution. My review includes a suggested fix for this issue.
|
Thanks for catching this race condition! Fixed in commit 3c29341 by adding the // Prevent race condition with signal handlers
if (isShuttingDown) {
return;
}
isShuttingDown = true;Ready for another review! |
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces a robust, two-layer defense to prevent orphaned CLI processes from consuming high CPU, which is a critical fix. The use of signal handlers for graceful shutdown and a periodic TTY check for defense-in-depth is a solid approach. I appreciate the attention to detail in preventing race conditions with the isShuttingDown flag and the clean refactoring of the terminal raw mode cleanup. The new unit tests for signal handling are also a great addition. I have one suggestion to make the tests for the TTY check even more comprehensive.
There was a problem hiding this comment.
Code Review
This pull request introduces a robust solution to prevent orphaned processes by adding signal handlers for graceful shutdown and a periodic TTY check as a defense-in-depth mechanism. The changes are well-structured and include comprehensive unit tests. My main feedback concerns the TTY check's effectiveness in the tmux detach scenario, as the underlying assumption about isTTY becoming false upon detachment may be incorrect, potentially leaving that part of the problem unsolved.
|
@gemini-code-assist Added the test case for TTY loss scenario in d358abb. Thanks for the suggestion! |
|
That's excellent news, @yuvrajangadsingh! I've reviewed the commit Thank you for addressing this suggestion so promptly and thoroughly! |
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces a robust, two-layer defense mechanism to prevent orphaned CLI processes from consuming 100% CPU when a terminal is closed. The changes include adding signal handlers for SIGHUP, SIGTERM, and SIGINT for graceful shutdown, and a periodic TTY check as a fallback. The implementation correctly handles potential race conditions using a shutdown flag and ensures proper cleanup sequencing. The addition of comprehensive unit tests for the new signal handling and TTY check logic is excellent and verifies the correctness of the fix. The code is well-structured and the changes effectively address the reported issues.
|
@gemini-code-assist review |
There was a problem hiding this comment.
Code Review
This pull request introduces robust signal handling and a TTY check to prevent orphaned processes, which is a great improvement for the CLI's stability. The implementation is solid and includes comprehensive tests.
My main feedback is a high-severity comment regarding a potential race condition in the shutdown logic. While the new isShuttingDown flag works well within this module, the shutdown process can be triggered from other parts of the application (like nonInteractiveCli.ts), which don't share this guard. I've suggested a refactoring to centralize the shutdown logic to make it more robust and prevent potential issues.
|
@gemini-code-assist review |
There was a problem hiding this comment.
Code Review
This pull request introduces a robust solution to prevent orphaned processes from consuming high CPU when the terminal is closed. The changes are well-implemented and include two layers of defense: direct signal handling for SIGHUP, SIGTERM, and SIGINT, and a periodic TTY check as a fallback. The code is clean, well-documented, and includes comprehensive unit tests that cover the new functionality and edge cases. The refactoring of raw mode restoration to use the new centralized cleanup mechanism is also a good improvement. Overall, this is a high-quality contribution that effectively solves the reported issues.
|
Hi @NTaylorMullen, this PR has been open for a few weeks now and all review feedback has been addressed. Would you or another maintainer be able to take a look when you get a chance? Thanks! |
339fc15 to
1adc270
Compare
|
Hey folks, just bumping this one again — it's been open for a while now and all review feedback has been addressed. Would really appreciate a look when someone has time. Thanks! |
bc5e50b to
6677aa0
Compare
|
Hey @Adib234 — would you be able to take a look at this one when you get a chance? It fixes orphaned child processes that spike to 100% CPU when the terminal closes. Has a community approval already and all checks pass. Thanks! |
8630fd8 to
0e53501
Compare
Head branch was pushed to by a user without write access
|
@scidomino the lint CI was failing on added |
08db48b to
3d33971
Compare
|
Cross-linking a fresh live repro that may be relevant to this PR: In today’s tmux freeze ( If useful, this suggests the EBADF/freeze path can also leave behind orphaned children in the same incident. |
|
@yuvrajangadsingh I didn't notice that you added a new file. Please don't. Delete |
|
@scidomino done. deleted gemini.test.ts, reverted the eslint config change, and moved the signal/TTY tests into cleanup.test.ts (since gemini.test.tsx has a module-level mock on cleanup.js that would prevent testing the real implementations). the tests for validateDnsResolutionOrder, getNodeMemoryArgs, and setupUnhandledRejectionHandler were already covered in gemini.test.tsx so those were just dropped. |
Address code review feedback: set isShuttingDown = true before calling runExitCleanup() in the TTY check to prevent signal handlers from calling runExitCleanup() concurrently if a signal arrives during the await.
Addresses code review feedback: adds test verifying that the process correctly exits when both stdin and stdout lose TTY status.
Extract gracefulShutdown to module scope so both setupSignalHandlers and setupTtyCheck share the same shutdown logic, eliminating duplication and ensuring consistent race condition prevention.
Moves setupSignalHandlers(), setupTtyCheck(), gracefulShutdown(), and isShuttingDown from gemini.tsx into utils/cleanup.ts to keep gemini.tsx focused on app initialization. Cleanup-related logic now lives in one place.
- Move isShuttingDown to top with other module vars - Convert gracefulShutdown from arrow to function declaration - Move JSDoc to gracefulShutdown, trim verbose comments - Remove debug log and unnecessary try/catch - Remove hallucinated GEMINI_NON_INTERACTIVE env check - Remove unnecessary comments in gemini.tsx - Inline stopTtyCheck variable - Revert raw mode comment to original wording - Update tests to match simplified cleanup
gemini.test.tsx and gemini.test.ts share the same stem, so TypeScript's project service resolves the .ts variant and skips the .tsx. This causes eslint to fail with "not found by the project service" — a pre-existing issue masked on main by the eslint cache.
…t.ts Addresses review feedback: removed the new gemini.test.ts file and moved signal handler and TTY check tests into cleanup.test.ts where they belong. Reverted the eslint allowDefaultProject workaround since deleting the .ts file eliminates the stem conflict.
13cb9de to
9becc22
Compare
… closes (google-gemini#16965) Co-authored-by: Tommaso Sciortino <sciortino@gmail.com>
… closes (google-gemini#16965) Co-authored-by: Tommaso Sciortino <sciortino@gmail.com>
Summary
This PR fixes issues #15874 and #15873 where closing the terminal window causes orphaned Gemini CLI processes to spin at 100% CPU, making the system unresponsive.
Root Cause
When a terminal is closed, the shell sends SIGHUP to child processes. Without proper signal handling, the CLI process continues running but loses its controlling terminal, causing it to spin in a tight loop trying to read from a closed TTY.
Solution
This PR implements a two-layer defense:
setupSignalHandlers): Handles SIGHUP, SIGTERM, and SIGINT signals with graceful shutdownsetupTtyCheck): Periodic check (every 5s) as defense-in-depth for cases where SIGHUP is not received (e.g., certain terminal emulators, tmux detach)Key improvements over PR #15889:
isShuttingDownflag to prevent concurrent shutdown attemptsChanges
setupSignalHandlers()function with proper signal handlingsetupTtyCheck()function with race condition preventionregisterSyncCleanupfor raw mode restorationgemini.test.tsTest Plan
Related Issues
Fixes #15874
Fixes #15873
Related to PR #15889 (addresses gaps in that implementation)