-
-
Notifications
You must be signed in to change notification settings - Fork 359
Fix copy to clipboard STA thread issue & Support retry for copy & Async build-in shortcut model & Refactor query update logic & Fix build shortcuts text replace issue #3314
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
base: dev
Are you sure you want to change the base?
Conversation
This comment has been minimized.
This comment has been minimized.
Be a legend 🏆 by adding a before and after screenshot of the changes you made, especially if they are around UI/UX. |
📝 WalkthroughWalkthroughThis set of changes introduces robust support for executing clipboard and other Windows UI operations on single-threaded apartment (STA) threads, addressing thread affinity issues. The PublicAPIInstance.CopyToClipboard method is refactored to run clipboard operations asynchronously on STA threads with retry logic, preventing errors when called from non-STA threads (such as JSON-RPC v2 plugins). Supporting changes include new helper methods in Win32Helper, new and refactored shortcut model classes for async expansion, updates to settings and main view model logic to accommodate asynchronous shortcut expansion, and minor improvements to error reporting, localization, and application startup. Changes
Sequence Diagram(s)sequenceDiagram
participant Plugin
participant PublicAPIInstance
participant Win32Helper
participant STAThread
participant Clipboard
Plugin->>PublicAPIInstance: CopyToClipboard(stringToCopy)
alt stringToCopy is file/directory or text
PublicAPIInstance->>Win32Helper: StartSTATaskAsync(Action)
Win32Helper->>STAThread: Start STA Thread
STAThread->>Clipboard: SetFileDropList/SetText
Clipboard-->>STAThread: (Operation result)
STAThread-->>Win32Helper: Complete Task
Win32Helper-->>PublicAPIInstance: Task result
PublicAPIInstance->>Plugin: Show notification (success/failure)
end
sequenceDiagram
participant MainViewModel
participant Settings
participant BuiltinShortcutModel/AsyncBuiltinShortcutModel
participant Win32Helper
participant Clipboard
MainViewModel->>Settings: Get BuiltinShortcuts
loop For each shortcut in BuiltinShortcuts
alt BuiltinShortcutModel
MainViewModel->>BuiltinShortcutModel: Expand()
else AsyncBuiltinShortcutModel
MainViewModel->>AsyncBuiltinShortcutModel: ExpandAsync()
AsyncBuiltinShortcutModel->>Win32Helper: StartSTATaskAsync(Func<string>)
Win32Helper->>STAThread: Start STA Thread
STAThread->>Clipboard: GetText()
Clipboard-->>STAThread: Clipboard text
STAThread-->>Win32Helper: Result
Win32Helper-->>AsyncBuiltinShortcutModel: Clipboard text
end
MainViewModel->>MainViewModel: Replace shortcut key in query
end
Assessment against linked issues
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (5)
🚧 Files skipped from review as they are similar to previous changes (5)
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
I am very against this. This is the core, most used, functionality, and it should be available to everyone. Plugins shouldn't need to reinvent the wheel every time. Especially considering Node and Python don't provide a way to do this. I remember seeing one plugin that packed another binary with itself just so it could copy text, which is absolutely ridiculous. |
Well, could you please give me the sample to test? |
After installing this plugin, type |
Many thanks! Sorry, I don't see this as a bug unique to the V2 plugin. In that case, the bug could be fixed. |
This reverts commit 9f8e829.
Sorry I cannot open your demo plugin. I have used 1.19.5 release version of FL (master branch) and downloaded embedded node.js environment from FL but I got this issue: I want to check it by myself, but I really know little about JavaScript. Could you please fix it? |
Could you please install a newer version of NodeJS (v22), specify it in settings (General — Node.js Path) and try again? Flow currently uses very old version that has reached end-of-life already. |
This comment has been minimized.
This comment has been minimized.
Thanks, I think I have fixed it. Now could you please help me check if copy to clipboard (directory) with JsonRPCV2 plugins can work? |
Nvm, I have checked it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (5)
Flow.Launcher.Infrastructure/Win32Helper.cs (2)
122-125
: Consider propagating exceptions rather than suppressing them.The current implementation catches all exceptions but doesn't propagate them to the caller, making it difficult to diagnose issues when they occur.
- catch (System.Exception) - { - taskCompletionSource.SetResult(); - } + catch (System.Exception ex) + { + taskCompletionSource.SetException(ex); + }
154-157
: Consider propagating exceptions rather than returning default values.Similar to the non-generic method, this implementation catches all exceptions but doesn't propagate them to the caller. Instead, it returns a default value which could mask errors.
- catch (System.Exception) - { - taskCompletionSource.SetResult(default); - } + catch (System.Exception ex) + { + taskCompletionSource.SetException(ex); + }Flow.Launcher/PublicAPIInstance.cs (3)
131-156
: Consolidate repetitive clipboard copy logic.The logic for direct file/folder copying (lines 131–156) and text copying (lines 160–181) mostly duplicates the retry mechanism and message handling. Consider extracting common retry logic into a shared helper or unify this code block to improve maintainability and clarity.
Also applies to: 160-181
164-164
: Fix minor typo in comment.Change "shouold" to "should" for clarity.
- // We shouold use SetText instead of SetDataObject to avoid the clipboard being locked by other applications + // We should use SetText instead of SetDataObject to avoid the clipboard being locked by other applications
185-204
: Reconsider returning the exception object fromRetryActionOnSTAThreadAsync
.Returning an
Exception
object directly is functional, but throwing an exception or returning a status (e.g.,bool
) might simplify the calling code. The current design requires the caller to check fornull
vs. non-null
to determine success/failure, which can be error-prone or less conventional.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
Flow.Launcher.Infrastructure/NativeMethods.txt
(1 hunks)Flow.Launcher.Infrastructure/UserSettings/Settings.cs
(1 hunks)Flow.Launcher.Infrastructure/Win32Helper.cs
(2 hunks)Flow.Launcher/Languages/en.xaml
(1 hunks)Flow.Launcher/PublicAPIInstance.cs
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: gitStream.cm
- GitHub Check: gitStream.cm
- GitHub Check: gitStream.cm
🔇 Additional comments (4)
Flow.Launcher.Infrastructure/NativeMethods.txt (1)
21-22
: Good addition of required OLE function declarations.These OLE function declarations are necessary for properly initializing and uninitializing the clipboard operations on STA threads, ensuring proper COM interop which is critical for clipboard functionality.
Flow.Launcher/Languages/en.xaml (1)
339-339
: Good addition of error message for clipboard operations.This new string resource will help provide better user feedback when clipboard operations fail, improving the overall user experience.
Flow.Launcher.Infrastructure/UserSettings/Settings.cs (1)
251-251
:✅ Verification successful
Ensure clipboard operations run on STA thread.
Good change to use the new
StartSTATaskAsync
method for clipboard text retrieval. This ensures proper threading model for COM operations.Note that using
.Result
could potentially lead to deadlocks in certain scenarios, but since this is only creating a model with a function reference to be executed later, it should be fine in this context.
🏁 Script executed:
#!/bin/bash # Check if this pattern is used elsewhere in the codebase where it might cause problems rg "StartSTATaskAsync.*\.Result" --type csLength of output: 248
Verified STA Thread Usage for Clipboard Operations
After reviewing the code and verifying the pattern usage in the codebase, it’s clear that the call to
Win32Helper.StartSTATaskAsync(Clipboard.GetText).Result
is only present inFlow.Launcher.Infrastructure/UserSettings/Settings.cs
. Since this function is used to defer clipboard text retrieval until later execution and is correctly encapsulated within a model creation, the use of.Result
here is acceptable and does not pose a deadlock risk in this context.
- Verified that the snippet is uniquely located in the intended file.
- Confirmed that clipboard operations are safely executed on an STA thread.
Flow.Launcher.Infrastructure/Win32Helper.cs (1)
104-174
: Good implementation of STA thread execution helpers.The implementation of these helper methods provides a clean way to execute actions on an STA thread, which is necessary for clipboard and other COM operations. Properly initializing and uninitializing OLE ensures resources are managed correctly.
🥷 Code experts: onesounds Jack251970, onesounds have most 👩💻 activity in the files. See details
Activity based on git-commit:
Knowledge based on git-blame:
Activity based on git-commit:
Knowledge based on git-blame:
Activity based on git-commit:
Knowledge based on git-blame:
Activity based on git-commit:
Knowledge based on git-blame:
Activity based on git-commit:
Knowledge based on git-blame: To learn more about /:\ gitStream - Visit our Docs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (2)
Flow.Launcher/ViewModel/MainViewModel.cs (2)
658-668
:⚠️ Potential issueisReQuery flag is lost when query text changes.
In
ChangeQueryText
whenQueryText != queryText
, you're callingQuery(false, isReQuery: false)
regardless of theisReQuery
parameter value. This means callers cannot force a re-query when changing the query text.if (QueryText != queryText) { // Change query text first QueryText = queryText; // When we are changing query from codes, we should not delay the query - Query(false, isReQuery: false); + Query(false, isReQuery); // set to false so the subsequent set true triggers // PropertyChanged and MoveQueryTextToEnd is called QueryTextCursorMovedToEnd = false; }
1203-1233
: 🛠️ Refactor suggestionShortcut expansion handling could lead to UI freezes.
When a shortcut is expanded (query is null), you appropriately wait for the lock before resetting UI state. However, this waiting happens on the UI thread before switching to the thread pool in line 1235, potentially freezing the UI if the lock is currently held.
if (query == null) // shortcut expanded { App.API.LogDebug(ClassName, $"Query null for QueryText: null"); + // Switch to ThreadPool thread to keep UI responsive when waiting update lock + await TaskScheduler.Default; // Wait last query to be canceled and then do resetting actions await _updateLock.WaitAsync(CancellationToken.None); try { // Reset results Results.Clear(); Results.Visibility = Visibility.Collapsed; // Reset plugin icon PluginIconPath = null; PluginIconSource = null; SearchIconVisibility = Visibility.Visible; // Reset progress bar ProgressBarVisibility = Visibility.Hidden; } finally { _updateLock.Release(); } return; }
🧹 Nitpick comments (3)
Flow.Launcher/ViewModel/MainViewModel.cs (3)
46-48
: Good improvement to concurrency control mechanism.Replacing simple boolean flags with proper concurrency primitives improves thread safety and query flow management:
_updateSource
allows cancellation of obsolete queries_updateToken
provides cancellation support to query tasks_updateLock
ensures only one query update executes at a timeConsider passing a cancellation token to
_updateLock.WaitAsync()
to avoid potential deadlocks if a lock is never released.-await _updateLock.WaitAsync(CancellationToken.None); +await _updateLock.WaitAsync(_updateSource?.Token ?? CancellationToken.None);
678-682
: UseChangeQueryText
instead of recursively callingChangeQueryTextAsync
.The async method recursively calls itself when dispatcher access is needed, but this creates unnecessary async state machines. Since the synchronous version already handles thread dispatching correctly, prefer calling it directly.
-await Application.Current.Dispatcher.InvokeAsync(() => ChangeQueryTextAsync(queryText, isReQuery)); +await Application.Current.Dispatcher.InvokeAsync(() => ChangeQueryText(queryText, isReQuery));
1289-1298
: Good removal of global query delay.Removing the 15ms delay for global queries improves responsiveness, especially for high-frequency queries. The commented-out code should be fully removed in a cleanup pass.
-/*if (string.IsNullOrEmpty(query.ActionKeyword)) -{ - // Wait 15 millisecond for query change in global query - // if query changes, return so that it won't be calculated - await Task.Delay(15, _updateSource.Token); - if (_updateSource.Token.IsCancellationRequested) - return; -}*/
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
Flow.Launcher/ViewModel/MainViewModel.cs
(11 hunks)
🧰 Additional context used
🧠 Learnings (1)
Flow.Launcher/ViewModel/MainViewModel.cs (1)
Learnt from: Yusyuriv
PR: Flow-Launcher/Flow.Launcher#3118
File: Flow.Launcher/ViewModel/MainViewModel.cs:1404-1413
Timestamp: 2024-12-08T21:12:12.060Z
Learning: In the `MainViewModel` class, the `_lastQuery` field is initialized in the constructor and is never null.
🔇 Additional comments (5)
Flow.Launcher/ViewModel/MainViewModel.cs (5)
35-35
: Good replacement of boolean flag with more informative object.Replacing a boolean flag with the actual query object provides more context and enables better tracking of the running query state.
646-652
: Good dispatcher thread safety check.Adding a check for dispatcher access before executing UI thread operations prevents potential deadlocks and UI freezing. This change ensures that UI updates are properly marshaled to the UI thread.
1080-1093
: Good separation of query handling by result type.Splitting the query handling logic based on the selected result type (Results, ContextMenu, or History) improves code organization and maintainability.
1835-1835
: Good resource cleanup practice.Adding disposal of the semaphore in the
Dispose
method prevents resource leaks and follows proper IDisposable pattern implementation.
1357-1419
: Improved QueryTaskAsync local function with better error handling and logging.The local function
QueryTaskAsync
has been enhanced with better error handling, cancellation checks, and detailed logging. These improvements help with debugging and make the code more robust.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copilot reviewed 9 out of 10 changed files in this pull request and generated 2 comments.
Files not reviewed (1)
- Flow.Launcher/Languages/en.xaml: Language not supported
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
Flow.Launcher/ViewModel/MainViewModel.cs (1)
658-658
:⚠️ Potential issue
isReQuery
flag lost when query text changes.Inside the "text changed" branch you hard-code
isReQuery: false
, therefore a caller that wants a re-query (e.g. after settings change) cannot force it when the query string happens to be modified at the same time.-Query(false, isReQuery: false); +Query(false, isReQuery);
🧹 Nitpick comments (1)
Flow.Launcher/ViewModel/MainViewModel.cs (1)
1357-1370
: Using token from shared field in query task.This code relies on
_updateSource.Token
inside the local function, which could lead to race conditions if_updateSource
is reassigned during query processing.Consider passing the token as a parameter to the function:
-async Task QueryTaskAsync(PluginPair plugin) +async Task QueryTaskAsync(PluginPair plugin, CancellationToken token) { if (searchDelay) { var searchDelayTime = plugin.Metadata.SearchDelayTime ?? Settings.SearchDelayTime; - await Task.Delay(searchDelayTime, _updateSource.Token); + await Task.Delay(searchDelayTime, token); - if (_updateSource.Token.IsCancellationRequested) + if (token.IsCancellationRequested) { App.API.LogDebug(ClassName, $"Cancel for QueryText 3: {QueryText}"); return; } } // ... } // And when calling: -var tasks = plugins.Select(plugin => plugin.Metadata.Disabled switch +var capturedToken = _updateToken; +var tasks = plugins.Select(plugin => plugin.Metadata.Disabled switch { - false => QueryTaskAsync(plugin), + false => QueryTaskAsync(plugin, capturedToken), true => Task.CompletedTask }).ToArray();
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
Flow.Launcher/ViewModel/MainViewModel.cs
(11 hunks)
🧰 Additional context used
🧠 Learnings (1)
Flow.Launcher/ViewModel/MainViewModel.cs (1)
Learnt from: Yusyuriv
PR: Flow-Launcher/Flow.Launcher#3118
File: Flow.Launcher/ViewModel/MainViewModel.cs:1404-1413
Timestamp: 2024-12-08T21:12:12.060Z
Learning: In the `MainViewModel` class, the `_lastQuery` field is initialized in the constructor and is never null.
🔇 Additional comments (10)
Flow.Launcher/ViewModel/MainViewModel.cs (10)
35-35
: Good addition of_runningQuery
field for tracking active queries.This field helps track which query is currently running, enabling better handling of concurrent query scenarios and supporting the proper display of progress indicators.
46-48
: Good addition of SemaphoreSlim for query synchronization.Adding the SemaphoreSlim helps ensure only one query update flow is active at a time, preventing race conditions. The descriptive comments also improve code maintainability.
646-651
: Good dispatcher access check to prevent UI thread blocking.Checking access with
CheckAccess()
before execution is an important improvement that prevents potential UI freezes that could occur if the method is called from a background thread.
679-682
: Good dispatcher access handling in async method.The async version correctly uses
InvokeAsync
instead ofInvoke
which prevents blocking a thread pool thread while waiting for the dispatcher.
1080-1093
: Improved query routing with debug logging.The Query method now properly routes to different query methods based on the selected results view with added debug logging which will help troubleshoot issues in production.
1203-1232
: Good handling of null query results from shortcuts.The code properly handles the case when a query is null after shortcut expansion, releasing resources and providing informative debug logs about the operations.
1236-1254
: Good semaphore usage and async flow management.The code properly acquires the semaphore before starting the query process and correctly handles cancellation states. The switch to the ThreadPool thread with
await TaskScheduler.Default
is also a good practice.
1298-1313
: Improved progress bar visibility logic.The progress bar is now only shown after a delay if the query is still running, which should reduce flickering for fast queries and provide a better user experience.
1421-1447
: Changed parameter type to support base shortcut model hierarchy.The
ConstructQuery
method now acceptsIEnumerable<BaseBuiltinShortcutModel>
instead of a specific type, which allows handling both synchronous and asynchronous shortcut models.
1834-1834
: Good resource cleanup for _updateLock in Dispose method.Adding the semaphore to the Dispose method ensures proper resource cleanup and prevents memory leaks.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (2)
Flow.Launcher/ViewModel/MainViewModel.cs (2)
660-682
: 🛠️ Refactor suggestionUse asynchronous dispatcher methods to prevent UI thread blocking
Using synchronous
Dispatcher.Invoke
can lead to deadlocks if the UI thread is busy. The asynchronous counterpart already usesInvokeAsync
(line 695), which is preferred.- Application.Current.Dispatcher.Invoke(() => ChangeQueryText(queryText, isReQuery)); + await Application.Current.Dispatcher.InvokeAsync(() => ChangeQueryText(queryText, isReQuery));Additionally, inside the "text changed" branch you hard-code
isReQuery: false
, therefore a caller that wants a re-query cannot force it when the query string happens to be modified at the same time.- Query(false, isReQuery: false); + Query(false, isReQuery);
1463-1507
:⚠️ Potential issuePotential deadlock risk in
BuildQuery
with synchronous execution of async operationsUsing
App.JTF.Run(() => asyncShortcut.ExpandAsync())
synchronously blocks the calling thread until the async operation completes. IfExpandAsync
attempts to access the UI thread (e.g., for clipboard operations), this can cause a deadlock if called from the UI thread.Consider making
ConstructQuery
and callers async to properly await async expansions:private async Task<Query> ConstructQueryAsync(string queryText, IEnumerable<CustomShortcutModel> customShortcuts, IEnumerable<BaseBuiltinShortcutModel> builtInShortcuts) { // ... existing code ... // Applying builtin shortcuts await BuildQueryAsync(builtInShortcuts, queryBuilder, queryBuilderTmp); return QueryBuilder.Build(queryBuilder.ToString().Trim(), PluginManager.NonGlobalPlugins); } private async Task BuildQueryAsync(IEnumerable<BaseBuiltinShortcutModel> builtInShortcuts, StringBuilder queryBuilder, StringBuilder queryBuilderTmp) { // ... existing code ... foreach (var shortcut in builtInShortcuts) { try { if (customExpanded.Contains(shortcut.Key)) { string expansion; if (shortcut is BuiltinShortcutModel syncShortcut) { expansion = syncShortcut.Expand(); } else if (shortcut is AsyncBuiltinShortcutModel asyncShortcut) { expansion = await asyncShortcut.ExpandAsync(); } else { continue; } queryBuilder.Replace(shortcut.Key, expansion); queryBuilderTmp.Replace(shortcut.Key, expansion); queryChanged = true; } } catch (Exception e) { App.API.LogException(ClassName, $"Error when expanding shortcut {shortcut.Key}", e); } } // ... rest of the method ... }
🧹 Nitpick comments (1)
Flow.Launcher/ViewModel/MainViewModel.cs (1)
1250-1368
: Good threading model improvements, but consider adding a timeout to semaphore waitThe code properly switches to a ThreadPool thread using
await TaskScheduler.Default
after acquiring the lock, which is good practice. However, consider adding a timeout to_updateLock.WaitAsync()
to prevent potential hangs if a lock is never released.- await _updateLock.WaitAsync(CancellationToken.None); + await _updateLock.WaitAsync(_updateSource?.Token ?? CancellationToken.None);Or with a timeout:
await _updateLock.WaitAsync(TimeSpan.FromSeconds(10));
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
Flow.Launcher/ViewModel/MainViewModel.cs
(14 hunks)
🧰 Additional context used
🧠 Learnings (1)
Flow.Launcher/ViewModel/MainViewModel.cs (1)
Learnt from: Yusyuriv
PR: Flow-Launcher/Flow.Launcher#3118
File: Flow.Launcher/ViewModel/MainViewModel.cs:1404-1413
Timestamp: 2024-12-08T21:12:12.060Z
Learning: In the `MainViewModel` class, the `_lastQuery` field is initialized in the constructor and is never null.
🔇 Additional comments (5)
Flow.Launcher/ViewModel/MainViewModel.cs (5)
35-48
: Good refactoring of concurrency controls!The change from a boolean flag to a more structured approach using
_runningQuery
,_updateSource
, and_updateLock
improves thread safety and cancellation handling. This is a good architectural improvement.
1217-1246
: Good cancellation and cleanup handling with proper lock usageThe null query handling with appropriate resource cleanup shows good attention to detail. The use of semaphore with try/finally ensures resources are properly released even in error scenarios.
1379-1395
: Good cancellation check improvements with detailed loggingThe enhanced cancellation checks and logging throughout the query execution path will greatly improve debugging and prevent wasted work on cancelled queries.
1435-1461
: Good method refactoring to support new shortcut modelThe refactoring to support the
BaseBuiltinShortcutModel
parameter type provides better flexibility for handling both synchronous and asynchronous shortcuts.
1848-1848
: Good resource cleanup in Dispose methodAdding the null check with
?
operator for_updateLock?.Dispose()
is a good practice to avoid potential null reference exceptions during application shutdown.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (2)
Flow.Launcher/ViewModel/MainViewModel.cs (2)
659-685
:⚠️ Potential issueFixed potential UI thread issues but lost isReQuery parameter.
The dispatcher check is a good improvement to prevent blocking the UI thread. However, the implementation has a bug at line 672 where
isReQuery
parameter is ignored when callingQuery()
- hard-codingfalse
will prevent re-querying when explicitly requested.Apply this fix to ensure the isReQuery parameter is preserved:
-Query(false, isReQuery: false); +Query(false, isReQuery: isReQuery);
46-49
: 💡 Verification agent🧩 Analysis chain
Good addition of semaphore for synchronization.
Using a
SemaphoreSlim
is an appropriate concurrency control mechanism to ensure only one query updating flow runs at a time. Consider passing cancellation tokens toWaitAsync
calls to avoid potential deadlocks if the owning task never releases the semaphore.
🏁 Script executed:
#!/bin/bash # Check if _updateLock.WaitAsync() calls include cancellation tokens throughout the codebase rg "(_updateLock|updateLock).WaitAsync\(\s*\)" -A 1 --multilineLength of output: 65
🏁 Script executed:
#!/bin/bash # Search for all WaitAsync invocations on _updateLock echo "=== _updateLock.WaitAsync calls ===" rg "_updateLock\.WaitAsync" -n # Search for any WaitAsync calls in the repository echo "=== All WaitAsync( calls ===" rg "WaitAsync\(" -nLength of output: 3193
Use the real cancellation token when waiting on the semaphore
Currently the two
WaitAsync
calls in MainViewModel.cs useCancellationToken.None
, which can never cancel and could still deadlock if a token was requested:• Flow.Launcher/ViewModel/MainViewModel.cs:
- Line 1233:
- await _updateLock.WaitAsync(CancellationToken.None);
- await _updateLock.WaitAsync(_updateToken);
- Line 1259:
- await _updateLock.WaitAsync(CancellationToken.None);
- await _updateLock.WaitAsync(_updateToken);
By passing
_updateToken
(from yourCancellationTokenSource
), any queued waiters will throw if the update is cancelled, avoiding potential deadlocks.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
Flow.Launcher/ViewModel/MainViewModel.cs
(14 hunks)
🧰 Additional context used
🧠 Learnings (1)
Flow.Launcher/ViewModel/MainViewModel.cs (1)
Learnt from: Yusyuriv
PR: Flow-Launcher/Flow.Launcher#3118
File: Flow.Launcher/ViewModel/MainViewModel.cs:1404-1413
Timestamp: 2024-12-08T21:12:12.060Z
Learning: In the `MainViewModel` class, the `_lastQuery` field is initialized in the constructor and is never null.
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: gitStream.cm
🔇 Additional comments (8)
Flow.Launcher/ViewModel/MainViewModel.cs (8)
34-36
: Improved state tracking with_runningQuery
object.Replacing a boolean flag with a
Query
object provides richer state information to track the currently running query, which is a good improvement for maintainability and debuggability.
692-697
: Improved async dispatcher invocation pattern.The recursive invocation pattern is a good approach to ensure the method runs on the UI thread. Using
InvokeAsync
instead ofInvoke
avoids blocking thread pool threads, which is particularly important for async operations.
1094-1107
: Improved query dispatch with appropriate logging.The method now correctly routes queries to the appropriate handler based on the selected results view, with helpful debug logging to track execution flow.
1217-1279
: Good enhancements to query processing with improved state management.The changes include:
- More robust error handling and cleanup for cancelled or expanded queries
- Better query state tracking with the
_runningQuery
object- Proper semaphore usage for synchronization
- Comprehensive debug logging for improved diagnostics
This is a significant improvement to the query execution flow.
1317-1325
: Performance improvement by removing unnecessary delay.Removing the commented-out 15ms delay improves responsiveness, particularly for quick query changes. This is a good optimization.
1345-1350
: Simplified plugin query task creation using pattern matching.The use of pattern matching with switch expression is a clean, modern approach to conditionally create query tasks based on plugin status.
1450-1476
: Support for polymorphic shortcut models.Changing the parameter type to
IEnumerable<BaseBuiltinShortcutModel>
allows for more flexibility by supporting different types of shortcut models in the same collection. This is a good design enhancement that supports the new asynchronous shortcut expansion feature.
1863-1864
: Proper resource cleanup with semaphore disposal.Adding the semaphore to the
Dispose
method is crucial to prevent resource leaks. The null-conditional operator?.
is also a good practice to avoid null reference exceptions.
This comment has been minimized.
This comment has been minimized.
@check-spelling-bot Report🔴 Please reviewSee the 📂 files view, the 📜action log, or 📝 job summary for details.
See ❌ Event descriptions for more information. If the flagged items are 🤯 false positivesIf items relate to a ...
|
Important
All test codes
Infrastructure.Logger.Log.Debug
inMainViewModel
should be removed.Fix copy to clipboard STA thread issue
Fix
CopyToClipboard
function in JsonRPCAPI for JsonRPCV2 plugins: we should use STA thread to execute clipboard options.Close #3071.
Add retry for clipboard copy
Sometimes clipboard is locked, we need to wait and retry.
Async buildin shortcut model
Since new clipboard function need async, we should add support for asynchronous build-in shortcut model.
Refactor query update logic
Fix build shortcuts text replace issue
Fix build shortcuts text replace issue from jjw24 and remove global query delay
Fix possible ui thread issue
Check dispatcher access before invoking dispatch to fix possible ui thread issue
Fix query resetting & updating issue
Add new
slim
&query
for fixing query resetting & update issue.Test