I use this class for scheduling bits of work on a single thread. Work can be scheduled to be executed immediately or at a future time with the use of timers. It has many producers (the work adders) and a single consumer (the thread which executes the work).
This class works well but I'm under no illusion that it's perfect. I'm looking for potential problems this class may exhibit especially relating to threading (dead locks etc).
internal delegate void MainLoopTask();
internal delegate object MainLoopJob();
internal static class MainLoop
{
private class DelegateTask
{
private bool _isBlocking;
private Exception _storedException;
private MainLoopTask _task;
private MainLoopJob _job;
private object _jobResult;
private ManualResetEvent _handle;
public DelegateTask()
{
}
public bool IsBlocking
{
get { return _isBlocking; }
set { _isBlocking = value; }
}
public Exception StoredException
{
get { return _storedException; }
set { _storedException = value; }
}
public object JobResult
{
get { return _jobResult; }
}
public MainLoopTask Task
{
get { return _task; }
set { _task = value; }
}
public MainLoopJob Job
{
get { return _job; }
set { _job = value; }
}
public ManualResetEvent WaitHandle
{
get
{
if (_handle == null)
_handle = new ManualResetEvent(false);
return _handle;
}
}
public void Execute()
{
try
{
if (_task != null)
_task();
if (_job != null)
_jobResult = _job();
}
catch (Exception ex)
{
_storedException = ex;
if (!_isBlocking)
throw;
}
finally
{
if (_isBlocking)
_handle.Set();
}
}
}
private class TaskInfo
{
public Timer timer;
public DelegateTask task;
}
private static AutoResetEvent _handle = new AutoResetEvent(false);
private static Queue<DelegateTask> _tasks = new Queue<DelegateTask>();
private static List<Timer> _timers = new List<Timer>();
private static volatile bool _running;
private static Thread _thread;
public static void Loop()
{
_thread = Thread.CurrentThread;
_running = true;
while (_running)
{
DelegateTask task = null;
lock (_tasks)
{
if (_tasks.Count > 0)
task = _tasks.Dequeue();
}
if (task == null)
{
_handle.WaitOne();
}
else
{
task.Execute();
}
}
}
public static void Quit()
{
_running = false;
_handle.Set();
}
private static void Queue(DelegateTask task)
{
lock (_tasks)
{
_tasks.Enqueue(task);
_handle.Set();
}
}
public static void Queue(MainLoopTask task)
{
DelegateTask dTask = new DelegateTask();
dTask.Task = task;
Queue(dTask);
}
public static void QueueWait(MainLoopTask task)
{
DelegateTask dTask = new DelegateTask();
dTask.Task = task;
QueueWait(dTask);
}
public static object QueueWait(MainLoopJob task)
{
DelegateTask dTask = new DelegateTask();
dTask.Job = task;
QueueWait(dTask);
return dTask.JobResult;
}
private static void QueueWait(DelegateTask t)
{
t.WaitHandle.Reset();
t.IsBlocking = true;
if (_thread == null || Thread.CurrentThread == _thread)
t.Execute();
else
Queue(t);
t.WaitHandle.WaitOne();
t.WaitHandle.Close();
if (t.StoredException != null)
{
throw t.StoredException;
}
}
public static void QueueTimeout(MainLoopTask task, TimeSpan timeSpan)
{
QueueTimeout(task, (uint)timeSpan.TotalMilliseconds);
}
public static void QueueTimeout(MainLoopTask task, uint milliSeconds)
{
if (milliSeconds <= 0)
Queue(task);
else
{
DelegateTask dTask = new DelegateTask();
dTask.Task = task;
TaskInfo ti = new TaskInfo();
ti.task = dTask;
ti.timer = new Timer(WaitProc, ti, milliSeconds, Timeout.Infinite);
lock (_timers)
_timers.Add(ti.timer);
}
}
private static void WaitProc(object state)
{
TaskInfo ti = (TaskInfo)state;
lock (_timers)
_timers.Remove(ti.timer);
ti.timer.Dispose();
Queue(ti.task);
}
}