예제 #1
0
        /// <summary>
        /// Call this method to suppress expensive blocking Gen 2 garbage GCs in
        /// scenarios where high-latency is unacceptable (e.g. processing typing input).
        ///
        /// Blocking GCs will be re-enabled automatically after a short duration unless
        /// UseLowLatencyModeForProcessingUserInput is called again.
        /// </summary>
        internal static void UseLowLatencyModeForProcessingUserInput()
        {
            if (s_delayMilliseconds <= 0)
            {
                // The registry key to opt out of Roslyn's SustainedLowLatency is set, or
                // we haven't yet initialized the value.
                return;
            }

            var currentMode  = GCSettings.LatencyMode;
            var currentDelay = s_delay;

            if (currentMode != GCLatencyMode.SustainedLowLatency)
            {
                GCSettings.LatencyMode = GCLatencyMode.SustainedLowLatency;

                // Restore the LatencyMode a short duration after the
                // last request to UseLowLatencyModeForProcessingUserInput.
                currentDelay = new ResettableDelay(s_delayMilliseconds, AsynchronousOperationListenerProvider.NullListener);
                currentDelay.Task.SafeContinueWith(_ => RestoreGCLatencyMode(currentMode), TaskScheduler.Default);
                s_delay = currentDelay;
            }

            if (currentDelay != null)
            {
                currentDelay.Reset();
            }
        }
예제 #2
0
        public TaskCenterSolutionAnalysisProgressReporter(
            SVsTaskStatusCenterService taskStatusCenterService,
            IDiagnosticService diagnosticService,
            VisualStudioWorkspace workspace)
        {
            _lastTimeReported = DateTimeOffset.UtcNow;
            _resettableDelay  = null;

            ResetProgressStatus();

            _taskCenterService = (IVsTaskStatusCenterService)taskStatusCenterService;

            _options = new TaskHandlerOptions()
            {
                Title = ServicesVSResources.Running_low_priority_background_processes,
                ActionsAfterCompletion = CompletionActions.None
            };

            var crawlerService = workspace.Services.GetService <ISolutionCrawlerService>();
            var reporter       = crawlerService.GetProgressReporter(workspace);

            StartedOrStopped(reporter.InProgress);

            // no event unsubscription since it will remain alive until VS shutdown
            reporter.ProgressChanged += OnSolutionCrawlerProgressChanged;
        }
예제 #3
0
        private static void RestoreGCLatencyMode(GCLatencyMode originalMode)
        {
            GCSettings.LatencyMode = originalMode;
            s_delay = null;

            // hint to the GC that if it postponed a gen 2 collection, now might be a good time to do it.
            GC.Collect(2, GCCollectionMode.Optimized);
        }
예제 #4
0
        private void ProgressUpdated()
        {
            // we prefer showing evaluating if progress is flipping between evaluate and pause
            // in short period of time.
            var forceUpdate = _lastShownProgressStatus == ProgressStatus.Paused &&
                              _lastProgressStatus == ProgressStatus.Evaluating;

            var current = DateTimeOffset.UtcNow;

            if (!forceUpdate && current - _lastTimeReported < s_minimumInterval)
            {
                // make sure we are not flooding UI.
                // this is just presentation, fine to not updating UI right away especially since
                // at the end, this notification will go away automatically
                // but to make UI to be updated to right status eventually if task takes long time to finish
                // we enqueue refresh task.
                EnqueueRefresh();
                return;
            }

            _lastShownProgressStatus = _lastProgressStatus;
            _lastTimeReported        = current;

            ChangeProgress(_taskHandler, GetMessage());

            string GetMessage()
            {
                var statusMessage = (_lastProgressStatus == ProgressStatus.Paused) ? ServicesVSResources.Paused_0_tasks_in_queue : ServicesVSResources.Evaluating_0_tasks_in_queue;

                return(string.Format(statusMessage, _lastPendingItemCount));
            }

            void EnqueueRefresh()
            {
                if (_resettableDelay != null)
                {
                    _resettableDelay.Reset();
                    return;
                }

                _resettableDelay = new ResettableDelay((int)s_minimumInterval.TotalMilliseconds, AsynchronousOperationListenerProvider.NullListener);
                _resettableDelay.Task.SafeContinueWith(_ =>
                {
                    _resettableDelay = null;
                    ProgressUpdated();
                }, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
            }
        }
예제 #5
0
        private void PostUpdate(Solution solution)
        {
            _delay = null;
            lock (_gate)
            {
                // See if each context is still alive. It's possible it's already been GC'ed meaning we should stop caring about the query
                _trackedQueries.RemoveAll(t => !IsTrackingContext(t.Item1));
                if (_trackedQueries.IsEmpty())
                {
                    _workspace.WorkspaceChanged -= OnWorkspaceChanged;
                    return;
                }
            }

            EnqueueUpdateIfSolutionIsStale(solution);
        }
예제 #6
0
            private WorkspaceChangeTracker(
                RemoteEndPoint endPoint,
                Workspace workspace,
                DocumentId documentId,
                VersionStamp dependentVersion,
                CancellationToken cancellationToken)
            {
                _gate            = new object();
                _eventSubscribed = false;

                _endPoint          = endPoint;
                _workspace         = workspace;
                _documentId        = documentId;
                _cancellationToken = cancellationToken;

                _lastVersion     = dependentVersion;
                _resettableDelay = ResettableDelay.CompletedDelay;

                ConnectEvents(subscription: true);
            }
예제 #7
0
        private void EnqueueUpdate()
        {
            const int WorkspaceUpdateDelay = 1500;
            var       delay = _delay;

            if (delay == null)
            {
                var newDelay = new ResettableDelay(WorkspaceUpdateDelay, _asyncListener);
                if (Interlocked.CompareExchange(ref _delay, newDelay, null) == null)
                {
                    var asyncToken = _asyncListener.BeginAsyncOperation("WorkspaceGraphQueryManager.EnqueueUpdate");
                    newDelay.Task.SafeContinueWithFromAsync(_ => UpdateAsync(), CancellationToken.None, TaskScheduler.Default)
                    .CompletesAsyncOperation(asyncToken);
                }

                return;
            }

            delay.Reset();
        }
예제 #8
0
            private WorkspaceChangeTracker(
                CodeAnalysisService owner,
                Workspace workspace,
                DocumentId documentId,
                VersionStamp dependentVersion,
                CancellationToken cancellationToken)
            {
                _gate            = new object();
                _eventSubscribed = false;

                _owner             = owner;
                _workspace         = workspace;
                _documentId        = documentId;
                _cancellationToken = cancellationToken;

                _lastVersion     = dependentVersion;
                _resettableDelay = ResettableDelay.CompletedDelay;

                ConnectEvents(subscription: true);
            }
예제 #9
0
            private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs e)
            {
                // workspace event is serialized events. and reset delay only get updated here
                if (!_resettableDelay.Task.IsCompleted)
                {
                    _resettableDelay.Reset();
                    return;
                }

                var delay = new ResettableDelay((int)s_delay.TotalMilliseconds, expeditableDelaySource: AsynchronousOperationListenerProvider.NullListener);

                _resettableDelay = delay;
                delay.Task.ContinueWith(InvalidateAsync, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);

                async Task InvalidateAsync(Task _)
                {
                    var document = _workspace.CurrentSolution.GetDocument(_documentId);

                    if (document == null)
                    {
                        return;
                    }

                    var newVersion = await document.Project.GetDependentVersionAsync(CancellationToken.None).ConfigureAwait(false);

                    if (newVersion == _lastVersion)
                    {
                        return;
                    }

                    // fire and forget.
                    // ignore any exception such as rpc already disposed (disconnected)

                    _lastVersion = newVersion;

                    await _endPoint.InvokeAsync(
                        nameof(IRemoteCodeLensDataPoint.Invalidate),
                        Array.Empty <object>(),
                        CancellationToken.None).ConfigureAwait(false);
                }
            }
예제 #10
0
            public Task <bool> IsFullyLoadedAsync(CancellationToken cancellationToken)
            {
                if (_workspace.Options.GetOption(ExperimentationOptions.SolutionStatusService_ForceDelay))
                {
                    // this is for prototype/mock for people/teams trying partial solution load prototyping
                    // it shouldn't concern anyone outside of that group.
                    //
                    // "experimentationService.IsExperimentEnabled(WellKnownExperimentNames.PartialLoadMode)" check above
                    // make sure this part of code "Service : IWorkspaceStatusService" is not exposed anyone outside of
                    // the partial solution load group.
                    //
                    // for ones that is in the group, one can try lightbulb behavior through internal option page
                    // such as moving around caret on lines where LB should show up.
                    //
                    // it works as below
                    // 1. when this is first called, we return false and start timer to change its status in delayInMS
                    // 2. once delayInMs passed, we clear the delay and return true.
                    // 3. if it is called before the timeout, we reset the timer and return false
                    if (_lastTimeCalled == null)
                    {
                        var delay = _workspace.Options.GetOption(ExperimentationOptions.SolutionStatusService_DelayInMS);
                        _lastTimeCalled = new ResettableDelay(delayInMilliseconds: delay, AsynchronousOperationListenerProvider.NullListener);
                        _ = _lastTimeCalled.Task.SafeContinueWith(_ => StatusChanged?.Invoke(this, true), TaskScheduler.Default);

                        return(SpecializedTasks.False);
                    }

                    if (_lastTimeCalled.Task.IsCompleted)
                    {
                        _lastTimeCalled = null;
                        return(SpecializedTasks.True);
                    }

                    _lastTimeCalled.Reset();
                    return(SpecializedTasks.False);
                }

                // we are using this API for now, until platform provide us new API for prototype
                return(KnownUIContexts.SolutionExistsAndFullyLoadedContext.IsActive ? SpecializedTasks.True : SpecializedTasks.False);
            }
예제 #11
0
 private static void RestoreGCLatencyMode(GCLatencyMode originalMode)
 {
     GCSettings.LatencyMode = originalMode;
     s_delay = null;
 }