// /** // ******** // * // * ORIGIONAL CODE BASE IS Copyright (C) 2006-2010 by Alphons van der Heijden // * The code was donated on 4/28/2010 by Alphons van der Heijden // * To Brandon'Dimentox Travanti' Husbands & Malcolm J. Kudra which in turn Liscense under the GPLv2. // * In agreement to Alphons van der Heijden wishes. // * // * The community would like to thank Alphons for all of his hard work, blood sweat and tears. // * Without his work the community would be stuck with crappy editors. // * // * The source code in this file ("Source Code") is provided by The LSLEditor Group // * to you under the terms of the GNU General Public License, version 2.0 // * ("GPL"), unless you have obtained a separate licensing agreement // * ("Other License"), formally executed by you and Linden Lab. Terms of // * the GPL can be found in the gplv2.txt document. // * // ******** // * GPLv2 Header // ******** // * LSLEditor, a External editor for the LSL Language. // * Copyright (C) 2010 The LSLEditor Group. // // * This program is free software; you can redistribute it and/or // * modify it under the terms of the GNU General Public License // * as published by the Free Software Foundation; either version 2 // * of the License, or (at your option) any later version. // * // * This program is distributed in the hope that it will be useful, // * but WITHOUT ANY WARRANTY; without even the implied warranty of // * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // * GNU General Public License for more details. // * // * You should have received a copy of the GNU General Public License // * along with this program; if not, write to the Free Software // * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // ******** // * // * The above copyright notice and this permission notice shall be included in all // * copies or substantial portions of the Software. // * // ******** // */ using System; using System.Reflection; using System.Collections; using System.Diagnostics; using System.Threading; // http://www.codeproject.com/csharp/messageloop.asp namespace LSLEditor.Helpers { /// /// Represents an object that performs a certain action asynchronously, by using an internal buffer queue /// and one internal thread. /// public class TaskQueue : IDisposable { #region Member Variables /// Reference to the thread used to empty the queue private Thread WorkerThread; /// Internal queue that serves as buffer for required actions private Queue Tasks; /// Used to signal the thread when a new object is added to the queue private AutoResetEvent SignalNewTask; /// Flag that notifies that the object should be disposed private bool stop; #endregion Member Variables #region Constructor /// Creates a new buffered object public TaskQueue() { WorkerThread = null; // Make sure the queue is synchronized. This is required because items are added to the queue // from a different thread than the thread that empties the queue Tasks = Queue.Synchronized(new Queue()); SignalNewTask = new AutoResetEvent(false); stop = false; } #endregion Ctor #region Public Methods public void Start() { Stop(); stop = false; Tasks.Clear(); WorkerThread = new Thread(new ThreadStart(Worker)); WorkerThread.IsBackground = true; WorkerThread.Start(); } public void Stop() { if(WorkerThread!=null) { WorkerThread.Abort(); if (!WorkerThread.Join(2000)) { // problems System.Windows.Forms.MessageBox.Show("TaskQueue thread not Aborted", "Oops..."); } WorkerThread = null; } } public void Invoke(object ActiveObject,string MethodName, params object[] args) { if (ActiveObject == null) return; try { // Add the object to the internal buffer Tasks.Enqueue(new Task(ActiveObject, MethodName, args)); // Signal the internal thread that there is some new object in the buffer SignalNewTask.Set(); } catch (Exception e) { Trace.WriteLine(string.Format("I An exception occurred in TaskQueue.Invoke: {0}", e.Message)); // Since the exception was not actually handled and only logged - propagate it throw; } } #endregion Public Methods #region Private Methods /// Method executed by the internal thread to empty the queue private void Worker() { Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US", false); Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US", false); while (!stop) { try { // Note: this code is safe (i.e. performing the .Count and .Dequeue not inside a lock) // because there is only one thread emptying the queue. // Even if .Count returns 0, and before Dequeue is called a new object is added to the Queue // then still the system will behave nicely: the next if statement will return false and // since this is run in an endless loop, in the next iteration we will have .Count > 0. if (Tasks.Count > 0) { (Tasks.Dequeue() as Task).Execute(); } // Wait until new objects are received or Dispose was called if (Tasks.Count == 0) { SignalNewTask.WaitOne(); } } catch (ThreadAbortException) { Trace.WriteLine("TaskQueue.Worker: ThreadAbortException, no problem"); } catch (Exception e) { Trace.WriteLine(string.Format("TaskQueue.Worker: {0}", e.Message)); // Since the exception was not actually handled and only logged - propagate it throw; } } } #endregion Private Methods #region IDisposable Members and Dispose Pattern private bool disposed = false; ~TaskQueue() { Dispose(false); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!this.disposed) { if (disposing) { try { stop = true; SignalNewTask.Set(); } catch (Exception e) { Trace.WriteLine(string.Format("An exception occurred in MessageLoop.AddToBuffer: {0}", e.Message)); // Since the exception was not actually handled and only logged - propagate it throw; } } this.disposed = true; } } #endregion IDisposable Members and Dispose Pattern #region Task /// The tasks being saved in the queue private class Task { private object ActiveObject; private object[] args; public string MethodName; public Task(object ActiveObject, string MethodName, params object[] args) { this.ActiveObject = ActiveObject; this.MethodName = MethodName; this.args = args; } public void Execute() { try { MethodInfo mi = ActiveObject.GetType().GetMethod(MethodName, BindingFlags.Public | BindingFlags.Instance | //BindingFlags.DeclaredOnly | BindingFlags.NonPublic ); mi.Invoke(ActiveObject, args); } catch (ThreadAbortException) { Trace.WriteLine("TaskQueue.Task.Execute: ThreadAbortException, no problem"); } catch (Exception exception) { Exception innerException = exception.InnerException; if (innerException == null) innerException = exception; string strMessage = OopsFormatter.ApplyFormatting(innerException.Message); string strStackTrace = OopsFormatter.ApplyFormatting(innerException.StackTrace); System.Windows.Forms.MessageBox.Show(strMessage + "\r\n" + strStackTrace, "Oops..."); } } } #endregion Task } }