From 526b4b7febe95c5ddd5b27707488767a6a92e0c0 Mon Sep 17 00:00:00 2001 From: Sergey Shandar Date: Thu, 30 Nov 2017 12:19:39 -0800 Subject: [PATCH] SyncTaskScheduler as a separete class. --- .../Commands.Common.Strategies.csproj | 1 + .../SyncTaskScheduler.cs} | 6 ++- .../Commands.Compute/Commands.Compute.csproj | 4 +- .../Strategies/AsyncCmdletExtensions.cs | 39 +++++++++++++++++++ .../Commands.Compute/Strategies/Client.cs | 25 ++++++++++++ .../Strategies/IAsyncCmdlet.cs | 11 ++++++ .../Strategies/ProgressReport.cs | 12 ++---- .../Strategies/ShouldProcess.cs | 14 +++---- .../Operation/NewAzureVMCommand.cs | 31 ++------------- 9 files changed, 96 insertions(+), 47 deletions(-) rename src/ResourceManager/{Compute/Commands.Compute/Strategies/MessageLoop.cs => Common/Commands.Common.Strategies/SyncTaskScheduler.cs} (88%) create mode 100644 src/ResourceManager/Compute/Commands.Compute/Strategies/AsyncCmdletExtensions.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/Strategies/Client.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/Strategies/IAsyncCmdlet.cs diff --git a/src/ResourceManager/Common/Commands.Common.Strategies/Commands.Common.Strategies.csproj b/src/ResourceManager/Common/Commands.Common.Strategies/Commands.Common.Strategies.csproj index 60377d81e54e..0b6380232453 100644 --- a/src/ResourceManager/Common/Commands.Common.Strategies/Commands.Common.Strategies.csproj +++ b/src/ResourceManager/Common/Commands.Common.Strategies/Commands.Common.Strategies.csproj @@ -76,6 +76,7 @@ + diff --git a/src/ResourceManager/Compute/Commands.Compute/Strategies/MessageLoop.cs b/src/ResourceManager/Common/Commands.Common.Strategies/SyncTaskScheduler.cs similarity index 88% rename from src/ResourceManager/Compute/Commands.Compute/Strategies/MessageLoop.cs rename to src/ResourceManager/Common/Commands.Common.Strategies/SyncTaskScheduler.cs index b9d8ae953473..e9ee866533a0 100644 --- a/src/ResourceManager/Compute/Commands.Compute/Strategies/MessageLoop.cs +++ b/src/ResourceManager/Common/Commands.Common.Strategies/SyncTaskScheduler.cs @@ -17,9 +17,9 @@ using System.Threading; using System.Threading.Tasks; -namespace Microsoft.Azure.Commands.Compute.Strategies +namespace Microsoft.Azure.Commands.Common.Strategies { - internal sealed class MessageLoop + public sealed class SyncTaskScheduler { readonly ConcurrentQueue _Tasks = new ConcurrentQueue(); @@ -27,6 +27,8 @@ public async Task Invoke(Func func) { var task = new Task(func); _Tasks.Enqueue(task); + // note: don't use 'await' keyword for the 'task' because it may start the task in + // another thread. while (!task.IsCompleted) { await Task.Yield(); diff --git a/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.csproj b/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.csproj index 9af801ecc843..855e3fd3c607 100644 --- a/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.csproj +++ b/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.csproj @@ -290,7 +290,9 @@ - + + + diff --git a/src/ResourceManager/Compute/Commands.Compute/Strategies/AsyncCmdletExtensions.cs b/src/ResourceManager/Compute/Commands.Compute/Strategies/AsyncCmdletExtensions.cs new file mode 100644 index 000000000000..267f7913448c --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Strategies/AsyncCmdletExtensions.cs @@ -0,0 +1,39 @@ +using Microsoft.Azure.Commands.Common.Strategies; +using System; +using System.Management.Automation; +using System.Threading.Tasks; + +namespace Microsoft.Azure.Commands.Compute.Strategies +{ + static class AsyncCmdletExtensions + { + /// + /// Note: the function must be called in the main PowerShell thread. + /// + /// + /// + public static void StartAndWait(this Cmdlet cmdlet, Func createAndStartTask) + { + var asyncCmdlet = new AsyncCmdlet(cmdlet); + asyncCmdlet.Scheduler.Wait(createAndStartTask(asyncCmdlet)); + } + + sealed class AsyncCmdlet : IAsyncCmdlet + { + public SyncTaskScheduler Scheduler { get; } = new SyncTaskScheduler(); + + readonly Cmdlet _Cmdlet; + + public AsyncCmdlet(Cmdlet cmdlet) + { + _Cmdlet = cmdlet; + } + + public void WriteVerbose(string message) + => Scheduler.BeginInvoke(() => _Cmdlet.WriteVerbose(message)); + + public Task ShouldProcessAsync(string target, string action) + => Scheduler.Invoke(() => _Cmdlet.ShouldProcess(target, action)); + } + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/Strategies/Client.cs b/src/ResourceManager/Compute/Commands.Compute/Strategies/Client.cs new file mode 100644 index 000000000000..512d9d871878 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Strategies/Client.cs @@ -0,0 +1,25 @@ +using Microsoft.Azure.Commands.Common.Authentication; +using Microsoft.Azure.Commands.Common.Authentication.Abstractions; +using Microsoft.Azure.Commands.Common.Strategies; +using Microsoft.Rest; + +namespace Microsoft.Azure.Commands.Compute.Strategies +{ + sealed class Client : IClient + { + public string SubscriptionId { get; } + + IAzureContext Context { get; } + + public Client(IAzureContext context) + { + Context = context; + SubscriptionId = Context.Subscription.Id; + } + + public T GetClient() + where T : ServiceClient + => AzureSession.Instance.ClientFactory.CreateArmClient( + Context, AzureEnvironment.Endpoint.ResourceManager); + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/Strategies/IAsyncCmdlet.cs b/src/ResourceManager/Compute/Commands.Compute/Strategies/IAsyncCmdlet.cs new file mode 100644 index 000000000000..eed661b0b8ce --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Strategies/IAsyncCmdlet.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; + +namespace Microsoft.Azure.Commands.Compute.Strategies +{ + interface IAsyncCmdlet + { + void WriteVerbose(string message); + + Task ShouldProcessAsync(string target, string action); + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/Strategies/ProgressReport.cs b/src/ResourceManager/Compute/Commands.Compute/Strategies/ProgressReport.cs index feebe399eb89..f619df553c60 100644 --- a/src/ResourceManager/Compute/Commands.Compute/Strategies/ProgressReport.cs +++ b/src/ResourceManager/Compute/Commands.Compute/Strategies/ProgressReport.cs @@ -13,26 +13,22 @@ // ---------------------------------------------------------------------------------- using Microsoft.Azure.Commands.Common.Strategies; -using System.Management.Automation; namespace Microsoft.Azure.Commands.Compute.Strategies { sealed class ProgressReport : IProgressReport { - readonly Cmdlet _Cmdlet; + readonly IAsyncCmdlet _Cmdlet; - readonly MessageLoop _MessageLoop; - - public ProgressReport(Cmdlet cmdlet, MessageLoop messageLoop) + public ProgressReport(IAsyncCmdlet cmdlet) { _Cmdlet = cmdlet; - _MessageLoop = messageLoop; } public void Report(ResourceConfig config, double progress) where TModel : class - => _MessageLoop.BeginInvoke(() => _Cmdlet.WriteVerbose(progress == 0 + => _Cmdlet.WriteVerbose(progress == 0 ? "Creating " + config.Name + " " + config.Strategy.Type + "..." - : config.Name + " " + config.Strategy.Type + " is created.")); + : config.Name + " " + config.Strategy.Type + " is created."); } } diff --git a/src/ResourceManager/Compute/Commands.Compute/Strategies/ShouldProcess.cs b/src/ResourceManager/Compute/Commands.Compute/Strategies/ShouldProcess.cs index ae1cb3f7fc41..3c798f5547b8 100644 --- a/src/ResourceManager/Compute/Commands.Compute/Strategies/ShouldProcess.cs +++ b/src/ResourceManager/Compute/Commands.Compute/Strategies/ShouldProcess.cs @@ -20,20 +20,16 @@ namespace Microsoft.Azure.Commands.Compute.Strategies { internal sealed class ShouldProcess : IShouldProcess { - readonly Cmdlet _Cmdlet; + readonly IAsyncCmdlet _Cmdlet; - readonly MessageLoop _MessageLoop; - - public ShouldProcess(Cmdlet cmdlet, MessageLoop messageLoop) + public ShouldProcess(IAsyncCmdlet cmdlet) { _Cmdlet = cmdlet; - _MessageLoop = messageLoop; } - public async Task ShouldCreate(ResourceConfig config, TModel model) + public Task ShouldCreate(ResourceConfig config, TModel model) where TModel : class - => await _MessageLoop.Invoke( - () => _Cmdlet.ShouldProcess( - config.Name, VerbsCommon.New + " " + config.Strategy.Type)); + => _Cmdlet.ShouldProcessAsync( + config.Name, VerbsCommon.New + " " + config.Strategy.Type); } } diff --git a/src/ResourceManager/Compute/Commands.Compute/VirtualMachine/Operation/NewAzureVMCommand.cs b/src/ResourceManager/Compute/Commands.Compute/VirtualMachine/Operation/NewAzureVMCommand.cs index e900a9a39c99..defce52d639a 100644 --- a/src/ResourceManager/Compute/Commands.Compute/VirtualMachine/Operation/NewAzureVMCommand.cs +++ b/src/ResourceManager/Compute/Commands.Compute/VirtualMachine/Operation/NewAzureVMCommand.cs @@ -161,7 +161,7 @@ public override void ExecuteCmdlet() switch (ParameterSetName) { case StrategyParameterSet: - StrategyExecuteCmdlet(); + this.StartAndWait(StrategyExecuteCmdletAsync); break; default: DefaultExecuteCmdlet(); @@ -169,27 +169,7 @@ public override void ExecuteCmdlet() } } - readonly MessageLoop _MessageLoop = new MessageLoop(); - - private sealed class Client : IClient - { - public string SubscriptionId { get; } - - IAzureContext Context { get; } - - public Client(IAzureContext context) - { - Context = context; - SubscriptionId = Context.Subscription.Id; - } - - public T GetClient() - where T : ServiceClient - => AzureSession.Instance.ClientFactory.CreateArmClient( - Context, AzureEnvironment.Endpoint.ResourceManager); - } - - public async Task StrategyExecuteCmdletAsync() + async Task StrategyExecuteCmdletAsync(IAsyncCmdlet asyncCmdlet) { ResourceGroupName = ResourceGroupName ?? Name; VirtualNetworkName = VirtualNetworkName ?? Name; @@ -252,14 +232,11 @@ public async Task StrategyExecuteCmdletAsync() client, target, new CancellationToken(), - new ShouldProcess(this, _MessageLoop), - new ProgressReport(this, _MessageLoop)); + new ShouldProcess(asyncCmdlet), + new ProgressReport(asyncCmdlet)); WriteObject(result); } - public void StrategyExecuteCmdlet() - => _MessageLoop.Wait(StrategyExecuteCmdletAsync()); - public void DefaultExecuteCmdlet() { base.ExecuteCmdlet();