1111using System . Threading ;
1212using System . Threading . Tasks ;
1313
14- namespace Microsoft . PowerShell . EditorServices . Protocol . Client
14+ namespace Microsoft . PowerShell . EditorServices . Protocol . MessageProtocol
1515{
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
1721 {
1822 private bool isStarted ;
1923 private int currentMessageId ;
20- private ChannelBase clientChannel ;
24+ private ChannelBase protocolChannel ;
2125 private MessageProtocolType messageProtocolType ;
26+ private TaskCompletionSource < bool > endpointExitedTask ;
2227 private SynchronizationContext originalSynchronizationContext ;
2328
2429 private Dictionary < string , TaskCompletionSource < Message > > pendingRequests =
2530 new Dictionary < string , TaskCompletionSource < Message > > ( ) ;
2631
2732 /// <summary>
28- /// Initializes an instance of the protocol client using the
33+ /// Initializes an instance of the protocol server using the
2934 /// specified channel for communication.
3035 /// </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 ,
3544 MessageProtocolType messageProtocolType )
3645 {
37- this . clientChannel = clientChannel ;
46+ this . protocolChannel = protocolChannel ;
3847 this . messageProtocolType = messageProtocolType ;
48+ this . originalSynchronizationContext = SynchronizationContext . Current ;
3949 }
4050
4151 /// <summary>
@@ -46,35 +56,50 @@ public async Task Start()
4656 {
4757 if ( ! this . isStarted )
4858 {
49- // Start the provided client channel
50- this . clientChannel . Start ( this . messageProtocolType ) ;
59+ // Start the provided protocol channel
60+ this . protocolChannel . Start ( this . messageProtocolType ) ;
5161
5262 // Set the handler for any message responses that come back
53- this . clientChannel . MessageDispatcher . SetResponseHandler ( this . HandleResponse ) ;
63+ this . protocolChannel . MessageDispatcher . SetResponseHandler ( this . HandleResponse ) ;
5464
5565 // Listen for unhandled exceptions from the dispatcher
56- this . clientChannel . MessageDispatcher . UnhandledException += MessageDispatcher_UnhandledException ;
66+ this . protocolChannel . MessageDispatcher . UnhandledException += MessageDispatcher_UnhandledException ;
5767
58- // Notify implementation about client start
68+ // Notify implementation about endpoint start
5969 await this . OnStart ( ) ;
6070
61- // Client is now started
71+ // Endpoint is now started
6272 this . isStarted = true ;
6373 }
6474 }
6575
76+ public void WaitForExit ( )
77+ {
78+ this . endpointExitedTask = new TaskCompletionSource < bool > ( ) ;
79+ this . endpointExitedTask . Task . Wait ( ) ;
80+ }
81+
6682 public async Task Stop ( )
6783 {
6884 if ( this . isStarted )
6985 {
86+ // Make sure no future calls try to stop the endpoint during shutdown
87+ this . isStarted = false ;
88+
7089 // Stop the implementation first
7190 await this . OnStop ( ) ;
91+ this . protocolChannel . Stop ( ) ;
7292
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+ }
7598 }
7699 }
77100
101+ #region Message Sending
102+
78103 /// <summary>
79104 /// Sends a request to the server
80105 /// </summary>
@@ -107,7 +132,7 @@ public async Task<TResult> SendRequest<TParams, TResult>(
107132 responseTask ) ;
108133 }
109134
110- await this . clientChannel . MessageWriter . WriteRequest < TParams , TResult > (
135+ await this . protocolChannel . MessageWriter . WriteRequest < TParams , TResult > (
111136 requestType ,
112137 requestParams ,
113138 this . currentMessageId ) ;
@@ -128,19 +153,64 @@ await this.clientChannel.MessageWriter.WriteRequest<TParams, TResult>(
128153 }
129154 }
130155
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 )
132202 {
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 ) ;
137206 }
138207
208+
139209 public void SetEventHandler < TParams > (
140210 EventType < TParams > eventType ,
141211 Func < TParams , EventContext , Task > eventHandler )
142212 {
143- this . clientChannel . MessageDispatcher . SetEventHandler (
213+ this . protocolChannel . MessageDispatcher . SetEventHandler (
144214 eventType ,
145215 eventHandler ,
146216 false ) ;
@@ -151,20 +221,12 @@ public void SetEventHandler<TParams>(
151221 Func < TParams , EventContext , Task > eventHandler ,
152222 bool overrideExisting )
153223 {
154- this . clientChannel . MessageDispatcher . SetEventHandler (
224+ this . protocolChannel . MessageDispatcher . SetEventHandler (
155225 eventType ,
156226 eventHandler ,
157227 overrideExisting ) ;
158228 }
159229
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-
168230 private void HandleResponse ( Message responseMessage )
169231 {
170232 TaskCompletionSource < Message > pendingRequestTask = null ;
@@ -176,6 +238,10 @@ private void HandleResponse(Message responseMessage)
176238 }
177239 }
178240
241+ #endregion
242+
243+ #region Subclass Lifetime Methods
244+
179245 protected virtual Task OnStart ( )
180246 {
181247 return Task . FromResult ( true ) ;
@@ -185,6 +251,25 @@ protected virtual Task OnStop()
185251 {
186252 return Task . FromResult ( true ) ;
187253 }
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
188273 }
189274}
190275
0 commit comments