Skip to content

Commit 192e1a8

Browse files
committed
Merge pull request koush#268 from nebulist/upstream-pullrequest
Socket.io configurable reconnect delay, reconnect bugfix, optional randomized reconnect delay, & named/minimized synchronousWorkers threads
2 parents 5530bb8 + 4efe252 commit 192e1a8

3 files changed

Lines changed: 116 additions & 26 deletions

File tree

AndroidAsync/src/com/koushikdutta/async/AsyncServer.java

Lines changed: 55 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,12 @@
3131
import java.util.WeakHashMap;
3232
import java.util.concurrent.ExecutorService;
3333
import java.util.concurrent.Executors;
34+
import java.util.concurrent.LinkedBlockingQueue;
3435
import java.util.concurrent.Semaphore;
36+
import java.util.concurrent.ThreadFactory;
37+
import java.util.concurrent.ThreadPoolExecutor;
38+
import java.util.concurrent.TimeUnit;
39+
import java.util.concurrent.atomic.AtomicInteger;
3540

3641
public class AsyncServer {
3742
public static final String LOGTAG = "NIO";
@@ -85,7 +90,7 @@ public static void post(Handler handler, Runnable runnable) {
8590
catch (Throwable ex) {
8691
}
8792
}
88-
93+
8994
static AsyncServer mInstance = new AsyncServer();
9095
public static AsyncServer getDefault() {
9196
return mInstance;
@@ -114,7 +119,7 @@ private void handleSocket(final AsyncNetworkSocket handler) throws ClosedChannel
114119
ckey.attach(handler);
115120
handler.setup(this, ckey);
116121
}
117-
122+
118123
public void removeAllCallbacks(Object scheduled) {
119124
synchronized (this) {
120125
mQueue.remove(scheduled);
@@ -134,7 +139,7 @@ public void run() {
134139
}
135140
});
136141
}
137-
142+
138143
public Object postDelayed(Runnable runnable, long delay) {
139144
Scheduled s;
140145
synchronized (this) {
@@ -161,11 +166,11 @@ public Object postDelayed(Runnable runnable, long delay) {
161166
}
162167
return s;
163168
}
164-
169+
165170
public Object post(Runnable runnable) {
166171
return postDelayed(runnable, 0);
167172
}
168-
173+
169174
public Object post(final CompletedCallback callback, final Exception e) {
170175
return post(new Runnable() {
171176
@Override
@@ -174,7 +179,7 @@ public void run() {
174179
}
175180
});
176181
}
177-
182+
178183
public void run(final Runnable runnable) {
179184
if (Thread.currentThread() == mAffinity) {
180185
post(runnable);
@@ -263,7 +268,7 @@ public void run() {
263268
catch (Exception e) {
264269
}
265270
}
266-
271+
267272
protected void onDataReceived(int transmitted) {
268273
}
269274

@@ -335,7 +340,7 @@ protected void cancelCleanup() {
335340
SocketChannel socket;
336341
ConnectCallback callback;
337342
}
338-
343+
339344
private ConnectFuture connectResolvedInetSocketAddress(final InetSocketAddress address, final ConnectCallback callback) {
340345
final ConnectFuture cancel = new ConnectFuture();
341346
assert !address.isUnresolved();
@@ -396,7 +401,14 @@ public Cancellable connectSocket(final String host, final int port, final Connec
396401
return connectSocket(InetSocketAddress.createUnresolved(host, port), callback);
397402
}
398403

399-
private static ExecutorService synchronousWorkers = Executors.newFixedThreadPool(4);
404+
private static ExecutorService newSynchronousWorkers() {
405+
ThreadFactory tf = new NamedThreadFactory("AsyncServer-worker-");
406+
ThreadPoolExecutor tpe = new ThreadPoolExecutor(1, 4, 10L,
407+
TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), tf);
408+
return tpe;
409+
}
410+
411+
private static ExecutorService synchronousWorkers = newSynchronousWorkers();
400412
public Future<InetAddress[]> getAllByName(final String host) {
401413
final SimpleFuture<InetAddress[]> ret = new SimpleFuture<InetAddress[]>();
402414
synchronousWorkers.execute(new Runnable() {
@@ -487,7 +499,7 @@ public void run() {
487499
});
488500
return handler;
489501
}
490-
502+
491503
public AsyncDatagramSocket connectDatagram(final SocketAddress remote) throws IOException {
492504
final DatagramChannel socket = DatagramChannel.open();
493505
final AsyncDatagramSocket handler = new AsyncDatagramSocket();
@@ -509,7 +521,7 @@ public void run() {
509521
});
510522
return handler;
511523
}
512-
524+
513525
final static WeakHashMap<Thread, AsyncServer> mServers = new WeakHashMap<Thread, AsyncServer>();
514526

515527
private boolean addMe() {
@@ -527,7 +539,7 @@ private boolean addMe() {
527539
public static AsyncServer getCurrentThreadServer() {
528540
return mServers.get(Thread.currentThread());
529541
}
530-
542+
531543
Thread mAffinity;
532544
private void run(boolean newThread) {
533545
final SelectorWrapper selector;
@@ -596,10 +608,10 @@ public void run() {
596608
}
597609
return;
598610
}
599-
611+
600612
run(this, selector, queue);
601613
}
602-
614+
603615
private static void run(final AsyncServer server, final SelectorWrapper selector, final PriorityQueue<Scheduled> queue) {
604616
// Log.i(LOGTAG, "****AsyncServer is starting.****");
605617
// at this point, this local queue and selector are owned
@@ -666,11 +678,11 @@ private static void shutdownEverything(SelectorWrapper selector) {
666678
catch (Exception e) {
667679
}
668680
}
669-
681+
670682
private static final long QUEUE_EMPTY = Long.MAX_VALUE;
671683
private static long lockAndRunQueue(final AsyncServer server, final PriorityQueue<Scheduled> queue) {
672684
long wait = QUEUE_EMPTY;
673-
685+
674686
// find the first item we can actually run
675687
while (true) {
676688
Scheduled run = null;
@@ -689,10 +701,10 @@ private static long lockAndRunQueue(final AsyncServer server, final PriorityQueu
689701
}
690702
}
691703
}
692-
704+
693705
if (run == null)
694706
break;
695-
707+
696708
run.runnable.run();
697709
}
698710

@@ -834,11 +846,11 @@ public void run() {
834846
}
835847
});
836848
}
837-
849+
838850
public Thread getAffinity() {
839851
return mAffinity;
840852
}
841-
853+
842854
public boolean isAffinityThread() {
843855
return mAffinity == Thread.currentThread();
844856
}
@@ -847,4 +859,27 @@ public boolean isAffinityThreadOrStopped() {
847859
Thread affinity = mAffinity;
848860
return affinity == null || affinity == Thread.currentThread();
849861
}
862+
863+
private static class NamedThreadFactory implements ThreadFactory {
864+
private final ThreadGroup group;
865+
private final AtomicInteger threadNumber = new AtomicInteger(1);
866+
private final String namePrefix;
867+
868+
NamedThreadFactory(String namePrefix) {
869+
SecurityManager s = System.getSecurityManager();
870+
group = (s != null) ? s.getThreadGroup() :
871+
Thread.currentThread().getThreadGroup();
872+
this.namePrefix = namePrefix;
873+
}
874+
875+
public Thread newThread(Runnable r) {
876+
Thread t = new Thread(group, r,
877+
namePrefix + threadNumber.getAndIncrement(), 0);
878+
if (t.isDaemon()) t.setDaemon(false);
879+
if (t.getPriority() != Thread.NORM_PRIORITY) {
880+
t.setPriority(Thread.NORM_PRIORITY);
881+
}
882+
return t;
883+
}
884+
}
850885
}

AndroidAsync/src/com/koushikdutta/async/http/socketio/SocketIOConnection.java

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,15 @@
3030
class SocketIOConnection {
3131
AsyncHttpClient httpClient;
3232
int heartbeat;
33+
long reconnectDelay;
3334
ArrayList<SocketIOClient> clients = new ArrayList<SocketIOClient>();
3435
SocketIOTransport transport;
3536
SocketIORequest request;
3637

3738
public SocketIOConnection(AsyncHttpClient httpClient, SocketIORequest request) {
3839
this.httpClient = httpClient;
3940
this.request = request;
41+
this.reconnectDelay = this.request.config.reconnectDelay;
4042
}
4143

4244
public boolean isConnected() {
@@ -104,7 +106,7 @@ void reconnect(final DependentCancellable child) {
104106

105107
request.logi("Reconnecting socket.io");
106108

107-
Cancellable connecting = httpClient.executeString(request, null)
109+
connecting = httpClient.executeString(request, null)
108110
.then(new TransformFuture<SocketIOTransport, String>() {
109111
@Override
110112
protected void transform(String result) throws Exception {
@@ -157,7 +159,7 @@ public void onCompleted(Exception e, SocketIOTransport result) {
157159
return;
158160
}
159161

160-
reconnectDelay = 1000L;
162+
reconnectDelay = request.config.reconnectDelay;
161163
SocketIOConnection.this.transport = result;
162164
attach();
163165
}
@@ -215,11 +217,23 @@ private void delayReconnect() {
215217
public void run() {
216218
reconnect(null);
217219
}
218-
}, reconnectDelay);
219-
reconnectDelay *= 2;
220+
}, nextReconnectDelay(reconnectDelay));
221+
222+
reconnectDelay = reconnectDelay * 2;
223+
if (request.config.reconnectDelayMax > 0L) {
224+
reconnectDelay = Math.min(reconnectDelay, request.config.reconnectDelayMax);
225+
}
226+
}
227+
228+
private long nextReconnectDelay(long targetDelay) {
229+
if (targetDelay < 2L || targetDelay > (Long.MAX_VALUE >> 1) ||
230+
!request.config.randomizeReconnectDelay)
231+
{
232+
return targetDelay;
233+
}
234+
return (targetDelay >> 1) + (long) (targetDelay * Math.random());
220235
}
221236

222-
long reconnectDelay = 1000L;
223237
private void reportDisconnect(final Exception ex) {
224238
if (ex != null) {
225239
request.loge("socket.io disconnected", ex);

AndroidAsync/src/com/koushikdutta/async/http/socketio/SocketIORequest.java

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.koushikdutta.async.http.socketio;
22

33
import android.net.Uri;
4-
import android.text.TextUtils;
54

65
import com.koushikdutta.async.http.AsyncHttpPost;
76

@@ -10,6 +9,11 @@ public SocketIORequest(String uri) {
109
this(uri, "");
1110
}
1211

12+
Config config;
13+
public Config getConfig() {
14+
return config;
15+
}
16+
1317
String endpoint;
1418
public String getEndpoint() {
1519
return endpoint;
@@ -25,8 +29,45 @@ public SocketIORequest(String uri, String endpoint) {
2529
}
2630

2731
public SocketIORequest(String uri, String endpoint, String query) {
32+
this(uri, endpoint, query, null);
33+
}
34+
35+
public SocketIORequest(String uri, String endpoint, String query, Config config) {
2836
super(Uri.parse(uri + (query == null ? "" : "?" + query)).buildUpon().encodedPath("/socket.io/1/").build().toString());
37+
this.config = (config != null) ? config : new Config();
2938
this.endpoint = endpoint;
3039
this.query = query;
3140
}
41+
42+
public static class Config {
43+
boolean randomizeReconnectDelay = false;
44+
public void setRandomizeReconnectDelay(boolean randomizeReconnectDelay) {
45+
this.randomizeReconnectDelay = randomizeReconnectDelay;
46+
}
47+
public boolean isRandomizeReconnectDelay() {
48+
return randomizeReconnectDelay;
49+
}
50+
51+
long reconnectDelay = 1000L;
52+
public void setReconnectDelay(long reconnectDelay) {
53+
if (reconnectDelay < 0L) {
54+
throw new IllegalArgumentException("reconnectDelay must be >= 0");
55+
}
56+
this.reconnectDelay = reconnectDelay;
57+
}
58+
public long getReconnectDelay() {
59+
return reconnectDelay;
60+
}
61+
62+
long reconnectDelayMax = 0L;
63+
public void setReconnectDelayMax(long reconnectDelayMax) {
64+
if (reconnectDelay < 0L) {
65+
throw new IllegalArgumentException("reconnectDelayMax must be >= 0");
66+
}
67+
this.reconnectDelayMax = reconnectDelayMax;
68+
}
69+
public long getReconnectDelayMax() {
70+
return reconnectDelayMax;
71+
}
72+
}
3273
}

0 commit comments

Comments
 (0)