ConcurrentModificationException in net.fabricmc.fabric.impl.client.texture.SpriteRegistryCallbackHolder.eventLocal(Identifier)
aromanelli opened this issue · 5 comments
Minecraft 1.14.2
fabric-loader-0.4.8+build.154-1.14.2+build.2
Fabric API 0.3.0+build.170 (though I've seen this problem with 'build.16#' versions too.)
Seeing an intermittent ConcurrentModificationException when Minecraft is starting up on the "MOJANG" progress bar window.
---- Minecraft Crash Report ----
// This doesn't make any sense!
Time: 5/28/19, 3:34 PM
Description: Rendering overlay
java.util.ConcurrentModificationException
at java.base/java.util.HashMap.computeIfAbsent(HashMap.java:1134)
at net.fabricmc.fabric.impl.client.texture.SpriteRegistryCallbackHolder.eventLocal(SpriteRegistryCallbackHolder.java:36)
at net.minecraft.class_1059.localvar$beforeSpriteLoad$zbi000(class_1059.java:587)
at net.minecraft.class_1059.method_18163(class_1059.java:117)
at net.minecraft.class_702.method_18832(class_702.java:199)
at java.base/java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:642)
at java.base/java.util.concurrent.CompletableFuture$Completion.exec(CompletableFuture.java:479)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)
A detailed walkthrough of the error, its code path and all known details is as follows:
---------------------------------------------------------------------------------------
-- Head --
Thread: Client thread
Stacktrace:
at java.base/java.util.HashMap.computeIfAbsent(HashMap.java:1134)
-- Overlay render details --
Details:
Overlay name: net.minecraft.class_425
Stacktrace:
at net.minecraft.class_757.method_3192(class_757.java:676)
at net.minecraft.class_310.method_1523(class_310.java:954)
at net.minecraft.class_310.method_1514(class_310.java:411)
at net.minecraft.client.main.Main.main(Main.java:154)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
at net.fabricmc.loader.game.MinecraftGameProvider.launch(MinecraftGameProvider.java:170)
at net.fabricmc.loader.launch.knot.Knot.init(Knot.java:129)
at net.fabricmc.loader.launch.knot.KnotClient.main(KnotClient.java:26)
-- System Details --
Details:
Minecraft Version: 1.14.2
Operating System: Linux (amd64) version 5.1.5-050105-generic
Java Version: 12.0.1, Private Build
Java VM Version: OpenJDK 64-Bit Server VM (mixed mode, sharing), Private Build
Memory: 1236544704 bytes (1179 MB) / 2113929216 bytes (2016 MB) up to 6442450944 bytes (6144 MB)
JVM Flags: 8 total; -Xss1M -Xmx6G -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC -XX:G1NewSizePercent=20 -XX:G1ReservePercent=20 -XX:MaxGCPauseMillis=50 -XX:G1HeapRegionSize=32M
Fabric Mods:
autoconfig1: Auto Config v1 1.0.1+mc1.14
cleardespawn: Clear Despawn 1.0.4
cloth-config: Cloth Config 0.2.1+build.14
fabric: Fabric API 0.3.0+build.170
fabric-api-base: fabric-api-base 0.1.0+59147463
fabric-commands-v0: fabric-commands-v0 0.1.1+25fd0c52
fabric-containers-v0: fabric-containers-v0 0.1.2+25fd0c52
fabric-content-registries-v0: fabric-content-registries-v0 0.1.1+45e1a1c8
fabric-crash-report-info-v1: fabric-crash-report-info-v1 0.1.0+59147463
fabric-events-interaction-v0: fabric-events-interaction-v0 0.1.0+59147463
fabric-events-lifecycle-v0: fabric-events-lifecycle-v0 0.1.0+59147463
fabric-item-groups-v0: fabric-item-groups-v0 0.1.0+59147463
fabric-keybindings-v0: fabric-keybindings-v0 0.1.0+59147463
fabric-mining-levels-v0: fabric-mining-levels-v0 0.1.0+59147463
fabric-models-v0: fabric-models-v0 0.1.0+59147463
fabric-networking-blockentity-v0: fabric-networking-blockentity-v0 0.1.1+25fd0c52
fabric-networking-v0: fabric-networking-v0 0.1.2+200eb5c2
fabric-object-builders-v0: fabric-object-builders-v0 0.1.1+9fe2f882
fabric-registry-sync-v0: fabric-registry-sync-v0 0.1.2+25fd0c52
fabric-renderer-api-v1: fabric-renderer-api-v1 0.1.0+02a46d5b
fabric-renderer-indigo: fabric-renderer-indigo 0.1.2+4076d79a
fabric-rendering-data-attachment-v1: fabric-rendering-data-attachment-v1 0.1.0+02a46d5b
fabric-rendering-fluids-v1: fabric-rendering-fluids-v1 0.1.0+dc4c57c2
fabric-rendering-v0: fabric-rendering-v0 0.1.0+59147463
fabric-resource-loader-v0: fabric-resource-loader-v0 0.1.1+59147463
fabric-tag-extensions-v0: fabric-tag-extensions-v0 0.1.0+59147463
fabric-textures-v0: fabric-textures-v0 0.1.2+25fd0c52
fabricloader: Fabric Loader 0.4.8+build.154
simplezoom: Simple Zoom 1.0.0
tbo: TheBiomeOverhaul 1.2.0
Launched Version: fabric-loader-0.4.8+build.154-1.14.2+build.2
LWJGL: 3.2.1 build 12
OpenGL: Radeon RX 580 Series (POLARIS10, DRM 3.30.0, 5.1.5-050105-generic, LLVM 8.0.0) GL version 4.5 (Compatibility Profile) Mesa 19.0.2, X.Org
GL Caps: Using GL 1.3 multitexturing.
Using GL 1.3 texture combiners.
Using framebuffer objects because OpenGL 3.0 is supported and separate blending is supported.
Shaders are available because OpenGL 2.1 is supported.
VBOs are available because OpenGL 1.5 is supported.
Using VBOs: Yes
Is Modded: Definitely; Client brand changed to 'fabric'
Type: Client (map_client.txt)
Resource Packs:
Current Language: English (US)
CPU: 12x Intel(R) Core(TM) i7 CPU 980 @ 3.33GHz
Looking at the source code for the SpriteRegistryCallbackHolder class it seems that the public 'eventLocal(Identifier)' method is not multi-threaded safe (synchronized, locking, etc.), or that the 'eventMap' HashMap being used should be a ConcurrentHashMap (depending on your preference for how to make a class thread safe).
Let me know if you need any further information.
For testing purposes I added the 'synchronized' keyword to the declaration for method SpriteRegistryCallbackHolder.eventLocal(Identifier)
and the exception does not occur.
@e00E Multithreading issues are usually driven based on race conditions, which is why you'd see it only 10% of the time.
For me, as I used earlier versions of the Fabric API library the problem happened less and less, and in (I think) 158 not at all, while always in v170.
@asiekierka Apologies for the backseat driving, but looking at the fix, correct me if I'm wrong, but I think the way its coded now you can have two puts into the Map (race condition on line 46 the null check, via time slicing). I'm not sure so double-check me on that, but I think you need the read lock to also encompass the null check???
Of course keys are unique, so you may just end up with one record anyway in the map (I haven't looked into what object createEvent() returns and how unique it is as a key, based on its equals method), but you'd have duplicate of effort (only need to insert into map once).
Since computeIfAbsent was the issue, you could get away with using an Optional (assuming Java 8 as a minimum version) for null checking without locking??
Anyway, thank you for the quick turnaround on a fix, its appreciated.