diff --git a/src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs b/src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs
index f272fe4750..5564195a8d 100644
--- a/src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs
+++ b/src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs
@@ -227,6 +227,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
}
AddToTypeSystemTranslationTable(this.currentAssembly, assemblyDefinition);
+ currentAssembly.Freeze();
var result = this.currentAssembly;
this.currentAssembly = null;
diff --git a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
index 872ae82fee..eabd7c0c11 100644
--- a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
+++ b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
@@ -745,11 +745,13 @@
+
+
diff --git a/src/Main/Base/Project/Src/Services/IShutdownService.cs b/src/Main/Base/Project/Src/Services/IShutdownService.cs
index 6f5bd90224..21a06cb440 100644
--- a/src/Main/Base/Project/Src/Services/IShutdownService.cs
+++ b/src/Main/Base/Project/Src/Services/IShutdownService.cs
@@ -49,9 +49,20 @@ namespace ICSharpCode.SharpDevelop
///
/// Gets a cancellation token that gets signalled when SharpDevelop is shutting down.
+ ///
+ /// This cancellation token may be used to stop background calculations.
///
CancellationToken ShutdownToken { get; }
+ ///
+ /// Gets a cancellation token that gets signalled a couple of seconds after the ShutdownToken.
+ ///
+ /// This cancellation token may be used to stop background calculations that should run
+ /// for a limited time after SharpDevelop is closed (e.g. saving state in caches
+ /// - work that should better run even though we're shutting down, but shouldn't take too long either)
+ ///
+ CancellationToken DelayedShutdownToken { get; }
+
///
/// Adds a background task on which SharpDevelop should wait on shutdown.
///
diff --git a/src/Main/Base/Project/Src/Services/ParserService/ParseProjectContent.cs b/src/Main/Base/Project/Src/Services/ParserService/ParseProjectContent.cs
index 53d0a7b9b6..327001fc05 100644
--- a/src/Main/Base/Project/Src/Services/ParserService/ParseProjectContent.cs
+++ b/src/Main/Base/Project/Src/Services/ParserService/ParseProjectContent.cs
@@ -121,7 +121,7 @@ namespace ICSharpCode.SharpDevelop.Parser
} else {
RemoveCache(cacheFileName);
}
- }, SD.ShutdownService.ShutdownToken);
+ }, SD.ShutdownService.DelayedShutdownToken);
SD.ShutdownService.AddBackgroundTask(task);
return task;
}
diff --git a/src/Main/Base/Project/Src/Util/CustomThreadPoolTaskScheduler.cs b/src/Main/Base/Project/Src/Util/CustomThreadPoolTaskScheduler.cs
new file mode 100644
index 0000000000..d3dcbd7686
--- /dev/null
+++ b/src/Main/Base/Project/Src/Util/CustomThreadPoolTaskScheduler.cs
@@ -0,0 +1,67 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace ICSharpCode.SharpDevelop.Util
+{
+ ///
+ /// A task scheduler that manages its own thread pool.
+ ///
+ public class CustomThreadPoolTaskScheduler : SimpleTaskScheduler
+ {
+ int currentThreadCount;
+ readonly int maxThreadCount;
+
+ public CustomThreadPoolTaskScheduler(int maxThreadCount)
+ {
+ this.maxThreadCount = maxThreadCount;
+ }
+
+ public override int MaximumConcurrencyLevel {
+ get { return maxThreadCount; }
+ }
+
+ protected override void QueueTask(Task task)
+ {
+ base.QueueTask(task);
+ if (IncrementThreadCount()) {
+ // Successfully incremented the thread count, we may start a thread
+ StartThread(RunThread);
+ return;
+ }
+ }
+
+ protected virtual void StartThread(ThreadStart start)
+ {
+ var t = new Thread(RunThread);
+ t.IsBackground = true;
+ t.Start();
+ }
+
+ bool IncrementThreadCount()
+ {
+ int c = Volatile.Read(ref currentThreadCount);
+ while (c < maxThreadCount) {
+ if (Interlocked.CompareExchange(ref currentThreadCount, c + 1, c) == c) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void RunThread()
+ {
+ do {
+ // Run tasks while they are available:
+ while (TryRunNextTask());
+ // Decrement the thread count:
+ Interlocked.Decrement(ref currentThreadCount);
+ // Tasks might have been added while we were decrementing the thread count,
+ // so if the queue isn't empty anymore, resume this thread
+ } while(ScheduledTaskCount > 0 && IncrementThreadCount());
+ }
+ }
+}
diff --git a/src/Main/Base/Project/Src/Util/IOTaskScheduler.cs b/src/Main/Base/Project/Src/Util/IOTaskScheduler.cs
index 3f765c0ce5..1399f7a377 100644
--- a/src/Main/Base/Project/Src/Util/IOTaskScheduler.cs
+++ b/src/Main/Base/Project/Src/Util/IOTaskScheduler.cs
@@ -12,9 +12,17 @@ namespace ICSharpCode.SharpDevelop.Util
///
public class IOTaskScheduler
{
- // TODO: use a limited-concurrency scheduler instead
+ static readonly CustomThreadPoolTaskScheduler scheduler = new CustomThreadPoolTaskScheduler(
+ Math.Min(Environment.ProcessorCount, 2));
+
+ static readonly TaskFactory factory = new TaskFactory(scheduler);
+
+ public static TaskScheduler Scheduler {
+ get { return scheduler; }
+ }
+
public static TaskFactory Factory {
- get { return Task.Factory; }
+ get { return factory; }
}
}
}
diff --git a/src/Main/Base/Project/Src/Util/SimpleTaskScheduler.cs b/src/Main/Base/Project/Src/Util/SimpleTaskScheduler.cs
new file mode 100644
index 0000000000..0da2b9f97b
--- /dev/null
+++ b/src/Main/Base/Project/Src/Util/SimpleTaskScheduler.cs
@@ -0,0 +1,82 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace ICSharpCode.SharpDevelop.Util
+{
+ ///
+ /// A simple scheduler that adds tasks to a queue.
+ /// This scheduler does not create any worker threads on its own,
+ /// but requires external code to call .
+ ///
+ public class SimpleTaskScheduler : TaskScheduler, IDisposable
+ {
+ [ThreadStatic]
+ static SimpleTaskScheduler activeScheduler;
+
+ BlockingCollection queue = new BlockingCollection();
+
+ protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
+ {
+ return activeScheduler == this && base.TryExecuteTask(task);
+ }
+
+ protected override void QueueTask(Task task)
+ {
+ queue.Add(task);
+ }
+
+ protected override IEnumerable GetScheduledTasks()
+ {
+ return queue;
+ }
+
+ protected int ScheduledTaskCount {
+ get { return queue.Count; }
+ }
+
+ ///
+ /// Runs the next task in the queue.
+ /// If no task is available, this method will block.
+ ///
+ /// Cancellation token that can be used to cancel
+ /// waiting for a task to become available. It cannot be used to cancel task execution!
+ public void RunNextTask(CancellationToken cancellationToken = default(CancellationToken))
+ {
+ Task task = queue.Take(cancellationToken);
+ RunTask(task);
+ }
+
+ public bool TryRunNextTask()
+ {
+ Task task;
+ if (queue.TryTake(out task)) {
+ RunTask(task);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ void RunTask(Task task)
+ {
+ var oldActiveScheduler = activeScheduler;
+ activeScheduler = this;
+ try {
+ base.TryExecuteTask(task);
+ } finally {
+ activeScheduler = oldActiveScheduler;
+ }
+ }
+
+ public virtual void Dispose()
+ {
+ queue.Dispose();
+ }
+ }
+}
diff --git a/src/Main/SharpDevelop/Parser/AssemblyParserService.cs b/src/Main/SharpDevelop/Parser/AssemblyParserService.cs
index 1e9da54474..6d024387d9 100644
--- a/src/Main/SharpDevelop/Parser/AssemblyParserService.cs
+++ b/src/Main/SharpDevelop/Parser/AssemblyParserService.cs
@@ -160,10 +160,9 @@ namespace ICSharpCode.SharpDevelop.Parser
}
}
l.CancellationToken = cancellationToken;
- l.InterningProvider = null;
pc = l.LoadAssembly(asm);
- //SaveToCacheAsync(cacheFileName, lastWriteTime, pc).FireAndForget();
- SaveToCache(cacheFileName, lastWriteTime, pc);
+ SaveToCacheAsync(cacheFileName, lastWriteTime, pc).FireAndForget();
+ //SaveToCache(cacheFileName, lastWriteTime, pc);
return pc;
}
diff --git a/src/Main/SharpDevelop/Workbench/ShutdownService.cs b/src/Main/SharpDevelop/Workbench/ShutdownService.cs
index 237822ede3..9b5513f2d9 100644
--- a/src/Main/SharpDevelop/Workbench/ShutdownService.cs
+++ b/src/Main/SharpDevelop/Workbench/ShutdownService.cs
@@ -13,15 +13,21 @@ namespace ICSharpCode.SharpDevelop.Workbench
{
sealed class ShutdownService : IShutdownService
{
- CancellationTokenSource cts = new CancellationTokenSource();
+ CancellationTokenSource shutdownCTS = new CancellationTokenSource();
+ CancellationTokenSource delayedShutdownCTS = new CancellationTokenSource();
public CancellationToken ShutdownToken {
- get { return cts.Token; }
+ get { return shutdownCTS.Token; }
+ }
+
+ public CancellationToken DelayedShutdownToken {
+ get { return delayedShutdownCTS.Token; }
}
internal void SignalShutdownToken()
{
- cts.Cancel();
+ shutdownCTS.Cancel();
+ delayedShutdownCTS.CancelAfter(2000);
}
public bool Shutdown()