<commit_before>using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Framework.DesignTimeHost.Models;
using Microsoft.Framework.Logging;

namespace OmniSharp.AspNet5
{
    public class PackagesRestoreTool
    {
        private readonly ILogger _logger;
        private readonly AspNet5Context _context;
        private readonly AspNet5Paths _paths;
        private readonly object _lock;
        private readonly IDictionary<string, AutoResetEvent> _gates;
        private readonly SemaphoreSlim _semaphore;

        public PackagesRestoreTool(ILoggerFactory logger, AspNet5Context context, AspNet5Paths paths)
        {
            _logger = logger.Create<PackagesRestoreTool>();
            _context = context;
            _paths = paths;
            _lock = new object();
            _gates = new Dictionary<string, AutoResetEvent>();
            _semaphore = new SemaphoreSlim(Environment.ProcessorCount / 2);
        }

        public void Run(Project project)
        {
            AutoResetEvent gate;
            lock (_lock)
            {
                if (!_gates.TryGetValue(project.Path, out gate))
                {
                    gate = new AutoResetEvent(true);
                    _gates.Add(project.Path, gate);
                }
            }

            gate.WaitOne();
            _semaphore.Wait();

            TryStartRestore(Path.GetDirectoryName(project.Path), (didStart) =>
            {
                _semaphore.Release();
                gate.Set();
                lock (_lock)
                {
                    _gates.Remove(project.Path);
                }

                if (didStart)
                {
                    _context.Connection.Post(new Message()
                    {
                        ContextId = project.ContextId,
                        MessageType = "RestoreComplete",
                        HostId = _context.HostId
                    });
                }
            });
        }

        private void TryStartRestore(string workingDir, Action<bool> onDone)
        {
            var psi = new ProcessStartInfo()
            {
                FileName = _paths.Dnu ?? _paths.Kpm,
                WorkingDirectory = workingDir,
                CreateNoWindow = true,
                UseShellExecute = false,
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                Arguments = "restore"
            };

            _logger.WriteInformation("restore packages {0} {1} for {2}", psi.FileName, psi.Arguments, psi.WorkingDirectory);

            var restoreProcess = Process.Start(psi);
            if (restoreProcess.HasExited)
            {
                _logger.WriteError("restore command ({0}) failed with error code {1}", psi.FileName, restoreProcess.ExitCode);
                onDone(false);
            }
            else
            {
                RedirectOutput(restoreProcess, _logger);
                restoreProcess.EnableRaisingEvents = true;
                restoreProcess.OnExit(() => onDone(true));
            }
        }

        private static void RedirectOutput(Process process, ILogger logger)
        {
            Task.Factory.StartNew(async () =>
            {
                string line;
                while ((line = await process.StandardOutput.ReadLineAsync()) != null)
                {
                    logger.WriteInformation(line);
                }
            });
            Task.Factory.StartNew(async () =>
            {
                string line;
                while ((line = await process.StandardError.ReadLineAsync()) != null)
                {
                    logger.WriteError(line);
                }
            });
        }
    }
}
<commit_msg>simplify locks and process handling<commit_after>using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Framework.DesignTimeHost.Models;
using Microsoft.Framework.Logging;

namespace OmniSharp.AspNet5
{
    public class PackagesRestoreTool
    {
        private readonly ILogger _logger;
        private readonly AspNet5Context _context;
        private readonly AspNet5Paths _paths;
        private readonly object _lock;
        private readonly IDictionary<string, object> _projectLocks;
        private readonly SemaphoreSlim _semaphore;

        public PackagesRestoreTool(ILoggerFactory logger, AspNet5Context context, AspNet5Paths paths)
        {
            _logger = logger.Create<PackagesRestoreTool>();
            _context = context;
            _paths = paths;
            _lock = new object();
            _projectLocks = new Dictionary<string, object>();
            _semaphore = new SemaphoreSlim(Environment.ProcessorCount / 2);
        }

        public void Run(Project project)
        {
            Task.Factory.StartNew(() =>
            {
                object projectLock;
                lock (_lock)
                {
                    if (!_projectLocks.TryGetValue(project.Path, out projectLock))
                    {
                        projectLock = new object();
                        _projectLocks.Add(project.Path, projectLock);
                    }
                }

                lock (projectLock)
                {
                    _semaphore.Wait();
                    try
                    {
                        var psi = new ProcessStartInfo()
                        {
                            FileName = _paths.Dnu ?? _paths.Kpm,
                            WorkingDirectory = Path.GetDirectoryName(project.Path),
                            CreateNoWindow = true,
                            UseShellExecute = false,
                            RedirectStandardOutput = true,
                            RedirectStandardError = true,
                            Arguments = "restore"
                        };

                        _logger.WriteInformation("restore packages {0} {1} for {2}", psi.FileName, psi.Arguments, psi.WorkingDirectory);

                        var restoreProcess = Process.Start(psi);
                        if (restoreProcess.HasExited)
                        {
                            _logger.WriteError("restore command ({0}) failed with error code {1}", psi.FileName, restoreProcess.ExitCode);
                        }
                        else
                        {
                            restoreProcess.OutputDataReceived += (sender, e) => _logger.WriteInformation(e.Data);
                            restoreProcess.ErrorDataReceived += (sender, e) => _logger.WriteError(e.Data);
                            restoreProcess.BeginOutputReadLine();
                            restoreProcess.BeginErrorReadLine();
                            restoreProcess.WaitForExit();

                            _context.Connection.Post(new Message()
                            {
                                ContextId = project.ContextId,
                                MessageType = "RestoreComplete",
                                HostId = _context.HostId
                            });
                        }
                    }
                    finally
                    {
                        _semaphore.Release();
                        _projectLocks.Remove(project.Path);
                    }
                }
            });
        }
    }
}

{'fraction_non_alphanumeric': 0.07758620689655173, 'fraction_numerical': 0.0029177718832891246, 'mean_word_length': 1.2858441952106698, 'pattern_counts': {'":': 0, '<': 10, '<?xml version=': 0, '>': 17, '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': '3424246', 'n_tokens_mistral': 1790, 'n_tokens_neox': 1684, 'n_words': 350}