Skip to content

fix: prevent orphaned processes from consuming 100% CPU when terminal closes#16965

Merged
scidomino merged 9 commits intogoogle-gemini:mainfrom
yuvrajangadsingh:fix/orphaned-process-signal-handling
Feb 26, 2026
Merged

fix: prevent orphaned processes from consuming 100% CPU when terminal closes#16965
scidomino merged 9 commits intogoogle-gemini:mainfrom
yuvrajangadsingh:fix/orphaned-process-signal-handling

Conversation

@yuvrajangadsingh
Copy link
Contributor

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:

  1. Signal Handlers (setupSignalHandlers): Handles SIGHUP, SIGTERM, and SIGINT signals with graceful shutdown
  2. TTY Check (setupTtyCheck): 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:

  • Race condition fix: Uses isShuttingDown flag to prevent concurrent shutdown attempts
  • Proper cleanup sequencing: Clears TTY check interval BEFORE starting cleanup
  • Comprehensive tests: Adds unit tests for signal handling functions
  • Both modes covered: Works in both interactive and non-interactive modes

Changes

  • Add setupSignalHandlers() function with proper signal handling
  • Add setupTtyCheck() function with race condition prevention
  • Use registerSyncCleanup for raw mode restoration
  • Register TTY check cleanup in interactive mode
  • Add comprehensive unit tests in gemini.test.ts
  • Update existing test to expect 4 cleanup registrations (was 3)

Test Plan

  • Unit tests pass for signal handling functions
  • TypeScript compiles without errors
  • Existing tests still pass with updated cleanup count
  • Manual testing: Start CLI, close terminal window → process should exit cleanly
  • Manual testing: Start CLI in tmux, detach → process should exit after ~5s

Related Issues

Fixes #15874
Fixes #15873
Related to PR #15889 (addresses gaps in that implementation)

@yuvrajangadsingh yuvrajangadsingh requested a review from a team as a code owner January 18, 2026 08:42
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @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

  • Robust Signal Handling: Introduces comprehensive signal handlers for SIGHUP, SIGTERM, and SIGINT to ensure the CLI gracefully shuts down under various termination scenarios, preventing orphaned processes.
  • Periodic TTY Check: Implements a defense-in-depth mechanism that periodically checks the TTY status. If the controlling terminal is lost (e.g., due to terminal closure without SIGHUP, or tmux detach), the process will initiate a graceful exit.
  • Race Condition Prevention and Cleanup Sequencing: Addresses potential race conditions during shutdown by using an isShuttingDown flag and ensures that cleanup operations, such as clearing the TTY check interval, are sequenced correctly before starting the main cleanup process.
  • Comprehensive Unit Tests: Adds a new dedicated test file (gemini.test.ts) with extensive unit tests covering the functionality of the new signal handlers and the TTY check, ensuring their reliability.
  • Broad Mode Coverage: The implemented solution is designed to work effectively in both interactive and non-interactive modes of the CLI, providing consistent behavior across different usage contexts.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@yuvrajangadsingh
Copy link
Contributor Author

Thanks for catching this race condition! Fixed in commit 3c29341 by adding the isShuttingDown check and setting it to true before calling runExitCleanup() in the TTY check block.

// Prevent race condition with signal handlers
if (isShuttingDown) {
  return;
}
isShuttingDown = true;

Ready for another review!

@yuvrajangadsingh
Copy link
Contributor Author

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@yuvrajangadsingh
Copy link
Contributor Author

@gemini-code-assist Added the test case for TTY loss scenario in d358abb. Thanks for the suggestion!

@gemini-code-assist
Copy link
Contributor

That's excellent news, @yuvrajangadsingh! I've reviewed the commit d358abbf4 and can confirm the addition of the test case for the TTY loss scenario in packages/cli/src/gemini.test.ts. This significantly improves the test coverage and robustness of the solution.

Thank you for addressing this suggestion so promptly and thoroughly!

@yuvrajangadsingh
Copy link
Contributor Author

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-cli gemini-cli bot added priority/p2 Important but can be addressed in a future release. area/platform Issues related to Build infra, Release mgmt, Testing, Eval infra, Capacity, Quota mgmt labels Jan 19, 2026
@jacob314 jacob314 added the help wanted We will accept PRs from all issues marked as "help wanted". Thanks for your support! label Jan 22, 2026
@yuvrajangadsingh
Copy link
Contributor Author

@gemini-code-assist review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@yuvrajangadsingh
Copy link
Contributor Author

@gemini-code-assist review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@yuvrajangadsingh
Copy link
Contributor Author

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!

@yuvrajangadsingh yuvrajangadsingh force-pushed the fix/orphaned-process-signal-handling branch from 339fc15 to 1adc270 Compare February 16, 2026 09:37
@yuvrajangadsingh
Copy link
Contributor Author

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!

@yuvrajangadsingh yuvrajangadsingh force-pushed the fix/orphaned-process-signal-handling branch 2 times, most recently from bc5e50b to 6677aa0 Compare February 19, 2026 15:36
@yuvrajangadsingh
Copy link
Contributor Author

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!

@yuvrajangadsingh yuvrajangadsingh force-pushed the fix/orphaned-process-signal-handling branch 4 times, most recently from 8630fd8 to 0e53501 Compare February 24, 2026 10:20
@scidomino scidomino enabled auto-merge February 25, 2026 21:51
auto-merge was automatically disabled February 26, 2026 09:03

Head branch was pushed to by a user without write access

@yuvrajangadsingh
Copy link
Contributor Author

@scidomino the lint CI was failing on gemini.test.tsx with "not found by the project service". this is a pre-existing issue — gemini.test.tsx and gemini.test.ts share the same stem, so TypeScript's project service resolves the .ts and skips the .tsx. it passes on main only because of eslint cache, but since we modified the file the cache busts and the error surfaces.

added allowDefaultProject for that one file in eslint.config.js to fix it. happy to adjust if you'd prefer a different approach — just wanted to unblock the auto-merge.

@yuvrajangadsingh yuvrajangadsingh force-pushed the fix/orphaned-process-signal-handling branch from 08db48b to 3d33971 Compare February 26, 2026 09:05
@craigvandotcom
Copy link

Cross-linking a fresh live repro that may be relevant to this PR:
#17729 (comment)

In today’s tmux freeze (ioctl(2) failed, EBADF), after killing the parent bun .../gemini --yolo process, the child bun .../@google/gemini-cli/dist/index.js --yolo remained alive and detached with PPID=1 until force-killed.

If useful, this suggests the EBADF/freeze path can also leave behind orphaned children in the same incident.

@scidomino
Copy link
Collaborator

@yuvrajangadsingh I didn't notice that you added a new file. Please don't. Delete gemini.test.ts and move those tests to gemini.test.tsx (or remove them entirely if they aren't valuable). And revert the change to the linter.

@yuvrajangadsingh
Copy link
Contributor Author

yuvrajangadsingh commented Feb 26, 2026

@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.
@yuvrajangadsingh yuvrajangadsingh force-pushed the fix/orphaned-process-signal-handling branch from 13cb9de to 9becc22 Compare February 26, 2026 21:35
@scidomino scidomino enabled auto-merge February 26, 2026 22:53
@scidomino scidomino added this pull request to the merge queue Feb 26, 2026
Merged via the queue into google-gemini:main with commit b8d6041 Feb 26, 2026
27 checks passed
BryanBradfo pushed a commit to BryanBradfo/gemini-cli that referenced this pull request Mar 5, 2026
… closes (google-gemini#16965)

Co-authored-by: Tommaso Sciortino <sciortino@gmail.com>
liamhelmer pushed a commit to badal-io/gemini-cli that referenced this pull request Mar 12, 2026
… closes (google-gemini#16965)

Co-authored-by: Tommaso Sciortino <sciortino@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/platform Issues related to Build infra, Release mgmt, Testing, Eval infra, Capacity, Quota mgmt help wanted We will accept PRs from all issues marked as "help wanted". Thanks for your support! priority/p2 Important but can be addressed in a future release.

Projects

None yet

5 participants