-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
876 additions
and
0 deletions.
There are no files selected for viewing
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
{ | ||
// Use IntelliSense to learn about possible attributes. | ||
// Hover to view descriptions of existing attributes. | ||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||
"version": "0.2.0", | ||
"configurations": [ | ||
{ | ||
"name": ".NET Core Launch (console)", | ||
"type": "coreclr", | ||
"request": "launch", | ||
"WARNING01": "*********************************************************************************", | ||
"WARNING02": "The C# extension was unable to automatically decode projects in the current", | ||
"WARNING03": "workspace to create a runnable launch.json file. A template launch.json file has", | ||
"WARNING04": "been created as a placeholder.", | ||
"WARNING05": "", | ||
"WARNING06": "If OmniSharp is currently unable to load your project, you can attempt to resolve", | ||
"WARNING07": "this by restoring any missing project dependencies (example: run 'dotnet restore')", | ||
"WARNING08": "and by fixing any reported errors from building the projects in your workspace.", | ||
"WARNING09": "If this allows OmniSharp to now load your project then --", | ||
"WARNING10": " * Delete this file", | ||
"WARNING11": " * Open the Visual Studio Code command palette (View->Command Palette)", | ||
"WARNING12": " * run the command: '.NET: Generate Assets for Build and Debug'.", | ||
"WARNING13": "", | ||
"WARNING14": "If your project requires a more complex launch configuration, you may wish to delete", | ||
"WARNING15": "this configuration and pick a different template using the 'Add Configuration...'", | ||
"WARNING16": "button at the bottom of this file.", | ||
"WARNING17": "*********************************************************************************", | ||
"preLaunchTask": "build", | ||
"program": "${workspaceFolder}/bin/Debug/<insert-target-framework-here>/<insert-project-name-here>.dll", | ||
"args": [], | ||
"cwd": "${workspaceFolder}", | ||
"console": "internalConsole", | ||
"stopAtEntry": false | ||
}, | ||
{ | ||
"name": ".NET Core Attach", | ||
"type": "coreclr", | ||
"request": "attach", | ||
"processId": "${command:pickProcess}" | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,280 @@ | ||
using System; | ||
using System.Collections.Concurrent; | ||
using System.Collections.Generic; | ||
using System.Management.Automation; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
namespace cmdlet | ||
{ | ||
public abstract class AsyncCmdlet : PSCmdlet | ||
{ | ||
private readonly ConcurrentQueue<ICmdletTask> _cmdletTaskQueue; | ||
|
||
private readonly List<Task> _processingTasks; | ||
|
||
private Task _beginProcessingTask; | ||
|
||
protected AsyncCmdlet() | ||
{ | ||
_processingTasks = new List<Task>(); | ||
_cmdletTaskQueue = new ConcurrentQueue<ICmdletTask>(); | ||
} | ||
|
||
protected abstract void HandleException(Exception e); | ||
|
||
protected virtual Task BeginProcessingAsync() => Task.CompletedTask; | ||
|
||
protected virtual Task ProcessRecordAsync() => Task.CompletedTask; | ||
|
||
protected virtual Task EndProcessingAsync() => Task.CompletedTask; | ||
|
||
protected override void BeginProcessing() | ||
{ | ||
// Kick off BeginProcessingAsync() | ||
_beginProcessingTask = BeginProcessingAsync(); | ||
|
||
// Drain queue to this point | ||
RunQueuedTasks(); | ||
} | ||
|
||
protected override void ProcessRecord() | ||
{ | ||
// If not yet processed, complete BeginProcessingAsync() queue, mark it as processed | ||
if (_beginProcessingTask is not null) | ||
{ | ||
try | ||
{ | ||
_beginProcessingTask.GetAwaiter().GetResult(); | ||
} | ||
catch (Exception e) | ||
{ | ||
HandleException(e); | ||
} | ||
|
||
RunQueuedTasks(); | ||
_beginProcessingTask = null; | ||
} | ||
|
||
// Kick off ProcessRecordAsync() | ||
_processingTasks.Add(ProcessRecordAsync()); | ||
|
||
// Drain queue to this point | ||
RunQueuedTasks(); | ||
} | ||
|
||
protected override void EndProcessing() | ||
{ | ||
// Complete ProcessRecord() queue | ||
foreach (Task processTask in _processingTasks) | ||
{ | ||
try | ||
{ | ||
processTask.GetAwaiter().GetResult(); | ||
} | ||
catch (Exception e) | ||
{ | ||
HandleException(e); | ||
} | ||
} | ||
|
||
RunQueuedTasks(); | ||
|
||
// Run EndProcessingAsync() | ||
try | ||
{ | ||
EndProcessingAsync().GetAwaiter().GetResult(); | ||
} | ||
catch (Exception e) | ||
{ | ||
HandleException(e); | ||
} | ||
} | ||
|
||
private void RunQueuedTasks() | ||
{ | ||
var tasks = new List<ICmdletTask>(); | ||
|
||
while (_cmdletTaskQueue.TryDequeue(out ICmdletTask task)) | ||
{ | ||
tasks.Add(task); | ||
} | ||
|
||
foreach (ICmdletTask task in tasks) | ||
{ | ||
task.DoInvoke(this); | ||
} | ||
} | ||
} | ||
|
||
internal interface ICmdletTask | ||
{ | ||
void DoInvoke(Cmdlet cmdlet); | ||
} | ||
|
||
internal abstract class CmdletTask<T> : ICmdletTask | ||
{ | ||
private readonly TaskCompletionSource<T> _taskCompletionSource; | ||
|
||
protected CmdletTask() | ||
{ | ||
_taskCompletionSource = new TaskCompletionSource<T>(); | ||
} | ||
|
||
public Task<T> ResultTask => _taskCompletionSource.Task; | ||
|
||
public abstract T Invoke(Cmdlet cmdlet); | ||
|
||
public void DoInvoke(Cmdlet cmdlet) | ||
{ | ||
try | ||
{ | ||
T result = Invoke(cmdlet); | ||
_taskCompletionSource.SetResult(result); | ||
} | ||
catch (Exception e) | ||
{ | ||
_taskCompletionSource.SetException(e); | ||
} | ||
} | ||
} | ||
|
||
internal abstract class CmdletTask : ICmdletTask | ||
{ | ||
private readonly TaskCompletionSource<object> _taskCompletionSource; | ||
|
||
protected CmdletTask() | ||
{ | ||
_taskCompletionSource = new TaskCompletionSource<object>(); | ||
} | ||
|
||
public Task Result => _taskCompletionSource.Task; | ||
|
||
public abstract void Invoke(Cmdlet cmdlet); | ||
|
||
public void DoInvoke(Cmdlet cmdlet) | ||
{ | ||
try | ||
{ | ||
Invoke(cmdlet); | ||
_taskCompletionSource.SetResult(null); | ||
} | ||
catch (Exception e) | ||
{ | ||
_taskCompletionSource.SetException(e); | ||
} | ||
} | ||
} | ||
|
||
internal abstract class StringMessage : CmdletTask | ||
{ | ||
protected StringMessage(string message) | ||
{ | ||
Message = message; | ||
} | ||
|
||
protected string Message { get; } | ||
} | ||
|
||
internal class VerboseMessage : StringMessage | ||
{ | ||
public VerboseMessage(string message) | ||
: base(message) | ||
{ | ||
} | ||
|
||
public override void Invoke(Cmdlet cmdlet) | ||
{ | ||
cmdlet.WriteVerbose(Message); | ||
} | ||
} | ||
|
||
internal class DebugMessage : StringMessage | ||
{ | ||
public DebugMessage(string message) | ||
: base(message) | ||
{ | ||
} | ||
|
||
public override void Invoke(Cmdlet cmdlet) | ||
{ | ||
cmdlet.WriteDebug(Message); | ||
} | ||
} | ||
|
||
internal class WarningMessage : StringMessage | ||
{ | ||
public WarningMessage(string message) | ||
: base(message) | ||
{ | ||
} | ||
|
||
public override void Invoke(Cmdlet cmdlet) | ||
{ | ||
cmdlet.WriteWarning(Message); | ||
} | ||
} | ||
|
||
internal class ErrorMessage : CmdletTask | ||
{ | ||
private readonly ErrorRecord _errorRecord; | ||
|
||
public ErrorMessage(ErrorRecord errorRecord) | ||
{ | ||
_errorRecord = errorRecord; | ||
} | ||
|
||
public override void Invoke(Cmdlet cmdlet) | ||
{ | ||
cmdlet.WriteError(_errorRecord); | ||
} | ||
} | ||
|
||
internal class InformationMessage : CmdletTask | ||
{ | ||
private readonly InformationRecord _informationRecord; | ||
|
||
public InformationMessage(InformationRecord informationRecord) | ||
{ | ||
_informationRecord = informationRecord; | ||
} | ||
|
||
public override void Invoke(Cmdlet cmdlet) | ||
{ | ||
cmdlet.WriteInformation(_informationRecord); | ||
} | ||
} | ||
|
||
internal class ObjectMessage : CmdletTask | ||
{ | ||
private readonly object _object; | ||
|
||
private readonly bool _enumerate; | ||
|
||
public ObjectMessage(object obj, bool enumerate) | ||
{ | ||
_object = obj; | ||
_enumerate = enumerate; | ||
} | ||
|
||
public override void Invoke(Cmdlet cmdlet) | ||
{ | ||
cmdlet.WriteObject(_object, _enumerate); | ||
} | ||
} | ||
|
||
internal class ProgressMessage : CmdletTask | ||
{ | ||
private readonly ProgressRecord _progressRecord; | ||
|
||
public ProgressMessage(ProgressRecord progressRecord) | ||
{ | ||
_progressRecord = progressRecord; | ||
} | ||
|
||
public override void Invoke(Cmdlet cmdlet) | ||
{ | ||
cmdlet.WriteProgress(_progressRecord); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
using System; | ||
using System.Collections.Concurrent; | ||
using System.Collections.Generic; | ||
using System.Management.Automation; | ||
using System.Net; | ||
using System.Net.Http; | ||
using System.Threading.Tasks; | ||
|
||
namespace cmdlet | ||
{ | ||
[Cmdlet(VerbsDiagnostic.Test, "AsyncExample")] | ||
public class AsyncExampleCmdlet : Cmdlet | ||
{ | ||
private readonly HttpClient _httpClient; | ||
|
||
private readonly List<Task> _tasks; | ||
|
||
private readonly ConcurrentQueue<HttpStatusCode> _results; | ||
|
||
public AsyncExampleCmdlet() | ||
{ | ||
_httpClient = new HttpClient(); | ||
_tasks = new List<Task>(); | ||
_results = new ConcurrentQueue<HttpStatusCode>(); | ||
} | ||
|
||
[Parameter(Mandatory = true, ValueFromPipeline = true)] | ||
public Uri[] Uri { get; set; } | ||
|
||
protected override void ProcessRecord() | ||
{ | ||
if (Uri is not null) | ||
{ | ||
foreach (Uri uri in Uri) | ||
{ | ||
_tasks.Add(GetUriAsync(uri)); | ||
} | ||
} | ||
|
||
while (_results.TryDequeue(out HttpStatusCode result)) | ||
{ | ||
WriteObject(result); | ||
} | ||
} | ||
|
||
protected override void EndProcessing() | ||
{ | ||
Task.WaitAll(_tasks.ToArray()); | ||
|
||
while (_results.TryDequeue(out HttpStatusCode result)) | ||
{ | ||
WriteObject(result); | ||
} | ||
} | ||
|
||
private async Task GetUriAsync(Uri uri) | ||
{ | ||
HttpResponseMessage response = await _httpClient.GetAsync(uri); | ||
_results.Enqueue(response.StatusCode); | ||
} | ||
} | ||
} |
Oops, something went wrong.