/// <summary> /// Clean up can be called by: /// 1. The user. AsyncUnaryCall.Dispose et al will call this on Dispose /// 2. <see cref="ValidateHeaders"/> will call dispose if errors fail validation /// 3. <see cref="FinishResponseAndCleanUp"/> will call dispose /// </summary> private void Cleanup(Status status) { if (!ResponseFinished) { // If the response is not finished then cancel any pending actions: // 1. Call HttpClient.SendAsync // 2. Response Stream.ReadAsync // 3. Client stream // - Getting the Stream from the Request.HttpContent // - Holding the Request.HttpContent.SerializeToStream open // - Writing to the client stream CancelCall(status); } else { _callTcs.TrySetResult(status); ClientStreamWriter?.WriteStreamTcs.TrySetCanceled(); ClientStreamWriter?.CompleteTcs.TrySetCanceled(); ClientStreamReader?.HttpResponseTcs.TrySetCanceled(); } _ctsRegistration?.Dispose(); _deadlineTimer?.Dispose(); HttpResponse?.Dispose(); ClientStreamReader?.Dispose(); ClientStreamWriter?.Dispose(); // To avoid racing with Dispose, skip disposing the call CTS. // This avoid Dispose potentially calling cancel on a disposed CTS. // The call CTS is not exposed externally and all dependent registrations // are cleaned up. }
/// <summary> /// Dispose can be called by: /// 1. The user. AsyncUnaryCall.Dispose et al will call this Dispose /// 2. <see cref="ValidateHeaders"/> will call dispose if errors fail validation /// 3. <see cref="FinishResponse"/> will call dispose /// </summary> public void Dispose() { if (!Disposed) { Disposed = true; if (!ResponseFinished) { // If the response is not finished then cancel any pending actions: // 1. Call HttpClient.SendAsync // 2. Response Stream.ReadAsync // 3. Client stream // - Getting the Stream from the Request.HttpContent // - Holding the Request.HttpContent.SerializeToStream open // - Writing to the client stream _callCts.Cancel(); } _ctsRegistration?.Dispose(); _writerCtsRegistration?.Dispose(); _deadlineTimer?.Dispose(); HttpResponse?.Dispose(); ClientStreamReader?.Dispose(); ClientStreamWriter?.Dispose(); // To avoid racing with Dispose, skip disposing the call CTS // This avoid Dispose potentially calling cancel on a disposed CTS // The call CTS is not exposed externally and all dependent registrations // are cleaned up } }
private void DisposeCore() { if (!Disposed) { // Locking on the call because: // 1. Its not exposed publically // 2. Nothing else locks on call // 3. We want to avoid allocating a private lock object lock (this) { if (!Disposed) { Disposed = true; if (!ResponseFinished) { // If the response is not finished then cancel any pending actions: // 1. Call HttpClient.SendAsync // 2. Response Stream.ReadAsync // 3. Client stream // - Getting the Stream from the Request.HttpContent // - Holding the Request.HttpContent.SerializeToStream open // - Writing to the client stream CancelCall(); } else { _writeStreamTcs?.TrySetCanceled(); _writeCompleteTcs?.TrySetCanceled(); } // If response has successfully finished then the status will come from the trailers // If it didn't finish then complete with a Cancelled status _callTcs.TrySetResult(StatusCode.Cancelled); _ctsRegistration?.Dispose(); _deadlineTimer?.Dispose(); HttpResponse?.Dispose(); ClientStreamReader?.Dispose(); ClientStreamWriter?.Dispose(); // To avoid racing with Dispose, skip disposing the call CTS // This avoid Dispose potentially calling cancel on a disposed CTS // The call CTS is not exposed externally and all dependent registrations // are cleaned up } } } }
/// <summary> /// Clean up can be called by: /// 1. The user. AsyncUnaryCall.Dispose et al will call this on Dispose /// 2. <see cref="GrpcCall.ValidateHeaders"/> will call dispose if errors fail validation /// 3. <see cref="FinishResponseAndCleanUp"/> will call dispose /// </summary> private void Cleanup(Status status) { if (!ResponseFinished) { // If the response is not finished then cancel any pending actions: // 1. Call HttpClient.SendAsync // 2. Response Stream.ReadAsync // 3. Client stream // - Getting the Stream from the Request.HttpContent // - Holding the Request.HttpContent.SerializeToStream open // - Writing to the client stream CancelCall(status); } else { _callTcs.TrySetResult(status); ClientStreamWriter?.WriteStreamTcs.TrySetCanceled(); ClientStreamWriter?.CompleteTcs.TrySetCanceled(); ClientStreamReader?.HttpResponseTcs.TrySetCanceled(); } Channel.FinishActiveCall(this); _ctsRegistration?.Dispose(); if (_deadlineTimer != null) { lock (this) { // Timer callback can call Timer.Change so dispose deadline timer in a lock // and set to null to indicate to the callback that it has been disposed. _deadlineTimer?.Dispose(); _deadlineTimer = null; } } HttpResponse?.Dispose(); ClientStreamReader?.Dispose(); ClientStreamWriter?.Dispose(); // To avoid racing with Dispose, skip disposing the call CTS. // This avoid Dispose potentially calling cancel on a disposed CTS. // The call CTS is not exposed externally and all dependent registrations // are cleaned up. }