Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 8 additions & 20 deletions src/NewRemoting/Toolkit/ITaskQueue.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace NewRemoting.Toolkit
{
public interface ITaskQueue
public interface ITaskQueue<T> : IEnumerable<T>
{
/// <summary>
/// Returns the number of tasks in the queue
Expand All @@ -20,37 +21,24 @@ int Count
bool TryAdd(Action action);

/// <summary>
/// Adds a delegate with parameters for sequential execution to the task queue.
/// Attention: Less performant than using an action.
/// Returns false if queue is disabled.
/// Tries to add an action for sequential execution to the task queue.
/// Throws if queue is disabled. The provided tag argument is readable
/// from outside and provided as argument to the action once it is started.
/// </summary>
bool TryAdd(Delegate del, object[] para);
void Add(Action<T> action, T tag);

/// <summary>
/// Adds a task for sequential execution to the task queue.
/// Tries to add an action for sequential execution to the task queue.
/// Returns false if queue is disabled.
/// </summary>
bool TryAdd(Task task);
bool TryAdd(Action<T> action, T tag);

/// <summary>
/// Tries to add an action for sequential execution to the task queue.
/// Throws if queue is disabled.
/// </summary>
void Add(Action action);

/// <summary>
/// Adds a delegate with parameters for sequential execution to the task queue.
/// Attention: Less performant than using an action.
/// Throws if queue is disabled.
/// </summary>
void Add(Delegate del, object[] para);

/// <summary>
/// Adds a task for sequential execution to the task queue.
/// Throws if queue is disabled.
/// </summary>
void Add(Task task);

/// <summary>
/// Waits until the tasks queue becomes empty
/// and all remaining tasks have finished
Expand Down
69 changes: 37 additions & 32 deletions src/NewRemoting/Toolkit/TaskQueue.cs
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace NewRemoting.Toolkit
{
/// <summary>
/// This class ensures, that all tasks passed to the queue are executed in sequential order
/// This class ensures, that all tasks passed to the queue are executed in sequential order.
/// Every task can have an object assigned to it that can also be queried from outside (e.g. to identify
/// what kind of tasks is in the queue)
/// </summary>
public sealed class TaskQueue : ITaskQueue
public class TaskQueue<T> : ITaskQueue<T>
{
private readonly Action<AggregateException> _exceptionHandler;
private readonly object _queueLock = new object();
private Queue<Task> _taskQueue;
private Queue<(Task Task, T Tag)> _taskQueue;
private Task _currentTask;

/// <summary>
/// Constructor without custom exception handling.
/// </summary>
public TaskQueue()
{
_taskQueue = new Queue<Task>();
_taskQueue = new Queue<(Task, T)>();
_currentTask = null;
}

Expand Down Expand Up @@ -57,60 +61,49 @@ public int Count
}
}

public void Add(Action action)
public IEnumerator<T> GetEnumerator()
{
AddInternal(action, true);
lock (_queueLock)
{
return _taskQueue.Select(x => x.Tag).GetEnumerator();
}
}

public void Add(Delegate del, object[] para)
IEnumerator IEnumerable.GetEnumerator()
{
AddInternal(del, para, true);
return GetEnumerator();
}

public void Add(Task task)
public void Add(Action action)
{
AddInternal(task, true);
AddInternal(action, true);
}

public bool TryAdd(Action action)
{
return AddInternal(action, false);
}

/// <summary>
/// Attention: is very slow consider wrapping time critical stuff into an action <see cref="Add(Action)" />.
/// <code>
/// Add(new Action(() => Call(p1, p2, p3, ...));
/// </code>
/// </summary>
public bool TryAdd(Delegate del, object[] para)
public void Add(Action<T> action, T tag)
{
return AddInternal(del, para, false);
AddInternal(action, tag, true);
}

public bool TryAdd(Task task)
public bool TryAdd(Action<T> action, T tag)
{
return AddInternal(task, false);
return AddInternal(action, tag, false);
}

private bool AddInternal(Action action, bool throwOnDisabled)
{
return AddInternal(new Task(action), throwOnDisabled);
}

/// <summary>
/// <see cref="Delegate.DynamicInvoke"/> is very slow
/// </summary>
private bool AddInternal(Delegate del, object[] para, bool throwOnDisabled)
{
return AddInternal(new Task(() => del.DynamicInvoke(para)), throwOnDisabled);
return AddInternal(x => action(), default, throwOnDisabled);
}

/// <summary>
/// Adds a Tasks to the task Queue
/// </summary>
/// <exception cref="InvalidOperationException">Adding tasks to disabled queue is not allowed</exception>
private bool AddInternal(Task task, bool throwOnDisabled)
private bool AddInternal(Action<T> action, T tag, bool throwOnDisabled)
{
var added = false;
lock (_queueLock)
Expand All @@ -122,7 +115,7 @@ private bool AddInternal(Task task, bool throwOnDisabled)

if (_taskQueue != null)
{
_taskQueue.Enqueue(task);
_taskQueue.Enqueue((new Task(() => action(tag)), tag));
added = true;
}

Expand Down Expand Up @@ -179,7 +172,7 @@ private void RunTask()
if (_currentTask == null && _taskQueue != null && _taskQueue.Count > 0)
{
// Leave currently executing task in queue until fully executed, this makes waiting for all tasks executed much easier than when removing here and handling the currently executing task separately
_currentTask = _taskQueue.Peek();
(_currentTask, _) = _taskQueue.Peek();
var whereToContinueTask = _currentTask;
if (_exceptionHandler != null)
{
Expand Down Expand Up @@ -207,4 +200,16 @@ private void OnTaskFinished(Task finishedTask)
}
}
}

public class TaskQueue : TaskQueue<object>
{
public TaskQueue()
{
}

public TaskQueue(Action<AggregateException> exceptionHandler)
: base(exceptionHandler)
{
}
}
}
Loading