Description
Description
Presumably due to a1dfa85 my application throws an error when loggers get changed from multiple threads.
This is likely because InternalLoggerRegistry#getLoggers
does not return a thread-safe stream.
Streams are by nature evaluated on the fly, thus still using the values of loggerRefByNameByMessageFactory
after readLock
was unlocked.
When multiple threads modify loggerRefByNameByMessageFactory
, sometimes it errors with the attached error.
This happens inconsistently because it's a race condition, but I've been able to get this error a few times.
Configuration
Version: 2.24.2
Operating system: Windows
JDK: openjdk version "21.0.4" 2024-07-16 LTS
Logs
java.util.ConcurrentModificationException: null
at java.base/java.util.HashMap$ValueSpliterator.forEachRemaining(HashMap.java:1792) ~[?:?]
at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:762) ~[?:?]
at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:276) ~[?:?]
at java.base/java.util.WeakHashMap$ValueSpliterator.forEachRemaining(WeakHashMap.java:1223) ~[?:?]
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) ~[?:?]
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) ~[?:?]
at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151) ~[?:?]
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174) ~[?:?]
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[?:?]
at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596) ~[?:?]
at org.apache.logging.log4j.core.LoggerContext.updateLoggers(LoggerContext.java:776) ~[?:?]
at org.apache.logging.log4j.core.LoggerContext.updateLoggers(LoggerContext.java:766) ~[?:?]
at org.apache.logging.log4j.core.config.Configurator.setLevel(Configurator.java:379) ~[?:?]
at org.apache.logging.log4j.core.config.Configurator.setLevel(Configurator.java:414) ~[?:?]
at com.soulfiremc.server.SoulFireServer.setupLogging(SoulFireServer.java:213) ~[?:?]
at com.soulfiremc.server.SoulFireServer.setupLoggingAndVia(SoulFireServer.java:208) ~[?:?]
at com.soulfiremc.server.InstanceManager.start(InstanceManager.java:194) ~[?:?]
at com.soulfiremc.server.SoulFireScheduler.lambda$wrapFuture$6(SoulFireScheduler.java:140) ~[?:?]
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804) ~[?:?]
at com.soulfiremc.server.SoulFireScheduler.runCommand(SoulFireScheduler.java:154) ~[?:?]
at com.soulfiremc.server.SoulFireScheduler.lambda$schedule$2(SoulFireScheduler.java:79) ~[?:?]
at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1423) ~[?:?]
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) ~[?:?]
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312) ~[?:?]
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843) ~[?:?]
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808) ~[?:?]
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188) ~[?:?]
This shows the stream being collected due to the forEach call in LoggerContext#updateLoggers
, but there being no read lock, therefore causing this race condition.
Reproduction
I unfortunately cannot provide an example because I do not have much experience with reproducing race conditions.