-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
F5 Hot Reload #52101
F5 Hot Reload #52101
Changes from 1 commit
5125bdc
6ba743a
251d382
67ee3a3
a739d6a
eecc4c7
53f9825
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -94,20 +94,22 @@ public async ValueTask StartDebuggingSessionAsync(Solution solution, IManagedEdi | |
|
||
var previousSession = Interlocked.CompareExchange(ref _debuggingSession, new DebuggingSession(solution, debuggerService, _compilationOutputsProvider, initialDocumentStates), null); | ||
Contract.ThrowIfFalse(previousSession == null, "New debugging session can't be started until the existing one has ended."); | ||
|
||
StartEditSession(inBreakState: false); | ||
} | ||
|
||
public void StartEditSession() | ||
private void StartEditSession(bool inBreakState) | ||
{ | ||
var debuggingSession = _debuggingSession; | ||
Contract.ThrowIfNull(debuggingSession, "Edit session can only be started during debugging session"); | ||
|
||
var newSession = new EditSession(debuggingSession, _editSessionTelemetry); | ||
var newSession = new EditSession(debuggingSession, _editSessionTelemetry, inBreakState); | ||
|
||
var previousSession = Interlocked.CompareExchange(ref _editSession, newSession, null); | ||
Contract.ThrowIfFalse(previousSession == null, "New edit session can't be started until the existing one has ended."); | ||
} | ||
|
||
public void EndEditSession(out ImmutableArray<DocumentId> documentsToReanalyze) | ||
private void EndEditSession(out ImmutableArray<DocumentId> documentsToReanalyze) | ||
{ | ||
// first, publish null session: | ||
var session = Interlocked.Exchange(ref _editSession, null); | ||
|
@@ -119,13 +121,19 @@ public void EndEditSession(out ImmutableArray<DocumentId> documentsToReanalyze) | |
// clear all reported rude edits: | ||
documentsToReanalyze = session.GetDocumentsWithReportedDiagnostics(); | ||
|
||
_debuggingSessionTelemetry.LogEditSession(_editSessionTelemetry.GetDataAndClear()); | ||
// TODO: report a separate telemetry data for hot reload sessions to preserve the semantics of the current telemetry data | ||
if (session.InBreakState) | ||
{ | ||
_debuggingSessionTelemetry.LogEditSession(_editSessionTelemetry.GetDataAndClear()); | ||
} | ||
|
||
session.Dispose(); | ||
} | ||
|
||
public void EndDebuggingSession() | ||
public void EndDebuggingSession(out ImmutableArray<DocumentId> documentsToReanalyze) | ||
{ | ||
EndEditSession(out documentsToReanalyze); | ||
|
||
var debuggingSession = Interlocked.Exchange(ref _debuggingSession, null); | ||
Contract.ThrowIfNull(debuggingSession, "Debugging session has not started."); | ||
|
||
|
@@ -137,6 +145,13 @@ public void EndDebuggingSession() | |
debuggingSession.Dispose(); | ||
} | ||
|
||
public void BreakStateEntered(out ImmutableArray<DocumentId> documentsToReanalyze) | ||
{ | ||
// Document analyses must be recalculated to account for active statements. | ||
EndEditSession(out documentsToReanalyze); | ||
StartEditSession(inBreakState: true); | ||
} | ||
|
||
internal static bool SupportsEditAndContinue(Project project) | ||
=> project.LanguageServices.GetService<IEditAndContinueAnalyzer>() != null; | ||
|
||
|
@@ -281,14 +296,17 @@ public async ValueTask<EmitSolutionUpdateResults> EmitSolutionUpdateAsync( | |
return new EmitSolutionUpdateResults(solutionUpdate.ModuleUpdates, solutionUpdate.Diagnostics); | ||
} | ||
|
||
public void CommitSolutionUpdate() | ||
public void CommitSolutionUpdate(out ImmutableArray<DocumentId> documentsToReanalyze) | ||
{ | ||
var editSession = _editSession; | ||
Contract.ThrowIfNull(editSession); | ||
|
||
var pendingUpdate = editSession.RetrievePendingUpdate(); | ||
editSession.DebuggingSession.CommitSolutionUpdate(pendingUpdate); | ||
editSession.ChangesApplied(); | ||
|
||
// restart edit session with no active statements (switching to run mode): | ||
EndEditSession(out documentsToReanalyze); | ||
StartEditSession(inBreakState: false); | ||
tmat marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
public void DiscardSolutionUpdate() | ||
|
@@ -306,7 +324,7 @@ public void DiscardSolutionUpdate() | |
public async ValueTask<ImmutableArray<ImmutableArray<(LinePositionSpan, ActiveStatementFlags)>>> GetBaseActiveStatementSpansAsync(Solution solution, ImmutableArray<DocumentId> documentIds, CancellationToken cancellationToken) | ||
{ | ||
var editSession = _editSession; | ||
if (editSession == null) | ||
if (editSession == null || !editSession.InBreakState) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is editSession ever null here now? Other than when EnC isn't active at all, but these calls shouldn't happen then, right? This could be a contract check perhaps? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll follow up with refactoring that cleans up edit session nullability. That can wait for Preview 3 though. |
||
{ | ||
return default; | ||
} | ||
|
@@ -339,7 +357,7 @@ public void DiscardSolutionUpdate() | |
public async ValueTask<ImmutableArray<(LinePositionSpan, ActiveStatementFlags)>> GetAdjustedActiveStatementSpansAsync(Document document, DocumentActiveStatementSpanProvider activeStatementSpanProvider, CancellationToken cancellationToken) | ||
{ | ||
var editSession = _editSession; | ||
if (editSession == null) | ||
if (editSession == null || !editSession.InBreakState) | ||
{ | ||
return default; | ||
} | ||
|
@@ -373,7 +391,7 @@ public void DiscardSolutionUpdate() | |
// It is allowed to call this method before entering or after exiting break mode. In fact, the VS debugger does so. | ||
// We return null since there the concept of active statement only makes sense during break mode. | ||
var editSession = _editSession; | ||
if (editSession == null) | ||
if (editSession == null || !editSession.InBreakState) | ||
{ | ||
return null; | ||
} | ||
|
@@ -431,7 +449,7 @@ public void DiscardSolutionUpdate() | |
try | ||
{ | ||
var editSession = _editSession; | ||
if (editSession == null) | ||
if (editSession == null || !editSession.InBreakState) | ||
{ | ||
return null; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i woudl love threading asserts on all these methods (i.e. AssertIsForeground/AssertIsBackground/ThisCanBeCalledOnAnyThread. It makes reading and understanding potential threading/sync/consistency issues more simply.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why? There is no thread affinity. By default we don't put attributes on methods that don't have affinity.