internal Task <TResponse> CallAsync(TRequest request, CallSettings callSettings) { var unaryCall = _asyncGrpcCall(request, callSettings.ToCallOptions(_clock)); var responseMetadataHandler = callSettings?.ResponseMetadataHandler; var trailingMetadataHandler = callSettings?.TrailingMetadataHandler; // If we don't have any response/trailing metadata handlers, it's simplest just to // return the gRPC task. if (responseMetadataHandler == null && trailingMetadataHandler == null) { return(unaryCall.ResponseAsync); } // Otherwise, we want to pass the returned metadata to the handlers before our returned task completes, // so we create a proxy task. return(WaitAndCallHandlers()); // Implementation note: we capture local variables here, forcing the compiler to create // a class to hold that state, as well as a regular async state machine. This could be avoided // with parameters, but the cost is small compared with the network access (and it's a relatively rare case). async Task <TResponse> WaitAndCallHandlers() { var response = await unaryCall.ResponseAsync.ConfigureAwait(false); var responseHeadersTask = unaryCall.ResponseHeadersAsync; if (responseHeadersTask.Status == TaskStatus.RanToCompletion) { responseMetadataHandler?.Invoke(responseHeadersTask.Result); } trailingMetadataHandler?.Invoke(unaryCall.GetTrailers()); return(response); } }
internal TResponse CallSync(TRequest request, CallSettings callSettings) { // If we don't have complicated requirements, use the gRPC sync call. Otherwise, // async the sync call. if (callSettings?.ResponseMetadataHandler == null && callSettings?.TrailingMetadataHandler == null) { return(_syncGrpcCall(request, callSettings.ToCallOptions(_clock))); } return(CallAsync(request, callSettings).ResultWithUnwrappedExceptions()); }