Java SE API Concurrency problem

Post here your questions about the Java client / Android API for SFS2X

Moderators: Lapo, Bax

rgipd
Posts: 3
Joined: 11 Sep 2012, 11:50

Java SE API Concurrency problem

Postby rgipd » 11 Sep 2012, 12:52

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.
User avatar
Lapo
Site Admin
Posts: 23008
Joined: 21 Mar 2005, 09:50
Location: Italy

Re: Java SE API Concurrency problem

Postby Lapo » 13 Sep 2012, 18:09

You don't need to remove event listeners as they are dispatched. The correct practice is to release the listeners upon disconnection.
Lapo
--
gotoAndPlay()
...addicted to flash games
rgipd
Posts: 3
Joined: 11 Sep 2012, 11:50

Re: Java SE API Concurrency problem

Postby rgipd » 13 Sep 2012, 18:32

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.
User avatar
Lapo
Site Admin
Posts: 23008
Joined: 21 Mar 2005, 09:50
Location: Italy

Re: Java SE API Concurrency problem

Postby Lapo » 14 Sep 2012, 08:12

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
Lapo

--

gotoAndPlay()

...addicted to flash games
rgipd
Posts: 3
Joined: 11 Sep 2012, 11:50

Re: Java SE API Concurrency problem

Postby rgipd » 14 Sep 2012, 10:28

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.
User avatar
Lapo
Site Admin
Posts: 23008
Joined: 21 Mar 2005, 09:50
Location: Italy

Re: Java SE API Concurrency problem

Postby Lapo » 14 Sep 2012, 10:56

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.
Lapo

--

gotoAndPlay()

...addicted to flash games

Return to “SFS2X Java / Android API”

Who is online

Users browsing this forum: No registered users and 16 guests