IllegalStateException in CharsetDecoder when inspecting JarFile contents on Java 15
Hi all, I observed the following stacktrace when inspecting a JarFile's contents using a parallel stream (i.e. FJ pool): Exception in thread "main" java.lang.IllegalStateException: java.lang.IllegalStateException: Current state = CODING_END, new state = FLUSHED at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:64) at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500) at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:481) at java.base/java.util.concurrent.ForkJoinTask.getThrowableException(ForkJoinTask.java:600) at java.base/java.util.concurrent.ForkJoinTask.reportException(ForkJoinTask.java:678) at java.base/java.util.concurrent.ForkJoinTask.invoke(ForkJoinTask.java:737) at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateParallel(ForEachOps.java:159) at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateParallel(ForEachOps.java:173) at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:233) at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:497) at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:575) at <stripped>.ClassVersions.conflicts(ClassVersions.java:703) at <stripped>.ClassVersions.check(ClassVersions.java:743) at <stripped>.ClassVersions.main(ClassVersions.java:839) Caused by: java.lang.IllegalStateException: Current state = CODING_END, new state = FLUSHED at java.base/java.nio.charset.CharsetDecoder.throwIllegalStateException(CharsetDecoder.java:998) at java.base/java.nio.charset.CharsetDecoder.flush(CharsetDecoder.java:681) at java.base/java.nio.charset.CharsetDecoder.decode(CharsetDecoder.java:810) at java.base/java.util.zip.ZipCoder.normalizedHashDecode(ZipCoder.java:136) at java.base/java.util.zip.ZipCoder$UTF8ZipCoder.normalizedHash(ZipCoder.java:228) at java.base/java.util.zip.ZipFile$Source.initCEN(ZipFile.java:1527) at java.base/java.util.zip.ZipFile$Source.<init>(ZipFile.java:1249) at java.base/java.util.zip.ZipFile$Source.get(ZipFile.java:1211) at java.base/java.util.zip.ZipFile$CleanableResource.<init>(ZipFile.java:701) at java.base/java.util.zip.ZipFile.<init>(ZipFile.java:240) at java.base/java.util.zip.ZipFile.<init>(ZipFile.java:171) at java.base/java.util.jar.JarFile.<init>(JarFile.java:347) at java.base/java.util.jar.JarFile.<init>(JarFile.java:318) at java.base/java.util.jar.JarFile.<init>(JarFile.java:284) at <stripped>.ClassVersions.lookupJar(ClassVersions.java:665) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195) at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:177) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195) at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:177) at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948) at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484) at java.base/java.util.stream.ForEachOps$ForEachTask.compute(ForEachOps.java:290) at java.base/java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:746) at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290) at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1016) at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1665) at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1598) at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183) (Note: I stripped off some package names to protect the innocent). While investigating this a bit, I noticed some code that looks a bit suspect. Namely: - ZipCoder has a singleton UTF8ZipCoder <https://github.com/openjdk/jdk/blob/05bcd67e6501f3824020edd1ab036c87888fa229/src/java.base/share/classes/java/util/zip/ZipCoder.java#L48> instance it uses for UTF8 decoding. - Source.get() creates a new Source <https://github.com/openjdk/jdk/blob/05bcd67e6501f3824020edd1ab036c87888fa229/src/java.base/share/classes/java/util/zip/ZipFile.java#L1225> instance outside of any synchronization The links above are to tip, but I think this code is the same in Java 15. It seems possible that multiple threads can end up in Source.get(), new up a Source, and proceed to use the decoder of UTF8ZipCoder racily. Per my understanding (and CharsetDecoder javadoc), CharsetDecoders are not threadsafe. Any thoughts? Did I miss anything that would suggest the stacktrace above is due to something else entirely? Thanks
Hi, yes, this seems like a bug introduced by JDK-8243469 where the previously thread-safe UTF8ZipCoder is now using a thread-unsafe decoder to calculate the normalized hash. I'll file a bug and take a look. /Claes On 2021-01-19 20:19, Vitaly Davidovich wrote:
Hi all,
I observed the following stacktrace when inspecting a JarFile's contents using a parallel stream (i.e. FJ pool):
Exception in thread "main" java.lang.IllegalStateException: java.lang.IllegalStateException: Current state = CODING_END, new state = FLUSHED
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:64)
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:481)
at java.base/java.util.concurrent.ForkJoinTask.getThrowableException(ForkJoinTask.java:600)
at java.base/java.util.concurrent.ForkJoinTask.reportException(ForkJoinTask.java:678)
at java.base/java.util.concurrent.ForkJoinTask.invoke(ForkJoinTask.java:737)
at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateParallel(ForEachOps.java:159)
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateParallel(ForEachOps.java:173)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:233)
at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:497)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:575)
at <stripped>.ClassVersions.conflicts(ClassVersions.java:703)
at <stripped>.ClassVersions.check(ClassVersions.java:743)
at <stripped>.ClassVersions.main(ClassVersions.java:839)
Caused by: java.lang.IllegalStateException: Current state = CODING_END, new state = FLUSHED
at java.base/java.nio.charset.CharsetDecoder.throwIllegalStateException(CharsetDecoder.java:998)
at java.base/java.nio.charset.CharsetDecoder.flush(CharsetDecoder.java:681)
at java.base/java.nio.charset.CharsetDecoder.decode(CharsetDecoder.java:810)
at java.base/java.util.zip.ZipCoder.normalizedHashDecode(ZipCoder.java:136)
at java.base/java.util.zip.ZipCoder$UTF8ZipCoder.normalizedHash(ZipCoder.java:228)
at java.base/java.util.zip.ZipFile$Source.initCEN(ZipFile.java:1527)
at java.base/java.util.zip.ZipFile$Source.<init>(ZipFile.java:1249)
at java.base/java.util.zip.ZipFile$Source.get(ZipFile.java:1211)
at java.base/java.util.zip.ZipFile$CleanableResource.<init>(ZipFile.java:701)
at java.base/java.util.zip.ZipFile.<init>(ZipFile.java:240)
at java.base/java.util.zip.ZipFile.<init>(ZipFile.java:171)
at java.base/java.util.jar.JarFile.<init>(JarFile.java:347)
at java.base/java.util.jar.JarFile.<init>(JarFile.java:318)
at java.base/java.util.jar.JarFile.<init>(JarFile.java:284)
at <stripped>.ClassVersions.lookupJar(ClassVersions.java:665)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:177)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:177)
at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
at java.base/java.util.stream.ForEachOps$ForEachTask.compute(ForEachOps.java:290)
at java.base/java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:746)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1016)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1665)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1598)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)
(Note: I stripped off some package names to protect the innocent).
While investigating this a bit, I noticed some code that looks a bit suspect. Namely:
- ZipCoder has a singleton UTF8ZipCoder <https://github.com/openjdk/jdk/blob/05bcd67e6501f3824020edd1ab036c87888fa229/src/java.base/share/classes/java/util/zip/ZipCoder.java#L48> instance it uses for UTF8 decoding. - Source.get() creates a new Source <https://github.com/openjdk/jdk/blob/05bcd67e6501f3824020edd1ab036c87888fa229/src/java.base/share/classes/java/util/zip/ZipFile.java#L1225> instance outside of any synchronization
The links above are to tip, but I think this code is the same in Java 15.
It seems possible that multiple threads can end up in Source.get(), new up a Source, and proceed to use the decoder of UTF8ZipCoder racily. Per my understanding (and CharsetDecoder javadoc), CharsetDecoders are not threadsafe.
Any thoughts? Did I miss anything that would suggest the stacktrace above is due to something else entirely?
Thanks
Filed: https://bugs.openjdk.java.net/browse/JDK-8260010 I have a potential fix ready, just trying to distill a minimal test case. /Claes On 2021-01-19 21:46, Claes Redestad wrote:
Hi,
yes, this seems like a bug introduced by JDK-8243469 where the previously thread-safe UTF8ZipCoder is now using a thread-unsafe decoder to calculate the normalized hash. I'll file a bug and take a look.
/Claes
On 2021-01-19 20:19, Vitaly Davidovich wrote:
Hi all,
I observed the following stacktrace when inspecting a JarFile's contents using a parallel stream (i.e. FJ pool):
Exception in thread "main" java.lang.IllegalStateException: java.lang.IllegalStateException: Current state = CODING_END, new state = FLUSHED
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native
Method)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:64)
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:481)
at java.base/java.util.concurrent.ForkJoinTask.getThrowableException(ForkJoinTask.java:600)
at java.base/java.util.concurrent.ForkJoinTask.reportException(ForkJoinTask.java:678)
at java.base/java.util.concurrent.ForkJoinTask.invoke(ForkJoinTask.java:737)
at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateParallel(ForEachOps.java:159)
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateParallel(ForEachOps.java:173)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:233)
at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:497)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:575)
at <stripped>.ClassVersions.conflicts(ClassVersions.java:703)
at <stripped>.ClassVersions.check(ClassVersions.java:743)
at <stripped>.ClassVersions.main(ClassVersions.java:839)
Caused by: java.lang.IllegalStateException: Current state = CODING_END, new state = FLUSHED
at java.base/java.nio.charset.CharsetDecoder.throwIllegalStateException(CharsetDecoder.java:998)
at java.base/java.nio.charset.CharsetDecoder.flush(CharsetDecoder.java:681)
at java.base/java.nio.charset.CharsetDecoder.decode(CharsetDecoder.java:810)
at java.base/java.util.zip.ZipCoder.normalizedHashDecode(ZipCoder.java:136)
at java.base/java.util.zip.ZipCoder$UTF8ZipCoder.normalizedHash(ZipCoder.java:228)
at java.base/java.util.zip.ZipFile$Source.initCEN(ZipFile.java:1527)
at java.base/java.util.zip.ZipFile$Source.<init>(ZipFile.java:1249)
at java.base/java.util.zip.ZipFile$Source.get(ZipFile.java:1211)
at java.base/java.util.zip.ZipFile$CleanableResource.<init>(ZipFile.java:701)
at java.base/java.util.zip.ZipFile.<init>(ZipFile.java:240)
at java.base/java.util.zip.ZipFile.<init>(ZipFile.java:171)
at java.base/java.util.jar.JarFile.<init>(JarFile.java:347)
at java.base/java.util.jar.JarFile.<init>(JarFile.java:318)
at java.base/java.util.jar.JarFile.<init>(JarFile.java:284)
at <stripped>.ClassVersions.lookupJar(ClassVersions.java:665)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:177)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:177)
at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
at java.base/java.util.stream.ForEachOps$ForEachTask.compute(ForEachOps.java:290)
at java.base/java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:746)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1016)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1665)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1598)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)
(Note: I stripped off some package names to protect the innocent).
While investigating this a bit, I noticed some code that looks a bit suspect. Namely:
- ZipCoder has a singleton UTF8ZipCoder <https://github.com/openjdk/jdk/blob/05bcd67e6501f3824020edd1ab036c87888fa229/src/java.base/share/classes/java/util/zip/ZipCoder.java#L48> instance it uses for UTF8 decoding. - Source.get() creates a new Source <https://github.com/openjdk/jdk/blob/05bcd67e6501f3824020edd1ab036c87888fa229/src/java.base/share/classes/java/util/zip/ZipFile.java#L1225> instance outside of any synchronization
The links above are to tip, but I think this code is the same in Java 15.
It seems possible that multiple threads can end up in Source.get(), new up a Source, and proceed to use the decoder of UTF8ZipCoder racily. Per my understanding (and CharsetDecoder javadoc), CharsetDecoders are not threadsafe.
Any thoughts? Did I miss anything that would suggest the stacktrace above is due to something else entirely?
Thanks
Hi Claes, You’re fast! :) Thanks for the bug report and working on a fix. Best, Vitaly On Tue, Jan 19, 2021 at 5:00 PM Claes Redestad <claes.redestad@oracle.com> wrote:
Filed: https://bugs.openjdk.java.net/browse/JDK-8260010
I have a potential fix ready, just trying to distill a minimal test case.
/Claes
On 2021-01-19 21:46, Claes Redestad wrote:
Hi,
yes, this seems like a bug introduced by JDK-8243469 where the previously thread-safe UTF8ZipCoder is now using a thread-unsafe decoder to calculate the normalized hash. I'll file a bug and take a look.
/Claes
On 2021-01-19 20:19, Vitaly Davidovich wrote:
Hi all,
I observed the following stacktrace when inspecting a JarFile's contents using a parallel stream (i.e. FJ pool):
Exception in thread "main" java.lang.IllegalStateException: java.lang.IllegalStateException: Current state = CODING_END, new state = FLUSHED
at
java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native
Method)
at
java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:64)
at
java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at
java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500)
at
java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:481)
at
java.base/java.util.concurrent.ForkJoinTask.getThrowableException(ForkJoinTask.java:600)
at
java.base/java.util.concurrent.ForkJoinTask.reportException(ForkJoinTask.java:678)
at
java.base/java.util.concurrent.ForkJoinTask.invoke(ForkJoinTask.java:737)
at
java.base/java.util.stream.ForEachOps$ForEachOp.evaluateParallel(ForEachOps.java:159)
at
java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateParallel(ForEachOps.java:173)
at
java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:233)
at
java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:497)
at
java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:575)
at <stripped>.ClassVersions.conflicts(ClassVersions.java:703)
at <stripped>.ClassVersions.check(ClassVersions.java:743)
at <stripped>.ClassVersions.main(ClassVersions.java:839)
Caused by: java.lang.IllegalStateException: Current state = CODING_END, new state = FLUSHED
at
java.base/java.nio.charset.CharsetDecoder.throwIllegalStateException(CharsetDecoder.java:998)
at java.base/java.nio.charset.CharsetDecoder.flush(CharsetDecoder.java:681)
at
java.base/java.nio.charset.CharsetDecoder.decode(CharsetDecoder.java:810)
at java.base/java.util.zip.ZipCoder.normalizedHashDecode(ZipCoder.java:136)
at
java.base/java.util.zip.ZipCoder$UTF8ZipCoder.normalizedHash(ZipCoder.java:228)
at java.base/java.util.zip.ZipFile$Source.initCEN(ZipFile.java:1527)
at java.base/java.util.zip.ZipFile$Source.<init>(ZipFile.java:1249)
at java.base/java.util.zip.ZipFile$Source.get(ZipFile.java:1211)
at
java.base/java.util.zip.ZipFile$CleanableResource.<init>(ZipFile.java:701)
at java.base/java.util.zip.ZipFile.<init>(ZipFile.java:240)
at java.base/java.util.zip.ZipFile.<init>(ZipFile.java:171)
at java.base/java.util.jar.JarFile.<init>(JarFile.java:347)
at java.base/java.util.jar.JarFile.<init>(JarFile.java:318)
at java.base/java.util.jar.JarFile.<init>(JarFile.java:284)
at <stripped>.ClassVersions.lookupJar(ClassVersions.java:665)
at
java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
at
java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:177)
at
java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
at
java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:177)
at
java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
at
java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
at
java.base/java.util.stream.ForEachOps$ForEachTask.compute(ForEachOps.java:290)
at
java.base/java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:746)
at
java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
at
java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1016)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1665)
at
java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1598)
at
java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)
(Note: I stripped off some package names to protect the innocent).
While investigating this a bit, I noticed some code that looks a bit suspect. Namely:
- ZipCoder has a singleton UTF8ZipCoder <
https://github.com/openjdk/jdk/blob/05bcd67e6501f3824020edd1ab036c87888fa229...
instance it uses for UTF8 decoding. - Source.get() creates a new Source <
https://github.com/openjdk/jdk/blob/05bcd67e6501f3824020edd1ab036c87888fa229...
instance outside of any synchronization
The links above are to tip, but I think this code is the same in Java 15.
It seems possible that multiple threads can end up in Source.get(), new up a Source, and proceed to use the decoder of UTF8ZipCoder racily. Per my understanding (and CharsetDecoder javadoc), CharsetDecoders are not threadsafe.
Any thoughts? Did I miss anything that would suggest the stacktrace above is due to something else entirely?
Thanks
-- Sent from my phone
participants (2)
-
Claes Redestad
-
Vitaly Davidovich