/* * Copyright (C) 2006-2007 Eskil Bylund * * 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 St, Fifth Floor, Boston, MA 02110-1301 USA */ using System; using System.Collections.Generic; using System.Threading; namespace DCSharp { // FIXME: Rename since it's not really a queue. /// /// Invokes methods, one at the time, on a separate thread. All methods can /// be aborted. /// /// /// The enqueued methods should abort if the /// from the AbortEvent property has been set. /// public class ActionQueue where T : class { private Dictionary workItems; private T active; private Thread thread; private ManualResetEvent abortEvent; private string threadName; /// /// Initializes a new ActionQueue instance. /// public ActionQueue() : this(null) { } /// /// Initializes a new ActionQueue instance. /// /// The name to use for the new thread. public ActionQueue(string threadName) { this.threadName = threadName; workItems = new Dictionary(); abortEvent = new ManualResetEvent(false); } #region Classes private class Handler { private WaitCallback callback; private object state; public Handler(WaitCallback callback, object state) { this.callback = callback; this.state = state; } public void Invoke() { callback(state); } } #endregion #region Properties /// /// Gets whether or not a method is running. /// /// Whether or not a method is running. public bool IsRunning { get { return thread != null; } } /// /// Gets the key of the active method. /// /// The key of the active method. public T Active { get { return active; } } /// /// Gets the event that tells if the active method should be stopped or not. /// /// The event that tells if the active method should be stopped or not. public ManualResetEvent AbortEvent { get { return abortEvent; } } public object SyncRoot { get { return workItems; } } #endregion #region Methods /// /// Queues a method for execution. /// /// The identifier for the method. /// The method to invoke. public void Queue(T key, WaitCallback callback) { Queue(key, callback, null); } /// /// Queues a method for execution. /// /// The identifier for the method. /// The method to invoke. /// The object to pass to the method. public void Queue(T key, WaitCallback callback, object state) { if (callback == null) { throw new ArgumentNullException("callback"); } lock (workItems) { if (!workItems.ContainsKey(key)) { workItems.Add(key, new Handler(callback, state)); if (thread == null) { thread = new Thread(Start); thread.Name = threadName; thread.IsBackground = true; thread.Start(); } } } } /// /// Checks if a method is queued for execution. /// /// The identifier for the method. /// True if the method was found; otherwise, false. public bool Contains(T key) { lock (workItems) { return workItems.ContainsKey(key); } } /// /// Removes a method from the queue or aborts the method if it's running. /// /// The identifier for the method. public void Abort(T key) { lock (workItems) { if (workItems.ContainsKey(key)) { workItems.Remove(key); } if (key.Equals(active)) { abortEvent.Set(); } } } private void Start() { while (true) { Handler handler; lock (workItems) { IEnumerator> enumerator = workItems.GetEnumerator(); if (enumerator.MoveNext()) { active = enumerator.Current.Key; handler = enumerator.Current.Value; workItems.Remove(active); } else { thread = null; break; } abortEvent.Reset(); } try { handler.Invoke(); } finally { active = null; } } } #endregion } }