maptool icon indicating copy to clipboard operation
maptool copied to clipboard

Quickly changing the facing of a token can create a java.util.ConcurrentModificationException

Open Merudo opened this issue 6 years ago • 3 comments

Describe the bug Quickly changing the facing of a token can create a java.util.ConcurrentModificationException:

java.util.ConcurrentModificationException
	at java.base/java.util.HashMap$HashIterator.nextNode(HashMap.java:1498)
	at java.base/java.util.HashMap$ValueIterator.next(HashMap.java:1526)
	at net.rptools.maptool.client.ui.zone.ZoneView.getDrawableLights(ZoneView.java:628)
	at net.rptools.maptool.client.ui.zone.ZoneRenderer.renderLights(ZoneRenderer.java:1462)
	at net.rptools.maptool.client.ui.zone.ZoneRenderer.renderZone(ZoneRenderer.java:1230)
	at net.rptools.maptool.client.ui.zone.ZoneRenderer.paintComponent(ZoneRenderer.java:872)
	at java.desktop/javax.swing.JComponent.paint(JComponent.java:1074)
	at java.desktop/javax.swing.JComponent.paintChildren(JComponent.java:907)
	at java.desktop/javax.swing.JComponent.paint(JComponent.java:1083)
	at java.desktop/javax.swing.JComponent.paintToOffscreen(JComponent.java:5255)
	at java.desktop/javax.swing.RepaintManager$PaintManager.paintDoubleBufferedImpl(RepaintManager.java:1633)
	at java.desktop/javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1608)
	at java.desktop/javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1546)
	at java.desktop/javax.swing.RepaintManager.paint(RepaintManager.java:1313)
	at java.desktop/javax.swing.JComponent._paintImmediately(JComponent.java:5203)
	at java.desktop/javax.swing.JComponent.paintImmediately(JComponent.java:5013)
	at java.desktop/javax.swing.RepaintManager$4.run(RepaintManager.java:857)
	at java.desktop/javax.swing.RepaintManager$4.run(RepaintManager.java:840)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:87)
	at java.desktop/javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:840)
	at java.desktop/javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:815)
	at java.desktop/javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:764)
	at java.desktop/javax.swing.RepaintManager.access$1200(RepaintManager.java:69)
	at java.desktop/javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1880)
	at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:313)
	at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:770)
	at java.desktop/java.awt.EventQueue.access$600(EventQueue.java:97)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:715)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:87)
	at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:740)
	at net.rptools.maptool.client.swing.MapToolEventQueue.dispatchEvent(MapToolEventQueue.java:43)
	at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
	at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)

or, the error

java.lang.NullPointerException
	at net.rptools.maptool.client.ui.zone.ZoneView.calculateVisibleArea(ZoneView.java:598)
	at net.rptools.maptool.client.ui.zone.ZoneView.getVisibleArea(ZoneView.java:92)
	at net.rptools.maptool.client.ui.zone.ZoneRenderer.renderFog(ZoneRenderer.java:1793)
	at net.rptools.maptool.client.ui.zone.ZoneRenderer.renderZone(ZoneRenderer.java:1285)
	at net.rptools.maptool.client.ui.zone.ZoneRenderer.paintComponent(ZoneRenderer.java:837)
	at java.desktop/javax.swing.JComponent.paint(JComponent.java:1074)
	at java.desktop/javax.swing.JComponent.paintChildren(JComponent.java:907)
	at java.desktop/javax.swing.JComponent.paint(JComponent.java:1083)
	at java.desktop/javax.swing.JComponent.paintToOffscreen(JComponent.java:5255)
	at java.desktop/javax.swing.RepaintManager$PaintManager.paintDoubleBufferedImpl(RepaintManager.java:1633)
	at java.desktop/javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1608)
	at java.desktop/javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1546)
	at java.desktop/javax.swing.RepaintManager.paint(RepaintManager.java:1313)
	at java.desktop/javax.swing.JComponent._paintImmediately(JComponent.java:5203)
	at java.desktop/javax.swing.JComponent.paintImmediately(JComponent.java:5013)
	at java.desktop/javax.swing.RepaintManager$4.run(RepaintManager.java:857)
	at java.desktop/javax.swing.RepaintManager$4.run(RepaintManager.java:840)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:87)
	at java.desktop/javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:840)
	at java.desktop/javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:815)
	at java.desktop/javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:764)
	at java.desktop/javax.swing.RepaintManager.access$1200(RepaintManager.java:69)
	at java.desktop/javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1880)
	at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:313)
	at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:770)
	at java.desktop/java.awt.EventQueue.access$600(EventQueue.java:97)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:715)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:87)
	at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:740)
	at net.rptools.maptool.client.swing.MapToolEventQueue.dispatchEvent(MapToolEventQueue.java:43)
	at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
	at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)


To Reproduce I'm having trouble replicating the issue, but it seems changing the facing of a token with a cone lightsource quickly generates the exception.

MapTool Info

  • Version: 1.5.7
  • Install: New

Desktop (please complete the following information):

  • OS: Windows
  • Version: 10

Merudo avatar Dec 03 '19 05:12 Merudo

Due to the rendering being done on a different thread now...? Might be enough to make a shallow copy in getDrawableLights() (or perhaps in renderLights()) of the data structure in question.

It would be more difficult — although likely a better solution — for a quick change of token facing to cancel the current render since a new one will be required anyway. I see two main issues with that, though: how to tell the separate thread to cancel the current render (there would need to be predefined points along the code path where it checks for cancelation and returns), and the fact that exposing FOW is done primarily in the rendering loop instead of outside of it. So a quick spin of token facing should expose 360° around the token, but if a render is canceled, it won't...

Or maybe there's a really simple solution and it has nothing to do with the rendering loop... 😉

Azhrei avatar Dec 03 '19 06:12 Azhrei

@Merudo Have you seen anything like this in recent versions? Lighting looks quite different internally now, and I remember squashing a bunch of concurrency-related issues as well. I didn't know about this bug report at the time, otherwise I would have been more deliberate in making sure it was fixed.

And just a note for future me or anyone debugging this: the provided stacktrace appears to be from 1.5.8 or later, not 1.5.7.

kwvanderlinde avatar Sep 27 '24 23:09 kwvanderlinde

I'm fairly certain that Merudo is (unfortunately) Foundry only these days.

FullBleed avatar Sep 28 '24 02:09 FullBleed

I did some testing on this in 1.17 and I can't duplicate this (windows 11). I do not believe this is a valid bug today

Jmr3366 avatar Apr 08 '25 11:04 Jmr3366