/*
* 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
}
}