<div dir="auto">Hi Robert, <div dir="auto"><br></div><div dir="auto">Since you have a reproducer that doesn't use RingBuffer, this is a little unnecessary, but I can explain a bit.</div><div dir="auto"><br></div><div dir="auto">1. When you see multiple atomic operations, you wonder if things can happen out of order. Who can mutate tail counter and who can mutate array value, and what precludes more mutations of tail and array values? If you can't prove constructively that it can't happen, you need to assume that it may happen. </div><div dir="auto"><br></div><div dir="auto">2. When you see integer arithmetic, you wonder what happens upon wraparound, and when does that happen?</div><div dir="auto"><br></div><div dir="auto">3. When you see a condition check (and an indicator that it is an error - an exception is thrown), you wonder what may be the flip side of that condition (may it go undetected?)</div><div dir="auto"><br></div><div dir="auto">Those are red flags that drive investigation. Even without a proof of how things go wrong, I would raise questions about its safety.</div><div dir="auto"><br></div><div dir="auto">However, in this case we can even show how to deadlock.</div><div dir="auto"><br></div><div dir="auto">Producer A and B, RingBuffer of size N.</div><div dir="auto"><br></div><div dir="auto">Consumer emptied the buffer, so head == tail.</div><div dir="auto"><br></div><div dir="auto">Producer A ready to offer, increments tail, and gets suspended before it updates array at position tail. (E.g. interrupt preempts the thread)</div><div dir="auto"><br></div><div dir="auto">Producer B offers N values. Observe that tail wraparound occurs.</div><div dir="auto"><br></div><div dir="auto">Consumer consumes all N values. Now array cell at tail is null again.</div><div dir="auto"><br></div><div dir="auto">Producer B offers N-2 values. Now array cell at tail is not null again. </div><div dir="auto"><br></div><div dir="auto">Consumer consumes at least 1 value. Now array cell at tail is null.</div><div dir="auto"><br></div><div dir="auto">Producer A resumes, stores the value, because the correctness condition is met. (the flip side of exception throw - the inconsistency went unobserved)</div><div dir="auto"><br></div><div dir="auto">Done. Now Consumer will not be able to access array cell with the value Producer A has just stored, and will wait indefinitely. You may need to track head to see why that is the case.</div><div dir="auto"><br></div><div dir="auto"><br></div><div dir="auto">Alex</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Wed, 28 Dec 2022, 12:50 Robert Engels, <<a href="mailto:rengels@ix.netcom.com">rengels@ix.netcom.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><br>
Alex,<br>
<br>
You write:<br>
<br>
> I won't go into detail, but the producers not synchronizing between themselves leads to hangs.<br>
<br>
<br>
Can you explain? Im fairly certain the producers do valid synchronization. As I said, using native threads the code runs to completion fine <br>
<br>
Now that I understand the issue I was able to reproduce the issue very simply without any queues. See SpinTest posted to the project. The “starver” thread fails to make any progress - this should not be possible is Thread.yield() was fair. <br>
<br>
> On Dec 27, 2022, at 11:15 PM, Alex Otenko <<a href="mailto:oleksandr.otenko@gmail.com" target="_blank" rel="noreferrer">oleksandr.otenko@gmail.com</a>> wrote:<br>
> I won't go into detail, but the producers not synchronizing between themselves leads to hangs.<br>
</blockquote></div>