提交 4c33d67e 编写于 作者: H Heejae Chang

made KeepAlive to be stateless so that it can call OOP without a lock. now...

made KeepAlive to be stateless so that it can call OOP without a lock. now solution must be explicitly passed around between VS and OOP since keep alive session is not tied to 1 specific solution
上级 fefcc593
......@@ -4,6 +4,7 @@
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Execution;
namespace Microsoft.CodeAnalysis.TodoComments
{
......@@ -12,6 +13,6 @@ namespace Microsoft.CodeAnalysis.TodoComments
/// </summary>
internal interface IRemoteTodoCommentService
{
Task<IList<TodoComment>> GetTodoCommentsAsync(DocumentId documentId, ImmutableArray<TodoCommentDescriptor> commentDescriptors, CancellationToken cancellationToken);
Task<IList<TodoComment>> GetTodoCommentsAsync(PinnedSolutionInfo solutionInfo, DocumentId documentId, ImmutableArray<TodoCommentDescriptor> commentDescriptors, CancellationToken cancellationToken);
}
}
......@@ -69,153 +69,149 @@ public Task<T> InvokeAsync<T>(string targetName, IReadOnlyList<object> arguments
/// This will let one to hold onto <see cref="RemoteHostClient.Connection"/> for a while.
/// this helper will let you not care about remote host being gone while you hold onto the connection if that ever happen
///
/// and also make sure state is correct even if multiple threads call TryInvokeAsync at the same time. but this
/// is not optimized to handle highly concurrent usage. if highly concurrent usage is required, either using
/// <see cref="RemoteHostClient.Connection"/> direclty or using <see cref="SessionWithSolution"/> would be better choice
/// when this is used, solution must be explicitly passed around between client (VS) and remote host (OOP)
/// </summary>
internal sealed class KeepAliveSession
{
private readonly SemaphoreSlim _gate;
private readonly IRemoteHostClientService _remoteHostClientService;
private readonly string _serviceName;
private readonly object _callbackTarget;
private readonly object _gate;
private RemoteHostClient _client;
private RemoteHostClient.Connection _connection;
public KeepAliveSession(RemoteHostClient client, RemoteHostClient.Connection connection, string serviceName, object callbackTarget)
{
Initialize_NoLock(client, connection);
_gate = new object();
_gate = new SemaphoreSlim(initialCount: 1);
_remoteHostClientService = client.Workspace.Services.GetService<IRemoteHostClientService>();
Initialize(client, connection);
_remoteHostClientService = client.Workspace.Services.GetService<IRemoteHostClientService>();
_serviceName = serviceName;
_callbackTarget = callbackTarget;
}
public void Shutdown(CancellationToken cancellationToken)
{
using (_gate.DisposableWait(cancellationToken))
RemoteHostClient.Connection connection;
lock (_gate)
{
if (_client != null)
{
_client.StatusChanged -= OnStatusChanged;
}
_connection?.Dispose();
connection = _connection;
_client = null;
_connection = null;
}
connection?.Dispose();
}
public async Task<bool> TryInvokeAsync(string targetName, IReadOnlyList<object> arguments, CancellationToken cancellationToken)
{
using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
var connection = await TryGetConnectionAsync(cancellationToken).ConfigureAwait(false);
if (connection == null)
{
var connection = await TryGetConnection_NoLockAsync(cancellationToken).ConfigureAwait(false);
if (connection == null)
{
return false;
}
await connection.InvokeAsync(targetName, arguments, cancellationToken).ConfigureAwait(false);
return true;
return false;
}
await connection.InvokeAsync(targetName, arguments, cancellationToken).ConfigureAwait(false);
return true;
}
public async Task<T> TryInvokeAsync<T>(string targetName, IReadOnlyList<object> arguments, CancellationToken cancellationToken)
{
using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
var connection = await TryGetConnectionAsync(cancellationToken).ConfigureAwait(false);
if (connection == null)
{
var connection = await TryGetConnection_NoLockAsync(cancellationToken).ConfigureAwait(false);
if (connection == null)
{
return default;
}
return await connection.InvokeAsync<T>(targetName, arguments, cancellationToken).ConfigureAwait(false);
return default;
}
return await connection.InvokeAsync<T>(targetName, arguments, cancellationToken).ConfigureAwait(false);
}
public async Task<bool> TryInvokeAsync(string targetName, IReadOnlyList<object> arguments, Func<Stream, CancellationToken, Task> funcWithDirectStreamAsync, CancellationToken cancellationToken)
{
using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
var connection = await TryGetConnectionAsync(cancellationToken).ConfigureAwait(false);
if (connection == null)
{
var connection = await TryGetConnection_NoLockAsync(cancellationToken).ConfigureAwait(false);
if (connection == null)
{
return false;
}
await connection.InvokeAsync(targetName, arguments, funcWithDirectStreamAsync, cancellationToken).ConfigureAwait(false);
return true;
return false;
}
await connection.InvokeAsync(targetName, arguments, funcWithDirectStreamAsync, cancellationToken).ConfigureAwait(false);
return true;
}
public async Task<T> TryInvokeAsync<T>(string targetName, IReadOnlyList<object> arguments, Func<Stream, CancellationToken, Task<T>> funcWithDirectStreamAsync, CancellationToken cancellationToken)
{
using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
var connection = await TryGetConnectionAsync(cancellationToken).ConfigureAwait(false);
if (connection == null)
{
var connection = await TryGetConnection_NoLockAsync(cancellationToken).ConfigureAwait(false);
if (connection == null)
{
return default;
}
return await connection.InvokeAsync(targetName, arguments, funcWithDirectStreamAsync, cancellationToken).ConfigureAwait(false);
return default;
}
return await connection.InvokeAsync(targetName, arguments, funcWithDirectStreamAsync, cancellationToken).ConfigureAwait(false);
}
public async Task<bool> TryInvokeAsync(string targetName, Solution solution, IReadOnlyList<object> arguments, CancellationToken cancellationToken)
{
using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
using (var pooledObject = SharedPools.Default<List<object>>().GetPooledObject())
using (var scope = await solution.GetPinnedScopeAsync(cancellationToken).ConfigureAwait(false))
{
var connection = await TryGetConnection_NoLockAsync(cancellationToken).ConfigureAwait(false);
var connection = await TryGetConnectionAsync(cancellationToken).ConfigureAwait(false);
if (connection == null)
{
return false;
}
await connection.RegisterPinnedRemotableDataScopeAsync(scope).ConfigureAwait(false);
await connection.InvokeAsync(targetName, arguments, cancellationToken).ConfigureAwait(false);
pooledObject.Object.Add(scope.SolutionInfo);
pooledObject.Object.AddRange(arguments);
await connection.InvokeAsync(targetName, pooledObject.Object, cancellationToken).ConfigureAwait(false);
return true;
}
}
public async Task<T> TryInvokeAsync<T>(string targetName, Solution solution, IReadOnlyList<object> arguments, CancellationToken cancellationToken)
{
using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
using (var pooledObject = SharedPools.Default<List<object>>().GetPooledObject())
using (var scope = await solution.GetPinnedScopeAsync(cancellationToken).ConfigureAwait(false))
{
var connection = await TryGetConnection_NoLockAsync(cancellationToken).ConfigureAwait(false);
var connection = await TryGetConnectionAsync(cancellationToken).ConfigureAwait(false);
if (connection == null)
{
return default;
}
await connection.RegisterPinnedRemotableDataScopeAsync(scope).ConfigureAwait(false);
return await connection.InvokeAsync<T>(targetName, arguments, cancellationToken).ConfigureAwait(false);
pooledObject.Object.Add(scope.SolutionInfo);
pooledObject.Object.AddRange(arguments);
return await connection.InvokeAsync<T>(targetName, pooledObject.Object, cancellationToken).ConfigureAwait(false);
}
}
public async Task<bool> TryInvokeAsync(
string targetName, Solution solution, IReadOnlyList<object> arguments, Func<Stream, CancellationToken, Task> funcWithDirectStreamAsync, CancellationToken cancellationToken)
{
using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
using (var pooledObject = SharedPools.Default<List<object>>().GetPooledObject())
using (var scope = await solution.GetPinnedScopeAsync(cancellationToken).ConfigureAwait(false))
{
var connection = await TryGetConnection_NoLockAsync(cancellationToken).ConfigureAwait(false);
var connection = await TryGetConnectionAsync(cancellationToken).ConfigureAwait(false);
if (connection == null)
{
return false;
}
await connection.RegisterPinnedRemotableDataScopeAsync(scope).ConfigureAwait(false);
await connection.InvokeAsync(targetName, arguments, funcWithDirectStreamAsync, cancellationToken).ConfigureAwait(false);
pooledObject.Object.Add(scope.SolutionInfo);
pooledObject.Object.AddRange(arguments);
await connection.InvokeAsync(targetName, pooledObject.Object, funcWithDirectStreamAsync, cancellationToken).ConfigureAwait(false);
return true;
}
}
......@@ -223,25 +219,30 @@ public async Task<T> TryInvokeAsync<T>(string targetName, Solution solution, IRe
public async Task<T> TryInvokeAsync<T>(
string targetName, Solution solution, IReadOnlyList<object> arguments, Func<Stream, CancellationToken, Task<T>> funcWithDirectStreamAsync, CancellationToken cancellationToken)
{
using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
using (var pooledObject = SharedPools.Default<List<object>>().GetPooledObject())
using (var scope = await solution.GetPinnedScopeAsync(cancellationToken).ConfigureAwait(false))
{
var connection = await TryGetConnection_NoLockAsync(cancellationToken).ConfigureAwait(false);
var connection = await TryGetConnectionAsync(cancellationToken).ConfigureAwait(false);
if (connection == null)
{
return default;
}
await connection.RegisterPinnedRemotableDataScopeAsync(scope).ConfigureAwait(false);
return await connection.InvokeAsync(targetName, arguments, funcWithDirectStreamAsync, cancellationToken).ConfigureAwait(false);
pooledObject.Object.Add(scope.SolutionInfo);
pooledObject.Object.AddRange(arguments);
return await connection.InvokeAsync(targetName, pooledObject.Object, funcWithDirectStreamAsync, cancellationToken).ConfigureAwait(false);
}
}
private async Task<RemoteHostClient.Connection> TryGetConnection_NoLockAsync(CancellationToken cancellationToken)
private async Task<RemoteHostClient.Connection> TryGetConnectionAsync(CancellationToken cancellationToken)
{
if (_connection != null)
lock (_gate)
{
return _connection;
if (_connection != null)
{
return _connection;
}
}
var client = await _remoteHostClientService.TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false);
......@@ -250,15 +251,15 @@ private async Task<RemoteHostClient.Connection> TryGetConnection_NoLockAsync(Can
return null;
}
var session = await client.TryCreateConnectionAsync(_serviceName, _callbackTarget, cancellationToken).ConfigureAwait(false);
if (session == null)
var connection = await client.TryCreateConnectionAsync(_serviceName, _callbackTarget, cancellationToken).ConfigureAwait(false);
if (connection == null)
{
return null;
}
Initialize_NoLock(client, session);
Initialize(client, connection);
return _connection;
return await TryGetConnectionAsync(cancellationToken).ConfigureAwait(false);
}
private void OnStatusChanged(object sender, bool connection)
......@@ -271,15 +272,18 @@ private void OnStatusChanged(object sender, bool connection)
Shutdown(CancellationToken.None);
}
private void Initialize_NoLock(RemoteHostClient client, RemoteHostClient.Connection connection)
private void Initialize(RemoteHostClient client, RemoteHostClient.Connection connection)
{
Contract.ThrowIfNull(client);
Contract.ThrowIfNull(connection);
_client = client;
_client.StatusChanged += OnStatusChanged;
lock (_gate)
{
_client = client;
_client.StatusChanged += OnStatusChanged;
_connection = connection;
_connection = connection;
}
}
}
}
......@@ -6,6 +6,7 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Execution;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.TodoComments;
......@@ -22,11 +23,11 @@ internal partial class CodeAnalysisService : IRemoteTodoCommentService
///
/// This will be called by ServiceHub/JsonRpc framework
/// </summary>
public async Task<IList<TodoComment>> GetTodoCommentsAsync(DocumentId documentId, ImmutableArray<TodoCommentDescriptor> tokens, CancellationToken cancellationToken)
public async Task<IList<TodoComment>> GetTodoCommentsAsync(PinnedSolutionInfo solutionInfo, DocumentId documentId, ImmutableArray<TodoCommentDescriptor> tokens, CancellationToken cancellationToken)
{
using (RoslynLogger.LogBlock(FunctionId.CodeAnalysisService_GetTodoCommentsAsync, documentId.ProjectId.DebugName, cancellationToken))
{
var solution = await GetSolutionAsync(cancellationToken).ConfigureAwait(false);
var solution = await GetSolutionAsync(solutionInfo, cancellationToken).ConfigureAwait(false);
var document = solution.GetDocument(documentId);
var service = document.GetLanguageService<ITodoCommentService>();
......
......@@ -102,8 +102,14 @@ protected Task<Solution> GetSolutionAsync(CancellationToken cancellationToken)
{
Contract.ThrowIfNull(_solutionInfo);
var solutionController = (ISolutionController)RoslynServices.SolutionService;
return solutionController.GetSolutionAsync(_solutionInfo.SolutionChecksum, _solutionInfo.FromPrimaryBranch, cancellationToken);
return GetSolutionAsync(_solutionInfo, cancellationToken);
}
protected Task<Solution> GetSolutionAsync(PinnedSolutionInfo solutionInfo, CancellationToken cancellationToken)
{
var localRoslynService = new RoslynServices(solutionInfo.ScopeId, AssetStorage);
var solutionController = (ISolutionController)localRoslynService.SolutionService;
return solutionController.GetSolutionAsync(solutionInfo.SolutionChecksum, solutionInfo.FromPrimaryBranch, cancellationToken);
}
protected virtual void Dispose(bool disposing)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册