CherryPick Exception Optimize

This commit is contained in:
Kevin Zhang 2021-06-11 12:40:07 +08:00 committed by 张弘韬
parent fe859f6041
commit f80ae3092e
6 changed files with 122 additions and 102 deletions

View file

@ -157,7 +157,7 @@ namespace Flow.Launcher.Core.Plugin
}
}
public static async Task<List<Result>> QueryForPlugin(PluginPair pair, Query query, CancellationToken token)
public static async Task<List<Result>> QueryForPluginAsync(PluginPair pair, Query query, CancellationToken token)
{
var results = new List<Result>();
try
@ -171,7 +171,7 @@ namespace Flow.Launcher.Core.Plugin
token.ThrowIfCancellationRequested();
if (results == null)
return results;
return null;
UpdatePluginMetadata(results, metadata, query);
metadata.QueryCount += 1;
@ -184,10 +184,6 @@ namespace Flow.Launcher.Core.Plugin
// null will be fine since the results will only be added into queue if the token hasn't been cancelled
return null;
}
catch (Exception e)
{
Log.Exception($"|PluginManager.QueryForPlugin|Exception for plugin <{pair.Metadata.Name}> when query <{query}>", e);
}
return results;
}

View file

@ -49,6 +49,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Ben.Demystifier" Version="0.4.1" />
<PackageReference Include="Microsoft.VisualStudio.Threading" Version="16.10.56" />
<PackageReference Include="NLog.Schema" Version="4.7.0-rc1" />
<PackageReference Include="NLog.Web.AspNetCore" Version="4.9.0" />
<PackageReference Include="System.Drawing.Common" Version="4.7.0" />

View file

@ -5,6 +5,9 @@ using NLog;
using NLog.Config;
using NLog.Targets;
using Flow.Launcher.Infrastructure.UserSettings;
using JetBrains.Annotations;
using NLog.Targets.Wrappers;
using System.Runtime.ExceptionServices;
namespace Flow.Launcher.Infrastructure.Logger
{
@ -23,11 +26,13 @@ namespace Flow.Launcher.Infrastructure.Logger
}
var configuration = new LoggingConfiguration();
var target = new FileTarget();
configuration.AddTarget("file", target);
target.FileName = CurrentLogDirectory.Replace(@"\", "/") + "/${shortdate}.txt";
var fileTarget = new FileTarget();
fileTarget.FileName = CurrentLogDirectory.Replace(@"\", "/") + "/${shortdate}.txt";
var fileTargetASyncWrapper = new AsyncTargetWrapper(fileTarget);
configuration.AddTarget("file", fileTargetASyncWrapper);
#if DEBUG
var rule = new LoggingRule("*", LogLevel.Debug, target);
var rule = new LoggingRule("*", LogLevel.Debug, fileTargetASyncWrapper);
#else
var rule = new LoggingRule("*", LogLevel.Info, target);
#endif
@ -50,13 +55,12 @@ namespace Flow.Launcher.Infrastructure.Logger
return valid;
}
[MethodImpl(MethodImplOptions.Synchronized)]
public static void Exception(string className, string message, System.Exception exception, [CallerMemberName] string methodName = "")
{
{
exception = exception.Demystify();
#if DEBUG
throw exception;
ExceptionDispatchInfo.Capture(exception).Throw();
#else
var classNameWithMethod = CheckClassAndMessageAndReturnFullClassWithMethod(className, message, methodName);
@ -131,8 +135,9 @@ namespace Flow.Launcher.Infrastructure.Logger
[MethodImpl(MethodImplOptions.Synchronized)]
public static void Exception(string message, System.Exception e)
{
e = e.Demystify();
#if DEBUG
throw e;
ExceptionDispatchInfo.Capture(e).Throw();
#else
if (FormatValid(message))
{

View file

@ -19,8 +19,10 @@ using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.SharedCommands;
using Flow.Launcher.Storage;
using Flow.Launcher.Infrastructure.Logger;
using Microsoft.VisualStudio.Threading;
using System.Threading.Channels;
using ISavable = Flow.Launcher.Plugin.ISavable;
using System.Windows.Threading;
namespace Flow.Launcher.ViewModel
{
@ -110,7 +112,9 @@ namespace Flow.Launcher.ViewModel
}
Log.Error("MainViewModel", "Unexpected ResultViewUpdate ends");
};
}
;
void continueAction(Task t)
{
@ -118,7 +122,7 @@ namespace Flow.Launcher.ViewModel
throw t.Exception;
#else
Log.Error($"Error happen in task dealing with viewupdate for results. {t.Exception}");
_resultsViewUpdateTask =
_resultsViewUpdateTask =
Task.Run(updateAction).ContinueWith(continueAction, TaskContinuationOptions.OnlyOnFaulted);
#endif
}
@ -137,7 +141,8 @@ namespace Flow.Launcher.ViewModel
if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(e.Results, pair.Metadata, e.Query, _updateToken)))
{
Log.Error("MainViewModel", "Unable to add item to Result Update Queue");
};
}
;
}
};
}
@ -237,21 +242,24 @@ namespace Flow.Launcher.ViewModel
ReloadPluginDataCommand = new RelayCommand(_ =>
{
var msg = new Msg { Owner = Application.Current.MainWindow };
var msg = new Msg
{
Owner = Application.Current.MainWindow
};
MainWindowVisibility = Visibility.Collapsed;
PluginManager
.ReloadData()
.ContinueWith(_ =>
Application.Current.Dispatcher.Invoke(() =>
{
msg.Show(
InternationalizationManager.Instance.GetTranslation("success"),
InternationalizationManager.Instance.GetTranslation("completedSuccessfully"),
"");
}))
.ConfigureAwait(false);
.ReloadData()
.ContinueWith(_ =>
Application.Current.Dispatcher.Invoke(() =>
{
msg.Show(
InternationalizationManager.Instance.GetTranslation("success"),
InternationalizationManager.Instance.GetTranslation("completedSuccessfully"),
"");
}))
.ConfigureAwait(false);
});
}
@ -422,7 +430,10 @@ namespace Flow.Launcher.ViewModel
Title = string.Format(title, h.Query),
SubTitle = string.Format(time, h.ExecutedDateTime),
IcoPath = "Images\\history.png",
OriginQuery = new Query { RawQuery = h.Query },
OriginQuery = new Query
{
RawQuery = h.Query
},
Action = _ =>
{
SelectedResults = Results;
@ -448,7 +459,9 @@ namespace Flow.Launcher.ViewModel
}
}
private void QueryResults()
private readonly IReadOnlyList<Result> _emptyResult = new List<Result>();
private async void QueryResults()
{
_updateSource?.Cancel();
@ -478,74 +491,73 @@ namespace Flow.Launcher.ViewModel
var plugins = PluginManager.ValidPluginsForQuery(query);
Task.Run(async () =>
if (query.ActionKeyword == Plugin.Query.GlobalPluginWildcardSign)
{
// Wait 45 millisecond for query change in global query
// if query changes, return so that it won't be calculated
await Task.Delay(45, currentCancellationToken);
if (currentCancellationToken.IsCancellationRequested)
return;
}
_ = Task.Delay(200, currentCancellationToken).ContinueWith(_ =>
{
// start the progress bar if query takes more than 200 ms and this is the current running query and it didn't finish yet
if (!currentCancellationToken.IsCancellationRequested && _isQueryRunning)
{
if (query.ActionKeyword == Plugin.Query.GlobalPluginWildcardSign)
{
// Wait 45 millisecond for query change in global query
// if query changes, return so that it won't be calculated
await Task.Delay(45, currentCancellationToken);
if (currentCancellationToken.IsCancellationRequested)
return;
}
ProgressBarVisibility = Visibility.Visible;
}
}, currentCancellationToken, TaskContinuationOptions.NotOnCanceled, TaskScheduler.Default);
_ = Task.Delay(200, currentCancellationToken).ContinueWith(_ =>
{
// start the progress bar if query takes more than 200 ms and this is the current running query and it didn't finish yet
if (!currentCancellationToken.IsCancellationRequested && _isQueryRunning)
{
ProgressBarVisibility = Visibility.Visible;
}
}, currentCancellationToken);
// plugins is ICollection, meaning LINQ will get the Count and preallocate Array
// plugins is ICollection, meaning LINQ will get the Count and preallocate Array
var tasks = plugins.Select(plugin => plugin.Metadata.Disabled switch
{
false => QueryTask(plugin),
true => Task.CompletedTask
}).ToArray();
Task[] tasks = plugins.Select(plugin => plugin.Metadata.Disabled switch
{
false => QueryTask(plugin),
true => Task.CompletedTask
}).ToArray();
try
{
// Check the code, WhenAll will translate all type of IEnumerable or Collection to Array, so make an array at first
await Task.WhenAll(tasks);
}
catch (OperationCanceledException)
{
// nothing to do here
}
try
{
// Check the code, WhenAll will translate all type of IEnumerable or Collection to Array, so make an array at first
await Task.WhenAll(tasks);
}
catch (OperationCanceledException)
{
// nothing to do here
}
if (currentCancellationToken.IsCancellationRequested)
return;
if (currentCancellationToken.IsCancellationRequested)
return;
// this should happen once after all queries are done so progress bar should continue
// until the end of all querying
_isQueryRunning = false;
if (!currentCancellationToken.IsCancellationRequested)
{
// update to hidden if this is still the current query
ProgressBarVisibility = Visibility.Hidden;
}
// this should happen once after all queries are done so progress bar should continue
// until the end of all querying
_isQueryRunning = false;
if (!currentCancellationToken.IsCancellationRequested)
{
// update to hidden if this is still the current query
ProgressBarVisibility = Visibility.Hidden;
}
// Local function
async Task QueryTask(PluginPair plugin)
{
// Since it is wrapped within a Task.Run, the synchronous context is null
// Task.Yield will force it to run in ThreadPool
await Task.Yield();
// Local function
async Task QueryTask(PluginPair plugin)
{
// Since it is wrapped within a Task.Run, the synchronous context is null
// Task.Yield will force it to run in ThreadPool
await Task.Yield();
var results = await PluginManager.QueryForPlugin(plugin, query, currentCancellationToken);
if (currentCancellationToken.IsCancellationRequested || results == null) return;
IReadOnlyList<Result> results = await PluginManager.QueryForPluginAsync(plugin, query, currentCancellationToken);
currentCancellationToken.ThrowIfCancellationRequested();
if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(results, plugin.Metadata, query, currentCancellationToken)))
{
Log.Error("MainViewModel", "Unable to add item to Result Update Queue");
};
}
}, currentCancellationToken)
.ContinueWith(t => Log.Exception("|MainViewModel|Plugins Query Exceptions", t.Exception),
TaskContinuationOptions.OnlyOnFaulted);
results ??= _emptyResult;
if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(results, plugin.Metadata, query, currentCancellationToken)))
{
Log.Error("MainViewModel", "Unable to add item to Result Update Queue");
}
}
}

View file

@ -8,7 +8,7 @@ namespace Flow.Launcher.ViewModel
{
public struct ResultsForUpdate
{
public List<Result> Results { get; }
public IReadOnlyList<Result> Results { get; }
public PluginMetadata Metadata { get; }
public string ID { get; }
@ -16,7 +16,7 @@ namespace Flow.Launcher.ViewModel
public Query Query { get; }
public CancellationToken Token { get; }
public ResultsForUpdate(List<Result> results, PluginMetadata metadata, Query query, CancellationToken token)
public ResultsForUpdate(IReadOnlyList<Result> results, PluginMetadata metadata, Query query, CancellationToken token)
{
Results = results;
Metadata = metadata;

View file

@ -17,20 +17,20 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
// Reserved keywords in oleDB
private const string reservedStringPattern = @"^[`\@\#\^,\&\/\\\$\%_]+$";
internal async static Task<List<Result>> ExecuteWindowsIndexSearchAsync(string indexQueryString, string connectionString, Query query, CancellationToken token)
internal static async Task<List<Result>> ExecuteWindowsIndexSearchAsync(string indexQueryString, string connectionString, Query query, CancellationToken token)
{
var results = new List<Result>();
var fileResults = new List<Result>();
try
{
using var conn = new OleDbConnection(connectionString);
await using var conn = new OleDbConnection(connectionString);
await conn.OpenAsync(token);
token.ThrowIfCancellationRequested();
using var command = new OleDbCommand(indexQueryString, conn);
await using var command = new OleDbCommand(indexQueryString, conn);
// Results return as an OleDbDataReader.
using var dataReaderResults = await command.ExecuteReaderAsync(token) as OleDbDataReader;
await using var dataReaderResults = await command.ExecuteReaderAsync(token) as OleDbDataReader;
token.ThrowIfCancellationRequested();
if (dataReaderResults.HasRows)
@ -42,18 +42,18 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
{
// # is URI syntax for the fragment component, need to be encoded so LocalPath returns complete path
var encodedFragmentPath = dataReaderResults
.GetString(1)
.Replace("#", "%23", StringComparison.OrdinalIgnoreCase);
.GetString(1)
.Replace("#", "%23", StringComparison.OrdinalIgnoreCase);
var path = new Uri(encodedFragmentPath).LocalPath;
if (dataReaderResults.GetString(2) == "Directory")
{
results.Add(ResultManager.CreateFolderResult(
dataReaderResults.GetString(0),
path,
path,
query, 0, true, true));
dataReaderResults.GetString(0),
path,
path,
query, 0, true, true));
}
else
{
@ -63,6 +63,11 @@ namespace Flow.Launcher.Plugin.Explorer.Search.WindowsIndex
}
}
}
catch (OperationCanceledException)
{
// return empty result when cancelled
return results;
}
catch (InvalidOperationException e)
{
// Internal error from ExecuteReader(): Connection closed.