Skip to content

Commit

Permalink
[ECO-5033] fix: race condition when callingAblyRealtime#connect() o…
Browse files Browse the repository at this point in the history
…n terminated state
  • Loading branch information
ttypic committed Oct 14, 2024
1 parent 5155516 commit 29c1395
Showing 1 changed file with 24 additions and 3 deletions.
27 changes: 24 additions & 3 deletions lib/src/main/java/io/ably/lib/transport/ConnectionManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ public class ConnectionManager implements ConnectListener {
static ErrorInfo REASON_REFUSED = new ErrorInfo("Access refused", 401, 40100);
static ErrorInfo REASON_TOO_BIG = new ErrorInfo("Connection closed; message too large", 400, 40000);

/**
* Flag that indicating that we entered termination state
*/
private boolean terminating = false;

/**
* Methods on the channels map owned by the {@link AblyRealtime} instance
* which the {@link ConnectionManager} needs access to.
Expand Down Expand Up @@ -696,6 +701,8 @@ public void run() {
/* indicate that this thread is committed to die */
handlerThread = null;
stopConnectivityListener();
terminating = false;
ConnectionManager.this.notifyAll();
return;
}

Expand Down Expand Up @@ -790,7 +797,13 @@ public synchronized State getConnectionState() {
public synchronized void connect() {
/* connect() is the only action that will bring the ConnectionManager out of a terminal currentState */
if(currentState.terminal || currentState.state == ConnectionState.initialized) {
startup();
try {
startup();
} catch(InterruptedException e) {
Thread.currentThread().interrupt();
Log.e(TAG, "Failed to start up connection", e);
return;
}
}
requestState(ConnectionState.connecting);
}
Expand Down Expand Up @@ -853,6 +866,7 @@ private synchronized ConnectionStateChange setState(ITransport transport, StateI
Log.v(TAG, "setState(): setting " + newState.state + "; reason " + reason);
ConnectionStateChange change = new ConnectionStateChange(currentState.state, newConnectionState, newState.timeout, reason);
currentState = newState;
terminating = currentState.terminal;
stateError = reason;

return change;
Expand Down Expand Up @@ -1338,10 +1352,17 @@ private void onHeartbeat(ProtocolMessage message) {
* ConnectionManager lifecycle
******************************/

private synchronized void startup() {
if(handlerThread == null) {
private synchronized void startup() throws InterruptedException {
while (terminating) {
Log.v(TAG, "Waiting for termination action to clean up handler thread");
wait();
}

if (handlerThread == null) {
(handlerThread = new Thread(new ActionHandler())).start();
startConnectivityListener();
} else {
Log.v(TAG, "`connect()` has been called twice on uninitialized or terminal state");
}
}

Expand Down

0 comments on commit 29c1395

Please sign in to comment.