From eb2500dcb230598346aeb0e8223fdb53ffbdfda4 Mon Sep 17 00:00:00 2001 From: Alexander Larsen Date: Tue, 28 May 2024 22:08:58 +0200 Subject: [PATCH 1/2] fix package manager hang condition Two issues caused the package manager to hang: 1. Incorrect installation detection caused an unhandled exception 2. Unawaited tasks caused the unhandled exception to get swallowed --- OpenTAP.TUI/Views/PackageListView.cs | 2 +- .../Windows/PackageVersionSelectorWindow.cs | 25 ++++++++++++++----- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/OpenTAP.TUI/Views/PackageListView.cs b/OpenTAP.TUI/Views/PackageListView.cs index e746a7a2..fe6df7ff 100644 --- a/OpenTAP.TUI/Views/PackageListView.cs +++ b/OpenTAP.TUI/Views/PackageListView.cs @@ -46,7 +46,7 @@ public override bool ProcessKey(KeyEvent keyEvent) public PackageListView() { - installation = new Installation(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)); + installation = Installation.Current; installedOpentap = installation.GetOpenTapPackage(); installedPackages = installation.GetPackages(); diff --git a/OpenTAP.TUI/Windows/PackageVersionSelectorWindow.cs b/OpenTAP.TUI/Windows/PackageVersionSelectorWindow.cs index dbd1940f..a18ef9e1 100644 --- a/OpenTAP.TUI/Windows/PackageVersionSelectorWindow.cs +++ b/OpenTAP.TUI/Windows/PackageVersionSelectorWindow.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; @@ -103,10 +104,22 @@ public PackageVersionSelectorWindow(PackageViewModel package, Installation insta bool running = true; Task.Run(() => { - versions = GetVersions(); - UpdateVersions(); - running = false; - }); + try + { + versions = GetVersions(); + UpdateVersions(); + } + catch (Exception ex) + { + var log = Log.CreateSource("Package Manager"); + log.Error($"Error getting package versions: '{ex.Message}'"); + log.Debug(ex); + } + finally + { + running = false; + } + }).GetAwaiter().GetResult(); Task.Run(() => { while (running) @@ -131,7 +144,7 @@ public PackageVersionSelectorWindow(PackageViewModel package, Installation insta Application.RequestStop(); } }); - }); + }).GetAwaiter().GetResult(); } void InstallButtonClicked(bool force) From d48ebedefd5d7bc24d99c5b329ce52dab7ed0f96 Mon Sep 17 00:00:00 2001 From: Alexander Larsen Date: Wed, 29 May 2024 16:59:44 +0200 Subject: [PATCH 2/2] Improve package manager latency by awaiting the task fetching package versions --- .../Windows/PackageVersionSelectorWindow.cs | 69 +++++++++---------- 1 file changed, 32 insertions(+), 37 deletions(-) diff --git a/OpenTAP.TUI/Windows/PackageVersionSelectorWindow.cs b/OpenTAP.TUI/Windows/PackageVersionSelectorWindow.cs index a18ef9e1..60d7facc 100644 --- a/OpenTAP.TUI/Windows/PackageVersionSelectorWindow.cs +++ b/OpenTAP.TUI/Windows/PackageVersionSelectorWindow.cs @@ -101,8 +101,7 @@ public PackageVersionSelectorWindow(PackageViewModel package, Installation insta Add(logFrame); // Load packages in parallel - bool running = true; - Task.Run(() => + var t1 = Task.Run(() => { try { @@ -115,28 +114,24 @@ public PackageVersionSelectorWindow(PackageViewModel package, Installation insta log.Error($"Error getting package versions: '{ex.Message}'"); log.Debug(ex); } - finally - { - running = false; - } - }).GetAwaiter().GetResult(); + }); Task.Run(() => { - while (running) + while (!t1.IsCompleted) { Application.MainLoop.Invoke(() => versionsFrame.Title = $"Versions "); - Thread.Sleep(100); - - for (int i = 0; i < 3 && running; i++) + t1.Wait(100); + + for (int i = 0; i < 3 && !t1.IsCompleted; i++) { Application.MainLoop.Invoke(() => versionsFrame.Title += "."); - Thread.Sleep(100); + t1.Wait(100); } } Application.MainLoop.Invoke(() => { versionsFrame.Title = $"Versions"; - + if (versions.Count == 0) { // this occurs if the architecture or OS does not match any of the packages. MessageBox.Query("No Plugin Packages Available", "No compatible plugin packages available.", @@ -144,7 +139,7 @@ public PackageVersionSelectorWindow(PackageViewModel package, Installation insta Application.RequestStop(); } }); - }).GetAwaiter().GetResult(); + }); } void InstallButtonClicked(bool force) @@ -152,7 +147,7 @@ void InstallButtonClicked(bool force) var selectedPackage = versions?.FirstOrDefault(); if (selectedPackage == null) return; - + for (int i = 0; i < versionsView.Source.Count; i++) { if (versionsView.Source.IsMarked(i)) @@ -165,12 +160,12 @@ void InstallButtonClicked(bool force) runningThread.Abort(); return; } - + if ((installedVersion != null && installedVersion.Version == selectedPackage.Version) == false) { var installAction = new PackageInstallAction() { - Packages = new[] {package.Name}, + Packages = new[] { package.Name }, Version = selectedPackage.Version.ToString(), Force = force }; @@ -178,13 +173,13 @@ void InstallButtonClicked(bool force) { Application.MainLoop.Invoke(() => MessageBox.ErrorQuery(50, 7, "Installation Error", exception.Message, "Ok")); }; - + installButton.Text = "Cancel"; runningThread = TapThread.Start(() => { // Add tui user input UserInput.SetInterface(new TuiUserInput()); - + installAction.Execute(TapThread.Current.AbortToken); Application.MainLoop.Invoke(UpdateVersions); runningThread = null; @@ -194,33 +189,33 @@ void InstallButtonClicked(bool force) { var uninstallAction = new PackageUninstallAction() { - Packages = new []{ package.Name }, + Packages = new[] { package.Name }, Force = force }; - + uninstallAction.Error += exception => { Application.MainLoop.Invoke(() => MessageBox.ErrorQuery(50, 7, "Uninstallation Error", exception.Message, "Ok")); }; - + installButton.Text = "Cancel"; runningThread = TapThread.Start(() => { // Add tui user input UserInput.SetInterface(new TuiUserInput()); - + uninstallAction.Execute(TapThread.Current.AbortToken); Application.MainLoop.Invoke(UpdateVersions); runningThread = null; }); - } + } } List GetVersions() { var list = new List(); var semvers = new HashSet(); - + var repos = PackageManagerSettings.Current.Repositories.Where(r => r.IsEnabled).OrderByDescending(r => r.Manager is FilePackageRepository); foreach (var repository in repos) { @@ -247,7 +242,7 @@ List GetFilePackages(FilePackageRepository repository) { TuiPm.Log.Info("Loading packages from: " + repository.Url); var list = new List(); - + var versions = repository.GetPackageVersions(package.Name, TuiPm.CancellationToken, installedOpentap); foreach (var version in versions) { @@ -255,14 +250,14 @@ List GetFilePackages(FilePackageRepository repository) if (packageDef != null) list.Add(new PackageViewModel(packageDef)); } - + return list; } List GetHttpPackages(HttpPackageRepository repository) { var list = new List(); - + TuiPm.Log.Info("Loading packages from: " + repository.Url); HttpClient hc = new HttpClient(); hc.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); @@ -282,10 +277,10 @@ List GetHttpPackages(HttpPackageRepository repository) }"); var response = hc.PostAsync("http://packages.opentap.io/3.1/Query", content, TuiPm.CancellationToken).Result; var jsonData = response.Content.ReadAsStringAsync().Result; - + // Remove unicode chars jsonData = Regex.Replace(jsonData, @"[^\u0000-\u007F]+", string.Empty); - + // Parse the json response data var jsonPackages = (JsonElement)JsonSerializer.Deserialize>(jsonData)["packages"]; foreach (var item in jsonPackages.EnumerateArray()) @@ -302,23 +297,23 @@ List GetHttpPackages(HttpPackageRepository repository) void UpdateVersions() { installedVersion = installation.GetPackages().FirstOrDefault(p => p.Name == package.Name); - + versionsView.SetSource(versions.Select(p => $"{p.Version}{(installedVersion?.Version == p.Version ? " (Installed)" : "")}").ToList()); versionsView.Source.SetMark(0, true); versionsView.SelectedItem = 0; - + installButton.Text = versions.FirstOrDefault()?.Version == installedVersion?.Version ? "Uninstall" : "Install"; } - - public override bool ProcessKey (KeyEvent keyEvent) + + public override bool ProcessKey(KeyEvent keyEvent) { if (KeyMapHelper.IsKey(keyEvent, KeyTypes.Cancel)) { Running = false; return true; } - - return base.ProcessKey (keyEvent); + + return base.ProcessKey(keyEvent); } } -} \ No newline at end of file +}