Skip to content
Merged
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: 3 additions & 2 deletions src/Dotnet.Watch/HotReloadClient/HotReloadClients.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@
namespace Microsoft.DotNet.HotReload;

/// <summary>
/// Facilitates Hot Reload updates across multiple clients/processes.
/// Facilitates Hot Reload updates across multiple clients/processes and browsers.
/// </summary>
/// <param name="clients">
/// Clients that handle managed updates and static asset updates if <paramref name="useRefreshServerToApplyStaticAssets"/> is false.
/// Clients that handle managed updates and static asset updates (if <paramref name="useRefreshServerToApplyStaticAssets"/> is false).
/// It can be empty if managed agents are not supported, otherwise it comprises one primary client and optional secondary client.
/// </param>
Comment thread
tmat marked this conversation as resolved.
/// <param name="browserRefreshServer">
/// Browser refresh server used to communicate managed code update status and errors to the browser,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public IRuntimeProcessLauncher Create(ProjectLauncher projectLauncher)
{
// Connect to control pipe if provided
var controlReader = controlPipeName != null
? new WatchControlReader(controlPipeName, projectLauncher.CompilationHandler, projectLauncher.Logger)
? new WatchControlReader(controlPipeName, projectLauncher)
: null;

return new Launcher(serverPipeName, controlReader, projectLauncher, statusWriter, launchProfile, shutdownCancellationToken);
Expand Down
31 changes: 15 additions & 16 deletions src/Dotnet.Watch/Watch.Aspire/Server/WatchControlReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,13 @@ namespace Microsoft.DotNet.Watch;

internal sealed class WatchControlReader : IAsyncDisposable
{
private readonly CompilationHandler _compilationHandler;
private readonly ProjectLauncher _launcher;
private readonly string _pipeName;
private readonly NamedPipeClientStream _pipe;
private readonly ILogger _logger;
private readonly CancellationTokenSource _disposalCancellationSource = new();
private readonly Task _listener;

public WatchControlReader(string pipeName, CompilationHandler compilationHandler, ILogger logger)
public WatchControlReader(string pipeName, ProjectLauncher launcher)
{
_pipe = new NamedPipeClientStream(
serverName: ".",
Expand All @@ -25,14 +24,16 @@ public WatchControlReader(string pipeName, CompilationHandler compilationHandler
PipeOptions.Asynchronous | PipeOptions.CurrentUserOnly);

_pipeName = pipeName;
_compilationHandler = compilationHandler;
_logger = logger;
_launcher = launcher;
_listener = ListenAsync(_disposalCancellationSource.Token);
}

private ILogger Logger
=> _launcher.Logger;

public async ValueTask DisposeAsync()
{
_logger.LogDebug("Disposing control pipe.");
Logger.LogDebug("Disposing control pipe.");

_disposalCancellationSource.Cancel();
await _listener;
Expand All @@ -53,7 +54,7 @@ private async Task ListenAsync(CancellationToken cancellationToken)
{
try
{
_logger.LogDebug("Connecting to control pipe '{PipeName}'.", _pipeName);
Logger.LogDebug("Connecting to control pipe '{PipeName}'.", _pipeName);
await _pipe.ConnectAsync(cancellationToken);

using var reader = new StreamReader(_pipe);
Expand All @@ -74,12 +75,12 @@ private async Task ListenAsync(CancellationToken cancellationToken)

if (command.Type == WatchControlCommand.Types.Rebuild)
{
_logger.LogDebug("Received request to restart projects");
Logger.LogDebug("Received request to restart projects");
await RestartProjectsAsync(command.Projects.Select(ProjectRepresentation.FromProjectOrEntryPointFilePath), cancellationToken);
}
else
{
_logger.LogError("Unknown control command: '{Type}'", command.Type);
Logger.LogError("Unknown control command: '{Type}'", command.Type);
}
}
}
Expand All @@ -89,25 +90,23 @@ private async Task ListenAsync(CancellationToken cancellationToken)
}
catch (Exception e)
{
_logger.LogDebug("Control pipe listener failed: {Message}", e.Message);
Logger.LogDebug("Control pipe listener failed: {Message}", e.Message);
}
}

private async ValueTask RestartProjectsAsync(IEnumerable<ProjectRepresentation> projects, CancellationToken cancellationToken)
{
var runningProjects = _compilationHandler.CurrentRunningProjects;
var projectsToRestart = projects.SelectMany(project => runningProjects.TryGetValue(project.ProjectGraphPath, out var array) ? array : []).ToArray();

await _compilationHandler.TerminatePeripheralProcessesAsync(projectsToRestart, cancellationToken);
var projectsToRestart = _launcher.RunningProjectsManager.GetRunningProjects(projects).ToArray();
await RunningProjectsManager.TerminatePeripheralProcessesAsync(projectsToRestart, cancellationToken);

foreach (var project in projects)
{
if (!projectsToRestart.Any(p => p.Options.Representation == project))
{
_compilationHandler.Logger.LogDebug("Restart of '{Project}' requested but the project is not running.", project);
Logger.LogDebug("Restart of '{Project}' requested but the project is not running.", project);
}
}

await _compilationHandler.RestartPeripheralProjectsAsync(projectsToRestart, cancellationToken);
await _launcher.RunningProjectsManager.RestartPeripheralProjectsAsync(projectsToRestart, cancellationToken);
}
}
Loading
Loading