@@ -19,7 +19,7 @@ namespace Microsoft.PowerShell.EditorServices.Session
19
19
/// <summary>
20
20
/// Provides the ability to take over the current pipeline in a runspace.
21
21
/// </summary>
22
- internal class InvocationEventQueue
22
+ internal class InvocationEventQueue : IDisposable
23
23
{
24
24
private const string ShouldProcessInExecutionThreadPropertyName = "ShouldProcessInExecutionThread" ;
25
25
@@ -35,24 +35,125 @@ internal class InvocationEventQueue
35
35
36
36
private readonly PowerShellContext _powerShellContext ;
37
37
38
+ private readonly ILogger _logger ;
39
+
40
+ private bool _isDisposed ;
41
+
38
42
private InvocationRequest _invocationRequest ;
39
43
40
- private SemaphoreSlim _lock = AsyncUtils . CreateSimpleLockingSemaphore ( ) ;
44
+ private PSEventSubscriber _onIdleSubscriber ;
45
+
46
+ private SemaphoreSlim _pipelineRequestHandle = AsyncUtils . CreateSimpleLockingSemaphore ( ) ;
47
+
48
+ private SemaphoreSlim _subscriberHandle = AsyncUtils . CreateSimpleLockingSemaphore ( ) ;
41
49
42
- private InvocationEventQueue ( PowerShellContext powerShellContext , PromptNest promptNest )
50
+ private InvocationEventQueue (
51
+ PowerShellContext powerShellContext ,
52
+ PromptNest promptNest ,
53
+ ILogger logger )
43
54
{
44
55
_promptNest = promptNest ;
45
56
_powerShellContext = powerShellContext ;
46
57
_runspace = powerShellContext . CurrentRunspace . Runspace ;
58
+ _logger = logger ;
59
+ }
60
+
61
+ public void Dispose ( ) => Dispose ( true ) ;
62
+
63
+ protected virtual void Dispose ( bool disposing )
64
+ {
65
+ if ( ! _isDisposed )
66
+ {
67
+ return ;
68
+ }
69
+
70
+ if ( disposing )
71
+ {
72
+ RemoveInvocationSubscriber ( ) ;
73
+ }
74
+
75
+ _isDisposed = true ;
47
76
}
48
77
49
- internal static InvocationEventQueue Create ( PowerShellContext powerShellContext , PromptNest promptNest )
78
+ internal static InvocationEventQueue Create (
79
+ PowerShellContext powerShellContext ,
80
+ PromptNest promptNest ,
81
+ ILogger logger )
50
82
{
51
- var eventQueue = new InvocationEventQueue ( powerShellContext , promptNest ) ;
83
+ var eventQueue = new InvocationEventQueue ( powerShellContext , promptNest , logger ) ;
52
84
eventQueue . CreateInvocationSubscriber ( ) ;
53
85
return eventQueue ;
54
86
}
55
87
88
+ /// <summary>
89
+ /// Creates a <see cref="PSEventSubscriber" /> subscribed the engine event
90
+ /// <see cref="PSEngineEvent.OnIdle" /> that handles requests for pipeline thread access.
91
+ /// </summary>
92
+ /// <returns>
93
+ /// The newly created <see cref="PSEventSubscriber" /> or an existing subscriber if
94
+ /// creation already occurred.
95
+ /// </returns>
96
+ internal PSEventSubscriber CreateInvocationSubscriber ( )
97
+ {
98
+ _subscriberHandle . Wait ( ) ;
99
+ try
100
+ {
101
+ if ( _onIdleSubscriber != null )
102
+ {
103
+ _logger . Write (
104
+ LogLevel . Error ,
105
+ "An attempt to create the ReadLine OnIdle subscriber was made when one already exists." ) ;
106
+ return _onIdleSubscriber ;
107
+ }
108
+
109
+ _onIdleSubscriber = _runspace . Events . SubscribeEvent (
110
+ source : null ,
111
+ eventName : PSEngineEvent . OnIdle ,
112
+ sourceIdentifier : PSEngineEvent . OnIdle ,
113
+ data : null ,
114
+ handlerDelegate : OnPowerShellIdle ,
115
+ supportEvent : true ,
116
+ forwardEvent : false ) ;
117
+
118
+ SetSubscriberExecutionThreadWithReflection ( _onIdleSubscriber ) ;
119
+
120
+ _onIdleSubscriber . Unsubscribed += OnInvokerUnsubscribed ;
121
+
122
+ return _onIdleSubscriber ;
123
+ }
124
+ finally
125
+ {
126
+ _subscriberHandle . Release ( ) ;
127
+ }
128
+ }
129
+
130
+ /// <summary>
131
+ /// Unsubscribes the existing <see cref="PSEventSubscriber" /> handling pipeline thread
132
+ /// access requests.
133
+ /// </summary>
134
+ internal void RemoveInvocationSubscriber ( )
135
+ {
136
+ _subscriberHandle . Wait ( ) ;
137
+ try
138
+ {
139
+ if ( _onIdleSubscriber == null )
140
+ {
141
+ _logger . Write (
142
+ LogLevel . Error ,
143
+ "An attempt to remove the ReadLine OnIdle subscriber was made before it was created." ) ;
144
+ return ;
145
+ }
146
+
147
+ _onIdleSubscriber . Unsubscribed -= OnInvokerUnsubscribed ;
148
+ _runspace . Events . UnsubscribeEvent ( _onIdleSubscriber ) ;
149
+ _onIdleSubscriber = null ;
150
+ }
151
+ finally
152
+ {
153
+ _subscriberHandle . Release ( ) ;
154
+ }
155
+ }
156
+
56
157
/// <summary>
57
158
/// Executes a command on the main pipeline thread through
58
159
/// eventing. A <see cref="PSEngineEvent.OnIdle" /> event subscriber will
@@ -136,7 +237,7 @@ internal async Task InvokeOnPipelineThread(Action<PowerShell> invocationAction)
136
237
private async Task WaitForExistingRequestAsync ( )
137
238
{
138
239
InvocationRequest existingRequest ;
139
- await _lock . WaitAsync ( ) ;
240
+ await _pipelineRequestHandle . WaitAsync ( ) ;
140
241
try
141
242
{
142
243
existingRequest = _invocationRequest ;
@@ -147,7 +248,7 @@ private async Task WaitForExistingRequestAsync()
147
248
}
148
249
finally
149
250
{
150
- _lock . Release ( ) ;
251
+ _pipelineRequestHandle . Release ( ) ;
151
252
}
152
253
153
254
await existingRequest . Task ;
@@ -156,22 +257,22 @@ private async Task WaitForExistingRequestAsync()
156
257
private async Task SetInvocationRequestAsync ( InvocationRequest request )
157
258
{
158
259
await WaitForExistingRequestAsync ( ) ;
159
- await _lock . WaitAsync ( ) ;
260
+ await _pipelineRequestHandle . WaitAsync ( ) ;
160
261
try
161
262
{
162
263
_invocationRequest = request ;
163
264
}
164
265
finally
165
266
{
166
- _lock . Release ( ) ;
267
+ _pipelineRequestHandle . Release ( ) ;
167
268
}
168
269
169
270
_powerShellContext . ForcePSEventHandling ( ) ;
170
271
}
171
272
172
273
private void OnPowerShellIdle ( object sender , EventArgs e )
173
274
{
174
- if ( ! _lock . Wait ( 0 ) )
275
+ if ( ! _pipelineRequestHandle . Wait ( 0 ) )
175
276
{
176
277
return ;
177
278
}
@@ -188,7 +289,7 @@ private void OnPowerShellIdle(object sender, EventArgs e)
188
289
}
189
290
finally
190
291
{
191
- _lock . Release ( ) ;
292
+ _pipelineRequestHandle . Release ( ) ;
192
293
}
193
294
194
295
_promptNest . PushPromptContext ( ) ;
@@ -202,24 +303,6 @@ private void OnPowerShellIdle(object sender, EventArgs e)
202
303
}
203
304
}
204
305
205
- private PSEventSubscriber CreateInvocationSubscriber ( )
206
- {
207
- PSEventSubscriber subscriber = _runspace . Events . SubscribeEvent (
208
- source : null ,
209
- eventName : PSEngineEvent . OnIdle ,
210
- sourceIdentifier : PSEngineEvent . OnIdle ,
211
- data : null ,
212
- handlerDelegate : OnPowerShellIdle ,
213
- supportEvent : true ,
214
- forwardEvent : false ) ;
215
-
216
- SetSubscriberExecutionThreadWithReflection ( subscriber ) ;
217
-
218
- subscriber . Unsubscribed += OnInvokerUnsubscribed ;
219
-
220
- return subscriber ;
221
- }
222
-
223
306
private void OnInvokerUnsubscribed ( object sender , PSEventUnsubscribedEventArgs e )
224
307
{
225
308
CreateInvocationSubscriber ( ) ;
0 commit comments