I am working on a free stack stack and splitting data in a queue where I can place as many elements as I want and collect all the elements in one call, I think my design is solid and it works as expected until until I started getting an unexpected exception, which I thought was impossible in pure C # envirement:
The Managed Debugging Assistant FatalExecutionEngineError has encountered a problem. Additional Information: The runtime encountered a fatal error. The error address was 0x6caf6ac7, in the stream 0x16f0. Error Code: 0xc0000005. This error may be an error in the CLR or in unsafe or unverifiable portions of user code. Common sources of this error include user marshaling errors for COM-interop or PInvoke, which can damage the stack.
It seems that I canβt find a way, this is happening and I would like to know if anyone can direct me to the cause of the error:
This is the implementation of stackbulkcollector:
public class StackBulkCollector<T> : IBulkCollector<T> { class Node { public T value; private bool m_isSet; private Node m_prev; public Node(T data) { value = data; } public Node() { } public Node Prev { set { m_prev = value; m_isSet = true; } get { if (!m_isSet) { SpinWait s = new SpinWait(); while (!m_isSet) { s.SpinOnce(); } } return m_prev; } } } class Enumerable : IEnumerable<T> { private Node m_last; public Enumerable(Node last) { m_last = last; } public IEnumerator<T> GetEnumerator() { return new Enumerator(m_last); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return new Enumerator(m_last); } } class Enumerator : IEnumerator<T> { private readonly Node m_last; private Node m_current; public Enumerator(Node last) { var node = new Node(); m_current = m_last = node; node.Prev = last; } public T Current { get { return m_current.value; } } public void Dispose() { } object System.Collections.IEnumerator.Current { get { return this; } } public bool MoveNext() { if (m_current == null) { return false; } m_current = m_current.Prev; return m_current != null; } public void Reset() { m_current = m_last; } } private Node m_last; public void Add(T data) { var node = new Node(data); node.Prev = Interlocked.Exchange(ref m_last, node); } public IEnumerable<T> GetBulk() { var last = Interlocked.Exchange(ref m_last, null); return new Enumerable(last); } }
And this is the tester program that I use to test it:
class Program { public static readonly int UseThreads = 4; public static readonly TimeSpan Duration = new TimeSpan(0,0,0,3); public static long[] AddedItems = new long[UseThreads]; static void Main(string[] args) { IBulkCollector<TestData> bulkCollector = new QueuedBulkCollector<TestData>(); while (true) { using (var countdownEvent = new CountdownEvent(UseThreads + 1)) { var results = new Dictionary<int, List<TestData>>(); if (bulkCollector is QueuedBulkCollector<TestData>) { bulkCollector = new StackBulkCollector<TestData>(); Console.WriteLine("Starting StackBulkCollector Test"); } else { bulkCollector = new StackBulkCollector<TestData>(); Console.WriteLine("Starting QueuedBulkCollector Test"); } for (int i = 0; i < UseThreads; i++) { results[i] = new List<TestData>(); Thread t = new Thread(PushTestData); t.Start(new object[] {i, bulkCollector, countdownEvent}); } var start = DateTime.Now; Thread readerThred = new Thread(() => { while ((DateTime.Now - start) < (Duration - new TimeSpan(0, 0, 0, 0, 100))) { foreach (var testData in bulkCollector.GetBulk()) { results[testData.Id].Add(testData); }
Any advice would be appreciated.
source share