Java-Gaming.org    
Featured games (79)
games approved by the League of Dukes
Games in Showcase (477)
Games in Android Showcase (109)
games submitted by our members
Games in WIP (536)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Strange AWT related threding problem  (Read 3330 times)
0 Members and 1 Guest are viewing this topic.
Offline BBjam

Junior Newbie





« Posted 2008-09-10 08:28:15 »

This is also posted here on the SDN, but seeing as I'm not sure how active those forums are, and that I can not do anything until this is fixed, I'm also posting here.
   
I am having some problems with the event handeling in the game engine I am writing:
I need to listen to key press events and release events and put them in to some Sets, which are read from the step()
method every frame and transalated into platform-independent GFKeyEvents for inturnal use.
The reason for doing so is to get rid of key repeating so the game objects just see "press, hold, hold, hold, release" no mater what the OS is doing.

As you will see the key event methods are synchronized as is step() yet some how I still get

1  
2  
3  
4  
5  
Exception in thread "Event thread 1" java.util.ConcurrentModificationException
   at java.util.HashMap$HashIterator.nextEntry(HashMap.java:810)
   at java.util.HashMap$KeyIterator.next(HashMap.java:845)
   at com.glassFlame.event.EventSystem.step(EventSystem.java:77)
   at com.glassFlame.event.EventThread.run(EventThread.java:36)



I have also wrapped the Sets using

1  
Collections.synchronizedSet()




here is the class with some of the irelivent methods removed:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
192  
193  
194  
195  
package com.glassFlame.event;
 
import java.awt.Component;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.HashSet;
import java.util.Vector;
import java.util.Collections;
import java.util.Set;
 
import com.glassFlame.GFObject;
import com.glassFlame.renderer.RenderManager;
import com.glassFlame.renderer.Renderer;
import com.glassFlame.scene.Scene;
import com.glassFlame.scene.bounding.BoundingStructure;
 
public class EventSystem implements KeyListener, MouseListener, MouseMotionListener{
 
   private BoundingStructure  graph;
   private EventThread        pump;
   private Component          source;
   private Scene              scene;
   private RenderManager      ren;
   private boolean            clear;
   private Vector<GFObject>   objs;
   private Vector<GFKeyListener> klobjs;
   private Set<GFKeyEvent>  keysHeld, keysPressedThisFrame, keysReleasedThisFrame, keysReleasedLastFrame, keysPressedLastFrame;
   private Vector<GFKeyEvent>  keyPresses, keyHolds, keyReleases;
   private Object keyLock = new Object();
 
   public EventSystem(BoundingStructure graph,Component source,RenderManager ren,int millsPerFrame)
   {
      this.clear  = true;
      this.graph  = graph;
      this.source = source;
      this.ren    = ren;
 
      pump = new EventThread(this,millsPerFrame);
 
      objs   = new Vector<GFObject>(100);
      klobjs = new Vector<GFKeyListener>(100);
 
      keysHeld              = Collections.synchronizedSet( new HashSet<GFKeyEvent>());
      keysPressedThisFrame  = Collections.synchronizedSet( new HashSet<GFKeyEvent>());
      keysReleasedThisFrame = Collections.synchronizedSet( new HashSet<GFKeyEvent>());
      keysReleasedLastFrame = Collections.synchronizedSet( new HashSet<GFKeyEvent>());
      keysPressedLastFrame  = Collections.synchronizedSet( new HashSet<GFKeyEvent>());
 
      keyPresses  = new Vector<GFKeyEvent>();
      keyHolds    = new Vector<GFKeyEvent>();
      keyReleases = new Vector<GFKeyEvent>();
 
      this.source.addKeyListener(this);
      this.source.addMouseListener(this);
      this.source.addMouseMotionListener(this);
   }
 
   public synchronized void addObject(GFObject obj)
   {
      graph.addObject(obj);
      objs.add(obj);
      if(obj instanceof GFKeyListener)
         klobjs.add((GFKeyListener) obj);
   }
 
   public synchronized void step(long millsPassed)
   {
      //TODO:event handling goes here

      //Process key events
     //this is required because they behave diferant on diferent platforms

      //remove keys that have been released for the held keys list
     for(GFKeyEvent ke : keysHeld)
      {
         //if the key in question was not touched this frame
        //and was released last frame
        if( (!keysPressedThisFrame.contains(ke) && !keysReleasedThisFrame.contains(ke))
               &&
               keysReleasedLastFrame.contains(ke))
            //the key has been released
        {
            System.out.println(ke.toString());
            keysHeld.remove(ke);
            keyReleases.add(ke);
         }
         else
            keyHolds.add(ke);
      }
 
      //ditect key presses by checking if a key pressed this frame
     //was not also presses the previous frame.
     //This makes it work on linux and turns extreamly fast key taps into
     //a key hold
     for(GFKeyEvent ke : keysPressedThisFrame)
      {
         if(keysPressedLastFrame.contains(ke) == false)
            keyPresses.add(ke);
      }
      //fire the key releases
     for(GFKeyListener kl : klobjs)
      {
         for(GFKeyEvent ke : keyReleases)
         {
            kl.keyReleased(ke);
         }
      }
      //fire the key holds
     for(GFKeyListener kl : klobjs)
      {
         for(GFKeyEvent ke : keyHolds)
         {
            kl.keyHeld(ke);
         }
      }
      //fire the key presses and transfer the events into the keysHeld array
     for(GFKeyListener kl : klobjs)
      {
         for(GFKeyEvent ke : keyPresses)
         {
            if(!keysHeld.contains(ke))
            {
               kl.keyPressed(ke);
               ke.setID(GFKeyEvent.KEY_TYPED);
               keysHeld.add(ke);
            }
         }
      }
 
      //System.out.println("keys presses  " + keyPresses.size());
     //System.out.println("keys holds    " + keyHolds.size());
     //System.out.println("keys releases " + keyReleases.size());
     //System.out.println(keysHeld.toString());

      //swap the last-frame and this-frame sets;
     Set<GFKeyEvent> tem;
 
      tem                  = keysPressedLastFrame;
      keysPressedLastFrame = keysPressedThisFrame;
      keysPressedThisFrame = tem;
 
      tem                   = keysReleasedLastFrame;
      keysReleasedLastFrame = keysReleasedThisFrame;
      keysReleasedThisFrame = tem;
      //clear all the events from last time
     keysPressedThisFrame.clear();
      keysReleasedThisFrame.clear();
 
      keyPresses.clear();
      keyHolds.clear();
      keyReleases.clear();
      //clear the buffer before drawing
     if(clear)
         ren.setBuffer(Renderer.BUFFER_DEFAULT);
      ren.clear();
      //call the draw events of all the objects
     ren.start();
      for(int i = 0; i < objs.size();i++)
      {
         objs.get(i).draw(ren);
      }
      ren.stop();
      ren.done();
   }
   public void start()
   {
      pump.start();
   }
   public void stop()
   {
      pump.pause();
   }
   public boolean running()
   {
      return pump.running();
   }
 
   @Override
   public synchronized void keyPressed(KeyEvent e) {
      keysPressedThisFrame.add(new GFKeyEvent(e,KeyEvent.KEY_PRESSED));
      System.out.println("press " + KeyEvent.getKeyText(e.getKeyCode()) );
   }
   @Override
   public synchronized void keyReleased(KeyEvent e) {
      keysReleasedThisFrame.add(new GFKeyEvent(e,KeyEvent.KEY_RELEASED));
      System.out.println("release " + KeyEvent.getKeyText(e.getKeyCode()) );
   }
   @Override
   public synchronized void keyTyped(KeyEvent e) {
//not used
  }
}




The class is construted and then the method start() is called which calls start() on the EventThread which in turn calls step about 30 times a second.
Offline Abuse

JGO Coder


Medals: 11


falling into the abyss of reality


« Reply #1 - Posted 2008-09-10 10:57:03 »

Your problem is not related to synchronization or threading.

You have fallen foul of the 'improved' for loop.

1  
for(GFKeyEvent ke : keysHeld)


This construct uses an Iterator to step over the contents of the iterable keysHeld.
While an Iterator is in use, you must not modify the underlying Collection - otherwise, as you have discovered, the Iterator will throw a ConcurrentModificationException the next time any of its methods are invoked. (lucky for you the HashMap implementation has a fail-fast behaviour; otherwise this bug would have been much harder to find)

You should change your for loop construct to one that explicitly declares the Iterator, and then use the Iterators own remove(...) method to modify the Collection.

Make Elite IV:Dangerous happen! Pledge your backing at KICKSTARTER here! https://dl.dropbox.com/u/54785909/EliteIVsmaller.png
Offline BBjam

Junior Newbie





« Reply #2 - Posted 2008-09-12 23:53:28 »

Thank you very much!  Cheesy
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

CogWheelz (18 views)
2014-07-30 21:08:39

Riven (25 views)
2014-07-29 18:09:19

Riven (15 views)
2014-07-29 18:08:52

Dwinin (13 views)
2014-07-29 10:59:34

E.R. Fleming (33 views)
2014-07-29 03:07:13

E.R. Fleming (12 views)
2014-07-29 03:06:25

pw (43 views)
2014-07-24 01:59:36

Riven (44 views)
2014-07-23 21:16:32

Riven (30 views)
2014-07-23 21:07:15

Riven (31 views)
2014-07-23 20:56:16
List of Learning Resources
by SilverTiger
2014-07-31 18:29:50

List of Learning Resources
by SilverTiger
2014-07-31 18:26:06

List of Learning Resources
by SilverTiger
2014-07-31 13:54:12

HotSpot Options
by dleskov
2014-07-08 03:59:08

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:58:24

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:47:22

How do I start Java Game Development?
by ra4king
2014-05-17 11:13:37

HotSpot Options
by Roquen
2014-05-15 09:59:54
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!