提交 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 @@ ...@@ -4,6 +4,7 @@
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Execution;
namespace Microsoft.CodeAnalysis.TodoComments namespace Microsoft.CodeAnalysis.TodoComments
{ {
...@@ -12,6 +13,6 @@ namespace Microsoft.CodeAnalysis.TodoComments ...@@ -12,6 +13,6 @@ namespace Microsoft.CodeAnalysis.TodoComments
/// </summary> /// </summary>
internal interface IRemoteTodoCommentService 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 ...@@ -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 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 /// 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 /// when this is used, solution must be explicitly passed around between client (VS) and remote host (OOP)
/// 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
/// </summary> /// </summary>
internal sealed class KeepAliveSession internal sealed class KeepAliveSession
{ {
private readonly SemaphoreSlim _gate;
private readonly IRemoteHostClientService _remoteHostClientService; private readonly IRemoteHostClientService _remoteHostClientService;
private readonly string _serviceName; private readonly string _serviceName;
private readonly object _callbackTarget; private readonly object _callbackTarget;
private readonly object _gate;
private RemoteHostClient _client; private RemoteHostClient _client;
private RemoteHostClient.Connection _connection; private RemoteHostClient.Connection _connection;
public KeepAliveSession(RemoteHostClient client, RemoteHostClient.Connection connection, string serviceName, object callbackTarget) public KeepAliveSession(RemoteHostClient client, RemoteHostClient.Connection connection, string serviceName, object callbackTarget)
{ {
Initialize_NoLock(client, connection); _gate = new object();
_gate = new SemaphoreSlim(initialCount: 1); Initialize(client, connection);
_remoteHostClientService = client.Workspace.Services.GetService<IRemoteHostClientService>();
_remoteHostClientService = client.Workspace.Services.GetService<IRemoteHostClientService>();
_serviceName = serviceName; _serviceName = serviceName;
_callbackTarget = callbackTarget; _callbackTarget = callbackTarget;
} }
public void Shutdown(CancellationToken cancellationToken) public void Shutdown(CancellationToken cancellationToken)
{ {
using (_gate.DisposableWait(cancellationToken)) RemoteHostClient.Connection connection;
lock (_gate)
{ {
if (_client != null) if (_client != null)
{ {
_client.StatusChanged -= OnStatusChanged; _client.StatusChanged -= OnStatusChanged;
} }
_connection?.Dispose(); connection = _connection;
_client = null; _client = null;
_connection = null; _connection = null;
} }
connection?.Dispose();
} }
public async Task<bool> TryInvokeAsync(string targetName, IReadOnlyList<object> arguments, CancellationToken cancellationToken) 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); return false;
if (connection == null)
{
return false;
}
await connection.InvokeAsync(targetName, arguments, cancellationToken).ConfigureAwait(false);
return true;
} }
await connection.InvokeAsync(targetName, arguments, cancellationToken).ConfigureAwait(false);
return true;
} }
public async Task<T> TryInvokeAsync<T>(string targetName, IReadOnlyList<object> arguments, CancellationToken cancellationToken) 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); return default;
if (connection == null)
{
return default;
}
return await connection.InvokeAsync<T>(targetName, arguments, cancellationToken).ConfigureAwait(false);
} }
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) 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); return false;
if (connection == null)
{
return false;
}
await connection.InvokeAsync(targetName, arguments, funcWithDirectStreamAsync, cancellationToken).ConfigureAwait(false);
return true;
} }
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) 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); return default;
if (connection == null)
{
return default;
}
return await connection.InvokeAsync(targetName, arguments, funcWithDirectStreamAsync, cancellationToken).ConfigureAwait(false);
} }
return await connection.InvokeAsync(targetName, arguments, funcWithDirectStreamAsync, cancellationToken).ConfigureAwait(false);
} }
public async Task<bool> TryInvokeAsync(string targetName, Solution solution, IReadOnlyList<object> arguments, CancellationToken cancellationToken) 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)) 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) if (connection == null)
{ {
return false; return false;
} }
await connection.RegisterPinnedRemotableDataScopeAsync(scope).ConfigureAwait(false); pooledObject.Object.Add(scope.SolutionInfo);
await connection.InvokeAsync(targetName, arguments, cancellationToken).ConfigureAwait(false); pooledObject.Object.AddRange(arguments);
await connection.InvokeAsync(targetName, pooledObject.Object, cancellationToken).ConfigureAwait(false);
return true; return true;
} }
} }
public async Task<T> TryInvokeAsync<T>(string targetName, Solution solution, IReadOnlyList<object> arguments, CancellationToken cancellationToken) 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)) 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) if (connection == null)
{ {
return default; return default;
} }
await connection.RegisterPinnedRemotableDataScopeAsync(scope).ConfigureAwait(false); pooledObject.Object.Add(scope.SolutionInfo);
return await connection.InvokeAsync<T>(targetName, arguments, cancellationToken).ConfigureAwait(false); pooledObject.Object.AddRange(arguments);
return await connection.InvokeAsync<T>(targetName, pooledObject.Object, cancellationToken).ConfigureAwait(false);
} }
} }
public async Task<bool> TryInvokeAsync( public async Task<bool> TryInvokeAsync(
string targetName, Solution solution, IReadOnlyList<object> arguments, Func<Stream, CancellationToken, Task> funcWithDirectStreamAsync, CancellationToken cancellationToken) 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)) 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) if (connection == null)
{ {
return false; return false;
} }
await connection.RegisterPinnedRemotableDataScopeAsync(scope).ConfigureAwait(false); pooledObject.Object.Add(scope.SolutionInfo);
await connection.InvokeAsync(targetName, arguments, funcWithDirectStreamAsync, cancellationToken).ConfigureAwait(false); pooledObject.Object.AddRange(arguments);
await connection.InvokeAsync(targetName, pooledObject.Object, funcWithDirectStreamAsync, cancellationToken).ConfigureAwait(false);
return true; return true;
} }
} }
...@@ -223,25 +219,30 @@ public async Task<T> TryInvokeAsync<T>(string targetName, Solution solution, IRe ...@@ -223,25 +219,30 @@ public async Task<T> TryInvokeAsync<T>(string targetName, Solution solution, IRe
public async Task<T> TryInvokeAsync<T>( public async Task<T> TryInvokeAsync<T>(
string targetName, Solution solution, IReadOnlyList<object> arguments, Func<Stream, CancellationToken, Task<T>> funcWithDirectStreamAsync, CancellationToken cancellationToken) 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)) 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) if (connection == null)
{ {
return default; return default;
} }
await connection.RegisterPinnedRemotableDataScopeAsync(scope).ConfigureAwait(false); pooledObject.Object.Add(scope.SolutionInfo);
return await connection.InvokeAsync(targetName, arguments, funcWithDirectStreamAsync, cancellationToken).ConfigureAwait(false); 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); var client = await _remoteHostClientService.TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false);
...@@ -250,15 +251,15 @@ private async Task<RemoteHostClient.Connection> TryGetConnection_NoLockAsync(Can ...@@ -250,15 +251,15 @@ private async Task<RemoteHostClient.Connection> TryGetConnection_NoLockAsync(Can
return null; return null;
} }
var session = await client.TryCreateConnectionAsync(_serviceName, _callbackTarget, cancellationToken).ConfigureAwait(false); var connection = await client.TryCreateConnectionAsync(_serviceName, _callbackTarget, cancellationToken).ConfigureAwait(false);
if (session == null) if (connection == null)
{ {
return 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) private void OnStatusChanged(object sender, bool connection)
...@@ -271,15 +272,18 @@ private void OnStatusChanged(object sender, bool connection) ...@@ -271,15 +272,18 @@ private void OnStatusChanged(object sender, bool connection)
Shutdown(CancellationToken.None); 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(client);
Contract.ThrowIfNull(connection); Contract.ThrowIfNull(connection);
_client = client; lock (_gate)
_client.StatusChanged += OnStatusChanged; {
_client = client;
_client.StatusChanged += OnStatusChanged;
_connection = connection; _connection = connection;
}
} }
} }
} }
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Execution;
using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.TodoComments; using Microsoft.CodeAnalysis.TodoComments;
...@@ -22,11 +23,11 @@ internal partial class CodeAnalysisService : IRemoteTodoCommentService ...@@ -22,11 +23,11 @@ internal partial class CodeAnalysisService : IRemoteTodoCommentService
/// ///
/// This will be called by ServiceHub/JsonRpc framework /// This will be called by ServiceHub/JsonRpc framework
/// </summary> /// </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)) 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 document = solution.GetDocument(documentId);
var service = document.GetLanguageService<ITodoCommentService>(); var service = document.GetLanguageService<ITodoCommentService>();
......
...@@ -102,8 +102,14 @@ protected Task<Solution> GetSolutionAsync(CancellationToken cancellationToken) ...@@ -102,8 +102,14 @@ protected Task<Solution> GetSolutionAsync(CancellationToken cancellationToken)
{ {
Contract.ThrowIfNull(_solutionInfo); Contract.ThrowIfNull(_solutionInfo);
var solutionController = (ISolutionController)RoslynServices.SolutionService; return GetSolutionAsync(_solutionInfo, cancellationToken);
return solutionController.GetSolutionAsync(_solutionInfo.SolutionChecksum, _solutionInfo.FromPrimaryBranch, 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) protected virtual void Dispose(bool disposing)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册