Skip to content
This repository has been archived by the owner on Nov 20, 2023. It is now read-only.

Ensure watched projects are not built in parallel to avoid file locking issues #895

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/Microsoft.Tye.Hosting/Model/Application.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +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.Tasks;

namespace Microsoft.Tye.Hosting.Model
{
Expand All @@ -25,6 +27,9 @@ public Application(FileInfo source, Dictionary<string, Service> services)

public Dictionary<string, Service> Services { get; }

internal ConcurrentDictionary<string, TaskCompletionSource<ProcessResult>> OngoingBuildProjectProcesses { get; }
= new ConcurrentDictionary<string, TaskCompletionSource<ProcessResult>>();

public Dictionary<object, object> Items { get; } = new Dictionary<object, object>();

public string? Network { get; set; }
Expand Down
21 changes: 20 additions & 1 deletion src/Microsoft.Tye.Hosting/ProcessRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -359,9 +359,28 @@ async Task RunApplicationAsync(IEnumerable<(int ExternalPort, int Port, string?
},
Build = async () =>
{
if (service.Description.RunInfo is ProjectRunInfo)
if (service.Description.RunInfo is ProjectRunInfo projectRunInfo)
{
var projectFile = projectRunInfo.ProjectFile.FullName;
var newProcess = new TaskCompletionSource<ProcessResult>();
var ongoingProcess = application.OngoingBuildProjectProcesses.GetOrAdd(projectFile, newProcess);

if (ongoingProcess != newProcess)
{
_logger.LogDebug($"[{replica}] A build has already been triggered for project {projectFile}");
return (await ongoingProcess.Task).ExitCode;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a debug log here

}

_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<KVP<>>
ICollection<KeyValuePair<string, TaskCompletionSource<ProcessResult>>> projectProcesses = application.OngoingBuildProjectProcesses;
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);
Expand Down
5 changes: 3 additions & 2 deletions src/Microsoft.Tye.Hosting/Watch/DotNetWatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public async Task WatchAsync(ProcessSpec processSpec, IFileSetFactory fileSetFac
if (finishedTask == processTask)
{
// Now wait for a file to change before restarting process
await fileSetWatcher.GetChangedFileAsync(cancellationToken, () => _logger.LogWarning("Waiting for a file to change before restarting dotnet..."));
await fileSetWatcher.GetChangedFileAsync(cancellationToken, () => _logger.LogWarning("watch: {Replica} Waiting for a file to change before restarting dotnet...", replica));
}

if (!string.IsNullOrEmpty(fileSetTask.Result))
Expand All @@ -112,7 +112,8 @@ public async Task WatchAsync(ProcessSpec processSpec, IFileSetFactory fileSetFac
// Build failed, keep retrying builds until successful build.
}

await fileSetWatcher.GetChangedFileAsync(cancellationToken, () => _logger.LogWarning("Waiting for a file to change before restarting dotnet..."));
// Now wait for a file to change before restarting process
await fileSetWatcher.GetChangedFileAsync(cancellationToken, () => _logger.LogWarning("watch: {Replica} Waiting for a file to change before restarting dotnet...", replica));
}
}
}
Expand Down