-
Notifications
You must be signed in to change notification settings - Fork 4.1k
/
Copy pathRemoteCallback.cs
132 lines (117 loc) · 5.42 KB
/
RemoteCallback.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable enable
using System;
using System.IO;
using System.IO.Pipelines;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.ErrorReporting;
using Roslyn.Utilities;
using StreamJsonRpc;
namespace Microsoft.CodeAnalysis.Remote
{
/// <summary>
/// Wraps calls from a remote brokered service back to the client or to an in-proc brokered service.
/// The purpose of this type is to handle exceptions thrown by the underlying remoting infrastructure
/// in manner that's compatible with our exception handling policies.
///
/// TODO: This wrapper might not be needed once https://github.com/microsoft/vs-streamjsonrpc/issues/246 is fixed.
/// </summary>
internal readonly struct RemoteCallback<T>
where T : class
{
private readonly T _callback;
public RemoteCallback(T callback)
{
_callback = callback;
}
public async ValueTask InvokeAsync(Func<T, CancellationToken, ValueTask> invocation, CancellationToken cancellationToken)
{
try
{
await invocation(_callback, cancellationToken).ConfigureAwait(false);
}
catch (Exception exception) when (ReportUnexpectedException(exception, cancellationToken))
{
throw OnUnexpectedException(exception, cancellationToken);
}
}
public async ValueTask<TResult> InvokeAsync<TResult>(Func<T, CancellationToken, ValueTask<TResult>> invocation, CancellationToken cancellationToken)
{
try
{
return await invocation(_callback, cancellationToken).ConfigureAwait(false);
}
catch (Exception exception) when (ReportUnexpectedException(exception, cancellationToken))
{
throw OnUnexpectedException(exception, cancellationToken);
}
}
/// <summary>
/// Invokes a remote API that streams results back to the caller.
/// </summary>
public async ValueTask<TResult> InvokeAsync<TResult>(
Func<T, PipeWriter, CancellationToken, ValueTask> invocation,
Func<PipeReader, CancellationToken, ValueTask<TResult>> reader,
CancellationToken cancellationToken)
{
try
{
return await BrokeredServiceConnection<T>.InvokeStreamingServiceAsync(_callback, invocation, reader, cancellationToken).ConfigureAwait(false);
}
catch (Exception exception) when (ReportUnexpectedException(exception, cancellationToken))
{
throw OnUnexpectedException(exception, cancellationToken);
}
}
// Remote calls can only throw 4 types of exceptions that correspond to
//
// 1) Connection issue (connection dropped for any reason)
// 2) Serialization issue - bug in serialization of arguments (types are not serializable, etc.)
// 3) Remote exception - an exception was thrown by the callee
// 4) Cancelation
//
private static bool ReportUnexpectedException(Exception exception, CancellationToken cancellationToken)
{
if (exception is IOException)
{
// propagate intermittent exceptions without reporting telemetry:
return false;
}
if (exception is OperationCanceledException)
{
if (cancellationToken.IsCancellationRequested)
{
// Cancellation was requested and expected
return false;
}
return true;
}
// When a connection is dropped and CancelLocallyInvokedMethodsWhenConnectionIsClosed is
// set ConnectionLostException should not be thrown. Instead the cancellation token should be
// signaled and OperationCancelledException should be thrown.
// Seems to not work in all cases currently, so we need to cancel ourselves (bug https://github.com/microsoft/vs-streamjsonrpc/issues/551).
// Once this issue is fixed we can remov this if statement and fall back to reporting NFW
// as any observation of ConnectionLostException indicates a bug (e.g. https://github.com/microsoft/vs-streamjsonrpc/issues/549).
if (exception is ConnectionLostException)
{
return true;
}
// Indicates bug on client side or in serialization, report NFW and propagate the exception.
return FatalError.ReportWithoutCrashAndPropagate(exception);
}
private static Exception OnUnexpectedException(Exception exception, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (exception is ConnectionLostException)
{
throw new OperationCanceledException(exception.Message, exception);
}
// If this is hit the cancellation token passed to the service implementation did not use the correct token,
// and the resulting exception was not a ConnectionLostException.
return ExceptionUtilities.Unreachable;
}
}
}