From 7ec33b1bc5c37544e1b59be65298544f780b6796 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 20 Mar 2009 13:02:22 +0000 Subject: [PATCH] Don't constantly create new threads to poll the clipboard. git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/branches/3.0@3892 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../Project/ICSharpCode.SharpDevelop.csproj | 1 + .../Src/TextEditor/ClipboardHandling.cs | 34 +++--- .../Base/Project/Src/Util/WorkerThread.cs | 105 ++++++++++++++++++ 3 files changed, 124 insertions(+), 16 deletions(-) create mode 100644 src/Main/Base/Project/Src/Util/WorkerThread.cs diff --git a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj index 013f671baa..159bc2e922 100644 --- a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj +++ b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj @@ -717,6 +717,7 @@ + ExtractInterfaceDialog.cs diff --git a/src/Main/Base/Project/Src/TextEditor/ClipboardHandling.cs b/src/Main/Base/Project/Src/TextEditor/ClipboardHandling.cs index 82a2117efa..83575e204c 100644 --- a/src/Main/Base/Project/Src/TextEditor/ClipboardHandling.cs +++ b/src/Main/Base/Project/Src/TextEditor/ClipboardHandling.cs @@ -5,9 +5,10 @@ // $Revision$ // -using ICSharpCode.Core.WinForms; +using ICSharpCode.SharpDevelop.Util; using System; using System.Threading; +using ICSharpCode.Core.WinForms; using ICSharpCode.SharpDevelop.Gui; namespace ICSharpCode.SharpDevelop.DefaultEditor @@ -36,7 +37,7 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor UpdateClipboardContainsText(); } - static bool clipboardContainsText; + static volatile bool clipboardContainsText; public static bool GetClipboardContainsText() { @@ -47,28 +48,29 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor return clipboardContainsText; } - static volatile Thread updateThread; + static WorkerThread workerThread; + static IAsyncResult currentWorker; static void UpdateClipboardContainsText() { - if (updateThread != null) + if (currentWorker != null && !currentWorker.IsCompleted) return; - Thread t = new Thread(new ThreadStart(DoUpdate)); - t.SetApartmentState(ApartmentState.STA); - t.IsBackground = true; - t.Name = "clipboard access"; - updateThread = t; - t.Start(); - t.Join(50); // wait a few ms in case the clipboard can be accessed without problems + if (workerThread == null) { + workerThread = new WorkerThread(); + Thread t = new Thread(new ThreadStart(workerThread.RunLoop)); + t.SetApartmentState(ApartmentState.STA); + t.IsBackground = true; + t.Name = "clipboard access"; + t.Start(); + } + currentWorker = workerThread.Enqueue(DoUpdate); + // wait a few ms in case the clipboard can be accessed without problems + currentWorker.AsyncWaitHandle.WaitOne(50); } static void DoUpdate() { - try { - clipboardContainsText = ClipboardWrapper.ContainsText; - } finally { - updateThread = null; - } + clipboardContainsText = ClipboardWrapper.ContainsText; } } } diff --git a/src/Main/Base/Project/Src/Util/WorkerThread.cs b/src/Main/Base/Project/Src/Util/WorkerThread.cs new file mode 100644 index 0000000000..ad60da065b --- /dev/null +++ b/src/Main/Base/Project/Src/Util/WorkerThread.cs @@ -0,0 +1,105 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; +using System.Threading; + +namespace ICSharpCode.SharpDevelop.Util +{ + /// + /// A worker thread that normally sleeps, but can run a queue of commands. + /// + public class WorkerThread + { + sealed class AsyncTask : IAsyncResult + { + internal readonly ManualResetEvent manualResetEvent = new ManualResetEvent(false); + internal readonly Action method; + volatile bool isCompleted; + + internal AsyncTask(Action method) + { + this.method = method; + } + + internal void SetCompleted() + { + isCompleted = true; + manualResetEvent.Set(); + } + + public bool IsCompleted { + get { return isCompleted; } + } + + public WaitHandle AsyncWaitHandle { + get { return manualResetEvent; } + } + + public object AsyncState { get; set; } + public bool CompletedSynchronously { get { return false; } } + } + + /// + /// Runs on the worker thread. + /// + /// The method to run. + /// + public IAsyncResult Enqueue(Action method) + { + if (method == null) + throw new ArgumentNullException("method"); + AsyncTask task = new AsyncTask(method); + lock (lockObject) { + taskQueue.Enqueue(task); + Monitor.Pulse(lockObject); + } + return task; + } + + readonly object lockObject = new object(); + Queue taskQueue = new Queue(); + bool workerRunning = false; + + /// + /// Runs the worker thread loop on the current thread. + /// + public void RunLoop() + { + lock (lockObject) { + if (workerRunning) + throw new InvalidOperationException("There already is a worker running"); + workerRunning = true; + } + try { + while (workerRunning) { + AsyncTask task; + lock (lockObject) { + while (taskQueue.Count == 0) + Monitor.Wait(lockObject); + task = taskQueue.Dequeue(); + } + task.method(); + task.SetCompleted(); + } + } finally { + lock (lockObject) { + workerRunning = false; + } + } + } + + /// + /// Exits running the worker thread. + /// + public void ExitWorkerThread() + { + Enqueue(delegate { workerRunning = false; }); + } + } +}