Skip to content

Commit

Permalink
Fix
Browse files Browse the repository at this point in the history
  • Loading branch information
tmat committed Dec 9, 2024
1 parent c8f9ab0 commit 872f7d5
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 67 deletions.
98 changes: 42 additions & 56 deletions src/BuiltInTools/DotNetDeltaApplier/StartupHook.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ private static void ProtocolV1()
//
// Updates made before the process is launched need to be applied before loading the affected modules.

using var pipeClient = CreatePipe();
var pipeClient = CreatePipe();
try
{
pipeClient.Connect(ConnectionTimeoutMS);
Expand All @@ -93,18 +93,17 @@ private static void ProtocolV1()
// The debugger takes care of this when launching process with the debugger attached.
if (!Debugger.IsAttached)
{
ReceiveInitialUpdatesAsync(pipeClient, agent, CancellationToken.None).GetAwaiter().GetResult();
ReceiveAndApplyUpdatesAsync(pipeClient, agent, initialUpdates: true, CancellationToken.None).GetAwaiter().GetResult();
}

// fire and forget:
_ = ReceiveAndApplyUpdatesAsync(pipeClient, agent, CancellationToken.None);
_ = ReceiveAndApplyUpdatesAsync(pipeClient, agent, initialUpdates: false, CancellationToken.None);
}
catch (Exception ex)
{
Log(ex.Message);
pipeClient.Dispose();
}

Log("Stopped received delta updates. Server is no longer connected.");
}

private static void ProtocolV0()
Expand All @@ -129,14 +128,12 @@ private static void ProtocolV0()
try
{
SendCapabilities(pipeClient, agent);
await ReceiveAndApplyUpdatesAsync(pipeClient, agent, CancellationToken.None);
await ReceiveAndApplyUpdatesAsync(pipeClient, agent, initialUpdates: false, CancellationToken.None);
}
catch (Exception ex)
{
Log(ex.Message);
}

Log("Stopped received delta updates. Server is no longer connected.");
});
}

Expand All @@ -151,49 +148,50 @@ private static void SendCapabilities(NamedPipeClientStream pipeClient, HotReload
initPayload.Write(pipeClient);
}

private static async Task ReceiveInitialUpdatesAsync(NamedPipeClientStream pipeClient, HotReloadAgent agent, CancellationToken cancellationToken)
private static async Task ReceiveAndApplyUpdatesAsync(NamedPipeClientStream pipeClient, HotReloadAgent agent, bool initialUpdates, CancellationToken cancellationToken)
{
while (pipeClient.IsConnected)
try
{
var payloadType = await ReadPayloadTypeAsync(pipeClient, cancellationToken);
switch (payloadType)
{
case PayloadType.ManagedCodeUpdate:
await ReadAndApplyManagedUpdateAsync(pipeClient, agent, cancellationToken);
break;

case PayloadType.InitialUpdatesCompleted:
return;

default:
agent.Reporter.Report($"Unexpected payload type: {payloadType}", AgentMessageSeverity.Error);
byte[] buffer = new byte[1];

// can't continue, the pipe content is in an unknown state
return;
while (pipeClient.IsConnected)
{
var bytesRead = await pipeClient.ReadAsync(buffer, offset: 0, count: 1, cancellationToken);
if (bytesRead != 1)
{
continue;
}

var payloadType = (PayloadType)buffer[0];
switch (payloadType)
{
case PayloadType.ManagedCodeUpdate:
await ReadAndApplyManagedUpdateAsync(pipeClient, agent, cancellationToken);
break;

case PayloadType.StaticAssetUpdate when !initialUpdates:
await ReadAndApplyStaticAssetUpdateAsync(pipeClient, agent, cancellationToken);
break;

case PayloadType.InitialUpdatesCompleted when initialUpdates:
return;

default:
// can't continue, the pipe content is in an unknown state
Log($"Unexpected payload type: {payloadType}. Terminating agent.");
return;
}
}
}
}

private static async Task ReceiveAndApplyUpdatesAsync(NamedPipeClientStream pipeClient, HotReloadAgent agent, CancellationToken cancellationToken)
{
while (pipeClient.IsConnected)
catch (Exception ex)
{
var payloadType = await ReadPayloadTypeAsync(pipeClient, cancellationToken);
switch (payloadType)
Log(ex.Message);
}
finally
{
if (!pipeClient.IsConnected)
{
case PayloadType.ManagedCodeUpdate:
await ReadAndApplyManagedUpdateAsync(pipeClient, agent, cancellationToken);
break;

case PayloadType.StaticAssetUpdate:
await ReadAndApplyStaticAssetUpdateAsync(pipeClient, agent, cancellationToken);
break;

default:
agent.Reporter.Report($"Unexpected payload type: {payloadType}", AgentMessageSeverity.Error);

// can't continue, the pipe content is in an unknown state
return;
await pipeClient.DisposeAsync();
}
}
}
Expand Down Expand Up @@ -249,18 +247,6 @@ private static async ValueTask ReadAndApplyStaticAssetUpdateAsync(NamedPipeClien
}
}

private static async ValueTask<PayloadType> ReadPayloadTypeAsync(Stream stream, CancellationToken cancellationToken)
{
byte[] buffer = new byte[1];
int bytesRead = await stream.ReadAsync(buffer, 0, 1, cancellationToken);
if (bytesRead == 1)
{
return (PayloadType)buffer[0];
}

return PayloadType.Unknown;
}

public static bool IsMatchingProcess(string processPath, string targetProcessPath)
{
var comparison = Path.DirectorySeparatorChar == '\\' ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
Expand Down
1 change: 1 addition & 0 deletions src/BuiltInTools/dotnet-watch/EnvironmentVariables.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public static class Names
public const string DotNetWatchHotReloadTargetProcessPath = HotReload.AgentEnvironmentVariables.DotNetWatchHotReloadTargetProcessPath;
public const string DotNetStartupHooks = HotReload.AgentEnvironmentVariables.DotNetStartupHooks;
public const string DotNetModifiableAssemblies = HotReload.AgentEnvironmentVariables.DotNetModifiableAssemblies;
public const string HotReloadDeltaClientLogMessages = HotReload.AgentEnvironmentVariables.HotReloadDeltaClientLogMessages;
}

public static bool VerboseCliOutput => ReadBool("DOTNET_CLI_CONTEXT_VERBOSE");
Expand Down
4 changes: 1 addition & 3 deletions src/BuiltInTools/dotnet-watch/HotReload/NamedPipeContract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ namespace Microsoft.DotNet.Watch
{
internal enum PayloadType
{
Unknown = 0,
ManagedCodeUpdate = 1,
StaticAssetUpdate = 2,
InitialUpdatesCompleted = 3,
Expand All @@ -33,9 +32,8 @@ public async ValueTask WriteAsync(Stream stream, CancellationToken cancellationT
binaryWriter.Write(Version);
binaryWriter.Write(Deltas.Count);

for (var i = 0; i < Deltas.Count; i++)
foreach (var delta in Deltas)
{
var delta = Deltas[i];
binaryWriter.Write(delta.ModuleId.ToString());
await binaryWriter.WriteAsync(delta.MetadataDelta, cancellationToken);
await binaryWriter.WriteAsync(delta.ILDelta, cancellationToken);
Expand Down
12 changes: 4 additions & 8 deletions src/BuiltInTools/dotnet-watch/HotReload/ProjectLauncher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,10 @@ public EnvironmentOptions EnvironmentOptions
environmentBuilder.DotNetStartupHookDirective.Add(DeltaApplier.StartupHookPath);
environmentBuilder.SetDirective(EnvironmentVariables.Names.DotNetWatchHotReloadNamedPipeName, namedPipeName);

// Do not ask agent to log to stdout until https://github.com/dotnet/sdk/issues/40484 is fixed.
// For now we need to set the env variable explicitly when we need to diagnose issue with the agent.
// Build targets might launch a process and read it's stdout. If the agent is loaded into such process and starts logging
// to stdout it might interfere with the expected output.
//if (context.Options.Verbose)
//{
// environmentBuilder.SetVariable(EnvironmentVariables.Names.HotReloadDeltaClientLogMessages, "1");
//}
if (context.Options.Verbose)
{
environmentBuilder.SetVariable(EnvironmentVariables.Names.HotReloadDeltaClientLogMessages, "1");
}

// TODO: workaround for https://github.com/dotnet/sdk/issues/40484
var targetPath = projectNode.ProjectInstance.GetPropertyValue("RunCommand");
Expand Down

0 comments on commit 872f7d5

Please sign in to comment.