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);
            }
        }
예제 #2
0
        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);
                    }
                }
            }
        }
예제 #3
0
        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);
            }
        }
예제 #5
0
        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;
            }
        }
예제 #6
0
        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);
                }
            }
        }