Skip to main content

concurrency problem in scenario

2 replies [Last post]
Anonymous

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
Chris Campbell

Hi Rémi,

Your testcase creates the UI on the main thread, not the event
dispatch thread (EDT). Furthermore, it modifies the scene on a
separate worker thread, again off of the EDT.

Scenario follows the same threading restrictions as Swing, namely that
all code that touches the Swing hierarchy (or Scenario scene graph)
must be executed on the EDT. For more info:
http://java.sun.com/docs/books/tutorial/uiswing/concurrency/dispatch.html

Chris

On Apr 4, 2008, at 2:24 PM, Rémi Emonet wrote:
> Hi all,
>
> I had the occasion to try scenario on some tiny sized projects. I
> really start to like it.
> In these projects I sometimes encounters some exceptions that seemed
> to kill the refreshing thread.
>
> I attached a simple stress test that exhibits the error occurring in
> my projects.
> The stress test spawns animated circles. Spawning period is
> controllable via a slider. When diminishing this period, the error
> occurs.
> Two cases happen:
> - a deadlock (see details in attachment)
> - an exception (see attachment)
>
> The problem occurs at least with scenario 0.5 and 0.6, Java 6.0
> linux (32 and 64bits, not tested under windows).
> Am I doing something wrong in this use case? Is this problem know?
>
> Thanks in advance,
> Rémi Emonet (not in the mailing list)
>
> /*
> */
>
> import com.sun.scenario.animation.Clip;
> import com.sun.scenario.animation.TimingTargetAdapter;
> import com.sun.scenario.scenegraph.JSGPanel;
> import com.sun.scenario.scenegraph.SGGroup;
> import com.sun.scenario.scenegraph.SGShape;
> import com.sun.scenario.scenegraph.SGTransform;
> import com.sun.scenario.scenegraph.SGTransform.Scale;
> import com.sun.scenario.scenegraph.SGTransform.Translate;
> import java.awt.BasicStroke;
> import java.awt.BorderLayout;
> import java.awt.Color;
> import java.awt.Dimension;
> import java.awt.geom.Ellipse2D;
> import java.util.Random;
> import javax.swing.JFrame;
> import javax.swing.JSlider;
>
> /**
> *
> */
> public class TestConcurrentProblemInScenario {
>
> public static void main(String[] args) {
> final JFrame f = new JFrame("Buggy?");
> f.setSize(new Dimension(400, 300));
> f.setLayout(new BorderLayout());
> f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
> final JSlider slider = new JSlider(0, 1000, 1000);
> final SGGroup sceneNode = new SGGroup();
> final JSGPanel topPanel = new JSGPanel();
> topPanel.setOpaque(true);
> topPanel.setBackground(Color.BLACK);
> topPanel.setScene(sceneNode);
> f.setLayout(new BorderLayout());
> f.add(topPanel, BorderLayout.CENTER);
> f.add(slider, BorderLayout.AFTER_LAST_LINE);
> f.setVisible(true);
>
> new Thread(new Runnable() {
> public void run() {
> try {
> while (true) {
> Thread.sleep(slider.getModel().getValue());
> SGShape node = new SGShape();
> node.setShape(new Ellipse2D.Double(-5, -5,
> 10, 10));
> node.setFillPaint(Color.yellow);
> node.setDrawStroke(new BasicStroke(2.f));
> node.setDrawPaint(Color.RED);
> node.setMode(SGShape.Mode.STROKE_FILL);
> final Scale scale =
> SGTransform.createScale(1, 1, node);
> final Translate translation =
> SGTransform.createTranslation(50 + new Random().nextInt(300), 50 +
> new Random().nextInt(200), scale);
> sceneNode.add(translation);
> {
> int factor = 2;
> Clip clipX = Clip.create(400 * factor,
> scale, "scaleX", 1., 7., 14., 1., 1., 0.);
> Clip clipY = Clip.create(500 * factor,
> scale, "scaleY", 1., 10., 14., 1., 1., 0.);
> clipX.start();
> clipY.addTarget(new TimingTargetAdapter() {
> @Override
> public void end() {
> sceneNode.remove(translation); //
> same problem without this line
> }
> });
> clipY.start();
> }
> }
> } catch (InterruptedException ex) {
> Thread.interrupted();
> }
> }
> }).start();
> }
> }
>
>
> deadlock between:
> AWT-EventQueue-1
> owned java.lang.Object
> contended com.sun.scenario.animation.MasterTimer$MainLoop
> Thread-1
> owned com.sun.scenario.animation.MasterTimer$MainLoop
> contended java.lang.Object
>
> stacks:
> AWT-EventQueue-1
> com.sun.scenario.animation.MasterTimer
> $MainLoop.setJobsReady(MasterTimer.java:293)
> com.sun.scenario.animation.MasterTimer.timePulse(MasterTimer.java:
> 354)
> com.sun.scenario.animation.MasterTimer
> $MainLoop.run(MasterTimer.java:264)
> com.sun.scenario.scenegraph.SGEventQueue.doPulse(SGEventQueue.java:
> 475)
> com.sun.scenario.scenegraph.SGEventQueue.access
> $000(SGEventQueue.java:65)
> com.sun.scenario.scenegraph.SGEventQueue$2.run(SGEventQueue.java:
> 382)
> java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
> java.awt.EventQueue.dispatchEvent(EventQueue.java:597)
>
> com
> .sun
> .scenario.scenegraph.SGEventQueue.dispatchEvent(SGEventQueue.java:374)
>
> java
> .awt
> .EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:
> 273)
>
> java
> .awt
> .EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:183)
>
> java
> .awt
> .EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:
> 173)
> java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:
> 168)
> java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:
> 160)
> java.awt.EventDispatchThread.run(EventDispatchThread.java:121)
> Thread-1
>
> com
> .sun
> .scenario
> .scenegraph.SGEventQueue.setAnimationRunnable(SGEventQueue.java:485)
>
> com
> .sun
> .scenario
> .scenegraph.SwingGlueLayer.setAnimationRunnable(SwingGlueLayer.java:
> 87)
> com.sun.scenario.animation.MasterTimer
> $MainLoop.updateAnimationRunnable(MasterTimer.java:304)
> com.sun.scenario.animation.MasterTimer
> $MainLoop.setJobsReady(MasterTimer.java:295)
>
> com
> .sun.scenario.animation.MasterTimer.notifyJobsReady(MasterTimer.java:
> 238)
> com.sun.scenario.animation.FrameJob.wakeUp(FrameJob.java:34)
>
> com
> .sun
> .scenario
> .scenegraph.JSGPanelRepainter.addDirtyPanel(JSGPanelRepainter.java:58)
> com.sun.scenario.scenegraph.JSGPanel.markDirty(JSGPanel.java:614)
> com.sun.scenario.scenegraph.SGNode.markDirty(SGNode.java:495)
> com.sun.scenario.scenegraph.SGNode.markDirty(SGNode.java:493)
> com.sun.scenario.scenegraph.SGNode.markDirty(SGNode.java:493)
> com.sun.scenario.scenegraph.SGNode.markDirty(SGNode.java:493)
> com.sun.scenario.scenegraph.SGNode.markDirty(SGNode.java:502)
> com.sun.scenario.scenegraph.SGGroup.add(SGGroup.java:81)
> com.sun.scenario.scenegraph.SGGroup.add(SGGroup.java:86)
> info.TestConcurrentProblemInScenario
> $2.run(TestConcurrentProblemInScenario.java:98)
> java.lang.Thread.run(Thread.java:619)
>
> exception (when there is no deadlock),
> Unexpected exception caught in MasterTimer.timePulse():
> java.util.ConcurrentModificationException
> at java.util.AbstractList
> $Itr.checkForComodification(AbstractList.java:372)
> at java.util.AbstractList$Itr.next(AbstractList.java:343)
> at java.util.Collections$UnmodifiableCollection
> $1.next(Collections.java:1010)
> at com.sun.scenario.scenegraph.SGNode.render(SGNode.java:858)
> at com.sun.scenario.scenegraph.SGNode.render(SGNode.java:755)
> at com.sun.scenario.scenegraph.SGNode.render(SGNode.java:859)
> at com.sun.scenario.scenegraph.SGNode.render(SGNode.java:859)
> at
> com.sun.scenario.scenegraph.JSGPanel.paintComponent(JSGPanel.java:524)
> at javax.swing.JComponent.paint(JComponent.java:1022)
> at javax.swing.JComponent.paintToOffscreen(JComponent.java:
> 5104)
> at
> javax
> .swing
> .BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:285)
> at javax.swing.RepaintManager.paint(RepaintManager.java:1132)
> at javax.swing.JComponent._paintImmediately(JComponent.java:
> 5052)
> at javax.swing.JComponent.paintImmediately(JComponent.java:
> 4862)
> at javax.swing.JComponent.paintImmediately(JComponent.java:
> 4874)
> at
> com
> .sun.scenario.scenegraph.JSGPanel.repaintDirtyRegions(JSGPanel.java:
> 589)
> at
> com
> .sun
> .scenario
> .scenegraph.JSGPanelRepainter.repaintAll(JSGPanelRepainter.java:98)
> at com.sun.scenario.scenegraph.JSGPanelRepainter
> $FrameDisplay.run(JSGPanelRepainter.java:104)
> at
> com.sun.scenario.animation.MasterTimer.timePulse(MasterTimer.java:358)
> at com.sun.scenario.animation.MasterTimer
> $MainLoop.run(MasterTimer.java:264)
> at
> com.sun.scenario.scenegraph.SGEventQueue.doPulse(SGEventQueue.java:
> 475)
> at com.sun.scenario.scenegraph.SGEventQueue.access
> $000(SGEventQueue.java:65)
> at com.sun.scenario.scenegraph.SGEventQueue
> $2.run(SGEventQueue.java:382)
> at
> java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
> at java.awt.EventQueue.dispatchEvent(EventQueue.java:597)
> at
> com
> .sun
> .scenario.scenegraph.SGEventQueue.dispatchEvent(SGEventQueue.java:374)
> at
> java
> .awt
> .EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:
> 273)
> at
> java
> .awt
> .EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:183)
> at
> java
> .awt
> .EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:
> 173)
> at
> java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:168)
> at
> java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:160)
> at java.awt.EventDispatchThread.run(EventDispatchThread.java:
> 121)
>
> it kills the refreshing thread,
> resizing forces refresh but can generate more exceptions
> Exception in thread "AWT-EventQueue-1"
> java.util.ConcurrentModificationException
> at java.util.AbstractList
> $Itr.checkForComodification(AbstractList.java:372)
> at java.util.AbstractList$Itr.next(AbstractList.java:343)
> at java.util.Collections$UnmodifiableCollection
> $1.next(Collections.java:1010)
> at com.sun.scenario.scenegraph.SGNode.render(SGNode.java:858)
> at com.sun.scenario.scenegraph.SGNode.render(SGNode.java:755)
> at com.sun.scenario.scenegraph.SGNode.render(SGNode.java:859)
> at com.sun.scenario.scenegraph.SGNode.render(SGNode.java:859)
> at
> com.sun.scenario.scenegraph.JSGPanel.paintComponent(JSGPanel.java:524)
> at javax.swing.JComponent.paint(JComponent.java:1022)
> at javax.swing.JComponent.paintChildren(JComponent.java:859)
> at javax.swing.JComponent.paint(JComponent.java:1031)
> at javax.swing.JComponent.paintChildren(JComponent.java:859)
> at javax.swing.JComponent.paint(JComponent.java:1031)
> at javax.swing.JLayeredPane.paint(JLayeredPane.java:564)
> at javax.swing.JComponent.paintChildren(JComponent.java:859)
> at javax.swing.JComponent.paintToOffscreen(JComponent.java:
> 5111)
> at
> javax
> .swing
> .BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:285)
> at javax.swing.RepaintManager.paint(RepaintManager.java:1132)
> at javax.swing.JComponent.paint(JComponent.java:1008)
> at java.awt.GraphicsCallback
> $PaintCallback.run(GraphicsCallback.java:21)
> at
> sun.awt.SunGraphicsCallback.runOneComponent(SunGraphicsCallback.java:
> 60)
> at
> sun.awt.SunGraphicsCallback.runComponents(SunGraphicsCallback.java:97)
> at java.awt.Container.paint(Container.java:1797)
> at
> javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:738)
> at
> javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:683)
> at
> javax.swing.RepaintManager.seqPaintDirtyRegions(RepaintManager.java:
> 663)
> at javax.swing.SystemEventQueueUtilities
> $ComponentWorkRequest.run(SystemEventQueueUtilities.java:128)
> at
> java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
> at java.awt.EventQueue.dispatchEvent(EventQueue.java:597)
> at
> com
> .sun
> .scenario.scenegraph.SGEventQueue.dispatchEvent(SGEventQueue.java:374)
> at
> java
> .awt
> .EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:
> 273)
> at
> java
> .awt
> .EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:183)
> at
> java
> .awt
> .EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:
> 173)
> at
> java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:168)
> at
> java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:160)
> at java.awt.EventDispatchThread.run(EventDispatchThread.java:
> 121)
> Exception in thread "AWT-EventQueue-1"
> java.util.ConcurrentModificationException
> at java.util.AbstractList
> $Itr.checkForComodification(AbstractList.java:372)
> at java.util.AbstractList$Itr.next(AbstractList.java:343)
> at java.util.Collections$UnmodifiableCollection
> $1.next(Collections.java:1010)
> at com.sun.scenario.scenegraph.SGNode.render(SGNode.java:858)
> at com.sun.scenario.scenegraph.SGNode.render(SGNode.java:755)
> at com.sun.scenario.scenegraph.SGNode.render(SGNode.java:859)
> at com.sun.scenario.scenegraph.SGNode.render(SGNode.java:859)
> at
> com.sun.scenario.scenegraph.JSGPanel.paintComponent(JSGPanel.java:524)
> at javax.swing.JComponent.paint(JComponent.java:1022)
> at javax.swing.JComponent.paintChildren(JComponent.java:859)
> at javax.swing.JComponent.paint(JComponent.java:1031)
> at javax.swing.JComponent.paintChildren(JComponent.java:859)
> at javax.swing.JComponent.paint(JComponent.java:1031)
> at javax.swing.JLayeredPane.paint(JLayeredPane.java:564)
> at javax.swing.JComponent.paintChildren(JComponent.java:859)
> at javax.swing.JComponent.paintToOffscreen(JComponent.java:
> 5111)
> at
> javax
> .swing
> .BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:285)
> at javax.swing.RepaintManager.paint(RepaintManager.java:1132)
> at javax.swing.JComponent.paint(JComponent.java:1008)
> at java.awt.GraphicsCallback
> $PaintCallback.run(GraphicsCallback.java:21)
> at
> sun.awt.SunGraphicsCallback.runOneComponent(SunGraphicsCallback.java:
> 60)
> at
> sun.awt.SunGraphicsCallback.runComponents(SunGraphicsCallback.java:97)
> at java.awt.Container.paint(Container.java:1797)
> at
> javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:738)
> at
> javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:683)
> at
> javax.swing.RepaintManager.seqPaintDirtyRegions(RepaintManager.java:
> 663)
> at javax.swing.SystemEventQueueUtilities
> $ComponentWorkRequest.run(SystemEventQueueUtilities.java:128)
> at
> java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
> at java.awt.EventQueue.dispatchEvent(EventQueue.java:597)
> at
> com
> .sun
> .scenario.scenegraph.SGEventQueue.dispatchEvent(SGEventQueue.java:374)
> at
> java
> .awt
> .EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:
> 273)
> at
> java
> .awt
> .EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:183)
> at
> java
> .awt
> .EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:
> 173)
> at
> java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:168)
> at
> java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:160)
> at java.awt.EventDispatchThread.run(EventDispatchThread.java:
> 121)
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@scenegraph.dev.java.net
> For additional commands, e-mail: dev-help@scenegraph.dev.java.net

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@scenegraph.dev.java.net
For additional commands, e-mail: dev-help@scenegraph.dev.java.net

Remi Emonet

Hi Chris,

Thank you for your quick reply.
I think the magic behind Clip (that calls setters in the EDT) made me
think the scene graph was thread safe and forget my basic swing skills.
I'm happy the problem was on my side.

Best Regard,
Rémi

Chris Campbell wrote:
> Hi Rémi,
>
> Your testcase creates the UI on the main thread, not the event
> dispatch thread (EDT). Furthermore, it modifies the scene on a
> separate worker thread, again off of the EDT.
>
> Scenario follows the same threading restrictions as Swing, namely that
> all code that touches the Swing hierarchy (or Scenario scene graph)
> must be executed on the EDT. For more info:
> http://java.sun.com/docs/books/tutorial/uiswing/concurrency/dispatch.html
>
> Chris

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@scenegraph.dev.java.net
For additional commands, e-mail: dev-help@scenegraph.dev.java.net