Skip to content

Fix CancellationTokenSource leak: wire ChatViewModel.Dispose() to app shutdown#76

Open
murapadev with Copilot wants to merge 3 commits into
mainfrom
copilot/fix-chatviewmodel-dispose-leak
Open

Fix CancellationTokenSource leak: wire ChatViewModel.Dispose() to app shutdown#76
murapadev with Copilot wants to merge 3 commits into
mainfrom
copilot/fix-chatviewmodel-dispose-leak

Conversation

Copilot AI commented Apr 10, 2026

Copy link
Copy Markdown
Contributor

ChatViewModel implements IDisposable to cancel and dispose _pollCts (background connection poller) and _chatCts (active chat request), but Dispose() was never called — leaving both CancellationTokenSource instances open for the lifetime of the process.

Changes

  • MainWindowViewModel — implements IDisposable; Dispose() delegates to ChatViewModel?.Dispose(), nulls the reference, and unsubscribes from ConfigService.Instance.ConfigChanged
  • App.axaml.cs — hooks desktop.ShutdownRequested to prepare the window service for shutdown, and desktop.Exit via a dedicated OnExit handler that comprehensively disposes OllamaService, TrayService, ShortcutService, and MainWindowViewModel (which in turn disposes ChatViewModel)
// App.axaml.cs — OnExit handler
private void OnExit(object? sender, ControlledApplicationLifetimeExitEventArgs e)
{
    OllamaService.Instance.Dispose();
    TrayService.Instance.Dispose();
    ShortcutService.Instance?.Dispose();
    _mainWindowViewModel?.Dispose();

    if (ConfigService.Instance.GetConfig().Privacy.ClearOnClose)
        ConversationStore.Clear();
}

// MainWindowViewModel.cs
public void Dispose()
{
    ConfigService.Instance.ConfigChanged -= OnConfigChanged;
    ChatViewModel?.Dispose();
    ChatViewModel = null;
    WebBrowserViewModel = null;
}

Copilot AI changed the title [WIP] Fix ChatViewModel.Dispose() not being called to prevent leaks Fix CancellationTokenSource leak: wire ChatViewModel.Dispose() to app shutdown Apr 10, 2026
Copilot AI requested a review from murapadev April 10, 2026 16:49
@murapadev murapadev marked this pull request as ready for review April 10, 2026 16:52
Copilot AI review requested due to automatic review settings April 10, 2026 16:52

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Ensures ChatViewModel.Dispose() is invoked during application shutdown so its background CancellationTokenSource instances don’t remain alive for the lifetime of the process.

Changes:

  • Add IDisposable to MainWindowViewModel and forward disposal to ChatViewModel.
  • Hook Avalonia desktop lifetime Exit event to dispose the MainWindowViewModel on app shutdown.

Reviewed changes

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

File Description
ViewModels/MainWindowViewModel.cs Implements IDisposable and disposes/releases ChatViewModel on shutdown.
App.axaml.cs Calls MainWindowViewModel.Dispose() from desktop.Exit to guarantee cleanup at application exit.

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

murapadev pushed a commit that referenced this pull request Apr 10, 2026
, #79)

- MainWindowViewModel: implement IDisposable, dispose ChatViewModel
- ShortcutService: add IDisposable interface declaration
- App.OnExit: dispose TrayService, ShortcutService, MainWindowViewModel

Cherry-picked from copilot branches:
  8be25e3 (TrayService), b353ef1 (ShortcutService), 0f80c4d (ChatViewModel)
@murapadev

Copy link
Copy Markdown
Owner

@copilot resolve the merge conflicts in this pull request

…odel-dispose-leak

# Conflicts:
#	App.axaml.cs

Co-authored-by: murapadev <10557163+murapadev@users.noreply.github.com>

Copilot AI commented May 1, 2026

Copy link
Copy Markdown
Contributor Author

@copilot resolve the merge conflicts in this pull request

Merged origin/main into this branch and resolved the conflict in App.axaml.cs (commit f13d616).

Main had already evolved to include a comprehensive OnExit handler that calls _mainWindowViewModel?.Dispose() (along with other service cleanup), which is a superset of our inline lambda. Kept main's version and removed the duplicate Dispose method that resulted from the merge. Build passes with 0 warnings and 0 errors.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • av-build-tel-api-v1.avaloniaui.net
    • Triggering command: /usr/share/dotnet/dotnet dotnet exec --runtimeconfig /home/REDACTED/.nuget/packages/avalonia.buildservices/0.0.29/tools/netstandard2.0/runtimeconfig.json /home/REDACTED/.nuget/packages/avalonia.buildservices/0.0.29/tools/netstandard2.0/Avalonia.BuildServices.Collector.dll (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Sign up for free to 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.

[CRITICAL] ChatViewModel.Dispose() never called — CancellationTokenSources leak

3 participants