Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Graceful shutdown #3367

Open
wants to merge 9 commits into
base: dev
Choose a base branch
from

Conversation

Jack251970
Copy link
Contributor

@Jack251970 Jack251970 commented Mar 22, 2025

Graceful shutdown

Refer to PowerToys Run.

  • Add diposable interface for MainViewModel
  • Add diposable interface for MainWindow.xaml.cs
  • Improve hwnd source remove for MainWindow.xaml.cs
  • Improve code quality for SingleInstance.cs
  • Add more logs for FL exit events
  • Dispose MainWindow and MainViewModel when disposing App

TODO

  • Fix PluginManager.DisposePluginsAsync cannot be waited

This comment has been minimized.

This comment has been minimized.

@prlabeler prlabeler bot added the bug Something isn't working label Mar 22, 2025

This comment has been minimized.

@Jack251970 Jack251970 added enhancement New feature or request and removed bug Something isn't working labels Mar 22, 2025
@prlabeler prlabeler bot added the bug Something isn't working label Mar 22, 2025

This comment has been minimized.

This comment has been minimized.

This comment has been minimized.

@Jack251970 Jack251970 marked this pull request as ready for review March 22, 2025 05:40
Copy link

@check-spelling-bot Report

🔴 Please review

See the 📂 files view, the 📜action log, or 📝 job summary for details.

❌ Errors Count
❌ forbidden-pattern 24
⚠️ ignored-expect-variant 1
⚠️ non-alpha-in-dictionary 19

See ❌ Event descriptions for more information.

Forbidden patterns 🙅 (1)

In order to address this, you could change the content to not match the forbidden patterns (comments before forbidden patterns may help explain why they're forbidden), add patterns for acceptable instances, or adjust the forbidden patterns themselves.

These forbidden patterns matched content:

s.b. workaround(s)

\bwork[- ]arounds?\b
If the flagged items are 🤯 false positives

If items relate to a ...

  • binary file (or some other file you wouldn't want to check at all).

    Please add a file path to the excludes.txt file matching the containing file.

    File paths are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your files.

    ^ refers to the file's path from the root of the repository, so ^README\.md$ would exclude README.md (on whichever branch you're using).

  • well-formed pattern.

    If you can write a pattern that would match it,
    try adding it to the patterns.txt file.

    Patterns are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your lines.

    Note that patterns can't match multiline strings.

Copy link

gitstream-cm bot commented Mar 22, 2025

🥷 Code experts: onesounds

onesounds has most 👩‍💻 activity in the files.
onesounds, Jack251970 have most 🧠 knowledge in the files.

See details

Flow.Launcher/App.xaml.cs

Activity based on git-commit:

onesounds
MAR
FEB
JAN
DEC
NOV
OCT

Knowledge based on git-blame:
Jack251970: 42%

Flow.Launcher/Helper/SingleInstance.cs

Activity based on git-commit:

onesounds
MAR
FEB
JAN
DEC
NOV
OCT

Knowledge based on git-blame:

Flow.Launcher/MainWindow.xaml

Activity based on git-commit:

onesounds
MAR
FEB
JAN
DEC
NOV
OCT

Knowledge based on git-blame:
onesounds: 89%
Jack251970: 1%

Flow.Launcher/MainWindow.xaml.cs

Activity based on git-commit:

onesounds
MAR 304 additions & 119 deletions
FEB 8 additions & 4 deletions
JAN
DEC
NOV
OCT

Knowledge based on git-blame:
Jack251970: 53%
onesounds: 24%

Flow.Launcher/ViewModel/MainViewModel.cs

Activity based on git-commit:

onesounds
MAR 236 additions & 183 deletions
FEB 23 additions & 25 deletions
JAN
DEC
NOV
OCT

Knowledge based on git-blame:
onesounds: 14%
Jack251970: 12%

To learn more about /:\ gitStream - Visit our Docs

Copy link

gitstream-cm bot commented Mar 22, 2025

Be a legend 🏆 by adding a before and after screenshot of the changes you made, especially if they are around UI/UX.

Copy link
Contributor

coderabbitai bot commented Mar 22, 2025

📝 Walkthrough

Walkthrough

The changes update the application’s initialization and shutdown processes across several components. In the main application file, dependency injection, error handling, and disposal patterns are reorganized with added logging. The SingleInstance helper is refactored for asynchronous operations and constant-based mutex naming. Additionally, MainWindow’s XAML and code-behind now include an added closed event handler and enhanced disposal processes. The view model is updated with the IDisposable interface for improved resource cleanup.

Changes

File(s) Change Summary
Flow.Launcher/App.xaml.cs, Flow.Launcher/MainWindow.xaml.cs, Flow.Launcher/ViewModel/MainViewModel.cs Introduced structured resource management with IDisposable: added new fields, refactored Dispose patterns, updated dependency injection in the constructor, and enhanced logging for disposal and exit events.
Flow.Launcher/Helper/SingleInstance.cs Refactored asynchronous control flow: removed the unique parameter in InitializeAsFirstInstance, introduced constant InstanceMutexName, replaced the mutex field with a property, and renamed methods to CreateRemoteServiceAsync and SignalFirstInstanceAsync.
Flow.Launcher/MainWindow.xaml Added a Closed="OnClosed" event handler to enable additional post-window closure cleanup.

Sequence Diagram(s)

sequenceDiagram
    participant App as App.xaml.cs
    participant DI as Dependency Injection Container
    participant Single as SingleInstance<TApplication>
    participant MW as MainWindow
    participant Log as Logging/ExitEvents

    App->>DI: Retrieve MainViewModel (_mainVM)
    App->>Single: InitializeAsFirstInstance() 
    App->>MW: Create and assign MainWindow instance (_mainWindow)
    Note over App,MW: Enhanced error handling and logging
    MW->>Log: Register exit events (ProcessExit, AppExit, SessionEnding)
Loading
sequenceDiagram
    participant MW as MainWindow (UI)
    participant Dispose as Disposal Logic
    participant Plugin as Plugins

    MW->>MW: OnClosing Event Triggered
    alt Not ready to close
        MW->>Plugin: Dispose plugins
        MW->>MW: Set _canClose flag and cancel closure
        MW->>MW: Call Close() again
    else Ready to close
        MW->>Dispose: Invoke Dispose(bool disposing)
        Dispose->>MW: Clean up _hwndSource, _notifyIcon, etc.
        MW->>MW: OnClosed Event Triggered (unhook resources)
    end
Loading

Possibly related PRs

Suggested labels

5 min review

Suggested reviewers

  • jjw24
  • taooceros

Poem

I'm a rabbit on the run, hopping through the code,
With cleaner exits and async flow in my abode.
Resources managed and errors kept at bay,
I'll nibble on these changes all day.
A cheerful hop to each refactored line—
CodeRabbit smiles, everything's just fine!

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (5)
Flow.Launcher/Helper/SingleInstance.cs (3)

59-59: Consider potential collisions in multi-user scenarios.

Appending Environment.UserName to the mutex identifier may lead to unexpected behavior if multiple sessions run under the same username on the same machine. Typically unlikely, but worth noting.


67-67: Fire-and-forget async call might hide errors.

Consider logging exceptions or awaiting the call if you need error handling for CreateRemoteServiceAsync.


72-72: Similarly, consider exception handling for SignalFirstInstanceAsync.

Fire-and-forget can mask pipe connection failures.

Flow.Launcher/MainWindow.xaml.cs (1)

238-248: Graceful shutdown logic in OnClosing is well-designed.

Awaiting plugin disposal before completing the close sequence prevents resource leaks. Consider adding try-catch to handle disposal exceptions gracefully.

Flow.Launcher/App.xaml.cs (1)

105-113: Local function for fail-fast error handling.
Wrapping the message box and Environment.FailFast in a local function is a neat approach. Before crashing, consider whether logging or partial cleanup is needed to prevent resource leaks.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dda008f and 93ccdee.

📒 Files selected for processing (5)
  • Flow.Launcher/App.xaml.cs (6 hunks)
  • Flow.Launcher/Helper/SingleInstance.cs (7 hunks)
  • Flow.Launcher/MainWindow.xaml (1 hunks)
  • Flow.Launcher/MainWindow.xaml.cs (11 hunks)
  • Flow.Launcher/ViewModel/MainViewModel.cs (2 hunks)
🧰 Additional context used
🧬 Code Definitions (4)
Flow.Launcher/App.xaml.cs (3)
Flow.Launcher/MainWindow.xaml.cs (4) (4)
  • MainWindow (30-1043)
  • MainWindow (71-83)
  • Dispose (1021-1033)
  • Dispose (1035-1040)
Flow.Launcher/ViewModel/MainViewModel.cs (4) (4)
  • MainViewModel (30-1572)
  • MainViewModel (56-173)
  • Dispose (1550-1562)
  • Dispose (1564-1569)
Flow.Launcher/Helper/SingleInstance.cs (2) (2)
  • SingleInstance (27-142)
  • InitializeAsFirstInstance (56-75)
Flow.Launcher/ViewModel/MainViewModel.cs (2)
Flow.Launcher/MainWindow.xaml.cs (2) (2)
  • Dispose (1021-1033)
  • Dispose (1035-1040)
Flow.Launcher/App.xaml.cs (2) (2)
  • Dispose (283-315)
  • Dispose (317-322)
Flow.Launcher/MainWindow.xaml.cs (2)
Flow.Launcher/ViewModel/MainViewModel.cs (4) (4)
  • MainViewModel (30-1572)
  • MainViewModel (56-173)
  • Dispose (1550-1562)
  • Dispose (1564-1569)
Flow.Launcher/App.xaml.cs (4) (4)
  • App (28-334)
  • App (50-114)
  • Dispose (283-315)
  • Dispose (317-322)
Flow.Launcher/Helper/SingleInstance.cs (1)
Flow.Launcher/App.xaml.cs (1) (1)
  • OnSecondAppStarted (328-331)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: gitStream.cm
  • GitHub Check: gitStream.cm
🔇 Additional comments (49)
Flow.Launcher/Helper/SingleInstance.cs (13)

11-14: Interface definition looks good.

No concerns with this interface addition.


27-27: Generically typed static class usage is appropriate.

This approach promotes type safety while enabling reuse for different application types.


40-40: Constant naming is consistent.

Defining a dedicated constant for the mutex name enhances readability.


45-45: Consider concurrency implications with the static property.

While single-instance logic should be fine, keep in mind that a static property can be accessed anywhere. Ensure any future concurrency usage is well-managed.


56-57: Method signature is clear.

The logic for determining if the current process is the first instance is well-structured.


61-61: Channel name creation is straightforward.

String concat with delimiter is clear and readable.


64-64: Check for potential exceptions in mutex creation.

While rare, WaitHandleCannotBeOpenedException may occur if the mutex name is invalid or access is denied. Consider catching or verifying such cases if needed.


95-95: Creating the pipe server in a using statement is good practice.

This ensures the NamedPipeServerStream is properly disposed after use.


98-100: Infinite listen loop is expected for a single-instance server.

No immediate issues; remember to handle cancellation if you ever need to stop the server gracefully.


102-105: Dispatcher invocation for UI thread safety is well-handled.

This code properly avoids cross-thread access issues when activating the main instance.


117-117: Method for signaling the first instance is neatly encapsulated.

This separation of concerns improves clarity.


120-120: Using block for the client pipe ensures disposal.

Resource management appears solid here.


121-123: Connecting with a zero time-out.

Be aware that this might fail instantly if the server isn't ready, although that scenario is unlikely in normal usage.

Flow.Launcher/MainWindow.xaml (1)

20-20: Closed event handler usage is clear.

Attaching OnClosed to the Closed event is a good way to finalize resource cleanup.

Flow.Launcher/ViewModel/MainViewModel.cs (2)

30-30: IDisposable implementation on MainViewModel is valuable.

This facilitates a proper lifecycle and resource cleanup for the view model.


1545-1571: Dispose pattern is correctly applied.

The boolean guard _disposed and calling GC.SuppressFinalize(this) in Dispose() demonstrate a proper implementation, ensuring threads, tasks, and other resources are cleaned up.

Flow.Launcher/MainWindow.xaml.cs (20)

30-30: Implementing IDisposable on MainWindow is an excellent choice.

This allows for consistent and explicit resource deallocation in UI components.


42-42: Adding a dedicated context menu field.

Helps keep code organized and fosters reusability.


45-48: Boolean fields for close state and arrow key presses.

These flags improve readability and simplify logic flow.


55-55: Maintaining an HwndSource reference is valid for custom WndProc handling.


62-62: Use of _isClockPanelAnimating is clear.

Good naming for indicating animation state.


64-64: The _disposed field is key to preventing double-disposal.

This adheres to best practices for the Dispose pattern.


223-223: Listening to TextChanged event on QueryTextBox.

Updating the clock panel’s visibility conditionally is a neat approach.


226-226: Visibility changes on the context menu now trigger UI updates.

This helps keep the clock panel in sync with user navigation.


232-232: History panel visibility also triggers the same update logic.

Ensures a consistent UI experience.


251-263: OnClosed finalizes hook removal and resets _hwndSource.

This robust approach avoids potential WndProc calls on a destroyed window. Clean usage of try-catch.


307-307: KeyDown sets _isArrowKeyPressed for the Down arrow.

Clear approach to coordinate keyboard vs. mouse interactions.


312-312: Likewise, sets _isArrowKeyPressed for the Up arrow.

This keeps the same pattern for consistent navigation logic.


370-370: KeyUp resets _isArrowKeyPressed.

Allows mouse interactions again, preventing conflicts.


375-375: Ignores mouse moves when arrow keys are pressed.

Prevents unexpected selection changes during keyboard navigation.


545-549: Populating context menu items programmatically.

Open, GameMode, PositionReset, Settings, and Exit are clearly defined. Good approach for a tray menu.


560-560: Right-click sets _contextMenu.IsOpen to true.

Standard approach for system tray context menus.


562-562: Bringing the context menu to foreground.

Using SetForegroundWindow is a reliable cross-API method to ensure proper focus.


567-567: Focusing the context menu after displaying it.

Guarantees keyboard accessibility right away.


575-575: Local variable usage for easier updates.

Minor but tidy approach to referencing _contextMenu as menu.


1021-1042: Dispose pattern is implemented correctly.

Disposing _hwndSource and _notifyIcon inside Dispose(bool disposing) aligns with best practices. The _disposed check ensures idempotent disposal.

Flow.Launcher/App.xaml.cs (13)

30-34: Introduce public property API.
Defining the static API property and initializing it via DI helps centralize access to the public API. This is a clear, concise design.


36-45: Confirm intention of static _disposingLock.
Using a static lock for disposing prevents multiple concurrent calls triggered by various exit events. Given the single-instance context, this is acceptable. If there were ever a possibility of multiple App objects within the same process, reconsider whether an instance-level lock would be more appropriate.


48-49: No functional changes.
These lines primarily introduce region markers for clarity and have no functional impact.


92-97: Retrieve MainViewModel from DI.
Instantiating _mainVM from the DI container is consistent with the new structure and ensures it can also be properly disposed later.


120-129: Single-instance logic update.
Removing the old parameter and calling InitializeAsFirstInstance() directly aligns with the revised SingleInstance design. This cleanly ensures only one running instance.


165-169: Storing _mainWindow reference for disposal.
Assigning _mainWindow at startup facilitates a consistent disposal pattern later in the code, promoting maintainability.


237-237: No functional changes.
This line solely closes a region and has no functional effect.


239-239: No functional changes.
Another region marker line with no runtime impact.


242-259: Unified disposal calls for exit events.
Linking ProcessExit, ApplicationExit, and SessionEnding to the same disposal method ensures robust and consistent cleanup logic—even if multiple events are triggered.


281-283: Standard disposable region introduced.
Beginning the IDisposable pattern here improves clarity by grouping related cleanup logic sections.


284-315: Robust Dispose(bool disposing) implementation.
Acquiring the static _disposingLock and checking _disposed prevents concurrency issues if disposal is triggered more than once. Invoking window disposal on the UI thread is both necessary and well-handled.


317-323: Public Dispose() method follows standard pattern.
Calling Dispose(true) followed by GC.SuppressFinalize(this) ensures compliance with the common .NET disposal approach. No concerns here.


326-333: OnSecondAppStarted single-instance logic.
Invoking MainViewModel.Show() properly handles second instance activations. This aligns with the single-instance requirement.

@Jack251970 Jack251970 removed the bug Something isn't working label Mar 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
20 min review enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant