mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-16 18:47:57 -05:00
Fix filebrowser to handle cancellation and multiple open/expand requests (#499)
* add cancel filebrowser request * update expand request contract * cleanup * add queue * add expand queue and update cancellation * dispose cancelsource * remove unnecessary field * cleanup using directives * address pr comment * more changes * change contract
This commit is contained in:
@@ -26,6 +26,11 @@ namespace Microsoft.SqlTools.ServiceLayer.FileBrowser.Contracts
|
|||||||
/// File extension filter (e.g. *.bak)
|
/// File extension filter (e.g. *.bak)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string[] FileFilters;
|
public string[] FileFilters;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// True if this is a request to change file filter
|
||||||
|
/// </summary>
|
||||||
|
public bool ChangeFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using System.Collections.Generic;
|
|||||||
using System.Data.SqlClient;
|
using System.Data.SqlClient;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading;
|
||||||
using Microsoft.SqlTools.ServiceLayer.FileBrowser.Contracts;
|
using Microsoft.SqlTools.ServiceLayer.FileBrowser.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
|
namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
|
||||||
@@ -15,11 +16,14 @@ namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Implementation for file browser operation
|
/// Implementation for file browser operation
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class FileBrowserOperation : FileBrowserBase
|
internal class FileBrowserOperation : FileBrowserBase, IDisposable
|
||||||
{
|
{
|
||||||
private FileTree fileTree;
|
private FileTree fileTree;
|
||||||
private string expandPath;
|
private string expandPath;
|
||||||
private string[] fileFilters;
|
private string[] fileFilters;
|
||||||
|
private bool fileTreeCreated;
|
||||||
|
private CancellationTokenSource cancelSource;
|
||||||
|
private CancellationToken cancelToken;
|
||||||
|
|
||||||
#region Constructors
|
#region Constructors
|
||||||
|
|
||||||
@@ -29,6 +33,8 @@ namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
|
|||||||
public FileBrowserOperation()
|
public FileBrowserOperation()
|
||||||
{
|
{
|
||||||
this.fileTree = new FileTree();
|
this.fileTree = new FileTree();
|
||||||
|
this.cancelSource = new CancellationTokenSource();
|
||||||
|
this.cancelToken = cancelSource.Token;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -39,15 +45,7 @@ namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
|
|||||||
public FileBrowserOperation(SqlConnection connectionInfo, string expandPath, string[] fileFilters = null): this()
|
public FileBrowserOperation(SqlConnection connectionInfo, string expandPath, string[] fileFilters = null): this()
|
||||||
{
|
{
|
||||||
this.sqlConnection = connectionInfo;
|
this.sqlConnection = connectionInfo;
|
||||||
this.expandPath = expandPath;
|
this.Initialize(expandPath, fileFilters);
|
||||||
if (fileFilters == null)
|
|
||||||
{
|
|
||||||
this.fileFilters = new string[1] { "*" };
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.fileFilters = fileFilters;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -70,13 +68,65 @@ namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool FileTreeCreated
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return this.fileTreeCreated;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SqlConnection SqlConnection
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return this.sqlConnection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsCancellationRequested
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return this.cancelToken.IsCancellationRequested;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Cancel()
|
||||||
|
{
|
||||||
|
this.cancelSource.Cancel();
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
public void Initialize(string expandPath, string[] fileFilters)
|
||||||
|
{
|
||||||
|
this.expandPath = expandPath;
|
||||||
|
if (fileFilters == null)
|
||||||
|
{
|
||||||
|
this.fileFilters = new string[1] { "*" };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.fileFilters = fileFilters;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (this.sqlConnection != null)
|
||||||
|
{
|
||||||
|
this.sqlConnection.Close();
|
||||||
|
}
|
||||||
|
this.cancelSource.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
public void PopulateFileTree()
|
public void PopulateFileTree()
|
||||||
{
|
{
|
||||||
|
this.fileTreeCreated = false;
|
||||||
this.PathSeparator = GetPathSeparator(this.Enumerator, this.sqlConnection);
|
this.PathSeparator = GetPathSeparator(this.Enumerator, this.sqlConnection);
|
||||||
PopulateDrives();
|
PopulateDrives();
|
||||||
ExpandSelectedNode(this.expandPath);
|
ExpandSelectedNode(this.expandPath);
|
||||||
|
this.fileTreeCreated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -94,6 +144,11 @@ namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
|
|||||||
|
|
||||||
foreach (string dir in dirs)
|
foreach (string dir in dirs)
|
||||||
{
|
{
|
||||||
|
if (cancelToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
FileTreeNode currentNode = null;
|
FileTreeNode currentNode = null;
|
||||||
foreach (FileTreeNode node in currentChildren)
|
foreach (FileTreeNode node in currentChildren)
|
||||||
{
|
{
|
||||||
@@ -131,10 +186,42 @@ namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<FileTreeNode> GetChildren(string filepath)
|
public void PopulateDrives()
|
||||||
|
{
|
||||||
|
bool first = true;
|
||||||
|
if (!cancelToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
foreach (var fileInfo in EnumerateDrives(Enumerator, sqlConnection))
|
||||||
|
{
|
||||||
|
if (cancelToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Windows drive letter paths have a '\' at the end. Linux drive paths won't have a '\'.
|
||||||
|
var node = new FileTreeNode
|
||||||
|
{
|
||||||
|
Name = Convert.ToString(fileInfo.path, CultureInfo.InvariantCulture).TrimEnd('\\'),
|
||||||
|
FullPath = fileInfo.path
|
||||||
|
};
|
||||||
|
|
||||||
|
this.fileTree.RootNode.AddChildNode(node);
|
||||||
|
|
||||||
|
if (first)
|
||||||
|
{
|
||||||
|
this.fileTree.SelectedNode = node;
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
node.Children = this.GetChildren(node.FullPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<FileTreeNode> GetChildren(string filePath)
|
||||||
{
|
{
|
||||||
List<FileTreeNode> children = new List<FileTreeNode>();
|
List<FileTreeNode> children = new List<FileTreeNode>();
|
||||||
foreach (var file in EnumerateFilesInFolder(Enumerator, sqlConnection, filepath))
|
foreach (var file in EnumerateFilesInFolder(Enumerator, sqlConnection, filePath))
|
||||||
{
|
{
|
||||||
bool isFile = !string.IsNullOrEmpty(file.fileName);
|
bool isFile = !string.IsNullOrEmpty(file.fileName);
|
||||||
FileTreeNode treeNode = new FileTreeNode();
|
FileTreeNode treeNode = new FileTreeNode();
|
||||||
@@ -157,34 +244,9 @@ namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
|
|||||||
children.Add(treeNode);
|
children.Add(treeNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return children;
|
return children;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PopulateDrives()
|
|
||||||
{
|
|
||||||
bool first = true;
|
|
||||||
foreach (var fileInfo in EnumerateDrives(Enumerator, sqlConnection))
|
|
||||||
{
|
|
||||||
// Windows drive letter paths have a '\' at the end. Linux drive paths won't have a '\'.
|
|
||||||
var node = new FileTreeNode
|
|
||||||
{
|
|
||||||
Name = Convert.ToString(fileInfo.path, CultureInfo.InvariantCulture).TrimEnd('\\'),
|
|
||||||
FullPath = fileInfo.path
|
|
||||||
};
|
|
||||||
|
|
||||||
this.fileTree.RootNode.AddChildNode(node);
|
|
||||||
|
|
||||||
if (first)
|
|
||||||
{
|
|
||||||
this.fileTree.SelectedNode = node;
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
node.Children = this.GetChildren(node.FullPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Filter a filename based on the full mask provide. The full mask may be a collection a masks seperated by semi-colons.
|
/// Filter a filename based on the full mask provide. The full mask may be a collection a masks seperated by semi-colons.
|
||||||
/// For example: *; *.txt
|
/// For example: *; *.txt
|
||||||
|
|||||||
@@ -5,14 +5,13 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Data.Common;
|
|
||||||
using System.Data.SqlClient;
|
using System.Data.SqlClient;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.SqlTools.Hosting.Protocol;
|
using Microsoft.SqlTools.Hosting.Protocol;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
|
|
||||||
using Microsoft.SqlTools.ServiceLayer.FileBrowser.Contracts;
|
using Microsoft.SqlTools.ServiceLayer.FileBrowser.Contracts;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Hosting;
|
using Microsoft.SqlTools.ServiceLayer.Hosting;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.LanguageServices;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
|
namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
|
||||||
@@ -20,7 +19,7 @@ namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Main class for file browser service
|
/// Main class for file browser service
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class FileBrowserService
|
public sealed class FileBrowserService: IDisposable
|
||||||
{
|
{
|
||||||
private static readonly Lazy<FileBrowserService> LazyInstance = new Lazy<FileBrowserService>(() => new FileBrowserService());
|
private static readonly Lazy<FileBrowserService> LazyInstance = new Lazy<FileBrowserService>(() => new FileBrowserService());
|
||||||
public static FileBrowserService Instance => LazyInstance.Value;
|
public static FileBrowserService Instance => LazyInstance.Value;
|
||||||
@@ -29,6 +28,9 @@ namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
|
|||||||
private readonly ConcurrentDictionary<string, FileBrowserOperation> ownerToFileBrowserMap = new ConcurrentDictionary<string, FileBrowserOperation>();
|
private readonly ConcurrentDictionary<string, FileBrowserOperation> ownerToFileBrowserMap = new ConcurrentDictionary<string, FileBrowserOperation>();
|
||||||
private readonly ConcurrentDictionary<string, ValidatePathsCallback> validatePathsCallbackMap = new ConcurrentDictionary<string, ValidatePathsCallback>();
|
private readonly ConcurrentDictionary<string, ValidatePathsCallback> validatePathsCallbackMap = new ConcurrentDictionary<string, ValidatePathsCallback>();
|
||||||
private ConnectionService connectionService;
|
private ConnectionService connectionService;
|
||||||
|
private ConnectedBindingQueue expandNodeQueue = new ConnectedBindingQueue(needsMetadata: false);
|
||||||
|
private static int DefaultExpandTimeout = 120000;
|
||||||
|
private string serviceName = "FileBrowser";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Signature for callback method that validates the selected file paths
|
/// Signature for callback method that validates the selected file paths
|
||||||
@@ -43,6 +45,7 @@ namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
|
|||||||
if (connectionService == null)
|
if (connectionService == null)
|
||||||
{
|
{
|
||||||
connectionService = ConnectionService.Instance;
|
connectionService = ConnectionService.Instance;
|
||||||
|
connectionService.RegisterConnectedQueue(this.serviceName, this.expandNodeQueue);
|
||||||
}
|
}
|
||||||
return connectionService;
|
return connectionService;
|
||||||
}
|
}
|
||||||
@@ -133,54 +136,98 @@ namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
|
|||||||
FileBrowserOperation removedOperation;
|
FileBrowserOperation removedOperation;
|
||||||
response.Succeeded = ownerToFileBrowserMap.TryRemove(fileBrowserParams.OwnerUri, out removedOperation);
|
response.Succeeded = ownerToFileBrowserMap.TryRemove(fileBrowserParams.OwnerUri, out removedOperation);
|
||||||
|
|
||||||
|
if (removedOperation != null && this.expandNodeQueue != null)
|
||||||
|
{
|
||||||
|
bool hasPendingQueueItems = this.expandNodeQueue.HasPendingQueueItems;
|
||||||
|
if (removedOperation.FileTreeCreated && !hasPendingQueueItems)
|
||||||
|
{
|
||||||
|
removedOperation.Dispose();
|
||||||
|
this.expandNodeQueue.CloseConnections(removedOperation.SqlConnection.DataSource, removedOperation.SqlConnection.Database, DefaultExpandTimeout);
|
||||||
|
}
|
||||||
|
else if (!removedOperation.FileTreeCreated)
|
||||||
|
{
|
||||||
|
removedOperation.Cancel();
|
||||||
|
}
|
||||||
|
else if (hasPendingQueueItems)
|
||||||
|
{
|
||||||
|
this.expandNodeQueue.StopQueueProcessor(DefaultExpandTimeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await requestContext.SendResult(response);
|
await requestContext.SendResult(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
this.expandNodeQueue.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
internal async Task RunFileBrowserOpenTask(FileBrowserOpenParams fileBrowserParams, RequestContext<bool> requestContext)
|
internal async Task RunFileBrowserOpenTask(FileBrowserOpenParams fileBrowserParams, RequestContext<bool> requestContext)
|
||||||
{
|
{
|
||||||
FileBrowserOpenedParams result = new FileBrowserOpenedParams();
|
FileBrowserOpenedParams result = new FileBrowserOpenedParams();
|
||||||
|
SqlConnection conn = null;
|
||||||
|
FileBrowserOperation browser = null;
|
||||||
|
bool isCancelRequested = false;
|
||||||
|
|
||||||
|
if (this.expandNodeQueue.IsCancelRequested)
|
||||||
|
{
|
||||||
|
this.expandNodeQueue.StartQueueProcessor();
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ConnectionInfo connInfo;
|
if (!fileBrowserParams.ChangeFilter)
|
||||||
this.ConnectionServiceInstance.TryFindConnection(fileBrowserParams.OwnerUri, out connInfo);
|
|
||||||
SqlConnection conn = null;
|
|
||||||
|
|
||||||
if (connInfo != null)
|
|
||||||
{
|
{
|
||||||
DbConnection dbConn;
|
ConnectionInfo connInfo;
|
||||||
connInfo.TryGetConnection(ConnectionType.Default, out dbConn);
|
this.ConnectionServiceInstance.TryFindConnection(fileBrowserParams.OwnerUri, out connInfo);
|
||||||
if (dbConn != null)
|
if (connInfo != null)
|
||||||
{
|
{
|
||||||
conn = ReliableConnectionHelper.GetAsSqlConnection(dbConn);
|
// Open new connection for each Open request
|
||||||
|
conn = ConnectionService.OpenSqlConnection(connInfo, this.serviceName);
|
||||||
|
browser = new FileBrowserOperation(conn, fileBrowserParams.ExpandPath, fileBrowserParams.FileFilters);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conn != null)
|
|
||||||
{
|
|
||||||
FileBrowserOperation browser = new FileBrowserOperation(conn, fileBrowserParams.ExpandPath, fileBrowserParams.FileFilters);
|
|
||||||
browser.PopulateFileTree();
|
|
||||||
|
|
||||||
ownerToFileBrowserMap.AddOrUpdate(fileBrowserParams.OwnerUri, browser, (key, value) => browser);
|
|
||||||
|
|
||||||
result.OwnerUri = fileBrowserParams.OwnerUri;
|
|
||||||
result.FileTree = browser.FileTree;
|
|
||||||
result.Succeeded = true;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result.Succeeded = false;
|
ownerToFileBrowserMap.TryGetValue(fileBrowserParams.OwnerUri, out browser);
|
||||||
|
if (browser != null)
|
||||||
|
{
|
||||||
|
browser.Initialize(fileBrowserParams.ExpandPath, fileBrowserParams.FileFilters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (browser != null)
|
||||||
|
{
|
||||||
|
ownerToFileBrowserMap.AddOrUpdate(fileBrowserParams.OwnerUri, browser, (key, value) => browser);
|
||||||
|
|
||||||
|
// Create file browser tree
|
||||||
|
browser.PopulateFileTree();
|
||||||
|
|
||||||
|
// Check if cancel was requested
|
||||||
|
if (browser.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
browser.Dispose();
|
||||||
|
isCancelRequested = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.OwnerUri = fileBrowserParams.OwnerUri;
|
||||||
|
result.FileTree = browser.FileTree;
|
||||||
|
result.Succeeded = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
result.Succeeded = false;
|
|
||||||
result.Message = ex.Message;
|
result.Message = ex.Message;
|
||||||
}
|
}
|
||||||
|
|
||||||
await requestContext.SendEvent(FileBrowserOpenedNotification.Type, result);
|
if (!isCancelRequested)
|
||||||
|
{
|
||||||
|
await requestContext.SendEvent(FileBrowserOpenedNotification.Type, result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task RunFileBrowserExpandTask(FileBrowserExpandParams fileBrowserParams, RequestContext<bool> requestContext)
|
internal async Task RunFileBrowserExpandTask(FileBrowserExpandParams fileBrowserParams, RequestContext<bool> requestContext)
|
||||||
@@ -188,13 +235,35 @@ namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
|
|||||||
FileBrowserExpandedParams result = new FileBrowserExpandedParams();
|
FileBrowserExpandedParams result = new FileBrowserExpandedParams();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
FileBrowserOperation browser;
|
FileBrowserOperation operation;
|
||||||
result.Succeeded = ownerToFileBrowserMap.TryGetValue(fileBrowserParams.OwnerUri, out browser);
|
ConnectionInfo connInfo;
|
||||||
if (result.Succeeded && browser != null)
|
result.Succeeded = ownerToFileBrowserMap.TryGetValue(fileBrowserParams.OwnerUri, out operation);
|
||||||
|
this.ConnectionServiceInstance.TryFindConnection(fileBrowserParams.OwnerUri, out connInfo);
|
||||||
|
|
||||||
|
if (result.Succeeded && operation != null && connInfo != null)
|
||||||
{
|
{
|
||||||
result.Children = browser.GetChildren(fileBrowserParams.ExpandPath).ToArray();
|
QueueItem queueItem = expandNodeQueue.QueueBindingOperation(
|
||||||
result.ExpandPath = fileBrowserParams.ExpandPath;
|
key: expandNodeQueue.AddConnectionContext(connInfo, this.serviceName),
|
||||||
result.OwnerUri = fileBrowserParams.OwnerUri;
|
bindingTimeout: DefaultExpandTimeout,
|
||||||
|
waitForLockTimeout: DefaultExpandTimeout,
|
||||||
|
bindOperation: (bindingContext, cancelToken) =>
|
||||||
|
{
|
||||||
|
result.ExpandPath = fileBrowserParams.ExpandPath;
|
||||||
|
result.Children = operation.GetChildren(fileBrowserParams.ExpandPath).ToArray();
|
||||||
|
result.OwnerUri = fileBrowserParams.OwnerUri;
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
|
queueItem.ItemProcessed.WaitOne();
|
||||||
|
|
||||||
|
if (this.expandNodeQueue.IsCancelRequested)
|
||||||
|
{
|
||||||
|
this.expandNodeQueue.CloseConnections(operation.SqlConnection.DataSource, operation.SqlConnection.Database, DefaultExpandTimeout);
|
||||||
|
}
|
||||||
|
else if (queueItem.GetResultAsT<FileBrowserExpandedParams>() != null)
|
||||||
|
{
|
||||||
|
result = queueItem.GetResultAsT<FileBrowserExpandedParams>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -238,7 +307,6 @@ namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
result.Succeeded = false;
|
|
||||||
result.Message = ex.Message;
|
result.Message = ex.Message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
|||||||
{
|
{
|
||||||
internal const int QueueThreadStackSize = 5 * 1024 * 1024;
|
internal const int QueueThreadStackSize = 5 * 1024 * 1024;
|
||||||
|
|
||||||
private CancellationTokenSource processQueueCancelToken = new CancellationTokenSource();
|
private CancellationTokenSource processQueueCancelToken = null;
|
||||||
|
|
||||||
private ManualResetEvent itemQueuedEvent = new ManualResetEvent(initialState: false);
|
private ManualResetEvent itemQueuedEvent = new ManualResetEvent(initialState: false);
|
||||||
|
|
||||||
@@ -44,8 +44,12 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
|||||||
public BindingQueue()
|
public BindingQueue()
|
||||||
{
|
{
|
||||||
this.BindingContextMap = new Dictionary<string, IBindingContext>();
|
this.BindingContextMap = new Dictionary<string, IBindingContext>();
|
||||||
|
this.StartQueueProcessor();
|
||||||
|
}
|
||||||
|
|
||||||
this.queueProcessorTask = StartQueueProcessor();
|
public void StartQueueProcessor()
|
||||||
|
{
|
||||||
|
this.queueProcessorTask = StartQueueProcessorAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -58,6 +62,18 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
|||||||
return this.queueProcessorTask.Wait(timeout);
|
return this.queueProcessorTask.Wait(timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if cancellation is requested
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public bool IsCancelRequested
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return this.processQueueCancelToken.IsCancellationRequested;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Queue a binding request item
|
/// Queue a binding request item
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -182,7 +198,7 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool HasPendingQueueItems
|
public bool HasPendingQueueItems
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
@@ -214,8 +230,14 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Starts the queue processing thread
|
/// Starts the queue processing thread
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private Task StartQueueProcessor()
|
private Task StartQueueProcessorAsync()
|
||||||
{
|
{
|
||||||
|
if (this.processQueueCancelToken != null)
|
||||||
|
{
|
||||||
|
this.processQueueCancelToken.Dispose();
|
||||||
|
}
|
||||||
|
this.processQueueCancelToken = new CancellationTokenSource();
|
||||||
|
|
||||||
return Task.Factory.StartNew(
|
return Task.Factory.StartNew(
|
||||||
ProcessQueue,
|
ProcessQueue,
|
||||||
this.processQueueCancelToken.Token,
|
this.processQueueCancelToken.Token,
|
||||||
@@ -368,6 +390,11 @@ namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
|||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
if (this.processQueueCancelToken != null)
|
||||||
|
{
|
||||||
|
this.processQueueCancelToken.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
if (itemQueuedEvent != null)
|
if (itemQueuedEvent != null)
|
||||||
{
|
{
|
||||||
itemQueuedEvent.Dispose();
|
itemQueuedEvent.Dispose();
|
||||||
|
|||||||
Reference in New Issue
Block a user