Switch from events to locks during parsing (#87)

* Switch from event to locks in binding.

* Remove unneeded null check.
This commit is contained in:
Karl Burtram
2016-10-12 15:09:24 -07:00
committed by GitHub
parent 9f39ac6014
commit c5f44ccee1
9 changed files with 87 additions and 85 deletions

View File

@@ -4,6 +4,7 @@
// //
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading;
using Microsoft.SqlServer.Management.SqlParser.Binder; using Microsoft.SqlServer.Management.SqlParser.Binder;
using Microsoft.SqlServer.Management.SqlParser.Intellisense; using Microsoft.SqlServer.Management.SqlParser.Intellisense;
using Microsoft.SqlServer.Management.SqlParser.Parser; using Microsoft.SqlServer.Management.SqlParser.Parser;
@@ -572,12 +573,10 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
var scriptFile = AutoCompleteHelper.WorkspaceServiceInstance.Workspace.GetFile(info.OwnerUri); var scriptFile = AutoCompleteHelper.WorkspaceServiceInstance.Workspace.GetFile(info.OwnerUri);
LanguageService.Instance.ParseAndBind(scriptFile, info); LanguageService.Instance.ParseAndBind(scriptFile, info);
if (scriptInfo.BuildingMetadataEvent.WaitOne(LanguageService.OnConnectionWaitTimeout)) if (Monitor.TryEnter(scriptInfo.BuildingMetadataLock, LanguageService.OnConnectionWaitTimeout))
{ {
try try
{ {
scriptInfo.BuildingMetadataEvent.Reset();
QueueItem queueItem = bindingQueue.QueueBindingOperation( QueueItem queueItem = bindingQueue.QueueBindingOperation(
key: scriptInfo.ConnectionKey, key: scriptInfo.ConnectionKey,
bindingTimeout: AutoCompleteHelper.PrepopulateBindTimeout, bindingTimeout: AutoCompleteHelper.PrepopulateBindTimeout,
@@ -631,7 +630,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
} }
finally finally
{ {
scriptInfo.BuildingMetadataEvent.Set(); Monitor.Exit(scriptInfo.BuildingMetadataLock);
} }
} }
} }

View File

@@ -191,19 +191,22 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
continue; continue;
} }
bool lockTaken = false;
try try
{ {
// 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 (!bindingContext.BindingLocked.WaitOne(bindTimeout)) if (!Monitor.TryEnter(bindingContext.BindingLock, bindTimeout))
{ {
queueItem.Result = queueItem.TimeoutOperation(bindingContext); queueItem.Result = queueItem.TimeoutOperation(bindingContext);
queueItem.ItemProcessed.Set(); queueItem.ItemProcessed.Set();
continue; continue;
} }
lockTaken = true;
// execute the binding operation // execute the binding operation
object result = null; object result = null;
CancellationTokenSource cancelToken = new CancellationTokenSource(); CancellationTokenSource cancelToken = new CancellationTokenSource();
@@ -237,7 +240,11 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
} }
finally finally
{ {
bindingContext.BindingLocked.Set(); if (lockTaken)
{
Monitor.Exit(bindingContext.BindingLock);
}
queueItem.ItemProcessed.Set(); queueItem.ItemProcessed.Set();
} }

View File

@@ -4,7 +4,6 @@
// //
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;
@@ -21,6 +20,8 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
{ {
private ParseOptions parseOptions; private ParseOptions parseOptions;
private object bindingLock;
private ServerConnection serverConnection; private ServerConnection serverConnection;
/// <summary> /// <summary>
@@ -28,7 +29,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
/// </summary> /// </summary>
public ConnectedBindingContext() public ConnectedBindingContext()
{ {
this.BindingLocked = new ManualResetEvent(initialState: true); this.bindingLock = new object();
this.BindingTimeout = ConnectedBindingQueue.DefaultBindingTimeout; this.BindingTimeout = ConnectedBindingQueue.DefaultBindingTimeout;
this.MetadataDisplayInfoProvider = new MetadataDisplayInfoProvider(); this.MetadataDisplayInfoProvider = new MetadataDisplayInfoProvider();
} }
@@ -72,9 +73,15 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
public IBinder Binder { get; set; } public IBinder Binder { get; set; }
/// <summary> /// <summary>
/// Gets or sets an event to signal if a binding operation is in progress /// Gets the binding lock object
/// </summary> /// </summary>
public ManualResetEvent BindingLocked { get; set; } public object BindingLock
{
get
{
return this.bindingLock;
}
}
/// <summary> /// <summary>
/// Gets or sets the binding operation timeout in milliseconds /// Gets or sets the binding operation timeout in milliseconds

View File

@@ -63,22 +63,22 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
string connectionKey = GetConnectionContextKey(connInfo); string connectionKey = GetConnectionContextKey(connInfo);
IBindingContext bindingContext = this.GetOrCreateBindingContext(connectionKey); IBindingContext bindingContext = this.GetOrCreateBindingContext(connectionKey);
try lock (bindingContext.BindingLock)
{ {
// increase the connection timeout to at least 30 seconds and and build connection string try
// enable PersistSecurityInfo to handle issues in SMO where the connection context is lost in reconnections
int? originalTimeout = connInfo.ConnectionDetails.ConnectTimeout;
bool? originalPersistSecurityInfo = connInfo.ConnectionDetails.PersistSecurityInfo;
connInfo.ConnectionDetails.ConnectTimeout = Math.Max(DefaultMinimumConnectionTimeout, originalTimeout ?? 0);
connInfo.ConnectionDetails.PersistSecurityInfo = true;
string connectionString = ConnectionService.BuildConnectionString(connInfo.ConnectionDetails);
connInfo.ConnectionDetails.ConnectTimeout = originalTimeout;
connInfo.ConnectionDetails.PersistSecurityInfo = originalPersistSecurityInfo;
// open a dedicated binding server connection
SqlConnection sqlConn = new SqlConnection(connectionString);
if (sqlConn != null)
{ {
// 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
int? originalTimeout = connInfo.ConnectionDetails.ConnectTimeout;
bool? originalPersistSecurityInfo = connInfo.ConnectionDetails.PersistSecurityInfo;
connInfo.ConnectionDetails.ConnectTimeout = Math.Max(DefaultMinimumConnectionTimeout, originalTimeout ?? 0);
connInfo.ConnectionDetails.PersistSecurityInfo = true;
string connectionString = ConnectionService.BuildConnectionString(connInfo.ConnectionDetails);
connInfo.ConnectionDetails.ConnectTimeout = originalTimeout;
connInfo.ConnectionDetails.PersistSecurityInfo = originalPersistSecurityInfo;
// open a dedicated binding server connection
SqlConnection sqlConn = new SqlConnection(connectionString);
sqlConn.Open(); sqlConn.Open();
// populate the binding context to work with the SMO metadata provider // populate the binding context to work with the SMO metadata provider
@@ -91,16 +91,12 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
bindingContext.Binder = BinderProvider.CreateBinder(bindingContext.SmoMetadataProvider); bindingContext.Binder = BinderProvider.CreateBinder(bindingContext.SmoMetadataProvider);
bindingContext.ServerConnection = serverConn; bindingContext.ServerConnection = serverConn;
bindingContext.BindingTimeout = ConnectedBindingQueue.DefaultBindingTimeout; bindingContext.BindingTimeout = ConnectedBindingQueue.DefaultBindingTimeout;
bindingContext.IsConnected = true; bindingContext.IsConnected = true;
} }
} catch (Exception)
catch (Exception) {
{ bindingContext.IsConnected = false;
bindingContext.IsConnected = false; }
}
finally
{
bindingContext.BindingLocked.Set();
} }
return connectionKey; return connectionKey;

View File

@@ -44,9 +44,9 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
IBinder Binder { get; set; } IBinder Binder { get; set; }
/// <summary> /// <summary>
/// Gets or sets an event to signal if a binding operation is in progress /// Gets the binding lock object
/// </summary> /// </summary>
ManualResetEvent BindingLocked { get; set; } object BindingLock { get; }
/// <summary> /// <summary>
/// Gets or sets the binding operation timeout in milliseconds /// Gets or sets the binding operation timeout in milliseconds

View File

@@ -452,12 +452,10 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
// get or create the current parse info object // get or create the current parse info object
ScriptParseInfo parseInfo = GetScriptParseInfo(scriptFile.ClientFilePath, createIfNotExists: true); ScriptParseInfo parseInfo = GetScriptParseInfo(scriptFile.ClientFilePath, createIfNotExists: true);
if (parseInfo.BuildingMetadataEvent.WaitOne(LanguageService.BindingTimeout)) if (Monitor.TryEnter(parseInfo.BuildingMetadataLock, LanguageService.BindingTimeout))
{ {
try try
{ {
parseInfo.BuildingMetadataEvent.Reset();
if (connInfo == null || !parseInfo.IsConnected) if (connInfo == null || !parseInfo.IsConnected)
{ {
// parse current SQL file contents to retrieve a list of errors // parse current SQL file contents to retrieve a list of errors
@@ -518,7 +516,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
} }
finally finally
{ {
parseInfo.BuildingMetadataEvent.Set(); Monitor.Exit(parseInfo.BuildingMetadataLock);
} }
} }
else else
@@ -538,11 +536,10 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
await Task.Run(() => await Task.Run(() =>
{ {
ScriptParseInfo scriptInfo = GetScriptParseInfo(info.OwnerUri, createIfNotExists: true); ScriptParseInfo scriptInfo = GetScriptParseInfo(info.OwnerUri, createIfNotExists: true);
if (scriptInfo.BuildingMetadataEvent.WaitOne(LanguageService.OnConnectionWaitTimeout)) if (Monitor.TryEnter(scriptInfo.BuildingMetadataLock, LanguageService.OnConnectionWaitTimeout))
{ {
try try
{ {
scriptInfo.BuildingMetadataEvent.Reset();
scriptInfo.ConnectionKey = this.BindingQueue.AddConnectionContext(info); scriptInfo.ConnectionKey = this.BindingQueue.AddConnectionContext(info);
scriptInfo.IsConnected = true; scriptInfo.IsConnected = true;
@@ -556,7 +553,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
{ {
// Set Metadata Build event to Signal state. // Set Metadata Build event to Signal state.
// (Tell Language Service that I am ready with Metadata Provider Object) // (Tell Language Service that I am ready with Metadata Provider Object)
scriptInfo.BuildingMetadataEvent.Set(); Monitor.Exit(scriptInfo.BuildingMetadataLock);
} }
} }
@@ -631,9 +628,8 @@ 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 (scriptParseInfo.BuildingMetadataEvent.WaitOne(LanguageService.FindCompletionStartTimeout)) if (Monitor.TryEnter(scriptParseInfo.BuildingMetadataLock, LanguageService.FindCompletionStartTimeout))
{ {
scriptParseInfo.BuildingMetadataEvent.Reset();
try try
{ {
QueueItem queueItem = this.BindingQueue.QueueBindingOperation( QueueItem queueItem = this.BindingQueue.QueueBindingOperation(
@@ -661,8 +657,8 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
} }
finally finally
{ {
scriptParseInfo.BuildingMetadataEvent.Set(); Monitor.Exit(scriptParseInfo.BuildingMetadataLock);
} }
} }
} }
@@ -691,8 +687,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
this.currentCompletionParseInfo = null; this.currentCompletionParseInfo = null;
// Take a reference to the list at a point in time in case we update and replace the list // get the current script parse info object
ScriptParseInfo scriptParseInfo = GetScriptParseInfo(textDocumentPosition.TextDocument.Uri); ScriptParseInfo scriptParseInfo = GetScriptParseInfo(textDocumentPosition.TextDocument.Uri);
if (connInfo == null || scriptParseInfo == null) if (connInfo == null || scriptParseInfo == null)
{ {
@@ -711,18 +706,18 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
} }
if (scriptParseInfo.IsConnected if (scriptParseInfo.IsConnected
&& scriptParseInfo.BuildingMetadataEvent.WaitOne(LanguageService.FindCompletionStartTimeout)) && Monitor.TryEnter(scriptParseInfo.BuildingMetadataLock, LanguageService.FindCompletionStartTimeout))
{ {
scriptParseInfo.BuildingMetadataEvent.Reset(); try
{
QueueItem queueItem = this.BindingQueue.QueueBindingOperation( // queue the completion task with the binding queue
key: scriptParseInfo.ConnectionKey, QueueItem queueItem = this.BindingQueue.QueueBindingOperation(
bindingTimeout: LanguageService.BindingTimeout, key: scriptParseInfo.ConnectionKey,
bindOperation: (bindingContext, cancelToken) => bindingTimeout: LanguageService.BindingTimeout,
{ bindOperation: (bindingContext, cancelToken) =>
CompletionItem[] completions = null;
try
{ {
CompletionItem[] completions = null;
// get the completion list from SQL Parser // get the completion list from SQL Parser
scriptParseInfo.CurrentSuggestions = Resolver.FindCompletions( scriptParseInfo.CurrentSuggestions = Resolver.FindCompletions(
scriptParseInfo.ParseResult, scriptParseInfo.ParseResult,
@@ -738,26 +733,27 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
scriptParseInfo.CurrentSuggestions, scriptParseInfo.CurrentSuggestions,
startLine, startLine,
startColumn, startColumn,
endColumn); endColumn);
}
finally
{
scriptParseInfo.BuildingMetadataEvent.Set();
}
return completions; return completions;
}, },
timeoutOperation: (bindingContext) => timeoutOperation: (bindingContext) =>
{
return AutoCompleteHelper.GetDefaultCompletionItems(startLine, startColumn, endColumn, useLowerCaseSuggestions);
});
queueItem.ItemProcessed.WaitOne();
var completionItems = queueItem.GetResultAsT<CompletionItem[]>();
if (completionItems != null && completionItems.Length > 0)
{ {
return AutoCompleteHelper.GetDefaultCompletionItems(startLine, startColumn, endColumn, useLowerCaseSuggestions); return completionItems;
}); }
queueItem.ItemProcessed.WaitOne();
var completionItems = queueItem.GetResultAsT<CompletionItem[]>();
if (completionItems != null && completionItems.Length > 0)
{
return completionItems;
} }
finally
{
Monitor.Exit(scriptParseInfo.BuildingMetadataLock);
}
} }
return AutoCompleteHelper.GetDefaultCompletionItems(startLine, startColumn, endColumn, useLowerCaseSuggestions); return AutoCompleteHelper.GetDefaultCompletionItems(startLine, startColumn, endColumn, useLowerCaseSuggestions);

View File

@@ -5,7 +5,6 @@
using System; using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
{ {

View File

@@ -4,7 +4,6 @@
// //
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading;
using Microsoft.SqlServer.Management.SqlParser.Intellisense; using Microsoft.SqlServer.Management.SqlParser.Intellisense;
using Microsoft.SqlServer.Management.SqlParser.Parser; using Microsoft.SqlServer.Management.SqlParser.Parser;
@@ -15,14 +14,14 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
/// </summary> /// </summary>
internal class ScriptParseInfo internal class ScriptParseInfo
{ {
private ManualResetEvent buildingMetadataEvent = new ManualResetEvent(initialState: true); private object buildingMetadataLock = new object();
/// <summary> /// <summary>
/// Event which tells if MetadataProvider is built fully or not /// Event which tells if MetadataProvider is built fully or not
/// </summary> /// </summary>
public ManualResetEvent BuildingMetadataEvent public object BuildingMetadataLock
{ {
get { return this.buildingMetadataEvent; } get { return this.buildingMetadataLock; }
} }
/// <summary> /// <summary>

View File

@@ -4,7 +4,6 @@
// //
using System.Threading; using System.Threading;
using System.Threading.Tasks;
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;
@@ -25,7 +24,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.LanguageServices
{ {
public TestBindingContext() public TestBindingContext()
{ {
this.BindingLocked = new ManualResetEvent(initialState: true); this.BindingLock = new object();
this.BindingTimeout = 3000; this.BindingTimeout = 3000;
} }
@@ -39,7 +38,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.LanguageServices
public IBinder Binder { get; set; } public IBinder Binder { get; set; }
public ManualResetEvent BindingLocked { get; set; } public object BindingLock { get; set; }
public int BindingTimeout { get; set; } public int BindingTimeout { get; set; }