diff --git a/src/Microsoft.Tye.Hosting/Model/Application.cs b/src/Microsoft.Tye.Hosting/Model/Application.cs index a1b63fe8..0fa9f6e4 100644 --- a/src/Microsoft.Tye.Hosting/Model/Application.cs +++ b/src/Microsoft.Tye.Hosting/Model/Application.cs @@ -3,11 +3,12 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; -using System.Threading; +using System.Threading.Tasks; namespace Microsoft.Tye.Hosting.Model { @@ -18,18 +19,6 @@ namespace Microsoft.Tye.Hosting.Model Source = source.FullName; ContextDirectory = source.DirectoryName!; Services = services; - - foreach (var s in Services.Values) - { - if (s.Description.RunInfo is ProjectRunInfo projectRunInfo) - { - var projectFileName = projectRunInfo.ProjectFile.FullName; - if (!ProjectFileLocks.ContainsKey(projectFileName)) - { - ProjectFileLocks[projectFileName] = new SemaphoreSlim(1, 1); - } - } - } } public string Source { get; } @@ -38,7 +27,7 @@ namespace Microsoft.Tye.Hosting.Model public Dictionary Services { get; } - internal Dictionary ProjectFileLocks { get; } = new Dictionary(); + internal ConcurrentDictionary> ProjectProcesses { get; } = new ConcurrentDictionary>(); public Dictionary Items { get; } = new Dictionary(); diff --git a/src/Microsoft.Tye.Hosting/ProcessRunner.cs b/src/Microsoft.Tye.Hosting/ProcessRunner.cs index a912b686..47f107a5 100644 --- a/src/Microsoft.Tye.Hosting/ProcessRunner.cs +++ b/src/Microsoft.Tye.Hosting/ProcessRunner.cs @@ -361,22 +361,25 @@ namespace Microsoft.Tye.Hosting { if (service.Description.RunInfo is ProjectRunInfo projectRunInfo) { - ProcessResult buildResult; - var projectFile = projectRunInfo.ProjectFile.FullName; - var projectSemaphore = application.ProjectFileLocks[projectFile]; - await projectSemaphore.WaitAsync(); - try - { - _logger.LogDebug($"[{replica}] Building project {projectFile}:"); - buildResult = await ProcessUtil.RunAsync("dotnet", $"build \"{service.Status.ProjectFilePath}\" /nologo", throwOnError: false, workingDirectory: application.ContextDirectory); - } - finally + var newProcess = new TaskCompletionSource(); + var ongoingProcess = application.ProjectProcesses.GetOrAdd(projectFile, newProcess); + + if (ongoingProcess != newProcess) { - _logger.LogDebug($"[{replica}] Finished Building project {projectFile}:"); - projectSemaphore.Release(); + return (await ongoingProcess.Task).ExitCode; } + _logger.LogDebug($"[{replica}] Building project {projectFile}:"); + var buildResult = await ProcessUtil.RunAsync("dotnet", $"build \"{service.Status.ProjectFilePath}\" /nologo", throwOnError: false, workingDirectory: application.ContextDirectory); + _logger.LogDebug($"[{replica}] Finished Building project {projectFile}:"); + + ongoingProcess.SetResult(buildResult); + + // Cannot remove a specific KVP until net5.0. Workaround is to cast to ICollection> + ICollection>> projectProcesses = application.ProjectProcesses; + projectProcesses.Remove(KeyValuePair.Create(projectFile, ongoingProcess)); + if (buildResult.ExitCode != 0) { _logger.LogInformation("Building projects failed with exit code {ExitCode}: \r\n" + buildResult.StandardOutput, buildResult.ExitCode);