WorkItemQueue
A WorkItemQueue holds work items that are ready to
be run, either initially or after some dependency
has been satisfied.
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
namespace NUnit.Framework.Internal.Execution
{
[System.Runtime.CompilerServices.NullableContext(1)]
[System.Runtime.CompilerServices.Nullable(0)]
public class WorkItemQueue
{
[System.Runtime.CompilerServices.Nullable(0)]
private class SavedState
{
public readonly ConcurrentQueue<WorkItem>[] InnerQueues;
public readonly int AddId;
public readonly int RemoveId;
public SavedState(WorkItemQueue queue)
{
InnerQueues = queue._innerQueues;
AddId = queue._addId;
RemoveId = queue._removeId;
}
}
private const int SPIN_COUNT = 5;
private const int HIGH_PRIORITY = 0;
private const int NORMAL_PRIORITY = 1;
private const int PRIORITY_LEVELS = 2;
private readonly Logger _log = InternalTrace.GetLogger("WorkItemQueue");
private ConcurrentQueue<WorkItem>[] _innerQueues;
private readonly Stack<SavedState> _savedState = new Stack<SavedState>();
private readonly ManualResetEventSlim _mreAdd = new ManualResetEventSlim();
private int _addId = -2147483648;
private int _removeId = -2147483648;
private int _itemsProcessed;
private int _state;
public string Name { get; }
public bool IsParallelQueue { get; }
public ApartmentState TargetApartment { get; }
public int ItemsProcessed {
get {
return _itemsProcessed;
}
private set {
_itemsProcessed = value;
}
}
public WorkItemQueueState State {
get {
return (WorkItemQueueState)_state;
}
private set {
_state = (int)value;
}
}
public bool IsEmpty {
get {
ConcurrentQueue<WorkItem>[] innerQueues = _innerQueues;
foreach (ConcurrentQueue<WorkItem> concurrentQueue in innerQueues) {
if (!concurrentQueue.IsEmpty)
return false;
}
return true;
}
}
public WorkItemQueue(string name, bool isParallel, ApartmentState apartment)
{
Name = name;
IsParallelQueue = isParallel;
TargetApartment = apartment;
State = WorkItemQueueState.Paused;
ItemsProcessed = 0;
InitializeQueues();
}
[MemberNotNull("_innerQueues")]
private void InitializeQueues()
{
ConcurrentQueue<WorkItem>[] array = new ConcurrentQueue<WorkItem>[2];
for (int i = 0; i < 2; i++) {
array[i] = new ConcurrentQueue<WorkItem>();
}
_innerQueues = array;
_addId = (_removeId = 0);
}
public void Enqueue(WorkItem work)
{
Enqueue(work, (!(work is CompositeWorkItem.OneTimeTearDownWorkItem)) ? 1 : 0);
}
internal void Enqueue(WorkItem work, int priority)
{
Guard.ArgumentInRange(priority >= 0 && priority < 2, "Invalid priority specified", "priority");
int addId;
do {
addId = _addId;
} while (Interlocked.CompareExchange(ref _addId, addId + 1, addId) != addId);
_innerQueues[priority].Enqueue(work);
_mreAdd.Set();
}
[System.Runtime.CompilerServices.NullableContext(2)]
public WorkItem Dequeue()
{
SpinWait spinWait = default(SpinWait);
while (true) {
WorkItemQueueState state = State;
if (state == WorkItemQueueState.Stopped)
return null;
int removeId = _removeId;
int addId = _addId;
if (removeId == addId || state == WorkItemQueueState.Paused) {
if (spinWait.Count <= 5)
spinWait.SpinOnce();
else {
_mreAdd.Reset();
if ((removeId != _removeId || addId != _addId) && state != 0)
_mreAdd.Set();
else
_mreAdd.Wait(500);
}
} else if (Interlocked.CompareExchange(ref _removeId, removeId + 1, removeId) == removeId) {
break;
}
}
WorkItem result = null;
while (result == null) {
ConcurrentQueue<WorkItem>[] innerQueues = _innerQueues;
foreach (ConcurrentQueue<WorkItem> concurrentQueue in innerQueues) {
if (concurrentQueue.TryDequeue(out result))
break;
}
}
Interlocked.Increment(ref _itemsProcessed);
return result;
}
public void Start()
{
_log.Info("{0}.{1} starting", Name, _savedState.Count);
if (Interlocked.CompareExchange(ref _state, 1, 0) == 0)
_mreAdd.Set();
}
public void Stop()
{
_log.Info("{0}.{1} stopping - {2} WorkItems processed", Name, _savedState.Count, ItemsProcessed);
if (Interlocked.Exchange(ref _state, 2) != 2)
_mreAdd.Set();
}
public void Pause()
{
_log.Debug("{0}.{1} pausing", Name, _savedState.Count);
Interlocked.CompareExchange(ref _state, 0, 1);
}
internal void Save()
{
bool flag = State == WorkItemQueueState.Running;
if (flag)
Pause();
_savedState.Push(new SavedState(this));
InitializeQueues();
if (flag)
Start();
}
internal void Restore()
{
SavedState savedState = _savedState.Pop();
for (int i = 0; i < 2; i++) {
WorkItem result;
while (_innerQueues[i].TryDequeue(out result)) {
savedState.InnerQueues[i].Enqueue(result);
}
}
_innerQueues = savedState.InnerQueues;
_addId += savedState.AddId;
_removeId += savedState.RemoveId;
}
internal string DumpContents()
{
StringBuilder stringBuilder = new StringBuilder();
StringBuilder stringBuilder2 = stringBuilder;
StringBuilder stringBuilder3 = stringBuilder2;
StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(32, 2, stringBuilder2);
handler.AppendLiteral("Contents of ");
handler.AppendFormatted(Name);
handler.AppendLiteral(" at isolation level ");
handler.AppendFormatted(_savedState.Count);
stringBuilder3.AppendLine(ref handler);
if (IsEmpty)
stringBuilder.AppendLine(" <empty>");
else {
for (int i = 0; i < 2; i++) {
foreach (WorkItem item in _innerQueues[i]) {
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder4 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(6, 2, stringBuilder2);
handler.AppendLiteral("pri-");
handler.AppendFormatted(i);
handler.AppendLiteral(": ");
handler.AppendFormatted(item.Name);
stringBuilder4.AppendLine(ref handler);
}
}
}
int num = 0;
foreach (SavedState item2 in _savedState) {
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder5 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(12, 1, stringBuilder2);
handler.AppendLiteral("Saved State ");
handler.AppendFormatted(num++);
stringBuilder5.AppendLine(ref handler);
bool flag = true;
for (int j = 0; j < 2; j++) {
foreach (WorkItem item3 in item2.InnerQueues[j]) {
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder6 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(6, 2, stringBuilder2);
handler.AppendLiteral("pri-");
handler.AppendFormatted(j);
handler.AppendLiteral(": ");
handler.AppendFormatted(item3.Name);
stringBuilder6.AppendLine(ref handler);
flag = false;
}
}
if (flag)
stringBuilder.AppendLine(" <empty>");
}
return stringBuilder.ToString();
}
}
}