Fixed two potential race conditions in the DefaultClient.

In one case, closing a client while it was already closing on
another thread (say because the server is shutting down and
you are exiting at the same time) would cause an NPE if you
caught it just right.  Now the thing checking and setting the
connection state is synchronized to avoid the race.

The other more subtle one was caused by sending out the 'connected'
event before the services were all started.  It's quite common for
application code to start doing stuff when the 'connected' event comes
through like sending messages and stuff.  If the services hadn't been
fully started then even the serializers might not be registered yet...
and that = bad.

Now the client doesn't send the 'connected' event until the services
are started.  This should be safe and one could argue that it's more
'correct' but there is some small chance that it screws up certain
use-cases.  However, if a real use-case comes up that's not solved by
a service then we can always add some kind of prestarted event.
experimental
Paul Speed 9 years ago
parent d8d7d061f6
commit 6cb691592d
  1. 15
      jme3-networking/src/main/java/com/jme3/network/base/DefaultClient.java

@ -301,6 +301,7 @@ public class DefaultClient implements Client
protected void closeConnections( DisconnectInfo info ) protected void closeConnections( DisconnectInfo info )
{ {
synchronized(this) {
if( !isRunning ) if( !isRunning )
return; return;
@ -324,14 +325,16 @@ public class DefaultClient implements Client
// Just in case we never fully connected // Just in case we never fully connected
connecting.countDown(); connecting.countDown();
fireDisconnected(info);
isRunning = false; isRunning = false;
// Terminate the services // Terminate the services
services.terminate(); services.terminate();
} }
// Make sure we aren't synched while firing events
fireDisconnected(info);
}
@Override @Override
public void addClientStateListener( ClientStateListener listener ) public void addClientStateListener( ClientStateListener listener )
{ {
@ -462,11 +465,17 @@ public class DefaultClient implements Client
this.id = (int)crm.getId(); this.id = (int)crm.getId();
log.log( Level.FINE, "Connection established, id:{0}.", this.id ); log.log( Level.FINE, "Connection established, id:{0}.", this.id );
connecting.countDown(); connecting.countDown();
fireConnected(); //fireConnected();
} else { } else {
// Else it's a message letting us know that the // Else it's a message letting us know that the
// hosted services have been started // hosted services have been started
startServices(); startServices();
// Delay firing 'connected' until the services have all
// been started to avoid odd race conditions. If there is some
// need to get some kind of event before the services have been
// started then we should create a new event step.
fireConnected();
} }
return; return;
} else if( m instanceof ChannelInfoMessage ) { } else if( m instanceof ChannelInfoMessage ) {

Loading…
Cancel
Save