spring-projects / spring-statemachine

Spring Statemachine is a framework for application developers to use state machine concepts with Spring.
1.53k stars 599 forks source link

UmlStateMachineModelFactory not thread safe #1032

Open joeyzhu0422 opened 2 years ago

joeyzhu0422 commented 2 years ago

Hi jvalkeal: I have an appliction which use spring statemachine by UmlStateMachineModelFactory, when application running, sometimes cpu to 100%, I found UMLResourceImpl in uml-5.0.0-v20140602-0749.jar, this class use org.eclipse.uml2.common.util.CacheAdapter by singleton which is not thread safe (https://blog.abstratt.com/2011/01/25/using-uml2-in-a-server-side-application-read-this/), have any solution in this case?

exception for below

Schedulerx-Container-Thread-3688185420-0" #1032 prio=5 os_prio=0 tid=0x00007f3f0c006000 nid=0x357c runnable [0x00007f3dc48c7000]
   java.lang.Thread.State: RUNNABLE
  at java.util.WeakHashMap.getEntry(WeakHashMap.java:431)
  at java.util.WeakHashMap.containsKey(WeakHashMap.java:417)
  at java.util.WeakHashMap$KeySet.contains(WeakHashMap.java:885)
  at org.eclipse.uml2.common.util.CacheAdapter$2.contains(CacheAdapter.java:215)
  at org.eclipse.emf.ecore.util.ECrossReferenceAdapter.selfAdapt(ECrossReferenceAdapter.java:529)
  at org.eclipse.uml2.common.util.CacheAdapter.selfAdapt(CacheAdapter.java:576)
  at org.eclipse.emf.ecore.util.ECrossReferenceAdapter.notifyChanged(ECrossReferenceAdapter.java:497)
  at org.eclipse.uml2.common.util.CacheAdapter.notifyChanged(CacheAdapter.java:380)
  at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:374)
  at org.eclipse.emf.common.notify.impl.NotifyingListImpl.dispatchNotification(NotifyingListImpl.java:261)
  at org.eclipse.emf.common.notify.impl.NotifyingListImpl.clear(NotifyingListImpl.java:1090)
  at org.eclipse.emf.ecore.resource.impl.ResourceImpl.doUnload(ResourceImpl.java:1654)
  at org.eclipse.emf.ecore.xmi.impl.XMLResourceImpl.doUnload(XMLResourceImpl.java:713)
  at org.eclipse.emf.ecore.resource.impl.ResourceImpl.unload(ResourceImpl.java:1676)
  at org.springframework.statemachine.uml.UmlStateMachineModelFactory.build(UmlStateMachineModelFactory.java:96)
  at org.springframework.statemachine.config.model.AbstractStateMachineModelFactory.build(AbstractStateMachineModelFactory.java:82)
  at org.springframework.statemachine.config.AbstractStateMachineFactory.resolveStateMachineModel(AbstractStateMachineFactory.java:452)
  at org.springframework.statemachine.config.AbstractStateMachineFactory.getStateMachine(AbstractStateMachineFactory.java:164)
  at org.springframework.statemachine.config.AbstractStateMachineFactory.getStateMachine(AbstractStateMachineFactory.java:140)
"Schedulerx-FutureExecutor-MapTaskMaster-6" #1030 prio=5 os_prio=0 tid=0x00007f3dd4254000 nid=0x357a waiting on condition [0x00007f3dc4acb000]
   java.lang.Thread.State: WAITING (parking)
  at sun.misc.Unsafe.park0(Native Method)
  - parking to wait for  <0x000000073725ada8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
  at sun.misc.Unsafe.park(Unsafe.java:1025)
  at java.util.concurrent.locks.LockSupport.park(LockSupport.java:176)
  at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
  at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:446)
  at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1077)
  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:627)
  at java.lang.Thread.run(Thread.java:852)

"Schedulerx-FutureExecutor-MapTaskMaster-5" #1029 prio=5 os_prio=0 tid=0x00007f3dd4253000 nid=0x3579 waiting on condition [0x00007f3dc4bcc000]
   java.lang.Thread.State: WAITING (parking)
  at sun.misc.Unsafe.park0(Native Method)
  - parking to wait for  <0x000000073725ada8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
  at sun.misc.Unsafe.park(Unsafe.java:1025)
  at java.util.concurrent.locks.LockSupport.park(LockSupport.java:176)
  at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
  at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:446)
giovanni-mazzucco-e-projectsrl commented 10 months ago

Exact same problem, any solution?

mehmetsalgar commented 10 months ago

Just of curiosity, how many instances of Spring State Machines you are to instantiate and how many threads are trying to access to those instances?

As I mentioned in this issue,

https://github.com/spring-projects/spring-statemachine/issues/1081#issuecomment-1367822849

Spring State Machine is not really good in scenarios in which thousand of instances / threads exists.

To make the things worst, your problem originates to an Eclipse Library from year 2014, the problem of Eclipse UML Libraries, Eclipse is using a dependency type from OSGI and repository type from .p2 and those are not compatible with Maven and Eclipse rarely export it dependencies to Maven Repositories as you can see from library date of 2014, so I don't think you will not get a fix this way.

But all hopes are not lost, as long as you are not changing you UML State Machine Design when your Spring State Machine is running, there is no reason to build the Spring State Machine based on Eclipse Papyrus UML Diagram on runtime, you can actually build it in Maven Build phase from UML Diagram.

Actually back in the day, I wrote a blog about how to create from Eclipse Papyrus UML Diagram a Spring State Machine with the help of the EnumStateMachineConfigurerAdapter and not the UmlStateMachineModelFactory.

This way, 'uml-5.0.0-v20140602-0749.jar,' will be only a build time dependency and will not appear in runtime classpath and would not have any effect on your application.

Only one word of caution, as you can see I wrote that blog at 2015 which was using the Eclipse Papyrus / Eclipse XPand stack, now a days more actual stack is Eclipse Papyrus / Eclipse XText / Eclipse XTend stack.

If you like to how that stack works, you can look the other comment mentioned above, the blogs mentioned on that issue are also using Eclipse Papyrus / Eclipse XText / Eclipse XTend stack, you can use those as example to switch to that stack, especially this one.

I hope those would help you...