9
9
using Microsoft . CodeAnalysis . Editor . Shared . Utilities ;
10
10
using Microsoft . CodeAnalysis . ErrorReporting ;
11
11
using Microsoft . CodeAnalysis . Execution ;
12
+ using Microsoft . CodeAnalysis . Extensions ;
12
13
using Microsoft . CodeAnalysis . Internal . Log ;
13
14
using Microsoft . CodeAnalysis . Remote ;
14
15
using Microsoft . ServiceHub . Client ;
@@ -175,27 +176,46 @@ private static async Task<TResult> RetryRemoteCallAsync<TException, TResult>(
175
176
{
176
177
const int retry_delayInMS = 50 ;
177
178
178
- var start = DateTime . UtcNow ;
179
- while ( DateTime . UtcNow - start < timeout )
179
+ using ( var pooledStopwatch = SharedPools . Default < Stopwatch > ( ) . GetPooledObject ( ) )
180
180
{
181
- cancellationToken . ThrowIfCancellationRequested ( ) ;
181
+ var watch = pooledStopwatch . Object ;
182
+ watch . Start ( ) ;
182
183
183
- try
184
+ while ( watch . Elapsed < timeout )
184
185
{
185
- return await funcAsync ( ) . ConfigureAwait ( false ) ;
186
- }
187
- catch ( TException )
188
- {
189
- // throw cancellation token if operation is cancelled
190
186
cancellationToken . ThrowIfCancellationRequested ( ) ;
191
- }
192
187
193
- // wait for retry_delayInMS before next try
194
- await Task . Delay ( retry_delayInMS , cancellationToken ) . ConfigureAwait ( false ) ;
188
+ try
189
+ {
190
+ return await funcAsync ( ) . ConfigureAwait ( false ) ;
191
+ }
192
+ catch ( TException )
193
+ {
194
+ // throw cancellation token if operation is cancelled
195
+ cancellationToken . ThrowIfCancellationRequested ( ) ;
196
+ }
197
+
198
+ // wait for retry_delayInMS before next try
199
+ await Task . Delay ( retry_delayInMS , cancellationToken ) . ConfigureAwait ( false ) ;
200
+
201
+ ReportTimeout ( watch ) ;
202
+ }
195
203
}
196
204
197
205
// operation timed out, more than we are willing to wait
198
- throw new TimeoutException ( "RequestServiceAsync timed out" ) ;
206
+ ShowInfoBar ( ) ;
207
+
208
+ // user didn't ask for cancellation, but we can't fullfill this request. so we
209
+ // create our own cancellation token and then throw it. this doesn't guarantee
210
+ // 100% that we won't crash, but this is at least safest way we know until user
211
+ // restart VS (with info bar)
212
+ using ( var ownCancellationSource = new CancellationTokenSource ( ) )
213
+ {
214
+ ownCancellationSource . Cancel ( ) ;
215
+ ownCancellationSource . Token . ThrowIfCancellationRequested ( ) ;
216
+ }
217
+
218
+ throw ExceptionUtilities . Unreachable ;
199
219
}
200
220
201
221
private static async Task < Stream > RequestServiceAsync (
@@ -256,6 +276,7 @@ private static async Task<Stream> RequestServiceAsync(
256
276
throw ExceptionUtilities . Unreachable ;
257
277
}
258
278
279
+ #region code related to make diagnosis easier later
259
280
private static int ReportDetailInfo ( IFaultUtility faultUtility )
260
281
{
261
282
// 0 means send watson, otherwise, cancel watson
@@ -264,6 +285,14 @@ private static int ReportDetailInfo(IFaultUtility faultUtility)
264
285
265
286
try
266
287
{
288
+ // add service hub process.
289
+ // we will record dumps for all service hub processes
290
+ foreach ( var p in Process . GetProcessesByName ( "ServiceHub.RoslynCodeAnalysisService32" ) )
291
+ {
292
+ // include all remote host processes
293
+ faultUtility . AddProcessDump ( p . Id ) ;
294
+ }
295
+
267
296
var logPath = Path . Combine ( Path . GetTempPath ( ) , "servicehub" , "logs" ) ;
268
297
if ( ! Directory . Exists ( logPath ) )
269
298
{
@@ -303,5 +332,37 @@ private static bool ReportNonIOException(Exception ex)
303
332
// catch all exception. not worth crashing VS.
304
333
return true ;
305
334
}
335
+
336
+ private static readonly TimeSpan s_reportTimeout = TimeSpan . FromMinutes ( 10 ) ;
337
+ private static bool s_timeoutReported = false ;
338
+
339
+ private static void ReportTimeout ( Stopwatch watch )
340
+ {
341
+ // if we tried for 10 min and still couldn't connect. NFW (non fatal watson) some data
342
+ if ( ! s_timeoutReported && watch . Elapsed > s_reportTimeout )
343
+ {
344
+ s_timeoutReported = true ;
345
+
346
+ // report service hub logs along with dump
347
+ WatsonReporter . Report ( "RequestServiceAsync Timeout" , new Exception ( "RequestServiceAsync Timeout" ) , ReportDetailInfo ) ;
348
+ }
349
+ }
350
+
351
+ private static bool s_infoBarReported = false ;
352
+
353
+ private static void ShowInfoBar ( )
354
+ {
355
+ // use info bar to show warning to users
356
+ if ( CodeAnalysis . PrimaryWorkspace . Workspace != null && ! s_infoBarReported )
357
+ {
358
+ // do not report it multiple times
359
+ s_infoBarReported = true ;
360
+
361
+ // use info bar to show warning to users
362
+ CodeAnalysis . PrimaryWorkspace . Workspace . Services . GetService < IErrorReportingService > ( ) ? . ShowGlobalErrorInfo (
363
+ ServicesVSResources . Unfortunately_a_process_used_by_Visual_Studio_has_encountered_an_unrecoverable_error_We_recommend_saving_your_work_and_then_closing_and_restarting_Visual_Studio ) ;
364
+ }
365
+ }
366
+ #endregion
306
367
}
307
368
}
0 commit comments