Skip to content

fix(core): add dispose functions to prevent subscription memory leaks - #7032

Closed
hendem wants to merge 15 commits into
anomalyco:devfrom
hendem:fix/memory-leak-subscription-cleanup
Closed

fix(core): add dispose functions to prevent subscription memory leaks#7032
hendem wants to merge 15 commits into
anomalyco:devfrom
hendem:fix/memory-leak-subscription-cleanup

Conversation

@hendem

@hendemhendem commented Jan 6, 2026

Copy link
Copy Markdown

Fixes#3013

Summary

  • Add dispose() functions to Share, ShareNext, Plugin, and Format namespaces to clean up Bus subscriptions
  • Add cleanupSession() and dispose() to ACP Agent for event subscription cleanup using AbortControllers
  • Add Bus._getSubscriptionCount() and Bus._getTotalSubscriptionCount() test helpers
  • Add memory tests to verify subscription cleanup works correctly

Problem

Bus subscriptions were accumulating during extended use because the return values from Bus.subscribe() were being ignored. Over time, this caused memory growth that could contribute to Bun running out of memory.

Solution

Each module that subscribes to Bus events now:

  1. Stores the unsubscribe function returned by Bus.subscribe()
  2. Provides a dispose() function that calls all stored unsubscribe functions

For ACP Agent specifically, session-scoped subscriptions use AbortControllers so they can be cleaned up when a session ends.

Testing

Added 10 unit tests in test/memory/ that verify:

  • Each module's dispose() properly unsubscribes
  • Multiple init/dispose cycles don't leak subscriptions
  • ACP session cleanup works correctly

All tests pass: bun test test/memory/

CopilotAI review requested due to automatic review settings January 6, 2026 04:30
@github-actions

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

Summary

No duplicate PRs found.

All searches returned only PR #7032 (the current PR), with one additional unrelated result about Intent abstraction (PR #6549).

The searches covered:

This appears to be a unique PR addressing subscription memory leaks through new dispose functionality.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR adds dispose() functions to multiple namespaces to prevent Bus subscription memory leaks that were causing Bun to run out of memory during extended use. The implementation adds cleanup mechanisms for Share, ShareNext, Plugin, and Format modules, along with AbortController-based session cleanup for the ACP Agent.

Key Changes:

  • Added dispose() functions that unsubscribe from Bus events across Share, ShareNext, Plugin, and Format namespaces
  • Implemented AbortController-based cleanup for ACP Agent session event subscriptions
  • Added Bus test helpers _getSubscriptionCount() and _getTotalSubscriptionCount() for testing

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 7 comments.

Show a summary per file
FileDescription
packages/opencode/src/bus/index.tsAdded internal test helper functions to query subscription counts
packages/opencode/src/share/share.tsAdded subscriptions array and dispose() function to clean up Bus subscriptions and pending queue
packages/opencode/src/share/share-next.tsAdded subscriptions array and dispose() function to clean up Bus subscriptions and queued timeouts
packages/opencode/src/plugin/index.tsAdded subscriptions array and dispose() function to clean up wildcard event subscriptions
packages/opencode/src/format/index.tsAdded subscriptions array and dispose() function to clean up File.Event.Edited subscriptions
packages/opencode/src/acp/agent.tsAdded sessionAbortControllers map, cleanupSession() and dispose() methods to manage session-scoped event subscriptions using AbortControllers
packages/opencode/test/memory/subscription-cleanup.test.tsAdded comprehensive unit tests verifying dispose functions work correctly for all modules
packages/opencode/test/memory/profile.tsAdded memory profiling script to validate no memory leaks occur during init/dispose cycles
packages/opencode/test/memory/acp-cleanup.test.tsAdded tests for ACP Agent session cleanup and AbortController management

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadpackages/opencode/src/share/share-next.ts
Comment threadpackages/opencode/src/format/index.ts
Comment threadpackages/opencode/src/acp/agent.ts
Comment threadpackages/opencode/test/memory/subscription-cleanup.test.ts
Comment threadpackages/opencode/src/acp/agent.ts Outdated
Comment threadpackages/opencode/src/share/share.ts Outdated
@hendem
hendem marked this pull request as draft January 6, 2026 04:40
@hendem
hendem marked this pull request as ready for review January 6, 2026 04:45
@hendem

Copy link
Copy Markdown
Author

All Copilot review feedback has been addressed:

  1. Share/ShareNext/Format init() accumulating subscriptions - Fixed in commit 7169a511: init() now calls dispose() at the start to clean up existing subscriptions before re-initializing.

  2. ACP event subscription errors silently ignored - Fixed in commit 7169a511: Added .catch() handler to log errors from the subscription promise chain.

  3. ACP race condition in finally block - Fixed in commit 7169a511: Added if (current === controller) check before deleting to prevent the race condition.

  4. Test doesn't verify queue cleanup - Fixed in commit 158dcf70: Added _getQueueSize() helper and assertion to verify the queue is cleared after dispose().

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadpackages/opencode/src/acp/agent.ts
Comment threadpackages/opencode/src/acp/agent.ts

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadpackages/opencode/test/memory/acp-cleanup.test.ts
Comment threadpackages/opencode/src/share/share.ts Outdated

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadpackages/opencode/src/acp/agent.ts Outdated
Comment threadpackages/opencode/test/memory/acp-cleanup.test.ts
Comment threadpackages/opencode/src/acp/agent.ts
Comment threadpackages/opencode/src/acp/agent.ts
Comment threadpackages/opencode/src/share/share.ts Outdated
CopilotAI reviewed Jan 7, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadpackages/opencode/src/acp/agent.ts
Comment threadpackages/opencode/src/share/share-next.ts Outdated
Comment threadpackages/opencode/src/acp/agent.ts
Comment threadpackages/opencode/test/memory/acp-cleanup.test.ts Outdated

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadpackages/opencode/src/acp/agent.ts
Mark Henderson added 5 commits January 10, 2026 14:16
- Have Instance.state dispose callbacks delegate to exported dispose() functions to avoid duplication
- Reorder init() to call dispose() before getting state reference for clearer semantics
- Add afterAll cleanup in subscription-cleanup.test.ts to clean up temp directory
- Wrap profile.ts test functions in Instance.provide for proper Instance context
- Add closed property to ACP mock in profile.ts
- Perform Instance.state dispose cleanup inline to prevent state()
reinitialization during Instance disposal
- Add AbortController to cancel in-flight fetch requests during dispose
- Pass abort signal to SDK event.subscribe to properly cancel SSE stream
- Update comments to accurately reflect full dispose behavior
- Add proper async generator cleanup in test
@hendem

Copy link
Copy Markdown
Author

This PR has been superseded by #7914, which is a clean re-implementation on current dev (this PR had merge conflicts that were complex to resolve).

The new PR includes the same functionality:

  • dispose() for Share, ShareNext, Plugin, Format namespaces
  • AbortController-based cleanup for ACP Agent
  • Memory tests verifying cleanup

Leaving this PR open per request, but #7914 should be reviewed instead.

hendem pushed a commit to hendem/opencode that referenced this pull request Jan 12, 2026
- Add dispose() to Share, ShareNext, Plugin, and Format namespaces
- Add cleanupSession() and dispose() to ACP Agent with AbortControllers
- Add Bus._getSubscriptionCount() test helpers
- Add memory tests to verify cleanup works correctly
Supersedes anomalyco#7032Fixesanomalyco#3013
@thdxr
thdxrforce-pushed the dev branch 3 times, most recently from f1ae801 to 08fa7f7CompareJanuary 30, 2026 14:37
@github-actions

Copy link
Copy Markdown
Contributor

Closing this pull request because it has had no updates for more than 60 days. If you plan to continue working on it, feel free to reopen or open a new PR.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Uses a huge amount of memory

3 participants

@hendem@fwang