Skip to content

Commit

Permalink
Fix analyzer failures
Browse files Browse the repository at this point in the history
  • Loading branch information
Adam Ratzman committed Mar 22, 2024
1 parent a6ddf4b commit 11d5cc0
Show file tree
Hide file tree
Showing 10 changed files with 83 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ internal class ProjectHotReloadSessionManager : OnceInitializedOnceDisposedAsync
// Protect the state from concurrent access. For example, our Process.Exited event
// handler may run on one thread while we're still setting up the session on
// another. To ensure consistent and proper behavior we need to serialize access.
private readonly AsyncSemaphore _semaphore = new(initialCount: 1);
private readonly ReentrantSemaphore _semaphore;

private readonly Dictionary<int, HotReloadState> _activeSessions = new();
private HotReloadState? _pendingSessionState = null;
private int _nextUniqueId = 1;
private CancellationToken _cancellationToken;

public bool HasActiveHotReloadSessions => _activeSessions.Count != 0;

Expand All @@ -39,7 +40,8 @@ public ProjectHotReloadSessionManager(
IActiveDebugFrameworkServices activeDebugFrameworkServices,
Lazy<IProjectHotReloadAgent> projectHotReloadAgent,
Lazy<IHotReloadDiagnosticOutputService> hotReloadDiagnosticOutputService,
Lazy<IProjectHotReloadNotificationService> projectHotReloadNotificationService)
Lazy<IProjectHotReloadNotificationService> projectHotReloadNotificationService,
JoinableTaskContext joinableTaskContext)
: base(threadingService.JoinableTaskContext)
{
_project = project;
Expand All @@ -48,12 +50,16 @@ public ProjectHotReloadSessionManager(
_projectHotReloadAgent = projectHotReloadAgent;
_hotReloadDiagnosticOutputService = hotReloadDiagnosticOutputService;
_projectHotReloadNotificationService = projectHotReloadNotificationService;
_semaphore = ReentrantSemaphore.Create(initialCount: 1, joinableTaskContext: joinableTaskContext, mode: ReentrantSemaphore.ReentrancyMode.NotRecognized);
}

public async Task ActivateSessionAsync(int processId, bool runningUnderDebugger, string projectName)
public Task ActivateSessionAsync(int processId, bool runningUnderDebugger, string projectName)
{
return _semaphore.ExecuteAsync(() => ActivateSessionInternalAsync(processId, runningUnderDebugger, projectName), _cancellationToken);
}

private async Task ActivateSessionInternalAsync(int processId, bool runningUnderDebugger, string projectName)
{
using AsyncSemaphore.Releaser semaphoreReleaser = await _semaphore.EnterAsync();

if (_pendingSessionState is not null)
{
Assumes.NotNull(_pendingSessionState.Session);
Expand Down Expand Up @@ -138,10 +144,13 @@ public async Task ActivateSessionAsync(int processId, bool runningUnderDebugger,
}
}

public async Task<bool> TryCreatePendingSessionAsync(IDictionary<string, string> environmentVariables)
public Task<bool> TryCreatePendingSessionAsync(IDictionary<string, string> environmentVariables)
{
return _semaphore.ExecuteAsync(() => TryCreatePendingSessionInternalAsync(environmentVariables), _cancellationToken).AsTask();
}

public async ValueTask<bool> TryCreatePendingSessionInternalAsync(IDictionary<string, string> environmentVariables)
{
using AsyncSemaphore.Releaser semaphoreReleaser = await _semaphore.EnterAsync();

if (await DebugFrameworkSupportsHotReloadAsync()
&& await GetDebugFrameworkVersionAsync() is string frameworkVersion
&& !string.IsNullOrWhiteSpace(frameworkVersion))
Expand Down Expand Up @@ -185,13 +194,17 @@ public async Task<bool> TryCreatePendingSessionAsync(IDictionary<string, string>

protected override Task InitializeCoreAsync(CancellationToken cancellationToken)
{
_cancellationToken = cancellationToken;
return Task.CompletedTask;
}

protected override async Task DisposeCoreAsync(bool initialized)
protected override Task DisposeCoreAsync(bool initialized)
{
return _semaphore.ExecuteAsync(DisposeCoreInternalAsync, _cancellationToken);
}

private Task DisposeCoreInternalAsync()
{
using AsyncSemaphore.Releaser semaphoreRelease = await _semaphore.EnterAsync();

foreach (HotReloadState sessionState in _activeSessions.Values)
{
Assumes.NotNull(sessionState.Process);
Expand All @@ -202,6 +215,8 @@ protected override async Task DisposeCoreAsync(bool initialized)
}

_activeSessions.Clear();

return Task.CompletedTask;
}

private void WriteOutputMessage(HotReloadLogMessage hotReloadLogMessage, CancellationToken cancellationToken) => _hotReloadDiagnosticOutputService.Value.WriteLine(hotReloadLogMessage, cancellationToken);
Expand Down Expand Up @@ -326,7 +341,7 @@ private async Task OnProcessExitedAsync(HotReloadState hotReloadState)
),
default);

await StopProjectAsync(hotReloadState, default);
await _semaphore.ExecuteAsync(() => StopProjectAsync(hotReloadState, default), _cancellationToken);

hotReloadState.Process.Exited -= hotReloadState.OnProcessExited;
}
Expand All @@ -347,13 +362,11 @@ private static Task OnAfterChangesAppliedAsync(HotReloadState hotReloadState, Ca
return Task.CompletedTask;
}

private async Task<bool> StopProjectAsync(HotReloadState hotReloadState, CancellationToken cancellationToken)
private async ValueTask<bool> StopProjectAsync(HotReloadState hotReloadState, CancellationToken cancellationToken)
{
Assumes.NotNull(hotReloadState.Session);
Assumes.NotNull(hotReloadState.Process);

using AsyncSemaphore.Releaser semaphoreReleaser = await _semaphore.EnterAsync();

int sessionCountOnEntry = _activeSessions.Count;

try
Expand Down Expand Up @@ -398,10 +411,13 @@ private async Task<bool> StopProjectAsync(HotReloadState hotReloadState, Cancell
}

/// <inheritdoc />
public async Task ApplyHotReloadUpdateAsync(Func<IDeltaApplier, CancellationToken, Task> applyFunction, CancellationToken cancelToken)
public Task ApplyHotReloadUpdateAsync(Func<IDeltaApplier, CancellationToken, Task> applyFunction, CancellationToken cancelToken)
{
return _semaphore.ExecuteAsync(() => ApplyHotReloadUpdateInternalAsync(applyFunction, cancelToken), cancelToken);
}

private async Task ApplyHotReloadUpdateInternalAsync(Func<IDeltaApplier, CancellationToken, Task> applyFunction, CancellationToken cancelToken)
{
using AsyncSemaphore.Releaser semaphoreRelease = await _semaphore.EnterAsync(cancelToken);

// Run the updates in parallel
List<Task> updateTasks = new List<Task>();
foreach (HotReloadState sessionState in _activeSessions.Values)
Expand Down Expand Up @@ -451,7 +467,7 @@ public Task OnAfterChangesAppliedAsync(CancellationToken cancellationToken)

public Task<bool> StopProjectAsync(CancellationToken cancellationToken)
{
return _sessionManager.StopProjectAsync(this, cancellationToken);
return _sessionManager._semaphore.ExecuteAsync(() => _sessionManager.StopProjectAsync(this, cancellationToken), _sessionManager._cancellationToken).AsTask();
}

public Task<bool> RestartProjectAsync(CancellationToken cancellationToken)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ protected override async Task InitializeCoreAsync(CancellationToken cancellation

ITargetBlock<IProjectVersionedValue<(ConfiguredProject ActiveConfiguredProject, ConfigurationSubscriptionSources Sources)>> actionBlock
= DataflowBlockFactory.CreateActionBlock<IProjectVersionedValue<(ConfiguredProject ActiveConfiguredProject, ConfigurationSubscriptionSources Sources)>>(
update => OnSlicesChanged(update, cancellationToken),
update => OnSlicesChangedAsync(update, cancellationToken),
_unconfiguredProject,
ProjectFaultSeverity.LimitedFunctionality,
nameFormat: "LanguageServiceHostSlices {1}");
Expand Down Expand Up @@ -190,7 +190,7 @@ protected override async Task InitializeCoreAsync(CancellationToken cancellation

return;

async Task OnSlicesChanged(IProjectVersionedValue<(ConfiguredProject ActiveConfiguredProject, ConfigurationSubscriptionSources Sources)> update, CancellationToken cancellationToken)
async Task OnSlicesChangedAsync(IProjectVersionedValue<(ConfiguredProject ActiveConfiguredProject, ConfigurationSubscriptionSources Sources)> update, CancellationToken cancellationToken)
{
ProjectConfiguration activeProjectConfiguration = update.Value.ActiveConfiguredProject.ProjectConfiguration;
ConfigurationSubscriptionSources sources = update.Value.Sources;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ public async Task OnRequestProcessFinishedAsync(IQueryProcessRequest request)
UIPropertyDataProducer.CreateUIPropertyValues(request.QueryExecutionContext, launchProfileEntity, state.ProjectState, state.PropertiesContext, state.Rule, s_requestedPropertyProperties));
launchProfileEntity.SetRelatedEntities(LaunchProfileType.PropertiesPropertyName, properties);

await PopulateEditorsAndValues(properties);
await PopulateEditorsAndValuesAsync(properties);
}

returnedLaunchProfiles.Add(launchProfileEntity);
Expand Down Expand Up @@ -165,7 +165,7 @@ public async Task OnRequestProcessFinishedAsync(IQueryProcessRequest request)

await ResultReceiver.OnRequestProcessFinishedAsync(request);

static async Task PopulateSupportedValuesAndConfigurations(ImmutableArray<IEntityValue> valueEntities)
static async Task PopulateSupportedValuesAndConfigurationsAsync(ImmutableArray<IEntityValue> valueEntities)
{
foreach (IEntityValue valueEntity in valueEntities)
{
Expand Down Expand Up @@ -197,7 +197,7 @@ static void PopulateEditorMetadata(ImmutableArray<IEntityValue> editors)
}
}

async Task PopulateEditorsAndValues(ImmutableArray<IEntityValue> properties)
async Task PopulateEditorsAndValuesAsync(ImmutableArray<IEntityValue> properties)
{
foreach (IEntityValue propertyEntity in properties)
{
Expand All @@ -215,7 +215,7 @@ async Task PopulateEditorsAndValues(ImmutableArray<IEntityValue> properties)
await UIPropertyValueDataProducer.CreateUIPropertyValueValuesAsync(request.QueryExecutionContext, propertyEntity, propertyProviderState.ProjectState, propertyProviderState.ContainingRule, propertyProviderState.PropertiesContext, propertyProviderState.PropertyName, s_requestedValueProperties));
propertyEntity.SetRelatedEntities(UIPropertyType.ValuesPropertyName, values);

await PopulateSupportedValuesAndConfigurations(values);
await PopulateSupportedValuesAndConfigurationsAsync(values);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,12 @@ public static async Task<IEnumerable<IEntityValue>> CreatePropertyPageValuesAsyn
{
if (await project.GetProjectLevelPropertyPagesCatalogAsync() is IPropertyPagesCatalog projectCatalog)
{
return createPropertyPageValuesAsync();
return createPropertyPageValues();
}

return Enumerable.Empty<IEntityValue>();

IEnumerable<IEntityValue> createPropertyPageValuesAsync()
IEnumerable<IEntityValue> createPropertyPageValues()
{
IProjectState projectState = new PropertyPageProjectState(project);
QueryProjectPropertiesContext propertiesContext = new QueryProjectPropertiesContext(isProjectFile: true, project.FullPath, itemType: null, itemName: null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ private sealed class Subscription : ISubscription
/// <summary>
/// Prevent overlapping requests.
/// </summary>
private readonly AsyncSemaphore _semaphore = new(1);
private readonly ReentrantSemaphore _semaphore;

private int _disposed;

Expand All @@ -58,8 +58,10 @@ private sealed class Subscription : ISubscription
/// </summary>
private readonly CancellationTokenSource _disposeTokenSource = new();

public Subscription(IUpToDateCheckConfiguredInputDataSource inputDataSource, ConfiguredProject configuredProject, IUpToDateCheckHost host, IUpToDateCheckStatePersistence persistence)
public Subscription(IUpToDateCheckConfiguredInputDataSource inputDataSource, ConfiguredProject configuredProject, IUpToDateCheckHost host, IUpToDateCheckStatePersistence persistence, JoinableTaskContext joinableTaskContext)
{
_semaphore = ReentrantSemaphore.Create(initialCount: 1, joinableTaskContext: joinableTaskContext, mode: ReentrantSemaphore.ReentrancyMode.NotRecognized);

Requires.NotNull(inputDataSource);
Requires.NotNull(configuredProject);

Expand Down Expand Up @@ -96,23 +98,24 @@ public async Task<bool> RunAsync(
}

// Prevent overlapping requests
using AsyncSemaphore.Releaser _ = await _semaphore.EnterAsync(token);

token.ThrowIfCancellationRequested();
return await _semaphore.ExecuteAsync(async () =>
{
token.ThrowIfCancellationRequested();

IProjectVersionedValue<UpToDateCheckConfiguredInput> state;
IProjectVersionedValue<UpToDateCheckConfiguredInput> state;

using (_inputDataSource.Join())
{
// Wait for our state to be up to date with that of the project
state = await _inputDataSource.SourceBlock.GetLatestVersionAsync(
_configuredProject,
cancellationToken: token);
}
using (_inputDataSource.Join())
{
// Wait for our state to be up to date with that of the project
state = await _inputDataSource.SourceBlock.GetLatestVersionAsync(
_configuredProject,
cancellationToken: token);
}

(bool upToDate, _lastCheckedConfigurations) = await func(state.Value, _persistence, token);
(bool upToDate, _lastCheckedConfigurations) = await func(state.Value, _persistence, token);

return upToDate;
return upToDate;
}, cancellationToken);
}

public async Task UpdateLastSuccessfulBuildStartTimeUtcAsync(DateTime lastSuccessfulBuildStartTimeUtc, bool isRebuild)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ internal sealed partial class BuildUpToDateCheck
private readonly ISafeProjectGuidService _guidService;
private readonly IUpToDateCheckHost _upToDateCheckHost;
private readonly ICopyItemAggregator _copyItemAggregator;
private readonly JoinableTaskContext _joinableTaskContext;

private IImmutableDictionary<string, string> _lastGlobalProperties = ImmutableStringDictionary<string>.EmptyOrdinal;
private string? _lastFailureReason;
Expand Down Expand Up @@ -85,7 +86,8 @@ public BuildUpToDateCheck(
IFileSystem fileSystem,
ISafeProjectGuidService guidService,
IUpToDateCheckHost upToDateCheckHost,
ICopyItemAggregator copyItemAggregator)
ICopyItemAggregator copyItemAggregator,
JoinableTaskContext joinableTaskContext)
{
_solutionBuildContextProvider = solutionBuildContextProvider;
_solutionBuildEventListener = solutionBuildEventListener;
Expand All @@ -99,10 +101,11 @@ public BuildUpToDateCheck(
_guidService = guidService;
_upToDateCheckHost = upToDateCheckHost;
_copyItemAggregator = copyItemAggregator;
_joinableTaskContext = joinableTaskContext;

UpToDateCheckers = new OrderPrecedenceImportCollection<IBuildUpToDateCheckProvider>(projectCapabilityCheckProvider: configuredProject);

_subscription = new Subscription(inputDataSource, configuredProject, upToDateCheckHost, persistence);
_subscription = new Subscription(inputDataSource, configuredProject, upToDateCheckHost, persistence, joinableTaskContext);
}

public Task ActivateAsync()
Expand Down Expand Up @@ -131,7 +134,7 @@ public void Dispose()

private void RecycleSubscription()
{
ISubscription subscription = Interlocked.Exchange(ref _subscription, new Subscription(_inputDataSource, _configuredProject, _upToDateCheckHost, _persistence));
ISubscription subscription = Interlocked.Exchange(ref _subscription, new Subscription(_inputDataSource, _configuredProject, _upToDateCheckHost, _persistence, _joinableTaskContext));

subscription.Dispose();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,11 @@ internal static async Task<LaunchProfile> ReplaceTokensAsync(ILaunchProfile prof
{
return profile switch
{
ILaunchProfile2 profile2 => ReplaceValuesAsync(profile2.OtherSettings, ReplaceIfString),
_ => ReplaceValuesAsync(profile.FlattenOtherSettings(), ReplaceIfString)
ILaunchProfile2 profile2 => ReplaceValuesAsync(profile2.OtherSettings, ReplaceIfStringAsync),
_ => ReplaceValuesAsync(profile.FlattenOtherSettings(), ReplaceIfStringAsync)
};

async Task<object> ReplaceIfString(object o)
async Task<object> ReplaceIfStringAsync(object o)
{
return o switch
{
Expand All @@ -94,7 +94,7 @@ async Task<object> ReplaceIfString(object o)
static async Task<ImmutableArray<(string Key, T Value)>> ReplaceValuesAsync<T>(ImmutableArray<(string Key, T Value)> source, Func<T, Task<T>> replaceAsync)
where T : class
{
// We will only allocate a new array if a substituion is made
// We will only allocate a new array if a substitution is made
ImmutableArray<(string, T)>.Builder? builder = null;

for (int index = 0; index < source.Length; index++)
Expand Down
2 changes: 2 additions & 0 deletions tests/Common/ThrowingTraceListener.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license. See the LICENSE.md file in the project root for more information.

using System.Diagnostics;
#if !NET8_0_OR_GREATER
using System.Runtime.Serialization;
#endif

namespace Microsoft.VisualStudio.Diagnostics
{
Expand Down
Loading

0 comments on commit 11d5cc0

Please sign in to comment.