Switch back to event from locks to fix blocking issues. (#111)

This commit is contained in:
Karl Burtram
2016-10-21 15:46:33 -07:00
committed by GitHub
parent b3d793dc85
commit 854a6a0eca
6 changed files with 34 additions and 25 deletions

View File

@@ -15,7 +15,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
/// Main class for the Binding Queue /// Main class for the Binding Queue
/// </summary> /// </summary>
public class BindingQueue<T> where T : IBindingContext, new() public class BindingQueue<T> where T : IBindingContext, new()
{ {
private CancellationTokenSource processQueueCancelToken = new CancellationTokenSource(); private CancellationTokenSource processQueueCancelToken = new CancellationTokenSource();
private ManualResetEvent itemQueuedEvent = new ManualResetEvent(initialState: false); private ManualResetEvent itemQueuedEvent = new ManualResetEvent(initialState: false);
@@ -197,14 +197,18 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
// prefer the queue item binding item, otherwise use the context default timeout // prefer the queue item binding item, otherwise use the context default timeout
int bindTimeout = queueItem.BindingTimeout ?? bindingContext.BindingTimeout; int bindTimeout = queueItem.BindingTimeout ?? bindingContext.BindingTimeout;
// handle the case a previous binding operation is still running // handle the case a previous binding operation is still running
if (!Monitor.TryEnter(bindingContext.BindingLock, bindTimeout)) if (!bindingContext.BindingLock.WaitOne(0))
{ {
queueItem.Result = queueItem.TimeoutOperation(bindingContext); queueItem.Result = queueItem.TimeoutOperation != null
queueItem.ItemProcessed.Set(); ? queueItem.TimeoutOperation(bindingContext)
: null;
continue; continue;
} }
bindingContext.BindingLock.Reset();
lockTaken = true; lockTaken = true;
// execute the binding operation // execute the binding operation
@@ -231,9 +235,10 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
{ {
queueItem.Result = queueItem.TimeoutOperation(bindingContext); queueItem.Result = queueItem.TimeoutOperation(bindingContext);
} }
lockTaken = false;
// we'll need to wait for the task to finsh canceling otherwise future binding will fail bindTask.ContinueWith((a) => bindingContext.BindingLock.Set());
bindTask.Wait();
} }
} }
catch (Exception ex) catch (Exception ex)
@@ -246,7 +251,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
{ {
if (lockTaken) if (lockTaken)
{ {
Monitor.Exit(bindingContext.BindingLock); bindingContext.BindingLock.Set();
} }
queueItem.ItemProcessed.Set(); queueItem.ItemProcessed.Set();

View File

@@ -4,6 +4,7 @@
// //
using System; using System;
using System.Threading;
using Microsoft.SqlServer.Management.Common; using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.SmoMetadataProvider; using Microsoft.SqlServer.Management.SmoMetadataProvider;
using Microsoft.SqlServer.Management.SqlParser.Binder; using Microsoft.SqlServer.Management.SqlParser.Binder;
@@ -20,7 +21,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
{ {
private ParseOptions parseOptions; private ParseOptions parseOptions;
private object bindingLock; private ManualResetEvent bindingLock;
private ServerConnection serverConnection; private ServerConnection serverConnection;
@@ -29,7 +30,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
/// </summary> /// </summary>
public ConnectedBindingContext() public ConnectedBindingContext()
{ {
this.bindingLock = new object(); this.bindingLock = new ManualResetEvent(initialState: true);
this.BindingTimeout = ConnectedBindingQueue.DefaultBindingTimeout; this.BindingTimeout = ConnectedBindingQueue.DefaultBindingTimeout;
this.MetadataDisplayInfoProvider = new MetadataDisplayInfoProvider(); this.MetadataDisplayInfoProvider = new MetadataDisplayInfoProvider();
} }
@@ -75,7 +76,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
/// <summary> /// <summary>
/// Gets the binding lock object /// Gets the binding lock object
/// </summary> /// </summary>
public object BindingLock public ManualResetEvent BindingLock
{ {
get get
{ {

View File

@@ -21,7 +21,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
/// </summary> /// </summary>
public class ConnectedBindingQueue : BindingQueue<ConnectedBindingContext> public class ConnectedBindingQueue : BindingQueue<ConnectedBindingContext>
{ {
internal const int DefaultBindingTimeout = 60000; internal const int DefaultBindingTimeout = 500;
internal const int DefaultMinimumConnectionTimeout = 30; internal const int DefaultMinimumConnectionTimeout = 30;
@@ -63,10 +63,12 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
string connectionKey = GetConnectionContextKey(connInfo); string connectionKey = GetConnectionContextKey(connInfo);
IBindingContext bindingContext = this.GetOrCreateBindingContext(connectionKey); IBindingContext bindingContext = this.GetOrCreateBindingContext(connectionKey);
lock (bindingContext.BindingLock) if (bindingContext.BindingLock.WaitOne())
{ {
try try
{ {
bindingContext.BindingLock.Reset();
// increase the connection timeout to at least 30 seconds and and build connection string // increase the connection timeout to at least 30 seconds and and build connection string
// enable PersistSecurityInfo to handle issues in SMO where the connection context is lost in reconnections // enable PersistSecurityInfo to handle issues in SMO where the connection context is lost in reconnections
int? originalTimeout = connInfo.ConnectionDetails.ConnectTimeout; int? originalTimeout = connInfo.ConnectionDetails.ConnectTimeout;
@@ -96,7 +98,11 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
catch (Exception) catch (Exception)
{ {
bindingContext.IsConnected = false; bindingContext.IsConnected = false;
} }
finally
{
bindingContext.BindingLock.Set();
}
} }
return connectionKey; return connectionKey;

View File

@@ -46,7 +46,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
/// <summary> /// <summary>
/// Gets the binding lock object /// Gets the binding lock object
/// </summary> /// </summary>
object BindingLock { get; } ManualResetEvent BindingLock { get; }
/// <summary> /// <summary>
/// Gets or sets the binding operation timeout in milliseconds /// Gets or sets the binding operation timeout in milliseconds

View File

@@ -37,11 +37,9 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
internal const int DiagnosticParseDelay = 750; internal const int DiagnosticParseDelay = 750;
internal const int HoverTimeout = 3000; internal const int HoverTimeout = 500;
internal const int BindingTimeout = 3000; internal const int BindingTimeout = 500;
internal const int FindCompletionStartTimeout = 50;
internal const int OnConnectionWaitTimeout = 300000; internal const int OnConnectionWaitTimeout = 300000;
@@ -252,7 +250,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
await Task.FromResult(true); await Task.FromResult(true);
} }
else else
{ {
// get the current list of completion items and return to client // get the current list of completion items and return to client
var scriptFile = LanguageService.WorkspaceServiceInstance.Workspace.GetFile( var scriptFile = LanguageService.WorkspaceServiceInstance.Workspace.GetFile(
textDocumentPosition.TextDocument.Uri); textDocumentPosition.TextDocument.Uri);
@@ -628,7 +626,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
ScriptParseInfo scriptParseInfo = GetScriptParseInfo(textDocumentPosition.TextDocument.Uri); ScriptParseInfo scriptParseInfo = GetScriptParseInfo(textDocumentPosition.TextDocument.Uri);
if (scriptParseInfo != null && scriptParseInfo.ParseResult != null) if (scriptParseInfo != null && scriptParseInfo.ParseResult != null)
{ {
if (Monitor.TryEnter(scriptParseInfo.BuildingMetadataLock, LanguageService.FindCompletionStartTimeout)) if (Monitor.TryEnter(scriptParseInfo.BuildingMetadataLock))
{ {
try try
{ {
@@ -711,8 +709,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
} }
Token token = GetToken(scriptParseInfo, line, column); Token token = GetToken(scriptParseInfo, line, column);
if (scriptParseInfo.IsConnected if (scriptParseInfo.IsConnected && Monitor.TryEnter(scriptParseInfo.BuildingMetadataLock))
&& Monitor.TryEnter(scriptParseInfo.BuildingMetadataLock, LanguageService.FindCompletionStartTimeout))
{ {
try try
{ {

View File

@@ -24,7 +24,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.LanguageServices
{ {
public TestBindingContext() public TestBindingContext()
{ {
this.BindingLock = new object(); this.BindingLock = new ManualResetEvent(true);
this.BindingTimeout = 3000; this.BindingTimeout = 3000;
} }
@@ -38,7 +38,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.LanguageServices
public IBinder Binder { get; set; } public IBinder Binder { get; set; }
public object BindingLock { get; set; } public ManualResetEvent BindingLock { get; set; }
public int BindingTimeout { get; set; } public int BindingTimeout { get; set; }