Skip to content

Commit 0fe1a84

Browse files
committed
Remove Nito.AsyncEx dependency, add replacements
This change removes all uses of the Nito.AsyncEx NuGet package from the solution. This change was made to remove the use of this third-party dependency so that consumers don't need to get extra open-source approval when using PowerShell Editor Services. Removing this dependency required the creation of our own custom implementations of the classes we were using from Nito.AsyncEx. These new classes were written from scratch. Resolves #115
1 parent e435301 commit 0fe1a84

28 files changed

+665
-168
lines changed

src/PowerShellEditorServices.Host.x86/PowerShellEditorServices.Host.x86.csproj

-13
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,6 @@
3737
</PropertyGroup>
3838
<ItemGroup>
3939
<Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed" />
40-
<Reference Include="Nito.AsyncEx, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
41-
<HintPath>..\..\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.dll</HintPath>
42-
<Private>True</Private>
43-
</Reference>
44-
<Reference Include="Nito.AsyncEx.Concurrent, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
45-
<HintPath>..\..\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.Concurrent.dll</HintPath>
46-
<Private>True</Private>
47-
</Reference>
48-
<Reference Include="Nito.AsyncEx.Enlightenment, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
49-
<HintPath>..\..\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.Enlightenment.dll</HintPath>
50-
<Private>True</Private>
51-
</Reference>
5240
<Reference Include="System" />
5341
<Reference Include="System.Core" />
5442
<Reference Include="System.Xml.Linq" />
@@ -71,7 +59,6 @@
7159
<None Include="..\PowerShellEditorServices.Host\App.config">
7260
<Link>App.config</Link>
7361
</None>
74-
<None Include="packages.config" />
7562
</ItemGroup>
7663
<ItemGroup>
7764
<ProjectReference Include="..\..\submodules\PSScriptAnalyzer\Engine\ScriptAnalyzerEngine.csproj">

src/PowerShellEditorServices.Host.x86/packages.config

-4
This file was deleted.

src/PowerShellEditorServices.Host/PowerShellEditorServices.Host.csproj

-13
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,6 @@
3939
</PropertyGroup>
4040
<ItemGroup>
4141
<Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed" />
42-
<Reference Include="Nito.AsyncEx, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
43-
<HintPath>..\..\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.dll</HintPath>
44-
<Private>True</Private>
45-
</Reference>
46-
<Reference Include="Nito.AsyncEx.Concurrent, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
47-
<HintPath>..\..\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.Concurrent.dll</HintPath>
48-
<Private>True</Private>
49-
</Reference>
50-
<Reference Include="Nito.AsyncEx.Enlightenment, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
51-
<HintPath>..\..\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.Enlightenment.dll</HintPath>
52-
<Private>True</Private>
53-
</Reference>
5442
<Reference Include="System" />
5543
<Reference Include="System.Core" />
5644
<Reference Include="System.Xml.Linq" />
@@ -69,7 +57,6 @@
6957
<Content Include="Microsoft.PowerShell.EditorServices.Host.DebugAdapter.cmd">
7058
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
7159
</Content>
72-
<None Include="packages.config" />
7360
</ItemGroup>
7461
<ItemGroup>
7562
<ProjectReference Include="..\..\submodules\PSScriptAnalyzer\Engine\ScriptAnalyzerEngine.csproj">

src/PowerShellEditorServices.Host/packages.config

-4
This file was deleted.

src/PowerShellEditorServices.Protocol/DebugAdapter/NextRequest.cs

-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
//
55

66
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol;
7-
using Nito.AsyncEx;
8-
using System.Threading.Tasks;
97

108
namespace Microsoft.PowerShell.EditorServices.Protocol.DebugAdapter
119
{

src/PowerShellEditorServices.Protocol/DebugAdapter/SetExceptionBreakpointsRequest.cs

-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
//
55

66
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol;
7-
using Nito.AsyncEx;
8-
using System.Threading.Tasks;
97

108
namespace Microsoft.PowerShell.EditorServices.Protocol.DebugAdapter
119
{

src/PowerShellEditorServices.Protocol/MessageProtocol/MessageDispatcher.cs

+13-9
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
//
55

66
using Microsoft.PowerShell.EditorServices.Utility;
7-
using Nito.AsyncEx;
87
using System;
98
using System.Collections.Generic;
109
using System.Threading;
@@ -26,6 +25,9 @@ public class MessageDispatcher
2625

2726
private Action<Message> responseHandler;
2827

28+
private CancellationTokenSource messageLoopCancellationToken =
29+
new CancellationTokenSource();
30+
2931
#endregion
3032

3133
#region Properties
@@ -65,22 +67,24 @@ public MessageDispatcher(
6567

6668
public void Start()
6769
{
70+
6871
// Start the main message loop thread. The Task is
6972
// not explicitly awaited because it is running on
7073
// an independent background thread.
71-
this.messageLoopThread = new AsyncContextThread(true);
74+
this.messageLoopThread = new AsyncContextThread("Message Dispatcher");
7275
this.messageLoopThread
73-
.Factory
74-
.Run(this.ListenForMessages)
76+
.Run(() => this.ListenForMessages(this.messageLoopCancellationToken.Token))
7577
.ContinueWith(this.OnListenTaskCompleted);
7678
}
7779

7880
public void Stop()
7981
{
80-
// By disposing the thread we cancel all existing work
81-
// and cause the thread to be destroyed.
82+
// Stop the message loop thread
8283
if (this.messageLoopThread != null)
83-
this.messageLoopThread.Dispose();
84+
{
85+
this.messageLoopCancellationToken.Cancel();
86+
this.messageLoopThread.Stop();
87+
}
8488
}
8589

8690
public void SetRequestHandler<TParams, TResult>(
@@ -181,13 +185,13 @@ protected void OnUnhandledException(Exception unhandledException)
181185

182186
#region Private Methods
183187

184-
private async Task ListenForMessages()
188+
private async Task ListenForMessages(CancellationToken cancellationToken)
185189
{
186190
this.SynchronizationContext = SynchronizationContext.Current;
187191

188192
// Run the message loop
189193
bool isRunning = true;
190-
while (isRunning)
194+
while (isRunning && !cancellationToken.IsCancellationRequested)
191195
{
192196
Message newMessage = null;
193197

src/PowerShellEditorServices.Protocol/MessageProtocol/MessageWriter.cs

-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using Microsoft.PowerShell.EditorServices.Utility;
77
using Newtonsoft.Json;
88
using Newtonsoft.Json.Linq;
9-
using Nito.AsyncEx;
109
using System.IO;
1110
using System.Text;
1211
using System.Threading.Tasks;

src/PowerShellEditorServices.Protocol/PowerShellEditorServices.Protocol.csproj

-12
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,6 @@
3838
<HintPath>..\..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
3939
<Private>True</Private>
4040
</Reference>
41-
<Reference Include="Nito.AsyncEx, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
42-
<HintPath>..\..\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.dll</HintPath>
43-
<Private>True</Private>
44-
</Reference>
45-
<Reference Include="Nito.AsyncEx.Concurrent, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
46-
<HintPath>..\..\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.Concurrent.dll</HintPath>
47-
<Private>True</Private>
48-
</Reference>
49-
<Reference Include="Nito.AsyncEx.Enlightenment, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
50-
<HintPath>..\..\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.Enlightenment.dll</HintPath>
51-
<Private>True</Private>
52-
</Reference>
5341
<Reference Include="System" />
5442
<Reference Include="System.Core" />
5543
<Reference Include="System.Xml.Linq" />

src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs

+5-4
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol.Channel;
99
using Microsoft.PowerShell.EditorServices.Protocol.Messages;
1010
using Microsoft.PowerShell.EditorServices.Utility;
11-
using Nito.AsyncEx;
1211
using System;
1312
using System.Collections.Generic;
1413
using System.IO;
@@ -780,7 +779,7 @@ private Task RunScriptDiagnostics(
780779
if (!this.currentSettings.ScriptAnalysis.Enable.Value)
781780
{
782781
// If the user has disabled script analysis, skip it entirely
783-
return TaskConstants.Completed;
782+
return Task.FromResult(true);
784783
}
785784

786785
// If there's an existing task, attempt to cancel it
@@ -806,7 +805,9 @@ private Task RunScriptDiagnostics(
806805
"Exception while cancelling analysis task:\n\n{0}",
807806
e.ToString()));
808807

809-
return TaskConstants.Canceled;
808+
TaskCompletionSource<bool> cancelTask = new TaskCompletionSource<bool>();
809+
cancelTask.SetCanceled();
810+
return cancelTask.Task;
810811
}
811812

812813
// Create a fresh cancellation token and then start the task.
@@ -826,7 +827,7 @@ private Task RunScriptDiagnostics(
826827
TaskCreationOptions.None,
827828
TaskScheduler.Default);
828829

829-
return TaskConstants.Completed;
830+
return Task.FromResult(true);
830831
}
831832

832833
private static async Task DelayThenInvokeDiagnostics(
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<packages>
33
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
4-
<package id="Nito.AsyncEx" version="3.0.1" targetFramework="net45" />
54
</packages>

src/PowerShellEditorServices/PowerShellEditorServices.csproj

+5-15
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,6 @@
3838
<DocumentationFile>bin\Release\Microsoft.PowerShell.EditorServices.XML</DocumentationFile>
3939
</PropertyGroup>
4040
<ItemGroup>
41-
<Reference Include="Nito.AsyncEx, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
42-
<HintPath>..\..\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.dll</HintPath>
43-
<Private>True</Private>
44-
</Reference>
45-
<Reference Include="Nito.AsyncEx.Concurrent, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
46-
<HintPath>..\..\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.Concurrent.dll</HintPath>
47-
<Private>True</Private>
48-
</Reference>
49-
<Reference Include="Nito.AsyncEx.Enlightenment, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
50-
<HintPath>..\..\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.Enlightenment.dll</HintPath>
51-
<Private>True</Private>
52-
</Reference>
5341
<Reference Include="System" />
5442
<Reference Include="System.Core" />
5543
<Reference Include="System.Management.Automation" />
@@ -109,7 +97,12 @@
10997
<Compile Include="Session\SessionPSHostRawUserInterface.cs" />
11098
<Compile Include="Session\SessionPSHostUserInterface.cs" />
11199
<Compile Include="Session\SessionStateChangedEventArgs.cs" />
100+
<Compile Include="Utility\AsyncContextThread.cs" />
101+
<Compile Include="Utility\AsyncLock.cs" />
102+
<Compile Include="Utility\AsyncQueue.cs" />
103+
<Compile Include="Utility\AsyncContext.cs" />
112104
<Compile Include="Utility\Logger.cs" />
105+
<Compile Include="Utility\ThreadSynchronizationContext.cs" />
113106
<Compile Include="Utility\Validate.cs" />
114107
<Compile Include="Workspace\BufferRange.cs" />
115108
<Compile Include="Workspace\FileChange.cs" />
@@ -119,9 +112,6 @@
119112
<Compile Include="Workspace\ScriptRegion.cs" />
120113
<Compile Include="Workspace\Workspace.cs" />
121114
</ItemGroup>
122-
<ItemGroup>
123-
<None Include="packages.config" />
124-
</ItemGroup>
125115
<ItemGroup>
126116
<ProjectReference Include="..\..\submodules\PSScriptAnalyzer\Engine\ScriptAnalyzerEngine.csproj">
127117
<Project>{f4bde3d0-3eef-4157-8a3e-722df7adef60}</Project>

src/PowerShellEditorServices/Session/PowerShellContext.cs

+27-39
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
using Microsoft.PowerShell.EditorServices.Console;
77
using Microsoft.PowerShell.EditorServices.Utility;
8-
using Nito.AsyncEx;
98
using System;
109
using System.Collections;
1110
using System.Globalization;
@@ -44,8 +43,7 @@ public class PowerShellContext : IDisposable
4443
private TaskCompletionSource<IPipelineExecutionRequest> pipelineResultTask;
4544

4645
private object runspaceMutex = new object();
47-
private RunspaceHandle currentRunspaceHandle;
48-
private IAsyncWaitQueue<RunspaceHandle> runspaceWaitQueue = new DefaultAsyncWaitQueue<RunspaceHandle>();
46+
private AsyncQueue<RunspaceHandle> runspaceWaitQueue = new AsyncQueue<RunspaceHandle>();
4947

5048
#endregion
5149

@@ -115,6 +113,7 @@ public PowerShellContext()
115113
this.ownsInitialRunspace = true;
116114

117115
this.Initialize(runspace);
116+
118117
}
119118

120119
/// <summary>
@@ -164,6 +163,10 @@ private void Initialize(Runspace initialRunspace)
164163
#endif
165164

166165
this.SessionState = PowerShellContextState.Ready;
166+
167+
// Now that the runspace is ready, enqueue it for first use
168+
RunspaceHandle runspaceHandle = new RunspaceHandle(this.currentRunspace, this);
169+
this.runspaceWaitQueue.EnqueueAsync(runspaceHandle).Wait();
167170
}
168171

169172
private Version GetPowerShellVersion()
@@ -198,21 +201,19 @@ private Version GetPowerShellVersion()
198201
/// <returns>A RunspaceHandle instance that gives access to the session's runspace.</returns>
199202
public Task<RunspaceHandle> GetRunspaceHandle()
200203
{
201-
lock (this.runspaceMutex)
202-
{
203-
if (this.currentRunspaceHandle == null)
204-
{
205-
this.currentRunspaceHandle = new RunspaceHandle(this.currentRunspace, this);
206-
TaskCompletionSource<RunspaceHandle> tcs = new TaskCompletionSource<RunspaceHandle>();
207-
tcs.SetResult(this.currentRunspaceHandle);
208-
return tcs.Task;
209-
}
210-
else
211-
{
212-
// TODO: Use CancellationToken?
213-
return this.runspaceWaitQueue.Enqueue();
214-
}
215-
}
204+
return this.GetRunspaceHandle(CancellationToken.None);
205+
}
206+
207+
/// <summary>
208+
/// Gets a RunspaceHandle for the session's runspace. This
209+
/// handle is used to gain temporary ownership of the runspace
210+
/// so that commands can be executed against it directly.
211+
/// </summary>
212+
/// <param name="cancellationToken">A CancellationToken that can be used to cancel the request.</param>
213+
/// <returns>A RunspaceHandle instance that gives access to the session's runspace.</returns>
214+
public Task<RunspaceHandle> GetRunspaceHandle(CancellationToken cancellationToken)
215+
{
216+
return this.runspaceWaitQueue.DequeueAsync(cancellationToken);
216217
}
217218

218219
/// <summary>
@@ -532,30 +533,17 @@ internal void ReleaseRunspaceHandle(RunspaceHandle runspaceHandle)
532533
{
533534
Validate.IsNotNull("runspaceHandle", runspaceHandle);
534535

535-
IDisposable dequeuedTask = null;
536-
537-
lock (this.runspaceMutex)
536+
if (this.runspaceWaitQueue.IsEmpty)
538537
{
539-
if (runspaceHandle != this.currentRunspaceHandle)
540-
{
541-
throw new InvalidOperationException("Released runspace handle was not the current handle.");
542-
}
543-
544-
this.currentRunspaceHandle = null;
545-
546-
if (!this.runspaceWaitQueue.IsEmpty)
547-
{
548-
this.currentRunspaceHandle = new RunspaceHandle(this.currentRunspace, this);
549-
dequeuedTask =
550-
this.runspaceWaitQueue.Dequeue(
551-
this.currentRunspaceHandle);
552-
}
538+
var newRunspaceHandle = new RunspaceHandle(this.currentRunspace, this);
539+
this.runspaceWaitQueue.EnqueueAsync(newRunspaceHandle).Wait();
553540
}
554-
555-
// If a queued task was dequeued, call Dispose to cause it to be executed.
556-
if (dequeuedTask != null)
541+
else
557542
{
558-
dequeuedTask.Dispose();
543+
// Write the situation to the log since this shouldn't happen
544+
Logger.Write(
545+
LogLevel.Error,
546+
"The PowerShellContext.runspaceWaitQueue has more than one item");
559547
}
560548
}
561549

0 commit comments

Comments
 (0)