From 97688ef11122858a8669ffccfd3fdee9904ff23b Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 15 Sep 2024 16:39:25 +0800 Subject: [PATCH 1/3] improve plugin loading performance --- .../Environments/AbstractPluginEnvironment.cs | 15 ++-- Flow.Launcher.Core/Plugin/PluginManager.cs | 4 +- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 85 +++++++++++-------- Flow.Launcher/App.xaml.cs | 2 +- 4 files changed, 59 insertions(+), 47 deletions(-) diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs index 30e812c6f05..01a7ef96957 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Windows.Forms; using Flow.Launcher.Core.Resource; +using System.Threading.Tasks; namespace Flow.Launcher.Core.ExternalPlugins.Environments { @@ -37,10 +38,10 @@ internal AbstractPluginEnvironment(List pluginMetadataList, Plug PluginSettings = pluginSettings; } - internal IEnumerable Setup() + internal IEnumerable Setup() { if (!PluginMetadataList.Any(o => o.Language.Equals(Language, StringComparison.OrdinalIgnoreCase))) - return new List(); + return new List(); if (!string.IsNullOrEmpty(PluginsSettingsFilePath) && FilesFolders.FileExists(PluginsSettingsFilePath)) { @@ -87,7 +88,7 @@ internal IEnumerable Setup() $"Not able to successfully set {EnvName} path, setting's plugin executable path variable is still an empty string.", $"{Language}Environment"); - return new List(); + return new List(); } } @@ -106,17 +107,17 @@ private void EnsureLatestInstalled(string expectedPath, string currentPath, stri internal abstract PluginPair CreatePluginPair(string filePath, PluginMetadata metadata); - private IEnumerable SetPathForPluginPairs(string filePath, string languageToSet) + private IEnumerable SetPathForPluginPairs(string filePath, string languageToSet) { - var pluginPairs = new List(); + var tasks = new List(); foreach (var metadata in PluginMetadataList) { if (metadata.Language.Equals(languageToSet, StringComparison.OrdinalIgnoreCase)) - pluginPairs.Add(CreatePluginPair(filePath, metadata)); + tasks.Add(Task.Run(() => CreatePluginPair(filePath, metadata))); } - return pluginPairs; + return tasks; } private string GetFileFromDialog(string title, string filter = "") diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 91cb36a0e3c..e2227ab25b5 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -146,12 +146,12 @@ static PluginManager() /// todo happlebao The API should be removed /// /// - public static void LoadPlugins(PluginsSettings settings) + public static async Task LoadPluginsAsync(PluginsSettings settings) { _metadatas = PluginConfig.Parse(Directories); Settings = settings; Settings.UpdatePluginSettings(_metadatas); - AllPlugins = PluginsLoader.Plugins(_metadatas, Settings); + AllPlugins = await PluginsLoader.PluginsAsync(_metadatas, Settings); } /// diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 0f2e4f996cb..eff6336f088 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -16,7 +17,11 @@ namespace Flow.Launcher.Core.Plugin { public static class PluginsLoader { - public static List Plugins(List metadatas, PluginsSettings settings) + private static readonly ConcurrentQueue Plugins = new(); + + private static readonly ConcurrentQueue ErroredPlugins = new(); + + public static async Task> PluginsAsync(List metadatas, PluginsSettings settings) { var dotnetPlugins = DotNetPlugins(metadatas); @@ -46,17 +51,34 @@ public static List Plugins(List metadatas, PluginsSe .Concat(executablePlugins) .Concat(executableV2Plugins) .ToList(); - return plugins; + + await Task.WhenAll(plugins); + + if (!ErroredPlugins.IsEmpty) + { + var errorPluginString = String.Join(Environment.NewLine, ErroredPlugins); + + var errorMessage = "The following " + + (ErroredPlugins.Count > 1 ? "plugins have " : "plugin has ") + + "errored and cannot be loaded:"; + + _ = Task.Run(() => + { + MessageBox.Show($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" + + $"{errorPluginString}{Environment.NewLine}{Environment.NewLine}" + + $"Please refer to the logs for more information", "", + MessageBoxButtons.OK, MessageBoxIcon.Warning); + }); + } + + return Plugins.ToList(); } - public static IEnumerable DotNetPlugins(List source) + public static IEnumerable DotNetPlugins(List source) { - var erroredPlugins = new List(); - - var plugins = new List(); var metadatas = source.Where(o => AllowedLanguage.IsDotNet(o.Language)); - foreach (var metadata in metadatas) + return metadatas.Select(metadata => Task.Run(() => { var milliseconds = Stopwatch.Debug( $"|PluginsLoader.DotNetPlugins|Constructor init cost for {metadata.Name}", () => @@ -100,53 +122,42 @@ public static IEnumerable DotNetPlugins(List source) if (plugin == null) { - erroredPlugins.Add(metadata.Name); + ErroredPlugins.Enqueue(metadata.Name); return; } - plugins.Add(new PluginPair { Plugin = plugin, Metadata = metadata }); + Plugins.Enqueue(new PluginPair { Plugin = plugin, Metadata = metadata }); }); metadata.InitTime += milliseconds; - } - - if (erroredPlugins.Count > 0) - { - var errorPluginString = String.Join(Environment.NewLine, erroredPlugins); - - var errorMessage = "The following " - + (erroredPlugins.Count > 1 ? "plugins have " : "plugin has ") - + "errored and cannot be loaded:"; - - _ = Task.Run(() => - { - MessageBox.Show($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" + - $"{errorPluginString}{Environment.NewLine}{Environment.NewLine}" + - $"Please refer to the logs for more information", "", - MessageBoxButtons.OK, MessageBoxIcon.Warning); - }); - } - - return plugins; + })); } - public static IEnumerable ExecutablePlugins(IEnumerable source) + public static IEnumerable ExecutablePlugins(IEnumerable source) { return source .Where(o => o.Language.Equals(AllowedLanguage.Executable, StringComparison.OrdinalIgnoreCase)) - .Select(metadata => new PluginPair + .Select(metadata => Task.Run(() => { - Plugin = new ExecutablePlugin(metadata.ExecuteFilePath), Metadata = metadata - }); + return new PluginPair + { + Plugin = new ExecutablePlugin(metadata.ExecuteFilePath), + Metadata = metadata + }; + })); } - public static IEnumerable ExecutableV2Plugins(IEnumerable source) + public static IEnumerable ExecutableV2Plugins(IEnumerable source) { return source .Where(o => o.Language.Equals(AllowedLanguage.ExecutableV2, StringComparison.OrdinalIgnoreCase)) - .Select(metadata => new PluginPair + .Select(metadata => Task.Run(() => { - Plugin = new ExecutablePluginV2(metadata.ExecuteFilePath), Metadata = metadata - }); + return new PluginPair + { + Plugin = new ExecutablePluginV2(metadata.ExecuteFilePath), + Metadata = metadata + }; + })); } } } diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 4d1adc6cd51..dafbab461d2 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -74,7 +74,7 @@ await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => InternationalizationManager.Instance.Settings = _settings; InternationalizationManager.Instance.ChangeLanguage(_settings.Language); - PluginManager.LoadPlugins(_settings.PluginSettings); + await PluginManager.LoadPluginsAsync(_settings.PluginSettings); _mainVM = new MainViewModel(_settings); API = new PublicAPIInstance(_settingsVM, _mainVM, _alphabet); From 0871a0ba893664c108ec6e6eee5f2a111a57c285 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 17 Sep 2024 14:53:22 +0800 Subject: [PATCH 2/3] use generic tasks --- .../Environments/AbstractPluginEnvironment.cs | 10 ++-- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 47 ++++++++++--------- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs index 01a7ef96957..219e923a4ea 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs @@ -38,10 +38,10 @@ internal AbstractPluginEnvironment(List pluginMetadataList, Plug PluginSettings = pluginSettings; } - internal IEnumerable Setup() + internal IEnumerable> Setup() { if (!PluginMetadataList.Any(o => o.Language.Equals(Language, StringComparison.OrdinalIgnoreCase))) - return new List(); + return new List>(); if (!string.IsNullOrEmpty(PluginsSettingsFilePath) && FilesFolders.FileExists(PluginsSettingsFilePath)) { @@ -88,7 +88,7 @@ internal IEnumerable Setup() $"Not able to successfully set {EnvName} path, setting's plugin executable path variable is still an empty string.", $"{Language}Environment"); - return new List(); + return new List>(); } } @@ -107,9 +107,9 @@ private void EnsureLatestInstalled(string expectedPath, string currentPath, stri internal abstract PluginPair CreatePluginPair(string filePath, PluginMetadata metadata); - private IEnumerable SetPathForPluginPairs(string filePath, string languageToSet) + private IEnumerable> SetPathForPluginPairs(string filePath, string languageToSet) { - var tasks = new List(); + var tasks = new List>(); foreach (var metadata in PluginMetadataList) { diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index eff6336f088..94abc30421a 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -25,6 +25,25 @@ public static async Task> PluginsAsync(List met { var dotnetPlugins = DotNetPlugins(metadatas); + await Task.WhenAll(dotnetPlugins); + + if (!ErroredPlugins.IsEmpty) + { + var errorPluginString = String.Join(Environment.NewLine, ErroredPlugins); + + var errorMessage = "The following " + + (ErroredPlugins.Count > 1 ? "plugins have " : "plugin has ") + + "errored and cannot be loaded:"; + + _ = Task.Run(() => + { + MessageBox.Show($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" + + $"{errorPluginString}{Environment.NewLine}{Environment.NewLine}" + + $"Please refer to the logs for more information", "", + MessageBoxButtons.OK, MessageBoxIcon.Warning); + }); + } + var pythonEnv = new PythonEnvironment(metadatas, settings); var pythonV2Env = new PythonV2Environment(metadatas, settings); var tsEnv = new TypeScriptEnvironment(metadatas, settings); @@ -41,8 +60,7 @@ public static async Task> PluginsAsync(List met var executablePlugins = ExecutablePlugins(metadatas); var executableV2Plugins = ExecutableV2Plugins(metadatas); - var plugins = dotnetPlugins - .Concat(pythonPlugins) + var plugins = pythonPlugins .Concat(pythonV2Plugins) .Concat(tsPlugins) .Concat(jsPlugins) @@ -52,26 +70,9 @@ public static async Task> PluginsAsync(List met .Concat(executableV2Plugins) .ToList(); - await Task.WhenAll(plugins); - - if (!ErroredPlugins.IsEmpty) - { - var errorPluginString = String.Join(Environment.NewLine, ErroredPlugins); - - var errorMessage = "The following " - + (ErroredPlugins.Count > 1 ? "plugins have " : "plugin has ") - + "errored and cannot be loaded:"; - - _ = Task.Run(() => - { - MessageBox.Show($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" + - $"{errorPluginString}{Environment.NewLine}{Environment.NewLine}" + - $"Please refer to the logs for more information", "", - MessageBoxButtons.OK, MessageBoxIcon.Warning); - }); - } + var pluginPairs = await Task.WhenAll(plugins); - return Plugins.ToList(); + return Plugins.Concat(pluginPairs).ToList(); } public static IEnumerable DotNetPlugins(List source) @@ -132,7 +133,7 @@ public static IEnumerable DotNetPlugins(List source) })); } - public static IEnumerable ExecutablePlugins(IEnumerable source) + public static IEnumerable> ExecutablePlugins(IEnumerable source) { return source .Where(o => o.Language.Equals(AllowedLanguage.Executable, StringComparison.OrdinalIgnoreCase)) @@ -146,7 +147,7 @@ public static IEnumerable ExecutablePlugins(IEnumerable so })); } - public static IEnumerable ExecutableV2Plugins(IEnumerable source) + public static IEnumerable> ExecutableV2Plugins(IEnumerable source) { return source .Where(o => o.Language.Equals(AllowedLanguage.ExecutableV2, StringComparison.OrdinalIgnoreCase)) From 14fd0e583166a86909d56437393608a2262e9dfb Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Tue, 17 Sep 2024 15:35:43 +0800 Subject: [PATCH 3/3] improve code quality --- .../Environments/AbstractPluginEnvironment.cs | 13 ++-- Flow.Launcher.Core/Plugin/PluginsLoader.cs | 60 ++++++++++--------- 2 files changed, 39 insertions(+), 34 deletions(-) diff --git a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs index 219e923a4ea..ada425f7e3e 100644 --- a/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs +++ b/Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs @@ -9,6 +9,7 @@ using System.Windows.Forms; using Flow.Launcher.Core.Resource; using System.Threading.Tasks; +using Flow.Launcher.Core.Plugin; namespace Flow.Launcher.Core.ExternalPlugins.Environments { @@ -38,10 +39,10 @@ internal AbstractPluginEnvironment(List pluginMetadataList, Plug PluginSettings = pluginSettings; } - internal IEnumerable> Setup() + internal IEnumerable Setup() { if (!PluginMetadataList.Any(o => o.Language.Equals(Language, StringComparison.OrdinalIgnoreCase))) - return new List>(); + return new List(); if (!string.IsNullOrEmpty(PluginsSettingsFilePath) && FilesFolders.FileExists(PluginsSettingsFilePath)) { @@ -88,7 +89,7 @@ internal IEnumerable> Setup() $"Not able to successfully set {EnvName} path, setting's plugin executable path variable is still an empty string.", $"{Language}Environment"); - return new List>(); + return new List(); } } @@ -107,14 +108,14 @@ private void EnsureLatestInstalled(string expectedPath, string currentPath, stri internal abstract PluginPair CreatePluginPair(string filePath, PluginMetadata metadata); - private IEnumerable> SetPathForPluginPairs(string filePath, string languageToSet) + private IEnumerable SetPathForPluginPairs(string filePath, string languageToSet) { - var tasks = new List>(); + var tasks = new List(); foreach (var metadata in PluginMetadataList) { if (metadata.Language.Equals(languageToSet, StringComparison.OrdinalIgnoreCase)) - tasks.Add(Task.Run(() => CreatePluginPair(filePath, metadata))); + tasks.Add(Task.Run(() => PluginsLoader.AddPlugin(CreatePluginPair(filePath, metadata)))); } return tasks; diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 94abc30421a..b40f2c5a7cd 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -21,29 +21,15 @@ public static class PluginsLoader private static readonly ConcurrentQueue ErroredPlugins = new(); + public static void AddPlugin(PluginPair plugin) + { + Plugins.Enqueue(plugin); + } + public static async Task> PluginsAsync(List metadatas, PluginsSettings settings) { var dotnetPlugins = DotNetPlugins(metadatas); - await Task.WhenAll(dotnetPlugins); - - if (!ErroredPlugins.IsEmpty) - { - var errorPluginString = String.Join(Environment.NewLine, ErroredPlugins); - - var errorMessage = "The following " - + (ErroredPlugins.Count > 1 ? "plugins have " : "plugin has ") - + "errored and cannot be loaded:"; - - _ = Task.Run(() => - { - MessageBox.Show($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" + - $"{errorPluginString}{Environment.NewLine}{Environment.NewLine}" + - $"Please refer to the logs for more information", "", - MessageBoxButtons.OK, MessageBoxIcon.Warning); - }); - } - var pythonEnv = new PythonEnvironment(metadatas, settings); var pythonV2Env = new PythonV2Environment(metadatas, settings); var tsEnv = new TypeScriptEnvironment(metadatas, settings); @@ -60,7 +46,8 @@ public static async Task> PluginsAsync(List met var executablePlugins = ExecutablePlugins(metadatas); var executableV2Plugins = ExecutableV2Plugins(metadatas); - var plugins = pythonPlugins + var plugins = dotnetPlugins + .Concat(pythonPlugins) .Concat(pythonV2Plugins) .Concat(tsPlugins) .Concat(jsPlugins) @@ -70,9 +57,26 @@ public static async Task> PluginsAsync(List met .Concat(executableV2Plugins) .ToList(); - var pluginPairs = await Task.WhenAll(plugins); + await Task.WhenAll(plugins); + + if (!ErroredPlugins.IsEmpty) + { + var errorPluginString = String.Join(Environment.NewLine, ErroredPlugins); + + var errorMessage = "The following " + + (ErroredPlugins.Count > 1 ? "plugins have " : "plugin has ") + + "errored and cannot be loaded:"; + + _ = Task.Run(() => + { + MessageBox.Show($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" + + $"{errorPluginString}{Environment.NewLine}{Environment.NewLine}" + + $"Please refer to the logs for more information", "", + MessageBoxButtons.OK, MessageBoxIcon.Warning); + }); + } - return Plugins.Concat(pluginPairs).ToList(); + return Plugins.ToList(); } public static IEnumerable DotNetPlugins(List source) @@ -133,31 +137,31 @@ public static IEnumerable DotNetPlugins(List source) })); } - public static IEnumerable> ExecutablePlugins(IEnumerable source) + public static IEnumerable ExecutablePlugins(IEnumerable source) { return source .Where(o => o.Language.Equals(AllowedLanguage.Executable, StringComparison.OrdinalIgnoreCase)) .Select(metadata => Task.Run(() => { - return new PluginPair + Plugins.Enqueue(new PluginPair { Plugin = new ExecutablePlugin(metadata.ExecuteFilePath), Metadata = metadata - }; + }); })); } - public static IEnumerable> ExecutableV2Plugins(IEnumerable source) + public static IEnumerable ExecutableV2Plugins(IEnumerable source) { return source .Where(o => o.Language.Equals(AllowedLanguage.ExecutableV2, StringComparison.OrdinalIgnoreCase)) .Select(metadata => Task.Run(() => { - return new PluginPair + Plugins.Enqueue(new PluginPair { Plugin = new ExecutablePluginV2(metadata.ExecuteFilePath), Metadata = metadata - }; + }); })); } }