/// <summary> /// Cancels an individual outbound pending request. /// </summary> /// <param name="state">The ID associated with the request to be canceled.</param> private void CancelPendingOutboundRequest(object state) { Task.Run(async delegate { try { Requires.NotNull(state, nameof(state)); object id = state; if (!this.disposed) { var cancellationMessage = JsonRpcMessage.CreateRequestWithNamedParameters(id: null, method: CancelRequestSpecialMethod, namedParameters: new { id = id }, parameterSerializer: DefaultJsonSerializer); await this.TransmitAsync(cancellationMessage, this.disposeCts.Token).ConfigureAwait(false); } } catch (OperationCanceledException) { } catch (ObjectDisposedException) { } #if DESKTOP catch (Exception ex) { Debug.Fail(ex.Message, ex.ToString()); } #else catch (Exception) { } #endif }); }
/// <summary> /// Invokes the specified RPC method /// </summary> /// <typeparam name="ReturnType">RPC method return type</typeparam> /// <param name="id">An identifier established by the Client that MUST contain a String, Number, or NULL value if included. /// If it is not included it is assumed to be a notification.</param> /// <param name="targetName">Name of the method to invoke.</param> /// <param name="arguments">Arguments to pass to the invoked method. If null, no arguments are passed.</param> /// <param name="cancellationToken">The token whose cancellation should signal the server to stop processing this request.</param> /// <param name="isParameterObject">Value which indicates if parameter should be passed as an object.</param> /// <returns>A task whose result is the deserialized response from the JSON-RPC server.</returns> protected virtual async Task <ReturnType> InvokeCoreAsync <ReturnType>(int?id, string targetName, IReadOnlyList <object> arguments, CancellationToken cancellationToken, bool isParameterObject) { Requires.NotNullOrEmpty(targetName, nameof(targetName)); Verify.NotDisposed(this); cancellationToken.ThrowIfCancellationRequested(); JsonRpcMessage request; if (isParameterObject) { object argument = arguments; if (argument != null) { if (arguments.Count != 1 || arguments[0] == null || !arguments[0].GetType().GetTypeInfo().IsClass) { throw new ArgumentException(Resources.ParameterNotObject); } argument = arguments[0]; } request = JsonRpcMessage.CreateRequestWithNamedParameters(id, targetName, argument, this.JsonSerializer); } else { arguments = arguments ?? EmptyObjectArray; request = JsonRpcMessage.CreateRequest(id, targetName, arguments, this.JsonSerializer); } using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, this.disposeCts.Token)) { if (id == null) { await this.TransmitAsync(request, cts.Token).ConfigureAwait(false); return(default(ReturnType)); } Verify.Operation(this.readLinesTask != null, Resources.InvalidBeforeListenHasStarted); var tcs = new TaskCompletionSource <ReturnType>(); Action <JsonRpcMessage> dispatcher = (response) => { lock (this.dispatcherMapLock) { this.resultDispatcherMap.Remove(id.Value); } try { if (response == null) { tcs.TrySetCanceled(); } else if (response.IsError) { tcs.TrySetException(CreateExceptionFromRpcError(response, targetName)); } else { tcs.TrySetResult(response.GetResult <ReturnType>(this.JsonSerializer)); } } catch (Exception ex) { tcs.TrySetException(ex); } }; var callData = new OutstandingCallData(tcs, dispatcher); lock (this.dispatcherMapLock) { this.resultDispatcherMap.Add(id.Value, callData); } await this.TransmitAsync(request, cts.Token).ConfigureAwait(false); // Arrange for sending a cancellation message if canceled while we're waiting for a response. using (cancellationToken.Register(this.cancelPendingOutboundRequestAction, id.Value, useSynchronizationContext: false)) { // This task will be completed when the Response object comes back from the other end of the pipe return(await tcs.Task.ConfigureAwait(false)); } } }