/// <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);
                currentDelay.Task.SafeContinueWith(_ => RestoreGCLatencyMode(currentMode), TaskScheduler.Default);
                s_delay = currentDelay;
            }

            if (currentDelay != null)
            {
                currentDelay.Reset();
            }
        }
        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);
        }
Example #3
0
        private void EnqueueUpdate()
        {
            const int WorkspaceUpdateDelay = 1500;
            var delay = _delay;
            if (delay == null)
            {
                var newDelay = new ResettableDelay(WorkspaceUpdateDelay);
                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();
        }
Example #4
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);
        }