<commit_before>﻿// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Internal.Log;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.Host
{
    internal class BackgroundCompiler : IDisposable
    {
        private Workspace _workspace;
        private readonly IWorkspaceTaskScheduler _compilationScheduler;

        // Used to keep a strong reference to the built compilations so they are not GC'd
        private Compilation[] _mostRecentCompilations;

        private readonly object _buildGate = new object();
        private CancellationTokenSource _cancellationSource;

        public BackgroundCompiler(Workspace workspace)
        {
            _workspace = workspace;

            // make a scheduler that runs on the thread pool
            var taskSchedulerFactory = workspace.Services.GetService<IWorkspaceTaskSchedulerFactory>();
            _compilationScheduler = taskSchedulerFactory.CreateBackgroundTaskScheduler();

            _cancellationSource = new CancellationTokenSource();
            _workspace.WorkspaceChanged += OnWorkspaceChanged;
            _workspace.DocumentOpened += OnDocumentOpened;
            _workspace.DocumentClosed += OnDocumentClosed;
        }

        public void Dispose()
        {
            if (_workspace != null)
            {
                CancelBuild(releasePreviousCompilations: true);

                _workspace.DocumentClosed -= OnDocumentClosed;
                _workspace.DocumentOpened -= OnDocumentOpened;
                _workspace.WorkspaceChanged -= OnWorkspaceChanged;

                _workspace = null;
            }
        }

        private void OnDocumentOpened(object sender, DocumentEventArgs args)
        {
            Rebuild(args.Document.Project.Solution, args.Document.Project.Id);
        }

        private void OnDocumentClosed(object sender, DocumentEventArgs args)
        {
            Rebuild(args.Document.Project.Solution, args.Document.Project.Id);
        }

        private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs args)
        {
            switch (args.Kind)
            {
                case WorkspaceChangeKind.SolutionCleared:
                case WorkspaceChangeKind.SolutionAdded:
                case WorkspaceChangeKind.SolutionRemoved:
                    CancelBuild(releasePreviousCompilations: true);
                    break;

                case WorkspaceChangeKind.SolutionChanged:
                case WorkspaceChangeKind.ProjectRemoved:
                    Rebuild(args.NewSolution);
                    break;

                default:
                    Rebuild(args.NewSolution, args.ProjectId);
                    break;
            }
        }

        private void Rebuild(Solution solution, ProjectId initialProject = null)
        {
            lock (_buildGate)
            {
                // Keep the previous compilations around so that we can incrementally
                // build the current compilations without rebuilding the entire DeclarationTable
                CancelBuild(releasePreviousCompilations: false);

                var allProjects = _workspace.GetOpenDocumentIds().Select(d => d.ProjectId).ToSet();

                // don't even get started if there is nothing to do
                if (allProjects.Count > 0)
                {
                    BuildCompilationsAsync(solution, initialProject, allProjects);
                }
            }
        }
<commit_msg>Fix a memory leak in BackgroundCompiler<commit_after>﻿// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Internal.Log;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.Host
{
    internal class BackgroundCompiler : IDisposable
    {
        private Workspace _workspace;
        private readonly IWorkspaceTaskScheduler _compilationScheduler;

        // Used to keep a strong reference to the built compilations so they are not GC'd
        private Compilation[] _mostRecentCompilations;

        private readonly object _buildGate = new object();
        private CancellationTokenSource _cancellationSource;

        public BackgroundCompiler(Workspace workspace)
        {
            _workspace = workspace;

            // make a scheduler that runs on the thread pool
            var taskSchedulerFactory = workspace.Services.GetService<IWorkspaceTaskSchedulerFactory>();
            _compilationScheduler = taskSchedulerFactory.CreateBackgroundTaskScheduler();

            _cancellationSource = new CancellationTokenSource();
            _workspace.WorkspaceChanged += OnWorkspaceChanged;
            _workspace.DocumentOpened += OnDocumentOpened;
            _workspace.DocumentClosed += OnDocumentClosed;
        }

        public void Dispose()
        {
            if (_workspace != null)
            {
                CancelBuild(releasePreviousCompilations: true);

                _workspace.DocumentClosed -= OnDocumentClosed;
                _workspace.DocumentOpened -= OnDocumentOpened;
                _workspace.WorkspaceChanged -= OnWorkspaceChanged;

                _workspace = null;
            }
        }

        private void OnDocumentOpened(object sender, DocumentEventArgs args)
        {
            Rebuild(args.Document.Project.Solution, args.Document.Project.Id);
        }

        private void OnDocumentClosed(object sender, DocumentEventArgs args)
        {
            Rebuild(args.Document.Project.Solution, args.Document.Project.Id);
        }

        private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs args)
        {
            switch (args.Kind)
            {
                case WorkspaceChangeKind.SolutionCleared:
                case WorkspaceChangeKind.SolutionAdded:
                case WorkspaceChangeKind.SolutionRemoved:
                    CancelBuild(releasePreviousCompilations: true);
                    break;

                case WorkspaceChangeKind.SolutionChanged:
                case WorkspaceChangeKind.ProjectRemoved:
                    if (args.NewSolution.ProjectIds.Count == 0)
                    {
                        // Close solution no longer triggers a SolutionRemoved event,
                        // so we need to make an explicitly check for ProjectRemoved event.
                        CancelBuild(releasePreviousCompilations: true);
                    }
                    else
                    {
                        Rebuild(args.NewSolution);
                    }

                    break;

                default:
                    Rebuild(args.NewSolution, args.ProjectId);
                    break;
            }
        }

        private void Rebuild(Solution solution, ProjectId initialProject = null)
        {
            lock (_buildGate)
            {
                // Keep the previous compilations around so that we can incrementally
                // build the current compilations without rebuilding the entire DeclarationTable
                CancelBuild(releasePreviousCompilations: false);

                var allProjects = _workspace.GetOpenDocumentIds().Select(d => d.ProjectId).ToSet();

                // don't even get started if there is nothing to do
                if (allProjects.Count > 0)
                {
                    BuildCompilationsAsync(solution, initialProject, allProjects);
                }
            }
        }

{'fraction_non_alphanumeric': 0.06078680203045685, 'fraction_numerical': 0.0008883248730964467, 'mean_word_length': 1.937383525903839, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2366895', 'n_tokens_mistral': 1866, 'n_tokens_neox': 1747, 'n_words': 458}