Skip to content

Commit

Permalink
feat: Improve HR diag view
Browse files Browse the repository at this point in the history
  • Loading branch information
dr1rrb committed Jun 17, 2024
1 parent f4148e0 commit 18cf119
Show file tree
Hide file tree
Showing 14 changed files with 520 additions and 144 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ private async Task ProcessMetadataChanges(IEnumerable<string> filePaths)
}
}

private async Task<bool> ProcessSolutionChanged(HotReloadOperation hotReload, string file, CancellationToken cancellationToken)
private async Task<bool> ProcessSolutionChanged(HotReloadServerOperation hotReload, string file, CancellationToken cancellationToken)
{
if (!await EnsureSolutionInitializedAsync() || _currentSolution is null || _hotReloadService is null)
{
Expand Down Expand Up @@ -219,12 +219,12 @@ private async Task<bool> ProcessSolutionChanged(HotReloadOperation hotReload, st
if (diagnostics.IsDefaultOrEmpty)
{
await UpdateMetadata(file, updates);
hotReload.NotifyIntermediate(file, HotReloadResult.NoChanges);
hotReload.NotifyIntermediate(file, HotReloadServerResult.NoChanges);
}
else
{
_reporter.Output($"Got {diagnostics.Length} errors");
hotReload.NotifyIntermediate(file, HotReloadResult.Failed);
hotReload.NotifyIntermediate(file, HotReloadServerResult.Failed);
}

// HotReloadEventSource.Log.HotReloadEnd(HotReloadEventSource.StartType.CompilationHandler);
Expand All @@ -241,7 +241,7 @@ private async Task<bool> ProcessSolutionChanged(HotReloadOperation hotReload, st
_reporter.Verbose(CSharpDiagnosticFormatter.Instance.Format(diagnostic, CultureInfo.InvariantCulture));
}

hotReload.NotifyIntermediate(file, HotReloadResult.RudeEdit);
hotReload.NotifyIntermediate(file, HotReloadServerResult.RudeEdit);

// HotReloadEventSource.Log.HotReloadEnd(HotReloadEventSource.StartType.CompilationHandler);
return false;
Expand All @@ -252,7 +252,7 @@ private async Task<bool> ProcessSolutionChanged(HotReloadOperation hotReload, st
sw.Stop();

await UpdateMetadata(file, updates);
hotReload.NotifyIntermediate(file, HotReloadResult.Success);
hotReload.NotifyIntermediate(file, HotReloadServerResult.Success);

// HotReloadEventSource.Log.HotReloadEnd(HotReloadEventSource.StartType.CompilationHandler);
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public async Task ProcessIdeMessage(IdeMessage message, CancellationToken ct)

#region Hot-relaod state
private HotReloadState _globalState; // This actually contains only the initializing stat (i.e. Disabled, Initializing, Idle). Processing state is _current != null.
private HotReloadOperation? _current; // I.e. head of the operation chain list
private HotReloadServerOperation? _current; // I.e. head of the operation chain list

public enum HotReloadEventSource
{
Expand All @@ -89,13 +89,13 @@ private async ValueTask EnsureHotReloadStarted()
}
}

private async ValueTask<HotReloadOperation> StartHotReload(ImmutableHashSet<string>? filesPaths)
private async ValueTask<HotReloadServerOperation> StartHotReload(ImmutableHashSet<string>? filesPaths)
{
var previous = _current;
HotReloadOperation? current, @new;
HotReloadServerOperation? current, @new;
while (true)
{
@new = new HotReloadOperation(this, previous, filesPaths);
@new = new HotReloadServerOperation(this, previous, filesPaths);
current = Interlocked.CompareExchange(ref _current, @new, previous);
if (current == previous)
{
Expand All @@ -113,13 +113,13 @@ private async ValueTask<HotReloadOperation> StartHotReload(ImmutableHashSet<stri
return @new;
}

private async ValueTask<HotReloadOperation> StartOrContinueHotReload(ImmutableHashSet<string>? filesPaths = null)
private async ValueTask<HotReloadServerOperation> StartOrContinueHotReload(ImmutableHashSet<string>? filesPaths = null)
=> _current is { } current && (filesPaths is null || current.TryMerge(filesPaths))
? current
: await StartHotReload(filesPaths);

private ValueTask AbortHotReload()
=> _current?.Complete(HotReloadResult.Aborted) ?? SendUpdate();
=> _current?.Complete(HotReloadServerResult.Aborted) ?? SendUpdate();

private async ValueTask Notify(HotReloadEvent evt, HotReloadEventSource source = HotReloadEventSource.DevServer)
{
Expand Down Expand Up @@ -147,31 +147,31 @@ private async ValueTask Notify(HotReloadEvent evt, HotReloadEventSource source =
break;

case HotReloadEvent.Completed:
await (await StartOrContinueHotReload()).DeferComplete(HotReloadResult.Success);
await (await StartOrContinueHotReload()).DeferComplete(HotReloadServerResult.Success);
break;

case HotReloadEvent.NoChanges:
await (await StartOrContinueHotReload()).Complete(HotReloadResult.NoChanges);
await (await StartOrContinueHotReload()).Complete(HotReloadServerResult.NoChanges);
break;
case HotReloadEvent.Failed:
await (await StartOrContinueHotReload()).Complete(HotReloadResult.Failed);
await (await StartOrContinueHotReload()).Complete(HotReloadServerResult.Failed);
break;

case HotReloadEvent.RudeEdit:
case HotReloadEvent.RudeEditDialogButton:
await (await StartOrContinueHotReload()).Complete(HotReloadResult.RudeEdit);
await (await StartOrContinueHotReload()).Complete(HotReloadServerResult.RudeEdit);
break;
}
}

private async ValueTask SendUpdate(HotReloadOperation? completing = null)
private async ValueTask SendUpdate(HotReloadServerOperation? completing = null)
{
var state = _globalState;
var operations = ImmutableList<HotReloadOperationInfo>.Empty;
var operations = ImmutableList<HotReloadServerOperationData>.Empty;

if (state is not HotReloadState.Disabled && (_current ?? completing) is { } current)
{
var infos = ImmutableList.CreateBuilder<HotReloadOperationInfo>();
var infos = ImmutableList.CreateBuilder<HotReloadServerOperationData>();
var foundCompleting = completing is null;
LoadInfos(current);
if (!foundCompleting)
Expand All @@ -181,7 +181,7 @@ private async ValueTask SendUpdate(HotReloadOperation? completing = null)

operations = infos.ToImmutable();

void LoadInfos(HotReloadOperation? operation)
void LoadInfos(HotReloadServerOperation? operation)
{
while (operation is not null)
{
Expand All @@ -191,7 +191,7 @@ void LoadInfos(HotReloadOperation? operation)
}

foundCompleting |= operation == completing;
infos.Add(new(operation.Id, operation.FilePaths, operation.Result));
infos.Add(new(operation.Id, operation.StartTime, operation.FilePaths, operation.CompletionTime, operation.Result));
operation = operation.Previous!;
}
}
Expand All @@ -203,7 +203,7 @@ void LoadInfos(HotReloadOperation? operation)
/// <summary>
/// A hot-reload operation that is in progress.
/// </summary>
private class HotReloadOperation
private class HotReloadServerOperation
{
// Delay to wait without any update to consider operation was aborted.
private static readonly TimeSpan _timeoutDelay = TimeSpan.FromSeconds(30);
Expand All @@ -212,7 +212,7 @@ private class HotReloadOperation
private static long _count;

private readonly ServerHotReloadProcessor _owner;
private readonly HotReloadOperation? _previous;
private readonly HotReloadServerOperation? _previous;
private readonly Timer _timeout;

private ImmutableHashSet<string> _filePaths;
Expand All @@ -221,21 +221,25 @@ private class HotReloadOperation

public long Id { get; } = Interlocked.Increment(ref _count);

public HotReloadOperation? Previous => _previous;
public DateTimeOffset StartTime { get; } = DateTimeOffset.Now;

public DateTimeOffset? CompletionTime { get; private set; }

public HotReloadServerOperation? Previous => _previous;

public ImmutableHashSet<string> FilePaths => _filePaths;

public HotReloadResult? Result => _result is -1 ? null : (HotReloadResult)_result;
public HotReloadServerResult? Result => _result is -1 ? null : (HotReloadServerResult)_result;

/// <param name="previous">The previous hot-reload operation which has to be considered as aborted when this new one completes.</param>
public HotReloadOperation(ServerHotReloadProcessor owner, HotReloadOperation? previous, ImmutableHashSet<string>? filePaths = null)
public HotReloadServerOperation(ServerHotReloadProcessor owner, HotReloadServerOperation? previous, ImmutableHashSet<string>? filePaths = null)
{
_owner = owner;
_previous = previous;
_filePaths = filePaths ?? _empty;

_timeout = new Timer(
static that => _ = ((HotReloadOperation)that!).Complete(HotReloadResult.Aborted),
static that => _ = ((HotReloadServerOperation)that!).Complete(HotReloadServerResult.Aborted),
this,
_timeoutDelay,
Timeout.InfiniteTimeSpan);
Expand Down Expand Up @@ -286,17 +290,17 @@ public bool TryMerge(ImmutableHashSet<string> filePaths)
}

// Note: This is a patch until the dev-server based hot-reload treat files per batch instead of file per file.
private HotReloadResult _aggregatedResult = HotReloadResult.NoChanges;
private HotReloadServerResult _aggregatedResult = HotReloadServerResult.NoChanges;
private int _aggregatedFilesCount;
public void NotifyIntermediate(string file, HotReloadResult result)
public void NotifyIntermediate(string file, HotReloadServerResult result)
{
if (Interlocked.Increment(ref _aggregatedFilesCount) is 1)
{
_aggregatedResult = result;
return;
}

_aggregatedResult = (HotReloadResult)Math.Max((int)_aggregatedResult, (int)result);
_aggregatedResult = (HotReloadServerResult)Math.Max((int)_aggregatedResult, (int)result);
_timeout.Change(_timeoutDelay, Timeout.InfiniteTimeSpan);
}

Expand All @@ -309,9 +313,9 @@ public async ValueTask CompleteUsingIntermediates()
/// <summary>
/// As errors might get a bit after the complete from the IDE, we can defer the completion of the operation.
/// </summary>
public async ValueTask DeferComplete(HotReloadResult result, Exception? exception = null)
public async ValueTask DeferComplete(HotReloadServerResult result, Exception? exception = null)
{
Debug.Assert(result != HotReloadResult.InternalError || exception is not null); // For internal error we should always provide an exception!
Debug.Assert(result != HotReloadServerResult.InternalError || exception is not null); // For internal error we should always provide an exception!

if (Interlocked.CompareExchange(ref _deferredCompletion, new CancellationTokenSource(), null) is null)
{
Expand All @@ -324,12 +328,12 @@ public async ValueTask DeferComplete(HotReloadResult result, Exception? exceptio
}
}

public ValueTask Complete(HotReloadResult result, Exception? exception = null)
public ValueTask Complete(HotReloadServerResult result, Exception? exception = null)
=> Complete(result, exception, isFromNext: false);

private async ValueTask Complete(HotReloadResult result, Exception? exception, bool isFromNext)
private async ValueTask Complete(HotReloadServerResult result, Exception? exception, bool isFromNext)
{
Debug.Assert(result != HotReloadResult.InternalError || exception is not null); // For internal error we should always provide an exception!
Debug.Assert(result != HotReloadServerResult.InternalError || exception is not null); // For internal error we should always provide an exception!

// Remove this from current
Interlocked.CompareExchange(ref _owner._current, null, this);
Expand All @@ -341,13 +345,14 @@ private async ValueTask Complete(HotReloadResult result, Exception? exception, b
return; // Already completed
}

CompletionTime = DateTimeOffset.Now;
await _timeout.DisposeAsync();

// Consider previous hot-reload operation(s) as aborted (this is actually a chain list)
if (_previous is not null)
{
await _previous.Complete(
HotReloadResult.Aborted,
HotReloadServerResult.Aborted,
new TimeoutException("An more recent hot-reload operation has completed."),
isFromNext: true);
}
Expand Down Expand Up @@ -463,7 +468,7 @@ private async Task ProcessUpdateFile(UpdateFile message)
}
catch (Exception ex)
{
await hotReload.Complete(HotReloadResult.InternalError, ex);
await hotReload.Complete(HotReloadServerResult.InternalError, ex);
await _remoteControlServer.SendFrame(new UpdateFileResponse(message.RequestId, message.FilePath, FileUpdateResult.Failed, ex.Message));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,17 +142,7 @@ private string[] GetMetadataUpdateCapabilities()
return Array.Empty<string>();
}

private enum HotReloadSource
{
Runtime,
DevServer,
Manual
}
#pragma warning disable CS0414 // Field is assigned but its value is never used
private static HotReloadSource _source;
#pragma warning restore CS0414 // Field is assigned but its value is never used

private void AssemblyReload(AssemblyDeltaReload assemblyDeltaReload)
private void ProcessAssemblyReload(AssemblyDeltaReload assemblyDeltaReload)
{
try
{
Expand Down Expand Up @@ -185,7 +175,7 @@ private void AssemblyReload(AssemblyDeltaReload assemblyDeltaReload)
UpdatedTypes = ReadIntArray(changedTypesReader)
};

_source = HotReloadSource.DevServer;
_status.ConfigureSourceForNextOperation(HotReloadSource.DevServer);
_agent?.ApplyDeltas(new[] { delta });

if (this.Log().IsEnabled(LogLevel.Trace))
Expand All @@ -210,7 +200,7 @@ private void AssemblyReload(AssemblyDeltaReload assemblyDeltaReload)
}
finally
{
_source = default; // runtime
_status.ConfigureSourceForNextOperation(default); // runtime
}
}

Expand Down
Loading

0 comments on commit 18cf119

Please sign in to comment.