public ServerCallDeadlineManager(HttpContextServerCallContext serverCallContext, ISystemClock clock, TimeSpan timeout, long maxTimerDueTime = DefaultMaxTimerDueTime) { // Set fields that need to exist before setting up deadline CTS // Ensures callback can run successfully before CTS timer starts _serverCallContext = serverCallContext; Deadline = clock.UtcNow.Add(timeout); _systemClock = clock; var timerMilliseconds = CommonGrpcProtocolHelpers.GetTimerDueTime(timeout, maxTimerDueTime); if (timerMilliseconds == maxTimerDueTime) { // Create timer and set to field before setting time. // Ensures there is no weird situation where the timer triggers // before the field is set. Shouldn't happen because only long deadlines // will take this path but better to be safe than sorry. _longDeadlineTimer = new Timer(DeadlineExceededLongDelegate, (this, maxTimerDueTime), Timeout.Infinite, Timeout.Infinite); _longDeadlineTimer.Change(timerMilliseconds, Timeout.Infinite); } else { _longDeadlineTimer = new Timer(DeadlineExceededDelegate, this, timerMilliseconds, Timeout.Infinite); } }
private void DeadlineExceededCallback(object?state) { // Deadline is only exceeded if the timeout has passed and // the response has not been finished or canceled if (!_callCts.IsCancellationRequested && !ResponseFinished) { TimeSpan remaining; lock (this) { // If _deadline is MaxValue then the DEADLINE_EXCEEDED status has // already been received by the client and the timer can stop. if (_deadline == DateTime.MaxValue) { return; } remaining = _deadline - Channel.Clock.UtcNow; if (remaining <= TimeSpan.Zero) { DeadlineExceeded(); return; } if (_deadlineTimer != null) { // Deadline has not been reached because timer maximum due time was smaller than deadline. // Reschedule DeadlineExceeded again until deadline has been exceeded. GrpcCallLog.DeadlineTimerRescheduled(Logger, remaining); var dueTime = CommonGrpcProtocolHelpers.GetTimerDueTime(remaining, Channel.MaxTimerDueTime); _deadlineTimer.Change(dueTime, Timeout.Infinite); } } } }
private (bool diagnosticSourceEnabled, Activity?activity) InitializeCall(HttpRequestMessage request, TimeSpan?timeout) { GrpcCallLog.StartingCall(Logger, Method.Type, request.RequestUri !); GrpcEventSource.Log.CallStart(Method.FullName); // Deadline will cancel the call CTS. // Only exceed deadline/start timer after reader/writer have been created, otherwise deadline will cancel // the call CTS before they are created and leave them in a non-canceled state. if (timeout != null && !Channel.DisableClientDeadline) { if (timeout.Value <= TimeSpan.Zero) { // Call was started with a deadline in the past so immediately trigger deadline exceeded. DeadlineExceeded(); } else { GrpcCallLog.StartingDeadlineTimeout(Logger, timeout.Value); var dueTime = CommonGrpcProtocolHelpers.GetTimerDueTime(timeout.Value, Channel.MaxTimerDueTime); _deadlineTimer = new Timer(DeadlineExceededCallback, null, dueTime, Timeout.Infinite); } } var diagnosticSourceEnabled = GrpcDiagnostics.DiagnosticListener.IsEnabled() && GrpcDiagnostics.DiagnosticListener.IsEnabled(GrpcDiagnostics.ActivityName, request); Activity?activity = null; // Set activity if: // 1. Diagnostic source is enabled // 2. Logging is enabled // 3. There is an existing activity (to enable activity propagation) if (diagnosticSourceEnabled || Logger.IsEnabled(LogLevel.Critical) || Activity.Current != null) { activity = new Activity(GrpcDiagnostics.ActivityName); activity.AddTag(GrpcDiagnostics.GrpcMethodTagName, Method.FullName); activity.Start(); if (diagnosticSourceEnabled) { GrpcDiagnostics.DiagnosticListener.Write(GrpcDiagnostics.ActivityStartKey, new { Request = request }); } } if (Options.CancellationToken.CanBeCanceled) { // The cancellation token will cancel the call CTS. // This must be registered after the client writer has been created // so that cancellation will always complete the writer. _ctsRegistration = Options.CancellationToken.Register(CancelCallFromCancellationToken); } return(diagnosticSourceEnabled, activity); }
private static void DeadlineExceededLongCallback(object?state) { var(manager, maxTimerDueTime) = (ValueTuple <ServerCallDeadlineManager, long>)state !; var remaining = manager.Deadline - manager._systemClock.UtcNow; if (remaining <= TimeSpan.Zero) { _ = manager.DeadlineExceededAsync(); } else { // Deadline has not been reached because timer maximum due time was smaller than deadline. // Reschedule DeadlineExceeded again until deadline has been exceeded. GrpcServerLog.DeadlineTimerRescheduled(manager._serverCallContext.Logger, remaining); manager._longDeadlineTimer.Change(CommonGrpcProtocolHelpers.GetTimerDueTime(remaining, maxTimerDueTime), Timeout.Infinite); } }
protected RetryCallBase(GrpcChannel channel, Method <TRequest, TResponse> method, CallOptions options, string loggerName, int retryAttempts) { Logger = channel.LoggerFactory.CreateLogger(loggerName); Channel = channel; Method = method; Options = options; _commitedCallTcs = new TaskCompletionSource <IGrpcCall <TRequest, TResponse> >(TaskCreationOptions.RunContinuationsAsynchronously); BufferedMessages = new List <ReadOnlyMemory <byte> >(); // Raise OnCancellation event for cancellation related clean up. CancellationTokenSource = new CancellationTokenSource(); CancellationTokenSource.Token.Register(state => ((RetryCallBase <TRequest, TResponse>)state !).OnCancellation(), this); // If the passed in token is canceled then we want to cancel the retry cancellation token. // Note that if the token is already canceled then callback is run inline. if (options.CancellationToken.CanBeCanceled) { _ctsRegistration = options.CancellationToken.Register(state => ((RetryCallBase <TRequest, TResponse>)state !).CancellationTokenSource.Cancel(), this); } var deadline = Options.Deadline.GetValueOrDefault(DateTime.MaxValue); if (deadline != DateTime.MaxValue) { var timeout = CommonGrpcProtocolHelpers.GetTimerDueTime(deadline - Channel.Clock.UtcNow, Channel.MaxTimerDueTime); CancellationTokenSource.CancelAfter(TimeSpan.FromMilliseconds(timeout)); } if (HasClientStream()) { // Run continuation synchronously so awaiters execute inside the lock NewActiveCallTcs = new TaskCompletionSource <IGrpcCall <TRequest, TResponse>?>(TaskCreationOptions.None); } if (retryAttempts > Channel.MaxRetryAttempts) { Log.MaxAttemptsLimited(Logger, retryAttempts, Channel.MaxRetryAttempts.GetValueOrDefault()); MaxRetryAttempts = Channel.MaxRetryAttempts.GetValueOrDefault(); } else { MaxRetryAttempts = retryAttempts; } }
private void DeadlineExceededCallback(object?state) { // Deadline is only exceeded if the timeout has passed and // the response has not been finished or canceled if (!_callCts.IsCancellationRequested && !ResponseFinished) { var remaining = _deadline - Channel.Clock.UtcNow; if (remaining <= TimeSpan.Zero) { DeadlineExceeded(); } else { // Deadline has not been reached because timer maximum due time was smaller than deadline. // Reschedule DeadlineExceeded again until deadline has been exceeded. GrpcCallLog.DeadlineTimerRescheduled(Logger, remaining); var dueTime = CommonGrpcProtocolHelpers.GetTimerDueTime(remaining, Channel.MaxTimerDueTime); _deadlineTimer !.Change(dueTime, Timeout.Infinite); } } }