11
11
using System . Threading ;
12
12
using System . Threading . Tasks ;
13
13
14
- namespace Microsoft . PowerShell . EditorServices . Protocol . Client
14
+ namespace Microsoft . PowerShell . EditorServices . Protocol . MessageProtocol
15
15
{
16
- public class ProtocolClient
16
+ /// <summary>
17
+ /// Provides behavior for a client or server endpoint that
18
+ /// communicates using the specified protocol.
19
+ /// </summary>
20
+ public class ProtocolEndpoint : IMessageSender
17
21
{
18
22
private bool isStarted ;
19
23
private int currentMessageId ;
20
- private ChannelBase clientChannel ;
24
+ private ChannelBase protocolChannel ;
21
25
private MessageProtocolType messageProtocolType ;
26
+ private TaskCompletionSource < bool > endpointExitedTask ;
22
27
private SynchronizationContext originalSynchronizationContext ;
23
28
24
29
private Dictionary < string , TaskCompletionSource < Message > > pendingRequests =
25
30
new Dictionary < string , TaskCompletionSource < Message > > ( ) ;
26
31
27
32
/// <summary>
28
- /// Initializes an instance of the protocol client using the
33
+ /// Initializes an instance of the protocol server using the
29
34
/// specified channel for communication.
30
35
/// </summary>
31
- /// <param name="clientChannel">The channel to use for communication with the server.</param>
32
- /// <param name="messageProtocolType">The type of message protocol used by the server.</param>
33
- public ProtocolClient (
34
- ChannelBase clientChannel ,
36
+ /// <param name="protocolChannel">
37
+ /// The channel to use for communication with the connected endpoint.
38
+ /// </param>
39
+ /// <param name="messageProtocolType">
40
+ /// The type of message protocol used by the endpoint.
41
+ /// </param>
42
+ public ProtocolEndpoint (
43
+ ChannelBase protocolChannel ,
35
44
MessageProtocolType messageProtocolType )
36
45
{
37
- this . clientChannel = clientChannel ;
46
+ this . protocolChannel = protocolChannel ;
38
47
this . messageProtocolType = messageProtocolType ;
48
+ this . originalSynchronizationContext = SynchronizationContext . Current ;
39
49
}
40
50
41
51
/// <summary>
@@ -46,35 +56,50 @@ public async Task Start()
46
56
{
47
57
if ( ! this . isStarted )
48
58
{
49
- // Start the provided client channel
50
- this . clientChannel . Start ( this . messageProtocolType ) ;
59
+ // Start the provided protocol channel
60
+ this . protocolChannel . Start ( this . messageProtocolType ) ;
51
61
52
62
// Set the handler for any message responses that come back
53
- this . clientChannel . MessageDispatcher . SetResponseHandler ( this . HandleResponse ) ;
63
+ this . protocolChannel . MessageDispatcher . SetResponseHandler ( this . HandleResponse ) ;
54
64
55
65
// Listen for unhandled exceptions from the dispatcher
56
- this . clientChannel . MessageDispatcher . UnhandledException += MessageDispatcher_UnhandledException ;
66
+ this . protocolChannel . MessageDispatcher . UnhandledException += MessageDispatcher_UnhandledException ;
57
67
58
- // Notify implementation about client start
68
+ // Notify implementation about endpoint start
59
69
await this . OnStart ( ) ;
60
70
61
- // Client is now started
71
+ // Endpoint is now started
62
72
this . isStarted = true ;
63
73
}
64
74
}
65
75
76
+ public void WaitForExit ( )
77
+ {
78
+ this . endpointExitedTask = new TaskCompletionSource < bool > ( ) ;
79
+ this . endpointExitedTask . Task . Wait ( ) ;
80
+ }
81
+
66
82
public async Task Stop ( )
67
83
{
68
84
if ( this . isStarted )
69
85
{
86
+ // Make sure no future calls try to stop the endpoint during shutdown
87
+ this . isStarted = false ;
88
+
70
89
// Stop the implementation first
71
90
await this . OnStop ( ) ;
91
+ this . protocolChannel . Stop ( ) ;
72
92
73
- this . clientChannel . Stop ( ) ;
74
- this . isStarted = false ;
93
+ // Notify anyone waiting for exit
94
+ if ( this . endpointExitedTask != null )
95
+ {
96
+ this . endpointExitedTask . SetResult ( true ) ;
97
+ }
75
98
}
76
99
}
77
100
101
+ #region Message Sending
102
+
78
103
/// <summary>
79
104
/// Sends a request to the server
80
105
/// </summary>
@@ -107,7 +132,7 @@ public async Task<TResult> SendRequest<TParams, TResult>(
107
132
responseTask ) ;
108
133
}
109
134
110
- await this . clientChannel . MessageWriter . WriteRequest < TParams , TResult > (
135
+ await this . protocolChannel . MessageWriter . WriteRequest < TParams , TResult > (
111
136
requestType ,
112
137
requestParams ,
113
138
this . currentMessageId ) ;
@@ -128,19 +153,64 @@ await this.clientChannel.MessageWriter.WriteRequest<TParams, TResult>(
128
153
}
129
154
}
130
155
131
- public async Task SendEvent < TParams > ( EventType < TParams > eventType , TParams eventParams )
156
+ /// <summary>
157
+ /// Sends an event to the channel's endpoint.
158
+ /// </summary>
159
+ /// <typeparam name="TParams">The event parameter type.</typeparam>
160
+ /// <param name="eventType">The type of event being sent.</param>
161
+ /// <param name="eventParams">The event parameters being sent.</param>
162
+ /// <returns>A Task that tracks completion of the send operation.</returns>
163
+ public Task SendEvent < TParams > (
164
+ EventType < TParams > eventType ,
165
+ TParams eventParams )
166
+ {
167
+ // Some events could be raised from a different thread.
168
+ // To ensure that messages are written serially, dispatch
169
+ // dispatch the SendEvent call to the message loop thread.
170
+
171
+ if ( ! this . protocolChannel . MessageDispatcher . InMessageLoopThread )
172
+ {
173
+ TaskCompletionSource < bool > writeTask = new TaskCompletionSource < bool > ( ) ;
174
+
175
+ this . protocolChannel . MessageDispatcher . SynchronizationContext . Post (
176
+ async ( obj ) =>
177
+ {
178
+ await this . protocolChannel . MessageWriter . WriteEvent (
179
+ eventType ,
180
+ eventParams ) ;
181
+
182
+ writeTask . SetResult ( true ) ;
183
+ } , null ) ;
184
+
185
+ return writeTask . Task ;
186
+ }
187
+ else
188
+ {
189
+ return this . protocolChannel . MessageWriter . WriteEvent (
190
+ eventType ,
191
+ eventParams ) ;
192
+ }
193
+ }
194
+
195
+ #endregion
196
+
197
+ #region Message Handling
198
+
199
+ public void SetRequestHandler < TParams , TResult > (
200
+ RequestType < TParams , TResult > requestType ,
201
+ Func < TParams , RequestContext < TResult > , Task > requestHandler )
132
202
{
133
- await this . clientChannel . MessageWriter . WriteMessage (
134
- Message . Event (
135
- eventType . MethodName ,
136
- JToken . FromObject ( eventParams ) ) ) ;
203
+ this . protocolChannel . MessageDispatcher . SetRequestHandler (
204
+ requestType ,
205
+ requestHandler ) ;
137
206
}
138
207
208
+
139
209
public void SetEventHandler < TParams > (
140
210
EventType < TParams > eventType ,
141
211
Func < TParams , EventContext , Task > eventHandler )
142
212
{
143
- this . clientChannel . MessageDispatcher . SetEventHandler (
213
+ this . protocolChannel . MessageDispatcher . SetEventHandler (
144
214
eventType ,
145
215
eventHandler ,
146
216
false ) ;
@@ -151,20 +221,12 @@ public void SetEventHandler<TParams>(
151
221
Func < TParams , EventContext , Task > eventHandler ,
152
222
bool overrideExisting )
153
223
{
154
- this . clientChannel . MessageDispatcher . SetEventHandler (
224
+ this . protocolChannel . MessageDispatcher . SetEventHandler (
155
225
eventType ,
156
226
eventHandler ,
157
227
overrideExisting ) ;
158
228
}
159
229
160
- private void MessageDispatcher_UnhandledException ( object sender , Exception e )
161
- {
162
- if ( this . originalSynchronizationContext != null )
163
- {
164
- this . originalSynchronizationContext . Post ( o => { throw e ; } , null ) ;
165
- }
166
- }
167
-
168
230
private void HandleResponse ( Message responseMessage )
169
231
{
170
232
TaskCompletionSource < Message > pendingRequestTask = null ;
@@ -176,6 +238,10 @@ private void HandleResponse(Message responseMessage)
176
238
}
177
239
}
178
240
241
+ #endregion
242
+
243
+ #region Subclass Lifetime Methods
244
+
179
245
protected virtual Task OnStart ( )
180
246
{
181
247
return Task . FromResult ( true ) ;
@@ -185,6 +251,25 @@ protected virtual Task OnStop()
185
251
{
186
252
return Task . FromResult ( true ) ;
187
253
}
254
+
255
+ #endregion
256
+
257
+ #region Event Handlers
258
+
259
+ private void MessageDispatcher_UnhandledException ( object sender , Exception e )
260
+ {
261
+ if ( this . endpointExitedTask != null )
262
+ {
263
+ this . endpointExitedTask . SetException ( e ) ;
264
+ }
265
+
266
+ else if ( this . originalSynchronizationContext != null )
267
+ {
268
+ this . originalSynchronizationContext . Post ( o => { throw e ; } , null ) ;
269
+ }
270
+ }
271
+
272
+ #endregion
188
273
}
189
274
}
190
275
0 commit comments