RouteGraphUtils.load contains invalid concurrent access to G2D IElement/SceneGraph structures that are already a part of the rendered scene
Reino sai Aprosissa lokiin tällaisia poikkeuksia muokatessa pipelinen rakennetta taulukosta, joka triggeröi pipelinen symbolin uudelleengeneroinnin:
!ENTRY org.simantics.utils.ui 4 0 2023-06-06 13:08:52.588
!MESSAGE java.util.ConcurrentModificationException
!STACK 0
org.simantics.db.exception.DatabaseException: java.util.ConcurrentModificationException
at org.simantics.db.impl.query.ReadEntry.computeForEach(ReadEntry.java:133)
at org.simantics.db.impl.query.QueryCache.runnerReadEntry(QueryCache.java:697)
at org.simantics.db.impl.graph.ReadGraphImpl$24.run0(ReadGraphImpl.java:5190)
at org.simantics.db.impl.query.QueryProcessor$SessionTask.run(QueryProcessor.java:234)
at org.simantics.db.impl.query.QueryProcessor.performPending(QueryProcessor.java:178)
at org.simantics.db.impl.graph.ReadGraphImpl.performPending(ReadGraphImpl.java:6462)
at org.simantics.db.impl.graph.AsyncBarrierImpl.waitBarrier(AsyncBarrierImpl.java:181)
at org.simantics.db.impl.BlockingAsyncProcedure.get(BlockingAsyncProcedure.java:87)
at org.simantics.db.impl.BlockingAsyncProcedure.performSync(BlockingAsyncProcedure.java:170)
at org.simantics.db.impl.query.AsyncReadEntry$1.recompute(AsyncReadEntry.java:96)
at org.simantics.db.impl.query.QueryProcessor.compareTo(QueryProcessor.java:1373)
at org.simantics.db.impl.query.QueryListening.fireListeners_(QueryListening.java:268)
at org.simantics.db.impl.query.QueryListening.fireListeners(QueryListening.java:227)
at fi.vtt.simantics.procore.internal.State.commitWriteTransaction(State.java:344)
at fi.vtt.simantics.procore.internal.SessionRequestManager$4.run0(SessionRequestManager.java:236)
at org.simantics.db.impl.query.QueryProcessor$SessionTask.run(QueryProcessor.java:234)
at org.simantics.db.impl.query.QueryThread.run(QueryThread.java:238)
Caused by: java.util.ConcurrentModificationException
at java.base/java.util.HashMap$HashIterator.nextNode(HashMap.java:1511)
at java.base/java.util.HashMap$ValueIterator.next(HashMap.java:1539)
at org.simantics.scenegraph.g2d.G2DParentNode.getBoundsInLocal(G2DParentNode.java:351)
at org.simantics.scenegraph.g2d.G2DParentNode.getBoundsInLocal(G2DParentNode.java:352)
at org.simantics.scenegraph.g2d.G2DParentNode.getBoundsInLocal(G2DParentNode.java:341)
at org.simantics.diagram.adapter.DefinedElementHandler.getBounds(DefinedElementHandler.java:85)
at org.simantics.g2d.element.ElementUtils.getElementBounds(ElementUtils.java:598)
at org.simantics.diagram.adapter.RouteGraphUtils.load(RouteGraphUtils.java:292)
at org.simantics.diagram.adapter.RouteGraphConnectionClassFactory.load(RouteGraphConnectionClassFactory.java:109)
at org.simantics.diagram.adapter.SyncElementFactory$2.perform(SyncElementFactory.java:52)
at org.simantics.diagram.adapter.SyncElementFactory$2.perform(SyncElementFactory.java:1)
at org.simantics.db.impl.query.ReadEntry.computeForEach(ReadEntry.java:121)
... 16 more
Show more
Kyseinen collection on siis org.simantics.scenegraph.ParentNode.children. Tässä tapauksessa kyseessä oli luultavasti taulukon kautta tehty muutos pipelineen.
RouteGraphUtils.load
käy katsomassa kytkentää ladatessa missä elementissä se kytkentä on kiinni ja laskemassa löytyneen kiinnittyneen IElement:n boundsit. Ja ilmeisesti jokin muuttaa samaan aikaan sen koosteisen elementin alirakennetta..
Hmm.. Paitsi tietenkin symboli tässä vaihtuu kun muokkaat pipelineä, niin symboli generoidaan uudestaan. Tästä seuraa se, että pipelinen IElementin alirakenteen noodit muuttuu, koska sinne päivitetään ehkä uusi SVGNode jonnekin pohjatasolle
Olennaisesti `org.simantics.diagram.adapter.RouteGraphUtils.load(RouteGraphUtils.java:292)´ metodia ajetaan tietokantasäikeestä samalla kun CanvasContextin säie (AWT) käy muokkaamassa elementtiä.
Yksi pipeline esimerkki näyttää tuolta
Tuosta näkee myös, että se laskee kolme tasoa rekursiivisesti getBoundsInLocalia kunnes tulee sen lapsista virhe.
Scenegraafi ei ole monisäikeinen. GraphToDiagramSynchronizer
pyrkii huolehtimaan siitä, että se kuuntelee DB:n muutoksia ja skeduloi päivityksiä ICanvasContextn säikeeseen. Mutta tässä käy nyt niin, että DB-säikeestä yritetään lukea asioita, jotka voivat muuttua samanaikaisesti CanvasContext
-säikeessä.
Vastaavalla tavalla uudelleenkirjoitettavia "dynaamisia" symboleita on ollut varsin vähän.
IElement te = graph.syncRequest(DiagramRequests.getElement(canvas, diagram, terminalElement, null));
ElementUtils.getElementBounds(te, bounds);
yritetään ladata IElement
iä vain siinä tarkoituksessa, että saataisiin ladattua sen boundsit. Käytännössä kuitenkin varmaan tuo IElement mitä tuolta palautuu tulee DB:n cachesta ja se on sitten jaettu. getBounds
in toteutus riippuu ihan siitä onko siellä takana IG2DNode vai ei. Tässä tapauksessa selvästi DB:n cachesta saadaan sellainen IElement, joka on jo alustettu osaksi IDiagramia ja scenegraafia, koska sillä on G2DNode
. Vain tässä tapauksessa tuo koodi kutsuu InternalSize.getBounds
ja siten scenegraafin noodien getBoundsInLocal
ia.
Tällä haavaa en keksi mitään korjausta, muuten kuin ton boundsien käytön poistamisen kokonaan tuosta RouteGraphUtils.load
sta. Käytännössä se johtaisi siihen, ettei tätä entistä kytkentöjen automaattista suunnistusta terminaaleista ulos enää olisi, eli kytkennät vois lähtökohtaisesti tulla aina mistä ilmansuunnasta hyvänsä ja kulkisi sitten symbolien läpi surutta jne.