Page 1 of 1

Java SE API Concurrency problem

Posted: 11 Sep 2012, 12:52
by rgipd
Hi!

I'm currently doing quick tests and demos with SFS2x Java API.
I'm using Swing UI for it.

I can post all the code here, but I'm using a quick example to show the problem...

I have the classe conn.Login.
It's implements the IEventListener SFS2x class for handling events from the client API.
Something like this:

Code: Select all

package conn;

public class Login implements IEventListener {
   
   private SmartFox clientAPI;
   
   
   public Login(SmartFox client) {
      clientAPI = client;
   }
   
   
   @Override
   public void dispatch(final BaseEvent event) throws SFSException {
      String eventType = event.getType();
      
      if(eventType.equals(SFSEvent.LOGIN)) {
         User user = (User)event.getArguments().get("user");
         ISFSObject data = (ISFSObject)event.getArguments().get("data");
         
         // Here the login success will be notified to UI interface, so the SwingUI can show that Login is done.
         // The method that will be called is onLoginSuccess().
      }
      else if(eventType.equals(SFSEvent.LOGIN_ERROR)) {
         String errorMessage = (String)event.getArguments().get("errorMessage");
         short errorCode = (Short)event.getArguments().get("errorCode");
         
         // Here the login error will be notified to UI interface, so the SwingUI can show that Login had errors..
      }
      else {}
   }
   
   public final void init() {
      clientAPI.addEventListener(SFSEvent.LOGIN, this);
      clientAPI.addEventListener(SFSEvent.LOGIN_ERROR, this);
   }

   public final void free() {
      clientAPI.removeEventListener(SFSEvent.LOGIN, this);
      clientAPI.removeEventListener(SFSEvent.LOGIN_ERROR, this);
   }
   
   
}



The first problem is:
When I receive the login, I want to free the class conn.Login.
The best time to do it is when the login is successful. So in the Controller's of SwingUI class I put something like it:

Code: Select all

   // Method called from the conn.Login::dispatch(SFSEvent.LOGIN).
   public final void onLoginSuccess() {
      myAppController.freeLogin();
   }


myAppController is the class that bind SwingUI (create view) and also has the conn.Login instance.

The freeLogin() methods is going to call conn.Login::free() that will removeEventListeners from clientAPI.
That happens in the same call of dispatch() that is using the event listeners list in the SFS2x Client API.
Then occurs the Concurrency Problem:

Code: Select all

java.util.ConcurrentModificationException
   at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
   at java.util.AbstractList$Itr.next(AbstractList.java:343)
   at sfs2x.client.core.EventDispatcher.dispatchEvent(EventDispatcher.java:51)
   at sfs2x.client.SmartFox.dispatchEvent(SmartFox.java:1265)
   at sfs2x.client.controllers.system.ResLogout.handleResponse(ResLogout.java:26)
   at sfs2x.client.controllers.SystemController.handleMessage(SystemController.java:104)
   at sfs2x.client.core.SFSProtocolCodec.dispatchRequest(SFSProtocolCodec.java:144)
   at sfs2x.client.core.SFSProtocolCodec.onPacketRead(SFSProtocolCodec.java:50)
   at sfs2x.client.core.SFSIOHandler.handlePacketData(SFSIOHandler.java:281)
   at sfs2x.client.core.SFSIOHandler.onDataRead(SFSIOHandler.java:130)
   at sfs2x.client.bitswarm.BitSwarmClient.onSocketData(BitSwarmClient.java:354)
   at sfs2x.client.bitswarm.BitSwarmClient.access$2(BitSwarmClient.java:348)
   at sfs2x.client.bitswarm.BitSwarmClient$3.dispatch(BitSwarmClient.java:105)
   at sfs2x.client.core.EventDispatcher.dispatchEvent(EventDispatcher.java:52)
   at sfs2x.client.core.sockets.TCPSocketLayer.callOnData(TCPSocketLayer.java:144)
   at sfs2x.client.core.sockets.TCPSocketLayer.handleBinaryData(TCPSocketLayer.java:138)
   at sfs2x.client.core.sockets.TCPSocketLayer.access$6(TCPSocketLayer.java:137)
   at sfs2x.client.core.sockets.TCPSocketLayer$NettyIOHandler.messageReceived(TCPSocketLayer.java:273)
   at org.jboss.netty.channel.SimpleChannelHandler.handleUpstream(SimpleChannelHandler.java:100)
   at org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:545)
   at org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:540)
   at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:274)
   at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:261)
   at org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:350)
   at org.jboss.netty.channel.socket.nio.NioWorker.processSelectedKeys(NioWorker.java:281)
   at org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:201)
   at org.jboss.netty.util.ThreadRenamingRunnable.run(ThreadRenamingRunnable.java:108)
   at org.jboss.netty.util.internal.IoWorkerRunnable.run(IoWorkerRunnable.java:46)
   at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
   at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
   at java.lang.Thread.run(Thread.java:680)


How I can remove the eventListeners of Login (aka free the conn.Login class) after the event login success?


The same problem will happen with ROOM_JOIN::

There are a "Global Lobby Room" that I enter by default
Inside it, I can stay there or go to 5 rooms I can choose (North America, South America, Europe, Australia, Asia)

The first ROOM_JOIN event. I need to remove it from the conn.Lobby class after I enter this room "Global Lobby Room"., so I can enable and use the ROOM_JOIN event in the class conn.CountryRoom to enter the "South America" room.

But will fall in Concurrency problem, like the LOGIN. Because I'm calling the removeEventListener(SFSEvent.ROOM_JOIN...) when the dispatch happens.

Any clues?
I will need one more Thread and queue to remove the handlers?
There's another way?
Because I can receive a private message, per example, at same moment that the other Thread is removing the eventListener.


Thanks in advance!
R.

Re: Java SE API Concurrency problem

Posted: 13 Sep 2012, 18:09
by Lapo
You don't need to remove event listeners as they are dispatched. The correct practice is to release the listeners upon disconnection.

Re: Java SE API Concurrency problem

Posted: 13 Sep 2012, 18:32
by rgipd
Hi Lapo,

So you think that is better have one class as IEventListener and add only this class to clientAPI.addEventListener() and only at appliication closes, disconnect and release handlers (clientAPI.removeEventListener()) ?!

Because I have two classes with IEventListener, that "watches" for ROOM_JOIN, per example, that I want to release and init, but it happens in the same dispatch() Thread. So I can't remove one of listener of clientAPI and, because this, both receives the same event at same time.

Basically I will need the change of my App behavior then, right?


Thanks!
R.

Re: Java SE API Concurrency problem

Posted: 14 Sep 2012, 08:12
by Lapo
So you think that is better have one class as IEventListener and add only this class to clientAPI.addEventListener() and only at appliication closes, disconnect and release handlers (clientAPI.removeEventListener()) ?!

Yes, that's one possibility.
If you don't like to use one class with a dozen of if (...) statements you can add individual listeners as well, it doesn't matter. The impact on memory is very low and almost equal between the two approaches.

Because I have two classes with IEventListener, that "watches" for ROOM_JOIN, per example, that I want to release and init, but it happens in the same dispatch() Thread. So I can't remove one of listener of clientAPI and, because this, both receives the same event at same time.

Using two listeners for the same event is okay, I don't think there should be problems there. What is not clear to me is why do you want to get rid from it at runtime. Shouldn't you keep all the listeners alive until the application is closed/disconnected?

In any case I will check with Thomas Lund this concurrency problem, maybe we need some xtra synchronization.

Thanks

Re: Java SE API Concurrency problem

Posted: 14 Sep 2012, 10:28
by rgipd
Ok!
Thanks Lapo!

Qucik question: the others API for clients, like ActionScript, iOS, works the same way? That is better not remove the current listener on dispatch() method?

By the way, I was developing an Application that I had the need to remove one listener because of memory and to give room to another listener. I'm already doing another implementation to workaround it.


Thanks!
R.

Re: Java SE API Concurrency problem

Posted: 14 Sep 2012, 10:56
by Lapo
Qucik question: the others API for clients, like ActionScript, iOS, works the same way? That is better not remove the current listener on dispatch() method?

Same thing, yes

By the way, I was developing an Application that I had the need to remove one listener because of memory and to give room to another listener. I'm already doing another implementation to workaround it.

Really? One listeners gives you memory problem? I mean unless the class is huge it should take a few KBytes.