From 8330dd356fef6079057695f3a80215510bd5f038 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Mon, 26 Jul 2021 12:18:49 +0800 Subject: [PATCH 1/5] Add a MemoryStream buffer to ReadStream first --- Flow.Launcher.Core/Flow.Launcher.Core.csproj | 1 + Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs | 55 +++++++++++++++----- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/Flow.Launcher.Core/Flow.Launcher.Core.csproj b/Flow.Launcher.Core/Flow.Launcher.Core.csproj index d7df11f8303..9e932a5085b 100644 --- a/Flow.Launcher.Core/Flow.Launcher.Core.csproj +++ b/Flow.Launcher.Core/Flow.Launcher.Core.csproj @@ -55,6 +55,7 @@ + diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index 431458881ec..5e44bef8697 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -12,6 +12,7 @@ using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Plugin; using JetBrains.Annotations; +using Microsoft.IO; namespace Flow.Launcher.Core.Plugin { @@ -33,9 +34,11 @@ internal abstract class JsonRPCPlugin : IAsyncPlugin, IContextMenu protected abstract string ExecuteCallback(JsonRPCRequestModel rpcRequest); protected abstract string ExecuteContextMenu(Result selectedResult); + private static readonly RecyclableMemoryStreamManager BufferManager = new(); + public List LoadContextMenus(Result selectedResult) { - string output = ExecuteContextMenu(selectedResult); + var output = ExecuteContextMenu(selectedResult); try { return DeserializedResult(output); @@ -61,12 +64,23 @@ private async Task> DeserializedResultAsync(Stream output) { if (output == Stream.Null) return null; - var queryResponseModel = await - JsonSerializer.DeserializeAsync(output, options); + try + { + var queryResponseModel = + await JsonSerializer.DeserializeAsync(output, options); - await output.DisposeAsync(); - - return ParseResults(queryResponseModel); + return ParseResults(queryResponseModel); + } + catch (JsonException e) + { + Log.Exception(GetType().FullName, "Unexpected Json Input", e); + } + finally + { + await output.DisposeAsync(); + } + + return null; } private List DeserializedResult(string output) @@ -81,7 +95,6 @@ private List DeserializedResult(string output) private List ParseResults(JsonRPCQueryResponseModel queryResponseModel) { - var results = new List(); if (queryResponseModel.Result == null) return null; if (!string.IsNullOrEmpty(queryResponseModel.DebugMessage)) @@ -89,7 +102,7 @@ private List ParseResults(JsonRPCQueryResponseModel queryResponseModel) context.API.ShowMsg(queryResponseModel.DebugMessage); } - foreach (JsonRPCResult result in queryResponseModel.Result) + foreach (var result in queryResponseModel.Result) { result.Action = c => { @@ -114,7 +127,8 @@ private List ParseResults(JsonRPCQueryResponseModel queryResponseModel) return !result.JsonRPCAction.DontHideAfterAction; } - var jsonRpcRequestModel = JsonSerializer.Deserialize(actionResponse, options); + var jsonRpcRequestModel = + JsonSerializer.Deserialize(actionResponse, options); if (jsonRpcRequestModel?.Method?.StartsWith("Flow.Launcher.") ?? false) { @@ -125,9 +139,12 @@ private List ParseResults(JsonRPCQueryResponseModel queryResponseModel) return !result.JsonRPCAction.DontHideAfterAction; }; - results.Add(result); } + var results = new List(); + + results.AddRange(queryResponseModel.Result); + return results; } @@ -226,7 +243,21 @@ protected async Task ExecuteAsync(ProcessStartInfo startInfo, Cancellati return Stream.Null; } - var result = process.StandardOutput.BaseStream; + var source = process.StandardOutput.BaseStream; + + var buffer = BufferManager.GetStream(); + + try + { + await source.CopyToAsync(buffer, token); + } + catch (OperationCanceledException) + { + await buffer.DisposeAsync(); + throw; + } + + buffer.Seek(0, SeekOrigin.Begin); token.ThrowIfCancellationRequested(); @@ -245,7 +276,7 @@ protected async Task ExecuteAsync(ProcessStartInfo startInfo, Cancellati return Stream.Null; } - return result; + return buffer; } catch (Exception e) { From 4e3746f77d6355ce6e2798258a9cb2e74e5ac42d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Tue, 27 Jul 2021 11:15:27 +0800 Subject: [PATCH 2/5] Don't rethrow OperationCanceledException --- Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index 5e44bef8697..58bd40807e6 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -254,7 +254,7 @@ protected async Task ExecuteAsync(ProcessStartInfo startInfo, Cancellati catch (OperationCanceledException) { await buffer.DisposeAsync(); - throw; + return Stream.Null; } buffer.Seek(0, SeekOrigin.Begin); @@ -289,11 +289,15 @@ protected async Task ExecuteAsync(ProcessStartInfo startInfo, Cancellati public async Task> QueryAsync(Query query, CancellationToken token) { - var output = await ExecuteQueryAsync(query, token); try { + var output = await ExecuteQueryAsync(query, token); return await DeserializedResultAsync(output); } + catch (OperationCanceledException) + { + return null; + } catch (Exception e) { Log.Exception($"|JsonRPCPlugin.Query|Exception when query <{query}>", e); From 220db44a6c42ced03fbdf2cbbbe065143d07e2cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Tue, 27 Jul 2021 13:54:13 +0800 Subject: [PATCH 3/5] Manually kill process when token is canceled --- Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index 58bd40807e6..59980a611b6 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -243,9 +243,18 @@ protected async Task ExecuteAsync(ProcessStartInfo startInfo, Cancellati return Stream.Null; } - var source = process.StandardOutput.BaseStream; + await using var source = process.StandardOutput.BaseStream; var buffer = BufferManager.GetStream(); + bool disposed = false; + process.Disposed += (_, _) => { disposed = true; }; + token.Register(() => + { + if (!disposed) + // ReSharper disable once AccessToDisposedClosure + // Manually Check whether disposed + process.Kill(); + }); try { @@ -260,7 +269,7 @@ protected async Task ExecuteAsync(ProcessStartInfo startInfo, Cancellati buffer.Seek(0, SeekOrigin.Begin); token.ThrowIfCancellationRequested(); - + if (!process.StandardError.EndOfStream) { using var standardError = process.StandardError; From d7c037d1562237757f293637731a089bd174bd4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BC=98=E9=9F=AC?= Date: Tue, 27 Jul 2021 14:11:48 +0800 Subject: [PATCH 4/5] fix dispose issue by manually dispose --- Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs | 23 ++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index 59980a611b6..08836b56f7c 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -11,6 +11,7 @@ using System.Windows.Forms; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Plugin; +using ICSharpCode.SharpZipLib.Zip; using JetBrains.Annotations; using Microsoft.IO; @@ -234,9 +235,11 @@ protected string Execute(ProcessStartInfo startInfo) protected async Task ExecuteAsync(ProcessStartInfo startInfo, CancellationToken token = default) { + Process process = null; + bool disposed = false; try { - using var process = Process.Start(startInfo); + process = Process.Start(startInfo); if (process == null) { Log.Error("|JsonRPCPlugin.ExecuteAsync|Can't start new process"); @@ -246,18 +249,21 @@ protected async Task ExecuteAsync(ProcessStartInfo startInfo, Cancellati await using var source = process.StandardOutput.BaseStream; var buffer = BufferManager.GetStream(); - bool disposed = false; - process.Disposed += (_, _) => { disposed = true; }; + token.Register(() => { - if (!disposed) + // ReSharper disable once AccessToDisposedClosure + // ReSharper disable once AccessToModifiedClosure + // Manually Check whether disposed + if (!disposed && !process.HasExited) // ReSharper disable once AccessToDisposedClosure - // Manually Check whether disposed process.Kill(); }); try { + // token expire won't instantly trigger the exception, + // manually kill process at before await source.CopyToAsync(buffer, token); } catch (OperationCanceledException) @@ -269,7 +275,7 @@ protected async Task ExecuteAsync(ProcessStartInfo startInfo, Cancellati buffer.Seek(0, SeekOrigin.Begin); token.ThrowIfCancellationRequested(); - + if (!process.StandardError.EndOfStream) { using var standardError = process.StandardError; @@ -294,6 +300,11 @@ protected async Task ExecuteAsync(ProcessStartInfo startInfo, Cancellati e); return Stream.Null; } + finally + { + process?.Dispose(); + disposed = true; + } } public async Task> QueryAsync(Query query, CancellationToken token) From 466b825611d5c43866da6b79dcb02369f48d0020 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Tue, 27 Jul 2021 19:01:27 +1000 Subject: [PATCH 5/5] fix comment formatting --- Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs index 08836b56f7c..22f547a5a75 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs @@ -252,18 +252,16 @@ protected async Task ExecuteAsync(ProcessStartInfo startInfo, Cancellati token.Register(() => { - // ReSharper disable once AccessToDisposedClosure // ReSharper disable once AccessToModifiedClosure // Manually Check whether disposed if (!disposed && !process.HasExited) - // ReSharper disable once AccessToDisposedClosure process.Kill(); }); try { // token expire won't instantly trigger the exception, - // manually kill process at before + // manually kill process at before await source.CopyToAsync(buffer, token); } catch (OperationCanceledException)