Calling relaxedOffer on a MpscCompoundQueue with queueParallelism>1 can lead to an infinite loop when the call to failFastOffer on one of the underlying array queues returns -1 (indicating CAS failure).
The reason is, that status == parallelQueues (in https://github.com/JCTools/JCTools/blob/v2.1.0/jctools-core/src/main/java/org/jctools/queues/MpscCompoundQueue.java#L216) can only be true if the failFastOffer calls to all underlying queues return 1.
But inside the for(;;) loop, only parallelQueues-1 queues are tried. So as soon as the execution path reaches status = 0 (in https://github.com/JCTools/JCTools/blob/v2.1.0/jctools-core/src/main/java/org/jctools/queues/MpscCompoundQueue.java#L220), this can never happen.
I created a small example that provokes this situation: https://gist.github.com/mlex/02dc82d6368381ed138e2727925a90cf
To reproduce, start the example and wait for the output "Thread X didn't join after 10 seconds - probably caught in infinite loop". Afterwards attach a debugger or pull a stacktrace and you will see Thread X hanging inside MpscCompoundQueue.relaxedOffer.
Calling
relaxedOfferon aMpscCompoundQueuewithqueueParallelism>1can lead to an infinite loop when the call tofailFastOfferon one of the underlying array queues returns -1 (indicating CAS failure).The reason is, that
status == parallelQueues(in https://github.com/JCTools/JCTools/blob/v2.1.0/jctools-core/src/main/java/org/jctools/queues/MpscCompoundQueue.java#L216) can only be true if thefailFastOffercalls to all underlying queues return1.But inside the
for(;;)loop, onlyparallelQueues-1queues are tried. So as soon as the execution path reachesstatus = 0(in https://github.com/JCTools/JCTools/blob/v2.1.0/jctools-core/src/main/java/org/jctools/queues/MpscCompoundQueue.java#L220), this can never happen.I created a small example that provokes this situation: https://gist.github.com/mlex/02dc82d6368381ed138e2727925a90cf
To reproduce, start the example and wait for the output "Thread X didn't join after 10 seconds - probably caught in infinite loop". Afterwards attach a debugger or pull a stacktrace and you will see Thread X hanging inside MpscCompoundQueue.relaxedOffer.