|
|
@ -54,6 +54,10 @@ namespace ICSharpCode.SharpDevelop.Parser |
|
|
|
List<ProjectEntry> entries = new List<ProjectEntry> { default(ProjectEntry) }; |
|
|
|
List<ProjectEntry> entries = new List<ProjectEntry> { default(ProjectEntry) }; |
|
|
|
ITextSourceVersion currentVersion; |
|
|
|
ITextSourceVersion currentVersion; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Lock ordering: runningAsyncParseLock, rwLock, lock(parserService.fileEntryDict)
|
|
|
|
|
|
|
|
// (to avoid deadlocks, the locks may only be acquired in this order)
|
|
|
|
|
|
|
|
internal readonly ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); |
|
|
|
|
|
|
|
|
|
|
|
public ParserServiceEntry(ParserService parserService, FileName fileName) |
|
|
|
public ParserServiceEntry(ParserService parserService, FileName fileName) |
|
|
|
{ |
|
|
|
{ |
|
|
|
this.parserService = parserService; |
|
|
|
this.parserService = parserService; |
|
|
@ -68,6 +72,7 @@ namespace ICSharpCode.SharpDevelop.Parser |
|
|
|
|
|
|
|
|
|
|
|
int FindIndexForProject(IProject parentProject) |
|
|
|
int FindIndexForProject(IProject parentProject) |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
Debug.Assert(rwLock.IsReadLockHeld || rwLock.IsUpgradeableReadLockHeld || rwLock.IsWriteLockHeld); |
|
|
|
if (parentProject == null) |
|
|
|
if (parentProject == null) |
|
|
|
return 0; |
|
|
|
return 0; |
|
|
|
for (int i = 0; i < entries.Count; i++) { |
|
|
|
for (int i = 0; i < entries.Count; i++) { |
|
|
@ -81,7 +86,8 @@ namespace ICSharpCode.SharpDevelop.Parser |
|
|
|
public void AddOwnerProject(IProject project, bool isLinkedFile) |
|
|
|
public void AddOwnerProject(IProject project, bool isLinkedFile) |
|
|
|
{ |
|
|
|
{ |
|
|
|
Debug.Assert(project != null); |
|
|
|
Debug.Assert(project != null); |
|
|
|
lock (this) { |
|
|
|
rwLock.EnterWriteLock(); |
|
|
|
|
|
|
|
try { |
|
|
|
if (FindIndexForProject(project) >= 0) |
|
|
|
if (FindIndexForProject(project) >= 0) |
|
|
|
throw new InvalidOperationException("The project alreadys owns the file"); |
|
|
|
throw new InvalidOperationException("The project alreadys owns the file"); |
|
|
|
ProjectEntry newEntry = new ProjectEntry(project, null, null); |
|
|
|
ProjectEntry newEntry = new ProjectEntry(project, null, null); |
|
|
@ -92,6 +98,8 @@ namespace ICSharpCode.SharpDevelop.Parser |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
entries.Insert(0, newEntry); |
|
|
|
entries.Insert(0, newEntry); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} finally { |
|
|
|
|
|
|
|
rwLock.ExitWriteLock(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -100,7 +108,8 @@ namespace ICSharpCode.SharpDevelop.Parser |
|
|
|
Debug.Assert(project != null); |
|
|
|
Debug.Assert(project != null); |
|
|
|
ProjectEntry oldEntry; |
|
|
|
ProjectEntry oldEntry; |
|
|
|
bool removedLastOwner = false; |
|
|
|
bool removedLastOwner = false; |
|
|
|
lock (this) { |
|
|
|
rwLock.EnterWriteLock(); |
|
|
|
|
|
|
|
try { |
|
|
|
int index = FindIndexForProject(project); |
|
|
|
int index = FindIndexForProject(project); |
|
|
|
if (index < 0) |
|
|
|
if (index < 0) |
|
|
|
throw new InvalidOperationException("The project does not own the file"); |
|
|
|
throw new InvalidOperationException("The project does not own the file"); |
|
|
@ -111,6 +120,8 @@ namespace ICSharpCode.SharpDevelop.Parser |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
entries.RemoveAt(index); |
|
|
|
entries.RemoveAt(index); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} finally { |
|
|
|
|
|
|
|
rwLock.ExitWriteLock(); |
|
|
|
} |
|
|
|
} |
|
|
|
if (oldEntry.UnresolvedFile != null) { |
|
|
|
if (oldEntry.UnresolvedFile != null) { |
|
|
|
project.OnParseInformationUpdated(new ParseInformationEventArgs(project, oldEntry.UnresolvedFile, null)); |
|
|
|
project.OnParseInformationUpdated(new ParseInformationEventArgs(project, oldEntry.UnresolvedFile, null)); |
|
|
@ -128,6 +139,7 @@ namespace ICSharpCode.SharpDevelop.Parser |
|
|
|
/// </summary>
|
|
|
|
/// </summary>
|
|
|
|
int CompareVersions(ITextSourceVersion newVersion) |
|
|
|
int CompareVersions(ITextSourceVersion newVersion) |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
Debug.Assert(rwLock.IsReadLockHeld || rwLock.IsUpgradeableReadLockHeld || rwLock.IsWriteLockHeld); |
|
|
|
if (currentVersion != null && newVersion != null && currentVersion.BelongsToSameDocumentAs(newVersion)) |
|
|
|
if (currentVersion != null && newVersion != null && currentVersion.BelongsToSameDocumentAs(newVersion)) |
|
|
|
return currentVersion.CompareAge(newVersion); |
|
|
|
return currentVersion.CompareAge(newVersion); |
|
|
|
else |
|
|
|
else |
|
|
@ -137,7 +149,8 @@ namespace ICSharpCode.SharpDevelop.Parser |
|
|
|
#region Expire Cache + GetExistingUnresolvedFile + GetCachedParseInformation
|
|
|
|
#region Expire Cache + GetExistingUnresolvedFile + GetCachedParseInformation
|
|
|
|
public void ExpireCache() |
|
|
|
public void ExpireCache() |
|
|
|
{ |
|
|
|
{ |
|
|
|
lock (this) { |
|
|
|
rwLock.EnterWriteLock(); |
|
|
|
|
|
|
|
try { |
|
|
|
if (PrimaryProject == null) { |
|
|
|
if (PrimaryProject == null) { |
|
|
|
parserService.RemoveEntry(this); |
|
|
|
parserService.RemoveEntry(this); |
|
|
|
} else { |
|
|
|
} else { |
|
|
@ -148,12 +161,15 @@ namespace ICSharpCode.SharpDevelop.Parser |
|
|
|
} |
|
|
|
} |
|
|
|
// force re-parse on next ParseFile() call even if unchanged
|
|
|
|
// force re-parse on next ParseFile() call even if unchanged
|
|
|
|
this.currentVersion = null; |
|
|
|
this.currentVersion = null; |
|
|
|
|
|
|
|
} finally { |
|
|
|
|
|
|
|
rwLock.ExitWriteLock(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public IUnresolvedFile GetExistingUnresolvedFile(ITextSourceVersion version, IProject parentProject) |
|
|
|
public IUnresolvedFile GetExistingUnresolvedFile(ITextSourceVersion version, IProject parentProject) |
|
|
|
{ |
|
|
|
{ |
|
|
|
lock (this) { |
|
|
|
rwLock.EnterReadLock(); |
|
|
|
|
|
|
|
try { |
|
|
|
if (version != null && CompareVersions(version) != 0) { |
|
|
|
if (version != null && CompareVersions(version) != 0) { |
|
|
|
return null; |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
@ -161,12 +177,15 @@ namespace ICSharpCode.SharpDevelop.Parser |
|
|
|
if (index < 0) |
|
|
|
if (index < 0) |
|
|
|
return null; |
|
|
|
return null; |
|
|
|
return entries[index].UnresolvedFile; |
|
|
|
return entries[index].UnresolvedFile; |
|
|
|
|
|
|
|
} finally { |
|
|
|
|
|
|
|
rwLock.ExitReadLock(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public ParseInformation GetCachedParseInformation(ITextSourceVersion version, IProject parentProject) |
|
|
|
public ParseInformation GetCachedParseInformation(ITextSourceVersion version, IProject parentProject) |
|
|
|
{ |
|
|
|
{ |
|
|
|
lock (this) { |
|
|
|
rwLock.EnterReadLock(); |
|
|
|
|
|
|
|
try { |
|
|
|
if (version != null && CompareVersions(version) != 0) { |
|
|
|
if (version != null && CompareVersions(version) != 0) { |
|
|
|
return null; |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
@ -174,6 +193,8 @@ namespace ICSharpCode.SharpDevelop.Parser |
|
|
|
if (index < 0) |
|
|
|
if (index < 0) |
|
|
|
return null; |
|
|
|
return null; |
|
|
|
return entries[index].CachedParseInformation; |
|
|
|
return entries[index].CachedParseInformation; |
|
|
|
|
|
|
|
} finally { |
|
|
|
|
|
|
|
rwLock.ExitReadLock(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
@ -218,7 +239,8 @@ namespace ICSharpCode.SharpDevelop.Parser |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
ProjectEntry result; |
|
|
|
ProjectEntry result; |
|
|
|
lock (this) { |
|
|
|
rwLock.EnterUpgradeableReadLock(); |
|
|
|
|
|
|
|
try { |
|
|
|
int index = FindIndexForProject(parentProject); |
|
|
|
int index = FindIndexForProject(parentProject); |
|
|
|
int versionComparison = CompareVersions(fileContent.Version); |
|
|
|
int versionComparison = CompareVersions(fileContent.Version); |
|
|
|
if (versionComparison > 0 || index < 0) { |
|
|
|
if (versionComparison > 0 || index < 0) { |
|
|
@ -253,6 +275,8 @@ namespace ICSharpCode.SharpDevelop.Parser |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Only if all parse runs succeeded, register the parse information.
|
|
|
|
// Only if all parse runs succeeded, register the parse information.
|
|
|
|
|
|
|
|
rwLock.EnterWriteLock(); |
|
|
|
|
|
|
|
try { |
|
|
|
currentVersion = fileContent.Version; |
|
|
|
currentVersion = fileContent.Version; |
|
|
|
for (int i = 0; i < entries.Count; i++) { |
|
|
|
for (int i = 0; i < entries.Count; i++) { |
|
|
|
if (fullParseInformationRequested || (entries[i].CachedParseInformation != null && results[i].NewParseInformation.IsFullParseInformation)) |
|
|
|
if (fullParseInformationRequested || (entries[i].CachedParseInformation != null && results[i].NewParseInformation.IsFullParseInformation)) |
|
|
@ -264,13 +288,19 @@ namespace ICSharpCode.SharpDevelop.Parser |
|
|
|
parserService.RaiseParseInformationUpdated(results[i]); |
|
|
|
parserService.RaiseParseInformationUpdated(results[i]); |
|
|
|
} |
|
|
|
} |
|
|
|
result = entries[index]; |
|
|
|
result = entries[index]; |
|
|
|
} // exit lock
|
|
|
|
} finally { |
|
|
|
|
|
|
|
rwLock.ExitWriteLock(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} finally { |
|
|
|
|
|
|
|
rwLock.ExitUpgradeableReadLock(); |
|
|
|
|
|
|
|
} |
|
|
|
parserService.RegisterForCacheExpiry(this); |
|
|
|
parserService.RegisterForCacheExpiry(this); |
|
|
|
return result; |
|
|
|
return result; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
ParseInformation ParseWithExceptionHandling(ITextSource fileContent, bool fullParseInformationRequested, IProject project, CancellationToken cancellationToken) |
|
|
|
ParseInformation ParseWithExceptionHandling(ITextSource fileContent, bool fullParseInformationRequested, IProject project, CancellationToken cancellationToken) |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
Debug.Assert(rwLock.IsUpgradeableReadLockHeld && !rwLock.IsWriteLockHeld); |
|
|
|
#if DEBUG
|
|
|
|
#if DEBUG
|
|
|
|
if (Debugger.IsAttached) |
|
|
|
if (Debugger.IsAttached) |
|
|
|
return parser.Parse(fileName, fileContent, fullParseInformationRequested, project, cancellationToken); |
|
|
|
return parser.Parse(fileName, fileContent, fullParseInformationRequested, project, cancellationToken); |
|
|
@ -285,6 +315,8 @@ namespace ICSharpCode.SharpDevelop.Parser |
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region ParseAsync
|
|
|
|
#region ParseAsync
|
|
|
|
|
|
|
|
/// <summary>lock object for protecting the runningAsyncParse* fields</summary>
|
|
|
|
|
|
|
|
object runningAsyncParseLock = new object(); |
|
|
|
Task<ProjectEntry> runningAsyncParseTask; |
|
|
|
Task<ProjectEntry> runningAsyncParseTask; |
|
|
|
ITextSourceVersion runningAsyncParseFileContentVersion; |
|
|
|
ITextSourceVersion runningAsyncParseFileContentVersion; |
|
|
|
IProject runningAsyncParseProject; |
|
|
|
IProject runningAsyncParseProject; |
|
|
@ -324,10 +356,12 @@ namespace ICSharpCode.SharpDevelop.Parser |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
Task<ProjectEntry> task; |
|
|
|
Task<ProjectEntry> task; |
|
|
|
lock (this) { |
|
|
|
lock (runningAsyncParseLock) { |
|
|
|
if (fileContent != null) { |
|
|
|
if (fileContent != null) { |
|
|
|
// Optimization:
|
|
|
|
// Optimization:
|
|
|
|
// don't start a background task if fileContent was specified and up-to-date parse info is available
|
|
|
|
// don't start a background task if fileContent was specified and up-to-date parse info is available
|
|
|
|
|
|
|
|
rwLock.EnterReadLock(); |
|
|
|
|
|
|
|
try { |
|
|
|
int index = FindIndexForProject(parentProject); |
|
|
|
int index = FindIndexForProject(parentProject); |
|
|
|
int versionComparison = CompareVersions(fileContent.Version); |
|
|
|
int versionComparison = CompareVersions(fileContent.Version); |
|
|
|
if (versionComparison == 0 && index >= 0) { |
|
|
|
if (versionComparison == 0 && index >= 0) { |
|
|
@ -338,6 +372,9 @@ namespace ICSharpCode.SharpDevelop.Parser |
|
|
|
return Task.FromResult(entries[index]); |
|
|
|
return Task.FromResult(entries[index]); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} finally { |
|
|
|
|
|
|
|
rwLock.ExitReadLock(); |
|
|
|
|
|
|
|
} |
|
|
|
// Optimization:
|
|
|
|
// Optimization:
|
|
|
|
// if an equivalent task is already running, return that one instead
|
|
|
|
// if an equivalent task is already running, return that one instead
|
|
|
|
if (runningAsyncParseTask != null && (!requestFullParseInformation || runningAsyncParseFullInfoRequested) |
|
|
|
if (runningAsyncParseTask != null && (!requestFullParseInformation || runningAsyncParseFullInfoRequested) |
|
|
@ -356,7 +393,7 @@ namespace ICSharpCode.SharpDevelop.Parser |
|
|
|
} |
|
|
|
} |
|
|
|
return DoParse(fileContent, parentProject, requestFullParseInformation, cancellationToken); |
|
|
|
return DoParse(fileContent, parentProject, requestFullParseInformation, cancellationToken); |
|
|
|
} finally { |
|
|
|
} finally { |
|
|
|
lock (this) { |
|
|
|
lock (runningAsyncParseLock) { |
|
|
|
runningAsyncParseTask = null; |
|
|
|
runningAsyncParseTask = null; |
|
|
|
runningAsyncParseFileContentVersion = null; |
|
|
|
runningAsyncParseFileContentVersion = null; |
|
|
|
runningAsyncParseProject = null; |
|
|
|
runningAsyncParseProject = null; |
|
|
@ -383,7 +420,8 @@ namespace ICSharpCode.SharpDevelop.Parser |
|
|
|
throw new ArgumentNullException("unresolvedFile"); |
|
|
|
throw new ArgumentNullException("unresolvedFile"); |
|
|
|
FreezableHelper.Freeze(unresolvedFile); |
|
|
|
FreezableHelper.Freeze(unresolvedFile); |
|
|
|
var newParseInfo = new ParseInformation(unresolvedFile, null, false); |
|
|
|
var newParseInfo = new ParseInformation(unresolvedFile, null, false); |
|
|
|
lock (this) { |
|
|
|
rwLock.EnterWriteLock(); |
|
|
|
|
|
|
|
try { |
|
|
|
int index = FindIndexForProject(project); |
|
|
|
int index = FindIndexForProject(project); |
|
|
|
if (index >= 0) { |
|
|
|
if (index >= 0) { |
|
|
|
currentVersion = null; |
|
|
|
currentVersion = null; |
|
|
@ -392,6 +430,8 @@ namespace ICSharpCode.SharpDevelop.Parser |
|
|
|
project.OnParseInformationUpdated(args); |
|
|
|
project.OnParseInformationUpdated(args); |
|
|
|
parserService.RaiseParseInformationUpdated(args); |
|
|
|
parserService.RaiseParseInformationUpdated(args); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} finally { |
|
|
|
|
|
|
|
rwLock.ExitWriteLock(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|