Hi Martin,
On 14/12/2020 12:48 pm, Martin Buchholz wrote:
1. JDK-8258187 <https://bugs.openjdk.java.net/browse/JDK-8258187>
IllegalMonitorStateException in ArrayBlockingQueue
It's surprising that you can have a repro which is essentially just
simple producer-consumer. Can we remove the forkjoinpool from the repro
(just threads?)
AFAICS the FJP is critical here as it is the unexpected exception from
managedBlock() that causes the problem.
Cheers,
David
On Sun, Dec 13, 2020 at 6:28 PM David Holmes <david.hol...@oracle.com
<mailto:david.hol...@oracle.com>> wrote:
Hi Dawid,
This appears to be a bug in AbstractQueuedSynchronizer and FJP
interaction, so cc'ing core-libs as this is not a hotspot issue. Also
cc'ing Martin and Doug as this is a j.u.c bug.
Cheers,
David
On 12/12/2020 12:56 am, Dawid Weiss wrote:
> So, I know what this is. Sort of.
>
> This isn't a compiler error (sigh of relief). It's a kind of
interplay
> between parallel streams (which uses the common ForkJoinPool) and the
> queue's blocking operations.
>
> In our code streams use an op closure which sends items to an
> ArrayBlockingQueue. This queue is being drained by a separate thread.
> Everything works up until a certain moment when this happens on
> queue.put() -- I've modified JDK 16 source code and recompiled it to
> see what's happening:
>
> Suppressed: java.util.concurrent.RejectedExecutionException: Thread
> limit exceeded replacing blocked worker
> at
java.base/java.util.concurrent.ForkJoinPool.tryCompensate(ForkJoinPool.java:1579)
> at
java.base/java.util.concurrent.ForkJoinPool.managedBlock(ForkJoinPool.java:3124)
> at
java.base/java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:1614)
> at
java.base/java.util.concurrent.ArrayBlockingQueue.put(ArrayBlockingQueue.java:371)
>
> This exception causes the try-finally block in ArrayBlockingQueue to
> hit the finally block unexpectedly (without the attempt to re-acquire
> the lock!), eventually leading to the original odd exception I
> reported:
>
> Caused by: java.lang.IllegalMonitorStateException
> at
java.base/java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:175)
> at
java.base/java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1006)
> at
java.base/java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:494)
> at
java.base/java.util.concurrent.ArrayBlockingQueue.put(ArrayBlockingQueue.java:378)
>
> This is then propagated up to the caller of queue.put()
>
> A full simplified repro (without streams) is here:
> https://gist.github.com/dweiss/4787b7aa503ef5702e94d73178c3c842
<https://gist.github.com/dweiss/4787b7aa503ef5702e94d73178c3c842>
>
> When you run it under JDK14+, it'll result in:
>
> java.lang.IllegalMonitorStateException
> at
java.base/java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:175)
> ...
>
> This code works on JDK11, by the way. What I find a bit
> counterintuitive is that the original test (code) doesn't make any
> explicit use of ForkJoinPool - it just collects items from a parallel
> stream and involves throwing random exceptions from ops on that
> stream... This was a bit unexpected.
>
> Dawid
>
>
> On Thu, Dec 10, 2020 at 5:25 PM Dawid Weiss
<dawid.we...@gmail.com <mailto:dawid.we...@gmail.com>> wrote:
>>
>> Hello,
>>
>> I'm scratching my head again over a bug we encountered in randomized
>> tests. The code is quite complex so before I start to try to
isolate I
>> thought I'd ask if it rings a bell for anybody.
>>
>> The bug is reproducible for certain seeds but happens only after
some
>> VM warmup - the same test is executed a few dozen times, then the
>> problem starts showing up. This only happens on jdk 14 and jdk 15
>> (didn't test on jdk 16 branch). Looks like something related to OSR/
>> compilation races.
>>
>> In the end, we get this exception:
>>
>> java.lang.IllegalMonitorStateException
>> at
java.base/java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:175)
>> at
java.base/java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1006)
>> at
java.base/java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:494)
>> at
java.base/java.util.concurrent.ArrayBlockingQueue.put(ArrayBlockingQueue.java:373)
>> [stack omitted]
>>
>> This doesn't seem possible from Java code alone -- it's this snippet
>> in ArrayBlockingQueue:
>>
>> lock.lockInterruptibly();
>> try {
>> while (count == items.length)
>> notFull.await();
>> enqueue(e);
>> } finally {
>> lock.unlock(); // <<< bam...
>> }
>>
>> If the code entered the lock-protected block it should never throw
>> IllegalMonitorStateException, right?
>>
>> I'll start digging in spare moments but hints at how to isolate
this/
>> verify what this can be (other than bisecting git repo) would be
very
>> welcome!
>>
>> Dawid